LCOV - code coverage report
Current view: directory - layout/generic - nsTextFrameThebes.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 3511 12 0.3 %
Date: 2012-06-02 Functions: 286 4 1.4 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is mozilla.org code.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * Netscape Communications Corporation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 1998
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   Robert O'Callahan <robert@ocallahan.org>
      24                 :  *   Roger B. Sidje <rbs@maths.uq.edu.au>
      25                 :  *   Pierre Phaneuf <pp@ludusdesign.com>
      26                 :  *   Prabhat Hegde <prabhat.hegde@sun.com>
      27                 :  *   Tomi Leppikangas <tomi.leppikangas@oulu.fi>
      28                 :  *   Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
      29                 :  *   Daniel Glazman <glazman@netscape.com>
      30                 :  *   Neil Deakin <neil@mozdevgroup.com>
      31                 :  *   Masayuki Nakano <masayuki@d-toybox.com>
      32                 :  *   Mats Palmgren <matspal@gmail.com>
      33                 :  *   Uri Bernstein <uriber@gmail.com>
      34                 :  *   Stephen Blackheath <entangled.mooched.stephen@blacksapphire.com>
      35                 :  *   Michael Ventnor <m.ventnor@gmail.com>
      36                 :  *   Ehsan Akhgari <ehsan.akhgari@gmail.com>
      37                 :  *
      38                 :  * Alternatively, the contents of this file may be used under the terms of
      39                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      40                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      41                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      42                 :  * of those above. If you wish to allow use of your version of this file only
      43                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      44                 :  * use your version of this file under the terms of the MPL, indicate your
      45                 :  * decision by deleting the provisions above and replace them with the notice
      46                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      47                 :  * the provisions above, a recipient may use your version of this file under
      48                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      49                 :  *
      50                 :  * ***** END LICENSE BLOCK ***** */
      51                 : 
      52                 : /* rendering object for textual content of elements */
      53                 : 
      54                 : #include "mozilla/Util.h"
      55                 : 
      56                 : #include "nsCOMPtr.h"
      57                 : #include "nsHTMLParts.h"
      58                 : #include "nsCRT.h"
      59                 : #include "nsSplittableFrame.h"
      60                 : #include "nsLineLayout.h"
      61                 : #include "nsString.h"
      62                 : #include "nsUnicharUtils.h"
      63                 : #include "nsPresContext.h"
      64                 : #include "nsIContent.h"
      65                 : #include "nsStyleConsts.h"
      66                 : #include "nsStyleContext.h"
      67                 : #include "nsCoord.h"
      68                 : #include "nsRenderingContext.h"
      69                 : #include "nsIPresShell.h"
      70                 : #include "nsITimer.h"
      71                 : #include "nsTArray.h"
      72                 : #include "nsIDocument.h"
      73                 : #include "nsCSSPseudoElements.h"
      74                 : #include "nsCSSFrameConstructor.h"
      75                 : #include "nsCompatibility.h"
      76                 : #include "nsCSSColorUtils.h"
      77                 : #include "nsLayoutUtils.h"
      78                 : #include "nsDisplayList.h"
      79                 : #include "nsFrame.h"
      80                 : #include "nsPlaceholderFrame.h"
      81                 : #include "nsTextFrameUtils.h"
      82                 : #include "nsTextRunTransformations.h"
      83                 : #include "nsFrameManager.h"
      84                 : #include "nsTextFrameTextRunCache.h"
      85                 : #include "nsExpirationTracker.h"
      86                 : #include "nsTextFrame.h"
      87                 : #include "nsUnicodeProperties.h"
      88                 : #include "nsUnicharUtilCIID.h"
      89                 : 
      90                 : #include "nsTextFragment.h"
      91                 : #include "nsGkAtoms.h"
      92                 : #include "nsFrameSelection.h"
      93                 : #include "nsISelection.h"
      94                 : #include "nsIDOMRange.h"
      95                 : #include "nsRange.h"
      96                 : #include "nsCSSRendering.h"
      97                 : #include "nsContentUtils.h"
      98                 : #include "nsLineBreaker.h"
      99                 : #include "nsIWordBreaker.h"
     100                 : #include "nsGenericDOMDataNode.h"
     101                 : 
     102                 : #include "nsILineIterator.h"
     103                 : 
     104                 : #include "nsIServiceManager.h"
     105                 : #ifdef ACCESSIBILITY
     106                 : #include "nsAccessibilityService.h"
     107                 : #endif
     108                 : #include "nsAutoPtr.h"
     109                 : 
     110                 : #include "nsBidiUtils.h"
     111                 : #include "nsPrintfCString.h"
     112                 : 
     113                 : #include "gfxFont.h"
     114                 : #include "gfxContext.h"
     115                 : #include "gfxImageSurface.h"
     116                 : 
     117                 : #include "mozilla/dom/Element.h"
     118                 : #include "mozilla/Util.h" // for DebugOnly
     119                 : #include "mozilla/LookAndFeel.h"
     120                 : 
     121                 : #ifdef NS_DEBUG
     122                 : #undef NOISY_BLINK
     123                 : #undef NOISY_REFLOW
     124                 : #undef NOISY_TRIM
     125                 : #else
     126                 : #undef NOISY_BLINK
     127                 : #undef NOISY_REFLOW
     128                 : #undef NOISY_TRIM
     129                 : #endif
     130                 : 
     131                 : using namespace mozilla;
     132                 : using namespace mozilla::dom;
     133                 : 
     134               0 : struct TabWidth {
     135               0 :   TabWidth(PRUint32 aOffset, PRUint32 aWidth)
     136               0 :     : mOffset(aOffset), mWidth(float(aWidth))
     137               0 :   { }
     138                 : 
     139                 :   PRUint32 mOffset; // character offset within the text covered by the
     140                 :                     // PropertyProvider
     141                 :   float    mWidth;  // extra space to be added at this position (in app units)
     142                 : };
     143                 : 
     144               0 : struct TabWidthStore {
     145               0 :   TabWidthStore()
     146               0 :     : mLimit(0)
     147               0 :   { }
     148                 : 
     149                 :   // Apply tab widths to the aSpacing array, which corresponds to characters
     150                 :   // beginning at aOffset and has length aLength. (Width records outside this
     151                 :   // range will be ignored.)
     152                 :   void ApplySpacing(gfxTextRun::PropertyProvider::Spacing *aSpacing,
     153                 :                     PRUint32 aOffset, PRUint32 aLength);
     154                 : 
     155                 :   PRUint32           mLimit;  // offset up to which tabs have been measured;
     156                 :                               // positions beyond this have not been calculated
     157                 :                               // yet but may be appended if needed later
     158                 :   nsTArray<TabWidth> mWidths; // (offset,width) records for each tab character
     159                 : };
     160                 : 
     161                 : void
     162               0 : TabWidthStore::ApplySpacing(gfxTextRun::PropertyProvider::Spacing *aSpacing,
     163                 :                             PRUint32 aOffset, PRUint32 aLength)
     164                 : {
     165                 :   // We could binary-search for the first record that falls within the range,
     166                 :   // but as the number of tabs is normally small and we usually process them
     167                 :   // sequentially from the beginning of the line, it doesn't seem worth doing
     168                 :   // at this point.
     169               0 :   for (PRUint32 i = 0; i < mWidths.Length(); ++i) {
     170               0 :     TabWidth& tw = mWidths[i];
     171               0 :     if (tw.mOffset < aOffset) {
     172               0 :       continue;
     173                 :     }
     174               0 :     if (tw.mOffset - aOffset >= aLength) {
     175               0 :       break;
     176                 :     }
     177               0 :     aSpacing[tw.mOffset - aOffset].mAfter += tw.mWidth;
     178                 :   }
     179               0 : }
     180                 : 
     181               0 : static void DestroyTabWidth(void* aPropertyValue)
     182                 : {
     183               0 :   delete static_cast<TabWidthStore*>(aPropertyValue);
     184               0 : }
     185                 : 
     186               0 : NS_DECLARE_FRAME_PROPERTY(TabWidthProperty, DestroyTabWidth)
     187                 : 
     188               0 : NS_DECLARE_FRAME_PROPERTY(OffsetToFrameProperty, nsnull)
     189                 : 
     190                 : // text runs are destroyed by the text run cache
     191               0 : NS_DECLARE_FRAME_PROPERTY(UninflatedTextRunProperty, nsnull)
     192                 : 
     193               0 : NS_DECLARE_FRAME_PROPERTY(FontSizeInflationProperty, nsnull)
     194                 : 
     195                 : // The following flags are set during reflow
     196                 : 
     197                 : // This bit is set on the first frame in a continuation indicating
     198                 : // that it was chopped short because of :first-letter style.
     199                 : #define TEXT_FIRST_LETTER    NS_FRAME_STATE_BIT(20)
     200                 : // This bit is set on frames that are logically adjacent to the start of the
     201                 : // line (i.e. no prior frame on line with actual displayed in-flow content).
     202                 : #define TEXT_START_OF_LINE   NS_FRAME_STATE_BIT(21)
     203                 : // This bit is set on frames that are logically adjacent to the end of the
     204                 : // line (i.e. no following on line with actual displayed in-flow content).
     205                 : #define TEXT_END_OF_LINE     NS_FRAME_STATE_BIT(22)
     206                 : // This bit is set on frames that end with a hyphenated break.
     207                 : #define TEXT_HYPHEN_BREAK    NS_FRAME_STATE_BIT(23)
     208                 : // This bit is set on frames that trimmed trailing whitespace characters when
     209                 : // calculating their width during reflow.
     210                 : #define TEXT_TRIMMED_TRAILING_WHITESPACE NS_FRAME_STATE_BIT(24)
     211                 : // This bit is set on frames that have justification enabled. We record
     212                 : // this in a state bit because we don't always have the containing block
     213                 : // easily available to check text-align on.
     214                 : #define TEXT_JUSTIFICATION_ENABLED       NS_FRAME_STATE_BIT(25)
     215                 : // Set this bit if the textframe has overflow area for IME/spellcheck underline.
     216                 : #define TEXT_SELECTION_UNDERLINE_OVERFLOWED NS_FRAME_STATE_BIT(26)
     217                 : 
     218                 : #define TEXT_REFLOW_FLAGS    \
     219                 :   (TEXT_FIRST_LETTER|TEXT_START_OF_LINE|TEXT_END_OF_LINE|TEXT_HYPHEN_BREAK| \
     220                 :    TEXT_TRIMMED_TRAILING_WHITESPACE|TEXT_JUSTIFICATION_ENABLED| \
     221                 :    TEXT_HAS_NONCOLLAPSED_CHARACTERS|TEXT_SELECTION_UNDERLINE_OVERFLOWED)
     222                 : 
     223                 : // Cache bits for IsEmpty().
     224                 : // Set this bit if the textframe is known to be only collapsible whitespace.
     225                 : #define TEXT_IS_ONLY_WHITESPACE    NS_FRAME_STATE_BIT(27)
     226                 : // Set this bit if the textframe is known to be not only collapsible whitespace.
     227                 : #define TEXT_ISNOT_ONLY_WHITESPACE NS_FRAME_STATE_BIT(28)
     228                 : 
     229                 : #define TEXT_WHITESPACE_FLAGS      (TEXT_IS_ONLY_WHITESPACE | \
     230                 :                                     TEXT_ISNOT_ONLY_WHITESPACE)
     231                 : // This bit is set while the frame is registered as a blinking frame.
     232                 : #define TEXT_BLINK_ON              NS_FRAME_STATE_BIT(29)
     233                 : 
     234                 : // Set when this text frame is mentioned in the userdata for a textrun
     235                 : #define TEXT_IN_TEXTRUN_USER_DATA  NS_FRAME_STATE_BIT(30)
     236                 : 
     237                 : // nsTextFrame.h has
     238                 : // #define TEXT_HAS_NONCOLLAPSED_CHARACTERS NS_FRAME_STATE_BIT(31)
     239                 : // #define TEXT_HAS_FONT_INFLATION          NS_FRAME_STATE_BIT(61)
     240                 : 
     241                 : // If true, then this frame is being removed due to a SetLength() on a
     242                 : // previous continuation and the style context of that previous
     243                 : // continuation is the same as this frame's
     244                 : #define TEXT_STYLE_MATCHES_PREV_CONTINUATION NS_FRAME_STATE_BIT(62)
     245                 : 
     246                 : // Whether this frame is cached in the Offset Frame Cache (OffsetToFrameProperty)
     247                 : #define TEXT_IN_OFFSET_CACHE       NS_FRAME_STATE_BIT(63)
     248                 : 
     249                 : /*
     250                 :  * Some general notes
     251                 :  * 
     252                 :  * Text frames delegate work to gfxTextRun objects. The gfxTextRun object
     253                 :  * transforms text to positioned glyphs. It can report the geometry of the
     254                 :  * glyphs and paint them. Text frames configure gfxTextRuns by providing text,
     255                 :  * spacing, language, and other information.
     256                 :  * 
     257                 :  * A gfxTextRun can cover more than one DOM text node. This is necessary to
     258                 :  * get kerning, ligatures and shaping for text that spans multiple text nodes
     259                 :  * but is all the same font. The userdata for a gfxTextRun object is a
     260                 :  * TextRunUserData* or an nsIFrame*.
     261                 :  * 
     262                 :  * We go to considerable effort to make sure things work even if in-flow
     263                 :  * siblings have different style contexts (i.e., first-letter and first-line).
     264                 :  * 
     265                 :  * Our convention is that unsigned integer character offsets are offsets into
     266                 :  * the transformed string. Signed integer character offsets are offsets into
     267                 :  * the DOM string.
     268                 :  * 
     269                 :  * XXX currently we don't handle hyphenated breaks between text frames where the
     270                 :  * hyphen occurs at the end of the first text frame, e.g.
     271                 :  *   <b>Kit&shy;</b>ty
     272                 :  */
     273                 : 
     274                 : /**
     275                 :  * We use an array of these objects to record which text frames
     276                 :  * are associated with the textrun. mStartFrame is the start of a list of
     277                 :  * text frames. Some sequence of its continuations are covered by the textrun.
     278                 :  * A content textnode can have at most one TextRunMappedFlow associated with it
     279                 :  * for a given textrun.
     280                 :  * 
     281                 :  * mDOMOffsetToBeforeTransformOffset is added to DOM offsets for those frames to obtain
     282                 :  * the offset into the before-transformation text of the textrun. It can be
     283                 :  * positive (when a text node starts in the middle of a text run) or
     284                 :  * negative (when a text run starts in the middle of a text node). Of course
     285                 :  * it can also be zero.
     286                 :  */
     287                 : struct TextRunMappedFlow {
     288                 :   nsTextFrame* mStartFrame;
     289                 :   PRInt32      mDOMOffsetToBeforeTransformOffset;
     290                 :   // The text mapped starts at mStartFrame->GetContentOffset() and is this long
     291                 :   PRUint32     mContentLength;
     292                 : };
     293                 : 
     294                 : /**
     295                 :  * This is our user data for the textrun, when textRun->GetFlags() does not
     296                 :  * have TEXT_IS_SIMPLE_FLOW set. When TEXT_IS_SIMPLE_FLOW is set, there is
     297                 :  * just one flow, the textrun's user data pointer is a pointer to mStartFrame
     298                 :  * for that flow, mDOMOffsetToBeforeTransformOffset is zero, and mContentLength
     299                 :  * is the length of the text node.
     300                 :  */
     301                 : struct TextRunUserData {
     302                 :   TextRunMappedFlow* mMappedFlows;
     303                 :   PRUint32           mMappedFlowCount;
     304                 :   PRUint32           mLastFlowIndex;
     305                 : };
     306                 : 
     307                 : /**
     308                 :  * This helper object computes colors used for painting, and also IME
     309                 :  * underline information. The data is computed lazily and cached as necessary.
     310                 :  * These live for just the duration of one paint operation.
     311                 :  */
     312                 : class nsTextPaintStyle {
     313                 : public:
     314                 :   nsTextPaintStyle(nsTextFrame* aFrame);
     315                 : 
     316                 :   nscolor GetTextColor();
     317                 :   /**
     318                 :    * Compute the colors for normally-selected text. Returns false if
     319                 :    * the normal selection is not being displayed.
     320                 :    */
     321                 :   bool GetSelectionColors(nscolor* aForeColor,
     322                 :                             nscolor* aBackColor);
     323                 :   void GetHighlightColors(nscolor* aForeColor,
     324                 :                           nscolor* aBackColor);
     325                 :   void GetURLSecondaryColor(nscolor* aForeColor);
     326                 :   void GetIMESelectionColors(PRInt32  aIndex,
     327                 :                              nscolor* aForeColor,
     328                 :                              nscolor* aBackColor);
     329                 :   // if this returns false, we don't need to draw underline.
     330                 :   bool GetSelectionUnderlineForPaint(PRInt32  aIndex,
     331                 :                                        nscolor* aLineColor,
     332                 :                                        float*   aRelativeSize,
     333                 :                                        PRUint8* aStyle);
     334                 : 
     335                 :   // if this returns false, we don't need to draw underline.
     336                 :   static bool GetSelectionUnderline(nsPresContext* aPresContext,
     337                 :                                       PRInt32 aIndex,
     338                 :                                       nscolor* aLineColor,
     339                 :                                       float* aRelativeSize,
     340                 :                                       PRUint8* aStyle);
     341                 : 
     342               0 :   nsPresContext* PresContext() const { return mPresContext; }
     343                 : 
     344                 :   enum {
     345                 :     eIndexRawInput = 0,
     346                 :     eIndexSelRawText,
     347                 :     eIndexConvText,
     348                 :     eIndexSelConvText,
     349                 :     eIndexSpellChecker
     350                 :   };
     351                 : 
     352               0 :   static PRInt32 GetUnderlineStyleIndexForSelectionType(PRInt32 aSelectionType)
     353                 :   {
     354               0 :     switch (aSelectionType) {
     355                 :       case nsISelectionController::SELECTION_IME_RAWINPUT:
     356               0 :         return eIndexRawInput;
     357                 :       case nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT:
     358               0 :         return eIndexSelRawText;
     359                 :       case nsISelectionController::SELECTION_IME_CONVERTEDTEXT:
     360               0 :         return eIndexConvText;
     361                 :       case nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT:
     362               0 :         return eIndexSelConvText;
     363                 :       case nsISelectionController::SELECTION_SPELLCHECK:
     364               0 :         return eIndexSpellChecker;
     365                 :       default:
     366               0 :         NS_WARNING("non-IME selection type");
     367               0 :         return eIndexRawInput;
     368                 :     }
     369                 :   }
     370                 : 
     371                 : protected:
     372                 :   nsTextFrame*   mFrame;
     373                 :   nsPresContext* mPresContext;
     374                 :   bool           mInitCommonColors;
     375                 :   bool           mInitSelectionColors;
     376                 : 
     377                 :   // Selection data
     378                 : 
     379                 :   PRInt16      mSelectionStatus; // see nsIDocument.h SetDisplaySelection()
     380                 :   nscolor      mSelectionTextColor;
     381                 :   nscolor      mSelectionBGColor;
     382                 : 
     383                 :   // Common data
     384                 : 
     385                 :   PRInt32 mSufficientContrast;
     386                 :   nscolor mFrameBackgroundColor;
     387                 : 
     388                 :   // selection colors and underline info, the colors are resolved colors,
     389                 :   // i.e., the foreground color and background color are swapped if it's needed.
     390                 :   // And also line color will be resolved from them.
     391                 :   struct nsSelectionStyle {
     392                 :     bool mInit;
     393                 :     nscolor mTextColor;
     394                 :     nscolor mBGColor;
     395                 :     nscolor mUnderlineColor;
     396                 :     PRUint8 mUnderlineStyle;
     397                 :     float   mUnderlineRelativeSize;
     398                 :   };
     399                 :   nsSelectionStyle mSelectionStyle[5];
     400                 : 
     401                 :   // Color initializations
     402                 :   void InitCommonColors();
     403                 :   bool InitSelectionColors();
     404                 : 
     405                 :   nsSelectionStyle* GetSelectionStyle(PRInt32 aIndex);
     406                 :   void InitSelectionStyle(PRInt32 aIndex);
     407                 : 
     408                 :   bool EnsureSufficientContrast(nscolor *aForeColor, nscolor *aBackColor);
     409                 : 
     410                 :   nscolor GetResolvedForeColor(nscolor aColor, nscolor aDefaultForeColor,
     411                 :                                nscolor aBackColor);
     412                 : };
     413                 : 
     414                 : static void
     415               0 : DestroyUserData(void* aUserData)
     416                 : {
     417               0 :   TextRunUserData* userData = static_cast<TextRunUserData*>(aUserData);
     418               0 :   if (userData) {
     419               0 :     nsMemory::Free(userData);
     420                 :   }
     421               0 : }
     422                 : 
     423                 : /**
     424                 :  * Remove |aTextRun| from the frame continuation chain starting at
     425                 :  * |aStartContinuation| if non-null, otherwise starting at |aFrame|.
     426                 :  * Unmark |aFrame| as a text run owner if it's the frame we start at.
     427                 :  * Return true if |aStartContinuation| is non-null and was found
     428                 :  * in the next-continuation chain of |aFrame|.
     429                 :  */
     430                 : static bool
     431               0 : ClearAllTextRunReferences(nsTextFrame* aFrame, gfxTextRun* aTextRun,
     432                 :                           nsTextFrame* aStartContinuation)
     433                 : {
     434               0 :   NS_PRECONDITION(aFrame, "");
     435               0 :   NS_PRECONDITION(!aStartContinuation ||
     436                 :                   (!aStartContinuation->GetTextRun(nsTextFrame::eInflated) ||
     437                 :                    aStartContinuation->GetTextRun(nsTextFrame::eInflated) == aTextRun) ||
     438                 :                   (!aStartContinuation->GetTextRun(nsTextFrame::eNotInflated) ||
     439                 :                    aStartContinuation->GetTextRun(nsTextFrame::eNotInflated) == aTextRun),
     440                 :                   "wrong aStartContinuation for this text run");
     441                 : 
     442               0 :   if (!aStartContinuation || aStartContinuation == aFrame) {
     443               0 :     aFrame->RemoveStateBits(TEXT_IN_TEXTRUN_USER_DATA);
     444                 :   } else {
     445               0 :     do {
     446               0 :       NS_ASSERTION(aFrame->GetType() == nsGkAtoms::textFrame, "Bad frame");
     447               0 :       aFrame = static_cast<nsTextFrame*>(aFrame->GetNextContinuation());
     448                 :     } while (aFrame && aFrame != aStartContinuation);
     449                 :   }
     450               0 :   bool found = aStartContinuation == aFrame;
     451               0 :   while (aFrame) {
     452               0 :     NS_ASSERTION(aFrame->GetType() == nsGkAtoms::textFrame, "Bad frame");
     453               0 :     if (!aFrame->RemoveTextRun(aTextRun))
     454               0 :       break;
     455               0 :     aFrame = static_cast<nsTextFrame*>(aFrame->GetNextContinuation());
     456                 :   }
     457               0 :   NS_POSTCONDITION(!found || aStartContinuation, "how did we find null?");
     458               0 :   return found;
     459                 : }
     460                 : 
     461                 : /**
     462                 :  * Kill all references to |aTextRun| starting at |aStartContinuation|.
     463                 :  * It could be referenced by any of its owners, and all their in-flows.
     464                 :  * If |aStartContinuation| is null then process all userdata frames
     465                 :  * and their continuations.
     466                 :  * @note the caller is expected to take care of possibly destroying the
     467                 :  * text run if all userdata frames were reset (userdata is deallocated
     468                 :  * by this function though). The caller can detect this has occured by
     469                 :  * checking |aTextRun->GetUserData() == nsnull|.
     470                 :  */
     471                 : static void
     472               0 : UnhookTextRunFromFrames(gfxTextRun* aTextRun, nsTextFrame* aStartContinuation)
     473                 : {
     474               0 :   if (!aTextRun->GetUserData())
     475               0 :     return;
     476                 : 
     477               0 :   if (aTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW) {
     478               0 :     nsIFrame* userDataFrame = static_cast<nsIFrame*>(aTextRun->GetUserData());
     479                 :     DebugOnly<bool> found =
     480                 :       ClearAllTextRunReferences(static_cast<nsTextFrame*>(userDataFrame),
     481               0 :                                 aTextRun, aStartContinuation);
     482               0 :     NS_ASSERTION(!aStartContinuation || found,
     483                 :                  "aStartContinuation wasn't found in simple flow text run");
     484               0 :     if (!(userDataFrame->GetStateBits() & TEXT_IN_TEXTRUN_USER_DATA)) {
     485               0 :       aTextRun->SetUserData(nsnull);
     486                 :     }
     487                 :   } else {
     488                 :     TextRunUserData* userData =
     489               0 :       static_cast<TextRunUserData*>(aTextRun->GetUserData());
     490               0 :     PRInt32 destroyFromIndex = aStartContinuation ? -1 : 0;
     491               0 :     for (PRUint32 i = 0; i < userData->mMappedFlowCount; ++i) {
     492               0 :       nsTextFrame* userDataFrame = userData->mMappedFlows[i].mStartFrame;
     493                 :       bool found =
     494                 :         ClearAllTextRunReferences(userDataFrame, aTextRun,
     495               0 :                                   aStartContinuation);
     496               0 :       if (found) {
     497               0 :         if (userDataFrame->GetStateBits() & TEXT_IN_TEXTRUN_USER_DATA) {
     498               0 :           destroyFromIndex = i + 1;
     499                 :         }
     500                 :         else {
     501               0 :           destroyFromIndex = i;
     502                 :         }
     503               0 :         aStartContinuation = nsnull;
     504                 :       }
     505                 :     }
     506               0 :     NS_ASSERTION(destroyFromIndex >= 0,
     507                 :                  "aStartContinuation wasn't found in multi flow text run");
     508               0 :     if (destroyFromIndex == 0) {
     509               0 :       DestroyUserData(userData);
     510               0 :       aTextRun->SetUserData(nsnull);
     511                 :     }
     512                 :     else {
     513               0 :       userData->mMappedFlowCount = PRUint32(destroyFromIndex);
     514               0 :       if (userData->mLastFlowIndex >= PRUint32(destroyFromIndex)) {
     515               0 :         userData->mLastFlowIndex = PRUint32(destroyFromIndex) - 1;
     516                 :       }
     517                 :     }
     518                 :   }
     519                 : }
     520                 : 
     521                 : class FrameTextRunCache;
     522                 : 
     523                 : static FrameTextRunCache *gTextRuns = nsnull;
     524                 : 
     525                 : /*
     526                 :  * Cache textruns and expire them after 3*10 seconds of no use.
     527                 :  */
     528                 : class FrameTextRunCache : public nsExpirationTracker<gfxTextRun,3> {
     529                 : public:
     530                 :   enum { TIMEOUT_SECONDS = 10 };
     531            1404 :   FrameTextRunCache()
     532            1404 :       : nsExpirationTracker<gfxTextRun,3>(TIMEOUT_SECONDS*1000) {}
     533            2806 :   ~FrameTextRunCache() {
     534            1403 :     AgeAllGenerations();
     535            1403 :   }
     536                 : 
     537               0 :   void RemoveFromCache(gfxTextRun* aTextRun) {
     538               0 :     if (aTextRun->GetExpirationState()->IsTracked()) {
     539               0 :       RemoveObject(aTextRun);
     540                 :     }
     541               0 :   }
     542                 : 
     543                 :   // This gets called when the timeout has expired on a gfxTextRun
     544               0 :   virtual void NotifyExpired(gfxTextRun* aTextRun) {
     545               0 :     UnhookTextRunFromFrames(aTextRun, nsnull);
     546               0 :     RemoveFromCache(aTextRun);
     547               0 :     delete aTextRun;
     548               0 :   }
     549                 : };
     550                 : 
     551                 : // Helper to create a textrun and remember it in the textframe cache,
     552                 : // for either 8-bit or 16-bit text strings
     553                 : template<typename T>
     554                 : gfxTextRun *
     555               0 : MakeTextRun(const T *aText, PRUint32 aLength,
     556                 :             gfxFontGroup *aFontGroup, const gfxFontGroup::Parameters* aParams,
     557                 :             PRUint32 aFlags)
     558                 : {
     559                 :     nsAutoPtr<gfxTextRun> textRun(aFontGroup->MakeTextRun(aText, aLength,
     560               0 :                                                           aParams, aFlags));
     561               0 :     if (!textRun) {
     562               0 :         return nsnull;
     563                 :     }
     564               0 :     nsresult rv = gTextRuns->AddObject(textRun);
     565               0 :     if (NS_FAILED(rv)) {
     566               0 :         gTextRuns->RemoveFromCache(textRun);
     567               0 :         return nsnull;
     568                 :     }
     569                 : #ifdef NOISY_BIDI
     570                 :     printf("Created textrun\n");
     571                 : #endif
     572               0 :     return textRun.forget();
     573                 : }
     574                 : 
     575                 : void
     576            1404 : nsTextFrameTextRunCache::Init() {
     577            1404 :     gTextRuns = new FrameTextRunCache();
     578            1404 : }
     579                 : 
     580                 : void
     581            1403 : nsTextFrameTextRunCache::Shutdown() {
     582            1403 :     delete gTextRuns;
     583            1403 :     gTextRuns = nsnull;
     584            1403 : }
     585                 : 
     586               0 : PRInt32 nsTextFrame::GetContentEnd() const {
     587               0 :   nsTextFrame* next = static_cast<nsTextFrame*>(GetNextContinuation());
     588               0 :   return next ? next->GetContentOffset() : mContent->GetText()->GetLength();
     589                 : }
     590                 : 
     591                 : struct FlowLengthProperty {
     592                 :   PRInt32 mStartOffset;
     593                 :   // The offset of the next fixed continuation after mStartOffset, or
     594                 :   // of the end of the text if there is none
     595                 :   PRInt32 mEndFlowOffset;
     596                 : 
     597               0 :   static void Destroy(void* aObject, nsIAtom* aPropertyName,
     598                 :                       void* aPropertyValue, void* aData)
     599                 :   {
     600                 :     delete static_cast<FlowLengthProperty*>(aPropertyValue);
     601               0 :   }
     602                 : };
     603                 : 
     604               0 : PRInt32 nsTextFrame::GetInFlowContentLength() {
     605               0 :   if (!(mState & NS_FRAME_IS_BIDI)) {
     606               0 :     return mContent->TextLength() - mContentOffset;
     607                 :   }
     608                 : 
     609                 :   FlowLengthProperty* flowLength =
     610               0 :     static_cast<FlowLengthProperty*>(mContent->GetProperty(nsGkAtoms::flowlength));
     611                 : 
     612                 :   /**
     613                 :    * This frame must start inside the cached flow. If the flow starts at
     614                 :    * mContentOffset but this frame is empty, logically it might be before the
     615                 :    * start of the cached flow.
     616                 :    */
     617               0 :   if (flowLength && 
     618                 :       (flowLength->mStartOffset < mContentOffset ||
     619               0 :        (flowLength->mStartOffset == mContentOffset && GetContentEnd() > mContentOffset)) &&
     620                 :       flowLength->mEndFlowOffset > mContentOffset) {
     621                 : #ifdef DEBUG
     622               0 :     NS_ASSERTION(flowLength->mEndFlowOffset >= GetContentEnd(),
     623                 :                  "frame crosses fixed continuation boundary");
     624                 : #endif
     625               0 :     return flowLength->mEndFlowOffset - mContentOffset;
     626                 :   }
     627                 : 
     628               0 :   nsTextFrame* nextBidi = static_cast<nsTextFrame*>(GetLastInFlow()->GetNextContinuation());
     629               0 :   PRInt32 endFlow = nextBidi ? nextBidi->GetContentOffset() : mContent->TextLength();
     630                 : 
     631               0 :   if (!flowLength) {
     632               0 :     flowLength = new FlowLengthProperty;
     633               0 :     if (NS_FAILED(mContent->SetProperty(nsGkAtoms::flowlength, flowLength,
     634                 :                                         FlowLengthProperty::Destroy))) {
     635                 :       delete flowLength;
     636               0 :       flowLength = nsnull;
     637                 :     }
     638                 :   }
     639               0 :   if (flowLength) {
     640               0 :     flowLength->mStartOffset = mContentOffset;
     641               0 :     flowLength->mEndFlowOffset = endFlow;
     642                 :   }
     643                 : 
     644               0 :   return endFlow - mContentOffset;
     645                 : }
     646                 : 
     647                 : // Smarter versions of XP_IS_SPACE.
     648                 : // Unicode is really annoying; sometimes a space character isn't whitespace ---
     649                 : // when it combines with another character
     650                 : // So we have several versions of IsSpace for use in different contexts.
     651                 : 
     652               0 : static bool IsSpaceCombiningSequenceTail(const nsTextFragment* aFrag, PRUint32 aPos)
     653                 : {
     654               0 :   NS_ASSERTION(aPos <= aFrag->GetLength(), "Bad offset");
     655               0 :   if (!aFrag->Is2b())
     656               0 :     return false;
     657                 :   return nsTextFrameUtils::IsSpaceCombiningSequenceTail(
     658               0 :     aFrag->Get2b() + aPos, aFrag->GetLength() - aPos);
     659                 : }
     660                 : 
     661                 : // Check whether aPos is a space for CSS 'word-spacing' purposes
     662               0 : static bool IsCSSWordSpacingSpace(const nsTextFragment* aFrag,
     663                 :                                     PRUint32 aPos, const nsStyleText* aStyleText)
     664                 : {
     665               0 :   NS_ASSERTION(aPos < aFrag->GetLength(), "No text for IsSpace!");
     666                 : 
     667               0 :   PRUnichar ch = aFrag->CharAt(aPos);
     668               0 :   switch (ch) {
     669                 :   case ' ':
     670                 :   case CH_NBSP:
     671               0 :     return !IsSpaceCombiningSequenceTail(aFrag, aPos + 1);
     672                 :   case '\r':
     673               0 :   case '\t': return !aStyleText->WhiteSpaceIsSignificant();
     674               0 :   case '\n': return !aStyleText->NewlineIsSignificant();
     675               0 :   default: return false;
     676                 :   }
     677                 : }
     678                 : 
     679                 : // Check whether the string aChars/aLength starts with space that's
     680                 : // trimmable according to CSS 'white-space:normal/nowrap'. 
     681               0 : static bool IsTrimmableSpace(const PRUnichar* aChars, PRUint32 aLength)
     682                 : {
     683               0 :   NS_ASSERTION(aLength > 0, "No text for IsSpace!");
     684                 : 
     685               0 :   PRUnichar ch = *aChars;
     686               0 :   if (ch == ' ')
     687               0 :     return !nsTextFrameUtils::IsSpaceCombiningSequenceTail(aChars + 1, aLength - 1);
     688               0 :   return ch == '\t' || ch == '\f' || ch == '\n' || ch == '\r';
     689                 : }
     690                 : 
     691                 : // Check whether the character aCh is trimmable according to CSS
     692                 : // 'white-space:normal/nowrap'
     693               0 : static bool IsTrimmableSpace(char aCh)
     694                 : {
     695               0 :   return aCh == ' ' || aCh == '\t' || aCh == '\f' || aCh == '\n' || aCh == '\r';
     696                 : }
     697                 : 
     698               0 : static bool IsTrimmableSpace(const nsTextFragment* aFrag, PRUint32 aPos,
     699                 :                                const nsStyleText* aStyleText)
     700                 : {
     701               0 :   NS_ASSERTION(aPos < aFrag->GetLength(), "No text for IsSpace!");
     702                 : 
     703               0 :   switch (aFrag->CharAt(aPos)) {
     704               0 :   case ' ': return !aStyleText->WhiteSpaceIsSignificant() &&
     705               0 :                    !IsSpaceCombiningSequenceTail(aFrag, aPos + 1);
     706               0 :   case '\n': return !aStyleText->NewlineIsSignificant();
     707                 :   case '\t':
     708                 :   case '\r':
     709               0 :   case '\f': return !aStyleText->WhiteSpaceIsSignificant();
     710               0 :   default: return false;
     711                 :   }
     712                 : }
     713                 : 
     714               0 : static bool IsSelectionSpace(const nsTextFragment* aFrag, PRUint32 aPos)
     715                 : {
     716               0 :   NS_ASSERTION(aPos < aFrag->GetLength(), "No text for IsSpace!");
     717               0 :   PRUnichar ch = aFrag->CharAt(aPos);
     718               0 :   if (ch == ' ' || ch == CH_NBSP)
     719               0 :     return !IsSpaceCombiningSequenceTail(aFrag, aPos + 1);
     720               0 :   return ch == '\t' || ch == '\n' || ch == '\f' || ch == '\r';
     721                 : }
     722                 : 
     723                 : // Count the amount of trimmable whitespace (as per CSS
     724                 : // 'white-space:normal/nowrap') in a text fragment. The first
     725                 : // character is at offset aStartOffset; the maximum number of characters
     726                 : // to check is aLength. aDirection is -1 or 1 depending on whether we should
     727                 : // progress backwards or forwards.
     728                 : static PRUint32
     729               0 : GetTrimmableWhitespaceCount(const nsTextFragment* aFrag,
     730                 :                             PRInt32 aStartOffset, PRInt32 aLength,
     731                 :                             PRInt32 aDirection)
     732                 : {
     733               0 :   PRInt32 count = 0;
     734               0 :   if (aFrag->Is2b()) {
     735               0 :     const PRUnichar* str = aFrag->Get2b() + aStartOffset;
     736               0 :     PRInt32 fragLen = aFrag->GetLength() - aStartOffset;
     737               0 :     for (; count < aLength; ++count) {
     738               0 :       if (!IsTrimmableSpace(str, fragLen))
     739               0 :         break;
     740               0 :       str += aDirection;
     741               0 :       fragLen -= aDirection;
     742                 :     }
     743                 :   } else {
     744               0 :     const char* str = aFrag->Get1b() + aStartOffset;
     745               0 :     for (; count < aLength; ++count) {
     746               0 :       if (!IsTrimmableSpace(*str))
     747               0 :         break;
     748               0 :       str += aDirection;
     749                 :     }
     750                 :   }
     751               0 :   return count;
     752                 : }
     753                 : 
     754                 : static bool
     755               0 : IsAllWhitespace(const nsTextFragment* aFrag, bool aAllowNewline)
     756                 : {
     757               0 :   if (aFrag->Is2b())
     758               0 :     return false;
     759               0 :   PRInt32 len = aFrag->GetLength();
     760               0 :   const char* str = aFrag->Get1b();
     761               0 :   for (PRInt32 i = 0; i < len; ++i) {
     762               0 :     char ch = str[i];
     763               0 :     if (ch == ' ' || ch == '\t' || ch == '\r' || (ch == '\n' && aAllowNewline))
     764               0 :       continue;
     765               0 :     return false;
     766                 :   }
     767               0 :   return true;
     768                 : }
     769                 : 
     770                 : /**
     771                 :  * This class accumulates state as we scan a paragraph of text. It detects
     772                 :  * textrun boundaries (changes from text to non-text, hard
     773                 :  * line breaks, and font changes) and builds a gfxTextRun at each boundary.
     774                 :  * It also detects linebreaker run boundaries (changes from text to non-text,
     775                 :  * and hard line breaks) and at each boundary runs the linebreaker to compute
     776                 :  * potential line breaks. It also records actual line breaks to store them in
     777                 :  * the textruns.
     778                 :  */
     779                 : class BuildTextRunsScanner {
     780                 : public:
     781               0 :   BuildTextRunsScanner(nsPresContext* aPresContext, gfxContext* aContext,
     782                 :       nsIFrame* aLineContainer, nsTextFrame::TextRunType aWhichTextRun,
     783                 :       float aInflation) :
     784                 :     mCurrentFramesAllSameTextRun(nsnull),
     785                 :     mContext(aContext),
     786                 :     mLineContainer(aLineContainer),
     787                 :     mInflation(aInflation),
     788               0 :     mBidiEnabled(aPresContext->BidiEnabled()),
     789                 :     mSkipIncompleteTextRuns(false),
     790                 :     mWhichTextRun(aWhichTextRun),
     791                 :     mNextRunContextInfo(nsTextFrameUtils::INCOMING_NONE),
     792               0 :     mCurrentRunContextInfo(nsTextFrameUtils::INCOMING_NONE) {
     793               0 :     ResetRunInfo();
     794               0 :   }
     795               0 :   ~BuildTextRunsScanner() {
     796               0 :     NS_ASSERTION(mBreakSinks.IsEmpty(), "Should have been cleared");
     797               0 :     NS_ASSERTION(mTextRunsToDelete.IsEmpty(), "Should have been cleared");
     798               0 :     NS_ASSERTION(mLineBreakBeforeFrames.IsEmpty(), "Should have been cleared");
     799               0 :     NS_ASSERTION(mMappedFlows.IsEmpty(), "Should have been cleared");
     800               0 :   }
     801                 : 
     802               0 :   void SetAtStartOfLine() {
     803               0 :     mStartOfLine = true;
     804               0 :     mCanStopOnThisLine = false;
     805               0 :   }
     806               0 :   void SetSkipIncompleteTextRuns(bool aSkip) {
     807               0 :     mSkipIncompleteTextRuns = aSkip;
     808               0 :   }
     809               0 :   void SetCommonAncestorWithLastFrame(nsIFrame* aFrame) {
     810               0 :     mCommonAncestorWithLastFrame = aFrame;
     811               0 :   }
     812               0 :   bool CanStopOnThisLine() {
     813               0 :     return mCanStopOnThisLine;
     814                 :   }
     815                 :   nsIFrame* GetCommonAncestorWithLastFrame() {
     816                 :     return mCommonAncestorWithLastFrame;
     817                 :   }
     818               0 :   void LiftCommonAncestorWithLastFrameToParent(nsIFrame* aFrame) {
     819               0 :     if (mCommonAncestorWithLastFrame &&
     820               0 :         mCommonAncestorWithLastFrame->GetParent() == aFrame) {
     821               0 :       mCommonAncestorWithLastFrame = aFrame;
     822                 :     }
     823               0 :   }
     824                 :   void ScanFrame(nsIFrame* aFrame);
     825                 :   bool IsTextRunValidForMappedFlows(gfxTextRun* aTextRun);
     826                 :   void FlushFrames(bool aFlushLineBreaks, bool aSuppressTrailingBreak);
     827                 :   void FlushLineBreaks(gfxTextRun* aTrailingTextRun);
     828               0 :   void ResetRunInfo() {
     829               0 :     mLastFrame = nsnull;
     830               0 :     mMappedFlows.Clear();
     831               0 :     mLineBreakBeforeFrames.Clear();
     832               0 :     mMaxTextLength = 0;
     833               0 :     mDoubleByteText = false;
     834               0 :   }
     835                 :   void AccumulateRunInfo(nsTextFrame* aFrame);
     836                 :   /**
     837                 :    * @return null to indicate either textrun construction failed or
     838                 :    * we constructed just a partial textrun to set up linebreaker and other
     839                 :    * state for following textruns.
     840                 :    */
     841                 :   gfxTextRun* BuildTextRunForFrames(void* aTextBuffer);
     842                 :   bool SetupLineBreakerContext(gfxTextRun *aTextRun);
     843                 :   void AssignTextRun(gfxTextRun* aTextRun);
     844                 :   nsTextFrame* GetNextBreakBeforeFrame(PRUint32* aIndex);
     845                 :   enum SetupBreakSinksFlags {
     846                 :     SBS_DOUBLE_BYTE =      (1 << 0),
     847                 :     SBS_EXISTING_TEXTRUN = (1 << 1),
     848                 :     SBS_SUPPRESS_SINK    = (1 << 2)
     849                 :   };
     850                 :   void SetupBreakSinksForTextRun(gfxTextRun* aTextRun,
     851                 :                                  const void* aTextPtr,
     852                 :                                  PRUint32    aFlags);
     853                 :   struct FindBoundaryState {
     854                 :     nsIFrame*    mStopAtFrame;
     855                 :     nsTextFrame* mFirstTextFrame;
     856                 :     nsTextFrame* mLastTextFrame;
     857                 :     bool mSeenTextRunBoundaryOnLaterLine;
     858                 :     bool mSeenTextRunBoundaryOnThisLine;
     859                 :     bool mSeenSpaceForLineBreakingOnThisLine;
     860                 :   };
     861                 :   enum FindBoundaryResult {
     862                 :     FB_CONTINUE,
     863                 :     FB_STOPPED_AT_STOP_FRAME,
     864                 :     FB_FOUND_VALID_TEXTRUN_BOUNDARY
     865                 :   };
     866                 :   FindBoundaryResult FindBoundaries(nsIFrame* aFrame, FindBoundaryState* aState);
     867                 : 
     868                 :   bool ContinueTextRunAcrossFrames(nsTextFrame* aFrame1, nsTextFrame* aFrame2);
     869                 : 
     870                 :   // Like TextRunMappedFlow but with some differences. mStartFrame to mEndFrame
     871                 :   // (exclusive) are a sequence of in-flow frames (if mEndFrame is null, then
     872                 :   // continuations starting from mStartFrame are a sequence of in-flow frames).
     873               0 :   struct MappedFlow {
     874                 :     nsTextFrame* mStartFrame;
     875                 :     nsTextFrame* mEndFrame;
     876                 :     // When we consider breaking between elements, the nearest common
     877                 :     // ancestor of the elements containing the characters is the one whose
     878                 :     // CSS 'white-space' property governs. So this records the nearest common
     879                 :     // ancestor of mStartFrame and the previous text frame, or null if there
     880                 :     // was no previous text frame on this line.
     881                 :     nsIFrame*    mAncestorControllingInitialBreak;
     882                 :     
     883               0 :     PRInt32 GetContentEnd() {
     884               0 :       return mEndFrame ? mEndFrame->GetContentOffset()
     885               0 :           : mStartFrame->GetContent()->GetText()->GetLength();
     886                 :     }
     887                 :   };
     888                 : 
     889                 :   class BreakSink : public nsILineBreakSink {
     890                 :   public:
     891               0 :     BreakSink(gfxTextRun* aTextRun, gfxContext* aContext, PRUint32 aOffsetIntoTextRun,
     892                 :               bool aExistingTextRun) :
     893                 :                 mTextRun(aTextRun), mContext(aContext),
     894                 :                 mOffsetIntoTextRun(aOffsetIntoTextRun),
     895               0 :                 mChangedBreaks(false), mExistingTextRun(aExistingTextRun) {}
     896                 : 
     897               0 :     virtual void SetBreaks(PRUint32 aOffset, PRUint32 aLength,
     898                 :                            PRUint8* aBreakBefore) {
     899               0 :       if (mTextRun->SetPotentialLineBreaks(aOffset + mOffsetIntoTextRun, aLength,
     900               0 :                                            aBreakBefore, mContext)) {
     901               0 :         mChangedBreaks = true;
     902                 :         // Be conservative and assume that some breaks have been set
     903               0 :         mTextRun->ClearFlagBits(nsTextFrameUtils::TEXT_NO_BREAKS);
     904                 :       }
     905               0 :     }
     906                 :     
     907               0 :     virtual void SetCapitalization(PRUint32 aOffset, PRUint32 aLength,
     908                 :                                    bool* aCapitalize) {
     909               0 :       NS_ASSERTION(mTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_TRANSFORMED,
     910                 :                    "Text run should be transformed!");
     911                 :       nsTransformedTextRun* transformedTextRun =
     912               0 :         static_cast<nsTransformedTextRun*>(mTextRun);
     913                 :       transformedTextRun->SetCapitalization(aOffset + mOffsetIntoTextRun, aLength,
     914               0 :                                             aCapitalize, mContext);
     915               0 :     }
     916                 : 
     917               0 :     void Finish() {
     918               0 :       NS_ASSERTION(!(mTextRun->GetFlags() &
     919                 :                      (gfxTextRunFactory::TEXT_UNUSED_FLAGS |
     920                 :                       nsTextFrameUtils::TEXT_UNUSED_FLAG)),
     921                 :                    "Flag set that should never be set! (memory safety error?)");
     922               0 :       if (mTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_TRANSFORMED) {
     923                 :         nsTransformedTextRun* transformedTextRun =
     924               0 :           static_cast<nsTransformedTextRun*>(mTextRun);
     925               0 :         transformedTextRun->FinishSettingProperties(mContext);
     926                 :       }
     927               0 :     }
     928                 : 
     929                 :     gfxTextRun*  mTextRun;
     930                 :     gfxContext*  mContext;
     931                 :     PRUint32     mOffsetIntoTextRun;
     932                 :     bool mChangedBreaks;
     933                 :     bool mExistingTextRun;
     934                 :   };
     935                 : 
     936                 : private:
     937                 :   nsAutoTArray<MappedFlow,10>   mMappedFlows;
     938                 :   nsAutoTArray<nsTextFrame*,50> mLineBreakBeforeFrames;
     939                 :   nsAutoTArray<nsAutoPtr<BreakSink>,10> mBreakSinks;
     940                 :   nsAutoTArray<gfxTextRun*,5>   mTextRunsToDelete;
     941                 :   nsLineBreaker                 mLineBreaker;
     942                 :   gfxTextRun*                   mCurrentFramesAllSameTextRun;
     943                 :   gfxContext*                   mContext;
     944                 :   nsIFrame*                     mLineContainer;
     945                 :   nsTextFrame*                  mLastFrame;
     946                 :   // The common ancestor of the current frame and the previous leaf frame
     947                 :   // on the line, or null if there was no previous leaf frame.
     948                 :   nsIFrame*                     mCommonAncestorWithLastFrame;
     949                 :   // mMaxTextLength is an upper bound on the size of the text in all mapped frames
     950                 :   // The value PR_UINT32_MAX represents overflow; text will be discarded
     951                 :   PRUint32                      mMaxTextLength;
     952                 :   float                         mInflation;
     953                 :   bool                          mDoubleByteText;
     954                 :   bool                          mBidiEnabled;
     955                 :   bool                          mStartOfLine;
     956                 :   bool                          mSkipIncompleteTextRuns;
     957                 :   bool                          mCanStopOnThisLine;
     958                 :   nsTextFrame::TextRunType      mWhichTextRun;
     959                 :   PRUint8                       mNextRunContextInfo;
     960                 :   PRUint8                       mCurrentRunContextInfo;
     961                 : };
     962                 : 
     963                 : static nsIFrame*
     964               0 : FindLineContainer(nsIFrame* aFrame)
     965                 : {
     966               0 :   while (aFrame && aFrame->CanContinueTextRun()) {
     967               0 :     aFrame = aFrame->GetParent();
     968                 :   }
     969               0 :   return aFrame;
     970                 : }
     971                 : 
     972                 : static bool
     973               0 : IsLineBreakingWhiteSpace(PRUnichar aChar)
     974                 : {
     975                 :   // 0x0A (\n) is not handled as white-space by the line breaker, since
     976                 :   // we break before it, if it isn't transformed to a normal space.
     977                 :   // (If we treat it as normal white-space then we'd only break after it.)
     978                 :   // However, it does induce a line break or is converted to a regular
     979                 :   // space, and either way it can be used to bound the region of text
     980                 :   // that needs to be analyzed for line breaking.
     981               0 :   return nsLineBreaker::IsSpace(aChar) || aChar == 0x0A;
     982                 : }
     983                 : 
     984                 : static bool
     985               0 : TextContainsLineBreakerWhiteSpace(const void* aText, PRUint32 aLength,
     986                 :                                   bool aIsDoubleByte)
     987                 : {
     988                 :   PRUint32 i;
     989               0 :   if (aIsDoubleByte) {
     990               0 :     const PRUnichar* chars = static_cast<const PRUnichar*>(aText);
     991               0 :     for (i = 0; i < aLength; ++i) {
     992               0 :       if (IsLineBreakingWhiteSpace(chars[i]))
     993               0 :         return true;
     994                 :     }
     995               0 :     return false;
     996                 :   } else {
     997               0 :     const PRUint8* chars = static_cast<const PRUint8*>(aText);
     998               0 :     for (i = 0; i < aLength; ++i) {
     999               0 :       if (IsLineBreakingWhiteSpace(chars[i]))
    1000               0 :         return true;
    1001                 :     }
    1002               0 :     return false;
    1003                 :   }
    1004                 : }
    1005                 : 
    1006                 : struct FrameTextTraversal {
    1007                 :   // These fields identify which frames should be recursively scanned
    1008                 :   // The first normal frame to scan (or null, if no such frame should be scanned)
    1009                 :   nsIFrame*    mFrameToScan;
    1010                 :   // The first overflow frame to scan (or null, if no such frame should be scanned)
    1011                 :   nsIFrame*    mOverflowFrameToScan;
    1012                 :   // Whether to scan the siblings of mFrameToDescendInto/mOverflowFrameToDescendInto
    1013                 :   bool mScanSiblings;
    1014                 : 
    1015                 :   // These identify the boundaries of the context required for
    1016                 :   // line breaking or textrun construction
    1017                 :   bool mLineBreakerCanCrossFrameBoundary;
    1018                 :   bool mTextRunCanCrossFrameBoundary;
    1019                 : 
    1020               0 :   nsIFrame* NextFrameToScan() {
    1021                 :     nsIFrame* f;
    1022               0 :     if (mFrameToScan) {
    1023               0 :       f = mFrameToScan;
    1024               0 :       mFrameToScan = mScanSiblings ? f->GetNextSibling() : nsnull;
    1025               0 :     } else if (mOverflowFrameToScan) {
    1026               0 :       f = mOverflowFrameToScan;
    1027               0 :       mOverflowFrameToScan = mScanSiblings ? f->GetNextSibling() : nsnull;
    1028                 :     } else {
    1029               0 :       f = nsnull;
    1030                 :     }
    1031               0 :     return f;
    1032                 :   }
    1033                 : };
    1034                 : 
    1035                 : static FrameTextTraversal
    1036               0 : CanTextCrossFrameBoundary(nsIFrame* aFrame, nsIAtom* aType)
    1037                 : {
    1038               0 :   NS_ASSERTION(aType == aFrame->GetType(), "Wrong type");
    1039                 : 
    1040                 :   FrameTextTraversal result;
    1041                 : 
    1042               0 :   bool continuesTextRun = aFrame->CanContinueTextRun();
    1043               0 :   if (aType == nsGkAtoms::placeholderFrame) {
    1044                 :     // placeholders are "invisible", so a text run should be able to span
    1045                 :     // across one. But don't descend into the out-of-flow.
    1046               0 :     result.mLineBreakerCanCrossFrameBoundary = true;
    1047               0 :     result.mOverflowFrameToScan = nsnull;
    1048               0 :     if (continuesTextRun) {
    1049                 :       // ... Except for first-letter floats, which are really in-flow
    1050                 :       // from the point of view of capitalization etc, so we'd better
    1051                 :       // descend into them. But we actually need to break the textrun for
    1052                 :       // first-letter floats since things look bad if, say, we try to make a
    1053                 :       // ligature across the float boundary.
    1054                 :       result.mFrameToScan =
    1055               0 :         (static_cast<nsPlaceholderFrame*>(aFrame))->GetOutOfFlowFrame();
    1056               0 :       result.mScanSiblings = false;
    1057               0 :       result.mTextRunCanCrossFrameBoundary = false;
    1058                 :     } else {
    1059               0 :       result.mFrameToScan = nsnull;
    1060               0 :       result.mTextRunCanCrossFrameBoundary = true;
    1061                 :     }
    1062                 :   } else {
    1063               0 :     if (continuesTextRun) {
    1064               0 :       result.mFrameToScan = aFrame->GetFirstPrincipalChild();
    1065                 :       result.mOverflowFrameToScan =
    1066               0 :         aFrame->GetFirstChild(nsIFrame::kOverflowList);
    1067               0 :       NS_WARN_IF_FALSE(!result.mOverflowFrameToScan,
    1068                 :                        "Scanning overflow inline frames is something we should avoid");
    1069               0 :       result.mScanSiblings = true;
    1070               0 :       result.mTextRunCanCrossFrameBoundary = true;
    1071               0 :       result.mLineBreakerCanCrossFrameBoundary = true;
    1072                 :     } else {
    1073               0 :       result.mFrameToScan = nsnull;
    1074               0 :       result.mOverflowFrameToScan = nsnull;
    1075               0 :       result.mTextRunCanCrossFrameBoundary = false;
    1076               0 :       result.mLineBreakerCanCrossFrameBoundary = false;
    1077                 :     }
    1078                 :   }    
    1079                 :   return result;
    1080                 : }
    1081                 : 
    1082                 : BuildTextRunsScanner::FindBoundaryResult
    1083               0 : BuildTextRunsScanner::FindBoundaries(nsIFrame* aFrame, FindBoundaryState* aState)
    1084                 : {
    1085               0 :   nsIAtom* frameType = aFrame->GetType();
    1086                 :   nsTextFrame* textFrame = frameType == nsGkAtoms::textFrame
    1087               0 :     ? static_cast<nsTextFrame*>(aFrame) : nsnull;
    1088               0 :   if (textFrame) {
    1089               0 :     if (aState->mLastTextFrame &&
    1090               0 :         textFrame != aState->mLastTextFrame->GetNextInFlow() &&
    1091               0 :         !ContinueTextRunAcrossFrames(aState->mLastTextFrame, textFrame)) {
    1092               0 :       aState->mSeenTextRunBoundaryOnThisLine = true;
    1093               0 :       if (aState->mSeenSpaceForLineBreakingOnThisLine)
    1094               0 :         return FB_FOUND_VALID_TEXTRUN_BOUNDARY;
    1095                 :     }
    1096               0 :     if (!aState->mFirstTextFrame) {
    1097               0 :       aState->mFirstTextFrame = textFrame;
    1098                 :     }
    1099               0 :     aState->mLastTextFrame = textFrame;
    1100                 :   }
    1101                 :   
    1102               0 :   if (aFrame == aState->mStopAtFrame)
    1103               0 :     return FB_STOPPED_AT_STOP_FRAME;
    1104                 : 
    1105               0 :   if (textFrame) {
    1106               0 :     if (!aState->mSeenSpaceForLineBreakingOnThisLine) {
    1107               0 :       const nsTextFragment* frag = textFrame->GetContent()->GetText();
    1108               0 :       PRUint32 start = textFrame->GetContentOffset();
    1109               0 :       const void* text = frag->Is2b()
    1110               0 :           ? static_cast<const void*>(frag->Get2b() + start)
    1111               0 :           : static_cast<const void*>(frag->Get1b() + start);
    1112               0 :       if (TextContainsLineBreakerWhiteSpace(text, textFrame->GetContentLength(),
    1113               0 :                                             frag->Is2b())) {
    1114               0 :         aState->mSeenSpaceForLineBreakingOnThisLine = true;
    1115               0 :         if (aState->mSeenTextRunBoundaryOnLaterLine)
    1116               0 :           return FB_FOUND_VALID_TEXTRUN_BOUNDARY;
    1117                 :       }
    1118                 :     }
    1119               0 :     return FB_CONTINUE; 
    1120                 :   }
    1121                 : 
    1122                 :   FrameTextTraversal traversal =
    1123               0 :     CanTextCrossFrameBoundary(aFrame, frameType);
    1124               0 :   if (!traversal.mTextRunCanCrossFrameBoundary) {
    1125               0 :     aState->mSeenTextRunBoundaryOnThisLine = true;
    1126               0 :     if (aState->mSeenSpaceForLineBreakingOnThisLine)
    1127               0 :       return FB_FOUND_VALID_TEXTRUN_BOUNDARY;
    1128                 :   }
    1129                 :   
    1130               0 :   for (nsIFrame* f = traversal.NextFrameToScan(); f;
    1131                 :        f = traversal.NextFrameToScan()) {
    1132               0 :     FindBoundaryResult result = FindBoundaries(f, aState);
    1133               0 :     if (result != FB_CONTINUE)
    1134               0 :       return result;
    1135                 :   }
    1136                 : 
    1137               0 :   if (!traversal.mTextRunCanCrossFrameBoundary) {
    1138               0 :     aState->mSeenTextRunBoundaryOnThisLine = true;
    1139               0 :     if (aState->mSeenSpaceForLineBreakingOnThisLine)
    1140               0 :       return FB_FOUND_VALID_TEXTRUN_BOUNDARY;
    1141                 :   }
    1142                 : 
    1143               0 :   return FB_CONTINUE;
    1144                 : }
    1145                 : 
    1146                 : // build text runs for the 200 lines following aForFrame, and stop after that
    1147                 : // when we get a chance.
    1148                 : #define NUM_LINES_TO_BUILD_TEXT_RUNS 200
    1149                 : 
    1150                 : /**
    1151                 :  * General routine for building text runs. This is hairy because of the need
    1152                 :  * to build text runs that span content nodes.
    1153                 :  * 
    1154                 :  * @param aForFrameLine the line containing aForFrame; if null, we'll figure
    1155                 :  * out the line (slowly)
    1156                 :  * @param aLineContainer the line container containing aForFrame; if null,
    1157                 :  * we'll walk the ancestors to find it.  It's required to be non-null when
    1158                 :  * aForFrameLine is non-null.
    1159                 :  */
    1160                 : static void
    1161               0 : BuildTextRuns(gfxContext* aContext, nsTextFrame* aForFrame,
    1162                 :               nsIFrame* aLineContainer,
    1163                 :               const nsLineList::iterator* aForFrameLine,
    1164                 :               nsTextFrame::TextRunType aWhichTextRun, float aInflation)
    1165                 : {
    1166               0 :   NS_ASSERTION(aForFrame || aLineContainer,
    1167                 :                "One of aForFrame or aLineContainer must be set!");
    1168               0 :   NS_ASSERTION(!aForFrameLine || aLineContainer,
    1169                 :                "line but no line container");
    1170                 :   
    1171               0 :   nsIFrame* lineContainerChild = aForFrame;
    1172               0 :   if (!aLineContainer) {
    1173               0 :     if (aForFrame->IsFloatingFirstLetterChild()) {
    1174               0 :       lineContainerChild = aForFrame->PresContext()->PresShell()->
    1175               0 :         GetPlaceholderFrameFor(aForFrame->GetParent());
    1176                 :     }
    1177               0 :     aLineContainer = FindLineContainer(lineContainerChild);
    1178                 :   } else {
    1179               0 :     NS_ASSERTION(!aForFrame ||
    1180                 :                  (aLineContainer == FindLineContainer(aForFrame) ||
    1181                 :                   (aLineContainer->GetType() == nsGkAtoms::letterFrame &&
    1182                 :                    aLineContainer->GetStyleDisplay()->IsFloating())),
    1183                 :                  "Wrong line container hint");
    1184                 :   }
    1185                 : 
    1186               0 :   nsPresContext* presContext = aLineContainer->PresContext();
    1187                 :   BuildTextRunsScanner scanner(presContext, aContext, aLineContainer,
    1188               0 :                                aWhichTextRun, aInflation);
    1189                 : 
    1190               0 :   nsBlockFrame* block = nsLayoutUtils::GetAsBlock(aLineContainer);
    1191                 : 
    1192               0 :   if (!block) {
    1193               0 :     NS_ASSERTION(!aLineContainer->GetPrevInFlow() && !aLineContainer->GetNextInFlow(),
    1194                 :                  "Breakable non-block line containers not supported");
    1195                 :     // Just loop through all the children of the linecontainer ... it's really
    1196                 :     // just one line
    1197               0 :     scanner.SetAtStartOfLine();
    1198               0 :     scanner.SetCommonAncestorWithLastFrame(nsnull);
    1199               0 :     nsIFrame* child = aLineContainer->GetFirstPrincipalChild();
    1200               0 :     while (child) {
    1201               0 :       scanner.ScanFrame(child);
    1202               0 :       child = child->GetNextSibling();
    1203                 :     }
    1204                 :     // Set mStartOfLine so FlushFrames knows its textrun ends a line
    1205               0 :     scanner.SetAtStartOfLine();
    1206               0 :     scanner.FlushFrames(true, false);
    1207                 :     return;
    1208                 :   }
    1209                 : 
    1210                 :   // Find the line containing 'lineContainerChild'.
    1211                 : 
    1212               0 :   bool isValid = true;
    1213               0 :   nsBlockInFlowLineIterator backIterator(block, &isValid);
    1214               0 :   if (aForFrameLine) {
    1215               0 :     backIterator = nsBlockInFlowLineIterator(block, *aForFrameLine);
    1216                 :   } else {
    1217               0 :     backIterator = nsBlockInFlowLineIterator(block, lineContainerChild, &isValid);
    1218               0 :     NS_ASSERTION(isValid, "aForFrame not found in block, someone lied to us");
    1219               0 :     NS_ASSERTION(backIterator.GetContainer() == block,
    1220                 :                  "Someone lied to us about the block");
    1221                 :   }
    1222               0 :   nsBlockFrame::line_iterator startLine = backIterator.GetLine();
    1223                 : 
    1224                 :   // Find a line where we can start building text runs. We choose the last line
    1225                 :   // where:
    1226                 :   // -- there is a textrun boundary between the start of the line and the
    1227                 :   // start of aForFrame
    1228                 :   // -- there is a space between the start of the line and the textrun boundary
    1229                 :   // (this is so we can be sure the line breaks will be set properly
    1230                 :   // on the textruns we construct).
    1231                 :   // The possibly-partial text runs up to and including the first space
    1232                 :   // are not reconstructed. We construct partial text runs for that text ---
    1233                 :   // for the sake of simplifying the code and feeding the linebreaker ---
    1234                 :   // but we discard them instead of assigning them to frames.
    1235                 :   // This is a little awkward because we traverse lines in the reverse direction
    1236                 :   // but we traverse the frames in each line in the forward direction.
    1237               0 :   nsBlockInFlowLineIterator forwardIterator = backIterator;
    1238               0 :   nsIFrame* stopAtFrame = lineContainerChild;
    1239               0 :   nsTextFrame* nextLineFirstTextFrame = nsnull;
    1240               0 :   bool seenTextRunBoundaryOnLaterLine = false;
    1241               0 :   bool mayBeginInTextRun = true;
    1242               0 :   while (true) {
    1243               0 :     forwardIterator = backIterator;
    1244               0 :     nsBlockFrame::line_iterator line = backIterator.GetLine();
    1245               0 :     if (!backIterator.Prev() || backIterator.GetLine()->IsBlock()) {
    1246               0 :       mayBeginInTextRun = false;
    1247               0 :       break;
    1248                 :     }
    1249                 : 
    1250                 :     BuildTextRunsScanner::FindBoundaryState state = { stopAtFrame, nsnull, nsnull,
    1251               0 :       bool(seenTextRunBoundaryOnLaterLine), false, false };
    1252               0 :     nsIFrame* child = line->mFirstChild;
    1253               0 :     bool foundBoundary = false;
    1254                 :     PRInt32 i;
    1255               0 :     for (i = line->GetChildCount() - 1; i >= 0; --i) {
    1256                 :       BuildTextRunsScanner::FindBoundaryResult result =
    1257               0 :           scanner.FindBoundaries(child, &state);
    1258               0 :       if (result == BuildTextRunsScanner::FB_FOUND_VALID_TEXTRUN_BOUNDARY) {
    1259               0 :         foundBoundary = true;
    1260               0 :         break;
    1261               0 :       } else if (result == BuildTextRunsScanner::FB_STOPPED_AT_STOP_FRAME) {
    1262               0 :         break;
    1263                 :       }
    1264               0 :       child = child->GetNextSibling();
    1265                 :     }
    1266               0 :     if (foundBoundary)
    1267               0 :       break;
    1268               0 :     if (!stopAtFrame && state.mLastTextFrame && nextLineFirstTextFrame &&
    1269               0 :         !scanner.ContinueTextRunAcrossFrames(state.mLastTextFrame, nextLineFirstTextFrame)) {
    1270                 :       // Found a usable textrun boundary at the end of the line
    1271               0 :       if (state.mSeenSpaceForLineBreakingOnThisLine)
    1272               0 :         break;
    1273               0 :       seenTextRunBoundaryOnLaterLine = true;
    1274               0 :     } else if (state.mSeenTextRunBoundaryOnThisLine) {
    1275               0 :       seenTextRunBoundaryOnLaterLine = true;
    1276                 :     }
    1277               0 :     stopAtFrame = nsnull;
    1278               0 :     if (state.mFirstTextFrame) {
    1279               0 :       nextLineFirstTextFrame = state.mFirstTextFrame;
    1280                 :     }
    1281                 :   }
    1282               0 :   scanner.SetSkipIncompleteTextRuns(mayBeginInTextRun);
    1283                 : 
    1284                 :   // Now iterate over all text frames starting from the current line. First-in-flow
    1285                 :   // text frames will be accumulated into textRunFrames as we go. When a
    1286                 :   // text run boundary is required we flush textRunFrames ((re)building their
    1287                 :   // gfxTextRuns as necessary).
    1288               0 :   bool seenStartLine = false;
    1289               0 :   PRUint32 linesAfterStartLine = 0;
    1290               0 :   do {
    1291               0 :     nsBlockFrame::line_iterator line = forwardIterator.GetLine();
    1292               0 :     if (line->IsBlock())
    1293               0 :       break;
    1294               0 :     line->SetInvalidateTextRuns(false);
    1295               0 :     scanner.SetAtStartOfLine();
    1296               0 :     scanner.SetCommonAncestorWithLastFrame(nsnull);
    1297               0 :     nsIFrame* child = line->mFirstChild;
    1298                 :     PRInt32 i;
    1299               0 :     for (i = line->GetChildCount() - 1; i >= 0; --i) {
    1300               0 :       scanner.ScanFrame(child);
    1301               0 :       child = child->GetNextSibling();
    1302                 :     }
    1303               0 :     if (line.get() == startLine.get()) {
    1304               0 :       seenStartLine = true;
    1305                 :     }
    1306               0 :     if (seenStartLine) {
    1307               0 :       ++linesAfterStartLine;
    1308               0 :       if (linesAfterStartLine >= NUM_LINES_TO_BUILD_TEXT_RUNS && scanner.CanStopOnThisLine()) {
    1309                 :         // Don't flush frames; we may be in the middle of a textrun
    1310                 :         // that we can't end here. That's OK, we just won't build it.
    1311                 :         // Note that we must already have finished the textrun for aForFrame,
    1312                 :         // because we've seen the end of a textrun in a line after the line
    1313                 :         // containing aForFrame.
    1314               0 :         scanner.FlushLineBreaks(nsnull);
    1315                 :         // This flushes out mMappedFlows and mLineBreakBeforeFrames, which
    1316                 :         // silences assertions in the scanner destructor.
    1317               0 :         scanner.ResetRunInfo();
    1318                 :         return;
    1319                 :       }
    1320                 :     }
    1321                 :   } while (forwardIterator.Next());
    1322                 : 
    1323                 :   // Set mStartOfLine so FlushFrames knows its textrun ends a line
    1324               0 :   scanner.SetAtStartOfLine();
    1325               0 :   scanner.FlushFrames(true, false);
    1326                 : }
    1327                 : 
    1328                 : static PRUnichar*
    1329               0 : ExpandBuffer(PRUnichar* aDest, PRUint8* aSrc, PRUint32 aCount)
    1330                 : {
    1331               0 :   while (aCount) {
    1332               0 :     *aDest = *aSrc;
    1333               0 :     ++aDest;
    1334               0 :     ++aSrc;
    1335               0 :     --aCount;
    1336                 :   }
    1337               0 :   return aDest;
    1338                 : }
    1339                 : 
    1340               0 : bool BuildTextRunsScanner::IsTextRunValidForMappedFlows(gfxTextRun* aTextRun)
    1341                 : {
    1342               0 :   if (aTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW)
    1343               0 :     return mMappedFlows.Length() == 1 &&
    1344               0 :       mMappedFlows[0].mStartFrame == static_cast<nsTextFrame*>(aTextRun->GetUserData()) &&
    1345               0 :       mMappedFlows[0].mEndFrame == nsnull;
    1346                 : 
    1347               0 :   TextRunUserData* userData = static_cast<TextRunUserData*>(aTextRun->GetUserData());
    1348               0 :   if (userData->mMappedFlowCount != mMappedFlows.Length())
    1349               0 :     return false;
    1350                 :   PRUint32 i;
    1351               0 :   for (i = 0; i < mMappedFlows.Length(); ++i) {
    1352               0 :     if (userData->mMappedFlows[i].mStartFrame != mMappedFlows[i].mStartFrame ||
    1353               0 :         PRInt32(userData->mMappedFlows[i].mContentLength) !=
    1354               0 :             mMappedFlows[i].GetContentEnd() - mMappedFlows[i].mStartFrame->GetContentOffset())
    1355               0 :       return false;
    1356                 :   }
    1357               0 :   return true;
    1358                 : }
    1359                 : 
    1360                 : /**
    1361                 :  * This gets called when we need to make a text run for the current list of
    1362                 :  * frames.
    1363                 :  */
    1364               0 : void BuildTextRunsScanner::FlushFrames(bool aFlushLineBreaks, bool aSuppressTrailingBreak)
    1365                 : {
    1366               0 :   gfxTextRun* textRun = nsnull;
    1367               0 :   if (!mMappedFlows.IsEmpty()) {
    1368               0 :     if (!mSkipIncompleteTextRuns && mCurrentFramesAllSameTextRun &&
    1369               0 :         ((mCurrentFramesAllSameTextRun->GetFlags() & nsTextFrameUtils::TEXT_INCOMING_WHITESPACE) != 0) ==
    1370                 :         ((mCurrentRunContextInfo & nsTextFrameUtils::INCOMING_WHITESPACE) != 0) &&
    1371               0 :         ((mCurrentFramesAllSameTextRun->GetFlags() & gfxTextRunFactory::TEXT_INCOMING_ARABICCHAR) != 0) ==
    1372                 :         ((mCurrentRunContextInfo & nsTextFrameUtils::INCOMING_ARABICCHAR) != 0) &&
    1373               0 :         IsTextRunValidForMappedFlows(mCurrentFramesAllSameTextRun)) {
    1374                 :       // Optimization: We do not need to (re)build the textrun.
    1375               0 :       textRun = mCurrentFramesAllSameTextRun;
    1376                 : 
    1377                 :       // Feed this run's text into the linebreaker to provide context.
    1378               0 :       if (!SetupLineBreakerContext(textRun)) {
    1379               0 :         return;
    1380                 :       }
    1381                 :  
    1382                 :       // Update mNextRunContextInfo appropriately
    1383               0 :       mNextRunContextInfo = nsTextFrameUtils::INCOMING_NONE;
    1384               0 :       if (textRun->GetFlags() & nsTextFrameUtils::TEXT_TRAILING_WHITESPACE) {
    1385               0 :         mNextRunContextInfo |= nsTextFrameUtils::INCOMING_WHITESPACE;
    1386                 :       }
    1387               0 :       if (textRun->GetFlags() & gfxTextRunFactory::TEXT_TRAILING_ARABICCHAR) {
    1388               0 :         mNextRunContextInfo |= nsTextFrameUtils::INCOMING_ARABICCHAR;
    1389                 :       }
    1390                 :     } else {
    1391               0 :       AutoFallibleTArray<PRUint8,BIG_TEXT_NODE_SIZE> buffer;
    1392               0 :       PRUint32 bufferSize = mMaxTextLength*(mDoubleByteText ? 2 : 1);
    1393               0 :       if (bufferSize < mMaxTextLength || bufferSize == PR_UINT32_MAX ||
    1394               0 :           !buffer.AppendElements(bufferSize)) {
    1395                 :         return;
    1396                 :       }
    1397               0 :       textRun = BuildTextRunForFrames(buffer.Elements());
    1398                 :     }
    1399                 :   }
    1400                 : 
    1401               0 :   if (aFlushLineBreaks) {
    1402               0 :     FlushLineBreaks(aSuppressTrailingBreak ? nsnull : textRun);
    1403                 :   }
    1404                 : 
    1405               0 :   mCanStopOnThisLine = true;
    1406               0 :   ResetRunInfo();
    1407                 : }
    1408                 : 
    1409               0 : void BuildTextRunsScanner::FlushLineBreaks(gfxTextRun* aTrailingTextRun)
    1410                 : {
    1411                 :   bool trailingLineBreak;
    1412               0 :   nsresult rv = mLineBreaker.Reset(&trailingLineBreak);
    1413                 :   // textRun may be null for various reasons, including because we constructed
    1414                 :   // a partial textrun just to get the linebreaker and other state set up
    1415                 :   // to build the next textrun.
    1416               0 :   if (NS_SUCCEEDED(rv) && trailingLineBreak && aTrailingTextRun) {
    1417               0 :     aTrailingTextRun->SetFlagBits(nsTextFrameUtils::TEXT_HAS_TRAILING_BREAK);
    1418                 :   }
    1419                 : 
    1420                 :   PRUint32 i;
    1421               0 :   for (i = 0; i < mBreakSinks.Length(); ++i) {
    1422               0 :     if (!mBreakSinks[i]->mExistingTextRun || mBreakSinks[i]->mChangedBreaks) {
    1423                 :       // TODO cause frames associated with the textrun to be reflowed, if they
    1424                 :       // aren't being reflowed already!
    1425                 :     }
    1426               0 :     mBreakSinks[i]->Finish();
    1427                 :   }
    1428               0 :   mBreakSinks.Clear();
    1429                 : 
    1430               0 :   for (i = 0; i < mTextRunsToDelete.Length(); ++i) {
    1431               0 :     gfxTextRun* deleteTextRun = mTextRunsToDelete[i];
    1432               0 :     gTextRuns->RemoveFromCache(deleteTextRun);
    1433               0 :     delete deleteTextRun;
    1434                 :   }
    1435               0 :   mTextRunsToDelete.Clear();
    1436               0 : }
    1437                 : 
    1438               0 : void BuildTextRunsScanner::AccumulateRunInfo(nsTextFrame* aFrame)
    1439                 : {
    1440               0 :   if (mMaxTextLength != PR_UINT32_MAX) {
    1441               0 :     NS_ASSERTION(mMaxTextLength < PR_UINT32_MAX - aFrame->GetContentLength(), "integer overflow");
    1442               0 :     if (mMaxTextLength >= PR_UINT32_MAX - aFrame->GetContentLength()) {
    1443               0 :       mMaxTextLength = PR_UINT32_MAX;
    1444                 :     } else {
    1445               0 :       mMaxTextLength += aFrame->GetContentLength();
    1446                 :     }
    1447                 :   }
    1448               0 :   mDoubleByteText |= aFrame->GetContent()->GetText()->Is2b();
    1449               0 :   mLastFrame = aFrame;
    1450               0 :   mCommonAncestorWithLastFrame = aFrame->GetParent();
    1451                 : 
    1452               0 :   MappedFlow* mappedFlow = &mMappedFlows[mMappedFlows.Length() - 1];
    1453               0 :   NS_ASSERTION(mappedFlow->mStartFrame == aFrame ||
    1454                 :                mappedFlow->GetContentEnd() == aFrame->GetContentOffset(),
    1455                 :                "Overlapping or discontiguous frames => BAD");
    1456               0 :   mappedFlow->mEndFrame = static_cast<nsTextFrame*>(aFrame->GetNextContinuation());
    1457               0 :   if (mCurrentFramesAllSameTextRun != aFrame->GetTextRun(mWhichTextRun)) {
    1458               0 :     mCurrentFramesAllSameTextRun = nsnull;
    1459                 :   }
    1460                 : 
    1461               0 :   if (mStartOfLine) {
    1462               0 :     mLineBreakBeforeFrames.AppendElement(aFrame);
    1463               0 :     mStartOfLine = false;
    1464                 :   }
    1465               0 : }
    1466                 : 
    1467               0 : static nscoord StyleToCoord(const nsStyleCoord& aCoord)
    1468                 : {
    1469               0 :   if (eStyleUnit_Coord == aCoord.GetUnit()) {
    1470               0 :     return aCoord.GetCoordValue();
    1471                 :   } else {
    1472               0 :     return 0;
    1473                 :   }
    1474                 : }
    1475                 : 
    1476                 : static bool
    1477               0 : HasTerminalNewline(const nsTextFrame* aFrame)
    1478                 : {
    1479               0 :   if (aFrame->GetContentLength() == 0)
    1480               0 :     return false;
    1481               0 :   const nsTextFragment* frag = aFrame->GetContent()->GetText();
    1482               0 :   return frag->CharAt(aFrame->GetContentEnd() - 1) == '\n';
    1483                 : }
    1484                 : 
    1485                 : bool
    1486               0 : BuildTextRunsScanner::ContinueTextRunAcrossFrames(nsTextFrame* aFrame1, nsTextFrame* aFrame2)
    1487                 : {
    1488                 :   // We don't need to check font size inflation, since
    1489                 :   // |FindLineContainer| above (via |nsIFrame::CanContinueTextRun|)
    1490                 :   // ensures that text runs never cross block boundaries.  This means
    1491                 :   // that the font size inflation on all text frames in the text run is
    1492                 :   // already guaranteed to be the same as each other (and for the line
    1493                 :   // container).
    1494               0 :   if (mBidiEnabled &&
    1495               0 :       NS_GET_EMBEDDING_LEVEL(aFrame1) != NS_GET_EMBEDDING_LEVEL(aFrame2))
    1496               0 :     return false;
    1497                 : 
    1498               0 :   nsStyleContext* sc1 = aFrame1->GetStyleContext();
    1499               0 :   const nsStyleText* textStyle1 = sc1->GetStyleText();
    1500                 :   // If the first frame ends in a preformatted newline, then we end the textrun
    1501                 :   // here. This avoids creating giant textruns for an entire plain text file.
    1502                 :   // Note that we create a single text frame for a preformatted text node,
    1503                 :   // even if it has newlines in it, so typically we won't see trailing newlines
    1504                 :   // until after reflow has broken up the frame into one (or more) frames per
    1505                 :   // line. That's OK though.
    1506               0 :   if (textStyle1->NewlineIsSignificant() && HasTerminalNewline(aFrame1))
    1507               0 :     return false;
    1508                 : 
    1509               0 :   if (aFrame1->GetContent() == aFrame2->GetContent() &&
    1510               0 :       aFrame1->GetNextInFlow() != aFrame2) {
    1511                 :     // aFrame2 must be a non-fluid continuation of aFrame1. This can happen
    1512                 :     // sometimes when the unicode-bidi property is used; the bidi resolver
    1513                 :     // breaks text into different frames even though the text has the same
    1514                 :     // direction. We can't allow these two frames to share the same textrun
    1515                 :     // because that would violate our invariant that two flows in the same
    1516                 :     // textrun have different content elements.
    1517               0 :     return false;
    1518                 :   }
    1519                 : 
    1520               0 :   nsStyleContext* sc2 = aFrame2->GetStyleContext();
    1521               0 :   if (sc1 == sc2)
    1522               0 :     return true;
    1523               0 :   const nsStyleFont* fontStyle1 = sc1->GetStyleFont();
    1524               0 :   const nsStyleFont* fontStyle2 = sc2->GetStyleFont();
    1525               0 :   const nsStyleText* textStyle2 = sc2->GetStyleText();
    1526               0 :   return fontStyle1->mFont.BaseEquals(fontStyle2->mFont) &&
    1527               0 :     sc1->GetStyleFont()->mLanguage == sc2->GetStyleFont()->mLanguage &&
    1528               0 :     nsLayoutUtils::GetTextRunFlagsForStyle(sc1, textStyle1, fontStyle1) ==
    1529               0 :       nsLayoutUtils::GetTextRunFlagsForStyle(sc2, textStyle2, fontStyle2);
    1530                 : }
    1531                 : 
    1532               0 : void BuildTextRunsScanner::ScanFrame(nsIFrame* aFrame)
    1533                 : {
    1534                 :   // First check if we can extend the current mapped frame block. This is common.
    1535               0 :   if (mMappedFlows.Length() > 0) {
    1536               0 :     MappedFlow* mappedFlow = &mMappedFlows[mMappedFlows.Length() - 1];
    1537               0 :     if (mappedFlow->mEndFrame == aFrame &&
    1538               0 :         (aFrame->GetStateBits() & NS_FRAME_IS_FLUID_CONTINUATION)) {
    1539               0 :       NS_ASSERTION(aFrame->GetType() == nsGkAtoms::textFrame,
    1540                 :                    "Flow-sibling of a text frame is not a text frame?");
    1541                 : 
    1542                 :       // Don't do this optimization if mLastFrame has a terminal newline...
    1543                 :       // it's quite likely preformatted and we might want to end the textrun here.
    1544                 :       // This is almost always true:
    1545               0 :       if (mLastFrame->GetStyleContext() == aFrame->GetStyleContext() &&
    1546               0 :           !HasTerminalNewline(mLastFrame)) {
    1547               0 :         AccumulateRunInfo(static_cast<nsTextFrame*>(aFrame));
    1548               0 :         return;
    1549                 :       }
    1550                 :     }
    1551                 :   }
    1552                 : 
    1553               0 :   nsIAtom* frameType = aFrame->GetType();
    1554                 :   // Now see if we can add a new set of frames to the current textrun
    1555               0 :   if (frameType == nsGkAtoms::textFrame) {
    1556               0 :     nsTextFrame* frame = static_cast<nsTextFrame*>(aFrame);
    1557                 : 
    1558               0 :     if (mLastFrame) {
    1559               0 :       if (!ContinueTextRunAcrossFrames(mLastFrame, frame)) {
    1560               0 :         FlushFrames(false, false);
    1561                 :       } else {
    1562               0 :         if (mLastFrame->GetContent() == frame->GetContent()) {
    1563               0 :           AccumulateRunInfo(frame);
    1564               0 :           return;
    1565                 :         }
    1566                 :       }
    1567                 :     }
    1568                 : 
    1569               0 :     MappedFlow* mappedFlow = mMappedFlows.AppendElement();
    1570               0 :     if (!mappedFlow)
    1571               0 :       return;
    1572                 : 
    1573               0 :     mappedFlow->mStartFrame = frame;
    1574               0 :     mappedFlow->mAncestorControllingInitialBreak = mCommonAncestorWithLastFrame;
    1575                 : 
    1576               0 :     AccumulateRunInfo(frame);
    1577               0 :     if (mMappedFlows.Length() == 1) {
    1578               0 :       mCurrentFramesAllSameTextRun = frame->GetTextRun(mWhichTextRun);
    1579               0 :       mCurrentRunContextInfo = mNextRunContextInfo;
    1580                 :     }
    1581               0 :     return;
    1582                 :   }
    1583                 : 
    1584                 :   FrameTextTraversal traversal =
    1585               0 :     CanTextCrossFrameBoundary(aFrame, frameType);
    1586               0 :   bool isBR = frameType == nsGkAtoms::brFrame;
    1587               0 :   if (!traversal.mLineBreakerCanCrossFrameBoundary) {
    1588                 :     // BR frames are special. We do not need or want to record a break opportunity
    1589                 :     // before a BR frame.
    1590               0 :     FlushFrames(true, isBR);
    1591               0 :     mCommonAncestorWithLastFrame = aFrame;
    1592               0 :     mNextRunContextInfo &= ~nsTextFrameUtils::INCOMING_WHITESPACE;
    1593               0 :     mStartOfLine = false;
    1594               0 :   } else if (!traversal.mTextRunCanCrossFrameBoundary) {
    1595               0 :     FlushFrames(false, false);
    1596                 :   }
    1597                 : 
    1598               0 :   for (nsIFrame* f = traversal.NextFrameToScan(); f;
    1599                 :        f = traversal.NextFrameToScan()) {
    1600               0 :     ScanFrame(f);
    1601                 :   }
    1602                 : 
    1603               0 :   if (!traversal.mLineBreakerCanCrossFrameBoundary) {
    1604                 :     // Really if we're a BR frame this is unnecessary since descendInto will be
    1605                 :     // false. In fact this whole "if" statement should move into the descendInto.
    1606               0 :     FlushFrames(true, isBR);
    1607               0 :     mCommonAncestorWithLastFrame = aFrame;
    1608               0 :     mNextRunContextInfo &= ~nsTextFrameUtils::INCOMING_WHITESPACE;
    1609               0 :   } else if (!traversal.mTextRunCanCrossFrameBoundary) {
    1610               0 :     FlushFrames(false, false);
    1611                 :   }
    1612                 : 
    1613               0 :   LiftCommonAncestorWithLastFrameToParent(aFrame->GetParent());
    1614                 : }
    1615                 : 
    1616                 : nsTextFrame*
    1617               0 : BuildTextRunsScanner::GetNextBreakBeforeFrame(PRUint32* aIndex)
    1618                 : {
    1619               0 :   PRUint32 index = *aIndex;
    1620               0 :   if (index >= mLineBreakBeforeFrames.Length())
    1621               0 :     return nsnull;
    1622               0 :   *aIndex = index + 1;
    1623               0 :   return static_cast<nsTextFrame*>(mLineBreakBeforeFrames.ElementAt(index));
    1624                 : }
    1625                 : 
    1626                 : static PRUint32
    1627               0 : GetSpacingFlags(nscoord spacing)
    1628                 : {
    1629               0 :   return spacing ? gfxTextRunFactory::TEXT_ENABLE_SPACING : 0;
    1630                 : }
    1631                 : 
    1632                 : static gfxFontGroup*
    1633               0 : GetFontGroupForFrame(nsIFrame* aFrame, float aFontSizeInflation,
    1634                 :                      nsFontMetrics** aOutFontMetrics = nsnull)
    1635                 : {
    1636               0 :   if (aOutFontMetrics)
    1637               0 :     *aOutFontMetrics = nsnull;
    1638                 : 
    1639               0 :   nsRefPtr<nsFontMetrics> metrics;
    1640                 :   nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(metrics),
    1641               0 :                                         aFontSizeInflation);
    1642                 : 
    1643               0 :   if (!metrics)
    1644               0 :     return nsnull;
    1645                 : 
    1646               0 :   if (aOutFontMetrics) {
    1647               0 :     *aOutFontMetrics = metrics;
    1648               0 :     NS_ADDREF(*aOutFontMetrics);
    1649                 :   }
    1650                 :   // XXX this is a bit bogus, we're releasing 'metrics' so the
    1651                 :   // returned font-group might actually be torn down, although because
    1652                 :   // of the way the device context caches font metrics, this seems to
    1653                 :   // not actually happen. But we should fix this.
    1654               0 :   return metrics->GetThebesFontGroup();
    1655                 : }
    1656                 : 
    1657                 : static already_AddRefed<gfxContext>
    1658               0 : GetReferenceRenderingContext(nsTextFrame* aTextFrame, nsRenderingContext* aRC)
    1659                 : {
    1660               0 :   nsRefPtr<nsRenderingContext> tmp = aRC;
    1661               0 :   if (!tmp) {
    1662               0 :     tmp = aTextFrame->PresContext()->PresShell()->GetReferenceRenderingContext();
    1663               0 :     if (!tmp)
    1664               0 :       return nsnull;
    1665                 :   }
    1666                 : 
    1667               0 :   gfxContext* ctx = tmp->ThebesContext();
    1668               0 :   NS_ADDREF(ctx);
    1669               0 :   return ctx;
    1670                 : }
    1671                 : 
    1672                 : /**
    1673                 :  * The returned textrun must be deleted when no longer needed.
    1674                 :  */
    1675                 : static gfxTextRun*
    1676               0 : GetHyphenTextRun(gfxTextRun* aTextRun, gfxContext* aContext, nsTextFrame* aTextFrame)
    1677                 : {
    1678               0 :   nsRefPtr<gfxContext> ctx = aContext;
    1679               0 :   if (!ctx) {
    1680               0 :     ctx = GetReferenceRenderingContext(aTextFrame, nsnull);
    1681                 :   }
    1682               0 :   if (!ctx)
    1683               0 :     return nsnull;
    1684                 : 
    1685               0 :   gfxFontGroup* fontGroup = aTextRun->GetFontGroup();
    1686               0 :   PRUint32 flags = gfxFontGroup::TEXT_IS_PERSISTENT;
    1687                 : 
    1688                 :   // only use U+2010 if it is supported by the first font in the group;
    1689                 :   // it's better to use ASCII '-' from the primary font than to fall back to U+2010
    1690                 :   // from some other, possibly poorly-matching face
    1691                 :   static const PRUnichar unicodeHyphen = 0x2010;
    1692               0 :   gfxFont *font = fontGroup->GetFontAt(0);
    1693               0 :   if (font && font->HasCharacter(unicodeHyphen)) {
    1694                 :     return fontGroup->MakeTextRun(&unicodeHyphen, 1, ctx,
    1695               0 :                                   aTextRun->GetAppUnitsPerDevUnit(), flags);
    1696                 :   }
    1697                 : 
    1698                 :   static const PRUint8 dash = '-';
    1699                 :   return fontGroup->MakeTextRun(&dash, 1, ctx,
    1700               0 :                                 aTextRun->GetAppUnitsPerDevUnit(), flags);
    1701                 : }
    1702                 : 
    1703                 : static gfxFont::Metrics
    1704               0 : GetFirstFontMetrics(gfxFontGroup* aFontGroup)
    1705                 : {
    1706               0 :   if (!aFontGroup)
    1707               0 :     return gfxFont::Metrics();
    1708               0 :   gfxFont* font = aFontGroup->GetFontAt(0);
    1709               0 :   if (!font)
    1710               0 :     return gfxFont::Metrics();
    1711               0 :   return font->GetMetrics();
    1712                 : }
    1713                 : 
    1714                 : PR_STATIC_ASSERT(NS_STYLE_WHITESPACE_NORMAL == 0);
    1715                 : PR_STATIC_ASSERT(NS_STYLE_WHITESPACE_PRE == 1);
    1716                 : PR_STATIC_ASSERT(NS_STYLE_WHITESPACE_NOWRAP == 2);
    1717                 : PR_STATIC_ASSERT(NS_STYLE_WHITESPACE_PRE_WRAP == 3);
    1718                 : PR_STATIC_ASSERT(NS_STYLE_WHITESPACE_PRE_LINE == 4);
    1719                 : 
    1720                 : static const nsTextFrameUtils::CompressionMode CSSWhitespaceToCompressionMode[] =
    1721                 : {
    1722                 :   nsTextFrameUtils::COMPRESS_WHITESPACE_NEWLINE, // normal
    1723                 :   nsTextFrameUtils::COMPRESS_NONE,               // pre
    1724                 :   nsTextFrameUtils::COMPRESS_WHITESPACE_NEWLINE, // nowrap
    1725                 :   nsTextFrameUtils::COMPRESS_NONE,               // pre-wrap
    1726                 :   nsTextFrameUtils::COMPRESS_WHITESPACE          // pre-line
    1727                 : };
    1728                 : 
    1729                 : gfxTextRun*
    1730               0 : BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
    1731                 : {
    1732               0 :   gfxSkipCharsBuilder builder;
    1733                 : 
    1734               0 :   const void* textPtr = aTextBuffer;
    1735               0 :   bool anySmallcapsStyle = false;
    1736               0 :   bool anyTextTransformStyle = false;
    1737               0 :   PRUint32 textFlags = nsTextFrameUtils::TEXT_NO_BREAKS;
    1738                 : 
    1739               0 :   if (mCurrentRunContextInfo & nsTextFrameUtils::INCOMING_WHITESPACE) {
    1740               0 :     textFlags |= nsTextFrameUtils::TEXT_INCOMING_WHITESPACE;
    1741                 :   }
    1742               0 :   if (mCurrentRunContextInfo & nsTextFrameUtils::INCOMING_ARABICCHAR) {
    1743               0 :     textFlags |= gfxTextRunFactory::TEXT_INCOMING_ARABICCHAR;
    1744                 :   }
    1745                 : 
    1746               0 :   nsAutoTArray<PRInt32,50> textBreakPoints;
    1747                 :   TextRunUserData dummyData;
    1748                 :   TextRunMappedFlow dummyMappedFlow;
    1749                 : 
    1750                 :   TextRunUserData* userData;
    1751                 :   TextRunUserData* userDataToDestroy;
    1752                 :   // If the situation is particularly simple (and common) we don't need to
    1753                 :   // allocate userData.
    1754               0 :   if (mMappedFlows.Length() == 1 && !mMappedFlows[0].mEndFrame &&
    1755               0 :       mMappedFlows[0].mStartFrame->GetContentOffset() == 0) {
    1756               0 :     userData = &dummyData;
    1757               0 :     userDataToDestroy = nsnull;
    1758               0 :     dummyData.mMappedFlows = &dummyMappedFlow;
    1759                 :   } else {
    1760                 :     userData = static_cast<TextRunUserData*>
    1761               0 :       (nsMemory::Alloc(sizeof(TextRunUserData) + mMappedFlows.Length()*sizeof(TextRunMappedFlow)));
    1762               0 :     userDataToDestroy = userData;
    1763               0 :     userData->mMappedFlows = reinterpret_cast<TextRunMappedFlow*>(userData + 1);
    1764                 :   }
    1765               0 :   userData->mMappedFlowCount = mMappedFlows.Length();
    1766               0 :   userData->mLastFlowIndex = 0;
    1767                 : 
    1768               0 :   PRUint32 currentTransformedTextOffset = 0;
    1769                 : 
    1770               0 :   PRUint32 nextBreakIndex = 0;
    1771               0 :   nsTextFrame* nextBreakBeforeFrame = GetNextBreakBeforeFrame(&nextBreakIndex);
    1772                 :   bool enabledJustification = mLineContainer &&
    1773               0 :     (mLineContainer->GetStyleText()->mTextAlign == NS_STYLE_TEXT_ALIGN_JUSTIFY ||
    1774               0 :      mLineContainer->GetStyleText()->mTextAlignLast == NS_STYLE_TEXT_ALIGN_JUSTIFY);
    1775                 : 
    1776                 :   PRUint32 i;
    1777               0 :   const nsStyleText* textStyle = nsnull;
    1778               0 :   const nsStyleFont* fontStyle = nsnull;
    1779               0 :   nsStyleContext* lastStyleContext = nsnull;
    1780               0 :   for (i = 0; i < mMappedFlows.Length(); ++i) {
    1781               0 :     MappedFlow* mappedFlow = &mMappedFlows[i];
    1782               0 :     nsTextFrame* f = mappedFlow->mStartFrame;
    1783                 : 
    1784               0 :     lastStyleContext = f->GetStyleContext();
    1785                 :     // Detect use of text-transform or font-variant anywhere in the run
    1786               0 :     textStyle = f->GetStyleText();
    1787               0 :     if (NS_STYLE_TEXT_TRANSFORM_NONE != textStyle->mTextTransform) {
    1788               0 :       anyTextTransformStyle = true;
    1789                 :     }
    1790               0 :     textFlags |= GetSpacingFlags(StyleToCoord(textStyle->mLetterSpacing));
    1791               0 :     textFlags |= GetSpacingFlags(textStyle->mWordSpacing);
    1792                 :     nsTextFrameUtils::CompressionMode compression =
    1793               0 :       CSSWhitespaceToCompressionMode[textStyle->mWhiteSpace];
    1794               0 :     if (enabledJustification && !textStyle->WhiteSpaceIsSignificant()) {
    1795               0 :       textFlags |= gfxTextRunFactory::TEXT_ENABLE_SPACING;
    1796                 :     }
    1797               0 :     fontStyle = f->GetStyleFont();
    1798               0 :     if (NS_STYLE_FONT_VARIANT_SMALL_CAPS == fontStyle->mFont.variant) {
    1799               0 :       anySmallcapsStyle = true;
    1800                 :     }
    1801                 : 
    1802                 :     // Figure out what content is included in this flow.
    1803               0 :     nsIContent* content = f->GetContent();
    1804               0 :     const nsTextFragment* frag = content->GetText();
    1805               0 :     PRInt32 contentStart = mappedFlow->mStartFrame->GetContentOffset();
    1806               0 :     PRInt32 contentEnd = mappedFlow->GetContentEnd();
    1807               0 :     PRInt32 contentLength = contentEnd - contentStart;
    1808                 : 
    1809               0 :     TextRunMappedFlow* newFlow = &userData->mMappedFlows[i];
    1810               0 :     newFlow->mStartFrame = mappedFlow->mStartFrame;
    1811               0 :     newFlow->mDOMOffsetToBeforeTransformOffset = builder.GetCharCount() -
    1812               0 :       mappedFlow->mStartFrame->GetContentOffset();
    1813               0 :     newFlow->mContentLength = contentLength;
    1814                 : 
    1815               0 :     while (nextBreakBeforeFrame && nextBreakBeforeFrame->GetContent() == content) {
    1816                 :       textBreakPoints.AppendElement(
    1817               0 :           nextBreakBeforeFrame->GetContentOffset() + newFlow->mDOMOffsetToBeforeTransformOffset);
    1818               0 :       nextBreakBeforeFrame = GetNextBreakBeforeFrame(&nextBreakIndex);
    1819                 :     }
    1820                 : 
    1821                 :     PRUint32 analysisFlags;
    1822               0 :     if (frag->Is2b()) {
    1823               0 :       NS_ASSERTION(mDoubleByteText, "Wrong buffer char size!");
    1824               0 :       PRUnichar* bufStart = static_cast<PRUnichar*>(aTextBuffer);
    1825                 :       PRUnichar* bufEnd = nsTextFrameUtils::TransformText(
    1826               0 :           frag->Get2b() + contentStart, contentLength, bufStart,
    1827               0 :           compression, &mNextRunContextInfo, &builder, &analysisFlags);
    1828               0 :       aTextBuffer = bufEnd;
    1829                 :     } else {
    1830               0 :       if (mDoubleByteText) {
    1831                 :         // Need to expand the text. First transform it into a temporary buffer,
    1832                 :         // then expand.
    1833               0 :         AutoFallibleTArray<PRUint8,BIG_TEXT_NODE_SIZE> tempBuf;
    1834               0 :         PRUint8* bufStart = tempBuf.AppendElements(contentLength);
    1835               0 :         if (!bufStart) {
    1836               0 :           DestroyUserData(userDataToDestroy);
    1837               0 :           return nsnull;
    1838                 :         }
    1839                 :         PRUint8* end = nsTextFrameUtils::TransformText(
    1840               0 :             reinterpret_cast<const PRUint8*>(frag->Get1b()) + contentStart, contentLength,
    1841               0 :             bufStart, compression, &mNextRunContextInfo, &builder, &analysisFlags);
    1842                 :         aTextBuffer = ExpandBuffer(static_cast<PRUnichar*>(aTextBuffer),
    1843               0 :                                    tempBuf.Elements(), end - tempBuf.Elements());
    1844                 :       } else {
    1845               0 :         PRUint8* bufStart = static_cast<PRUint8*>(aTextBuffer);
    1846                 :         PRUint8* end = nsTextFrameUtils::TransformText(
    1847               0 :             reinterpret_cast<const PRUint8*>(frag->Get1b()) + contentStart, contentLength,
    1848               0 :             bufStart, compression, &mNextRunContextInfo, &builder, &analysisFlags);
    1849               0 :         aTextBuffer = end;
    1850                 :       }
    1851                 :     }
    1852               0 :     textFlags |= analysisFlags;
    1853                 : 
    1854                 :     currentTransformedTextOffset =
    1855               0 :       (static_cast<const PRUint8*>(aTextBuffer) - static_cast<const PRUint8*>(textPtr)) >> mDoubleByteText;
    1856                 :   }
    1857                 : 
    1858                 :   // Check for out-of-memory in gfxSkipCharsBuilder
    1859               0 :   if (!builder.IsOK()) {
    1860               0 :     DestroyUserData(userDataToDestroy);
    1861               0 :     return nsnull;
    1862                 :   }
    1863                 : 
    1864                 :   void* finalUserData;
    1865               0 :   if (userData == &dummyData) {
    1866               0 :     textFlags |= nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW;
    1867               0 :     userData = nsnull;
    1868               0 :     finalUserData = mMappedFlows[0].mStartFrame;
    1869                 :   } else {
    1870               0 :     finalUserData = userData;
    1871                 :   }
    1872                 : 
    1873               0 :   PRUint32 transformedLength = currentTransformedTextOffset;
    1874                 : 
    1875                 :   // Now build the textrun
    1876               0 :   nsTextFrame* firstFrame = mMappedFlows[0].mStartFrame;
    1877               0 :   gfxFontGroup* fontGroup = GetFontGroupForFrame(firstFrame, mInflation);
    1878               0 :   if (!fontGroup) {
    1879               0 :     DestroyUserData(userDataToDestroy);
    1880               0 :     return nsnull;
    1881                 :   }
    1882                 : 
    1883               0 :   if (textFlags & nsTextFrameUtils::TEXT_HAS_TAB) {
    1884               0 :     textFlags |= gfxTextRunFactory::TEXT_ENABLE_SPACING;
    1885                 :   }
    1886               0 :   if (textFlags & nsTextFrameUtils::TEXT_HAS_SHY) {
    1887               0 :     textFlags |= gfxTextRunFactory::TEXT_ENABLE_HYPHEN_BREAKS;
    1888                 :   }
    1889               0 :   if (mBidiEnabled && (NS_GET_EMBEDDING_LEVEL(firstFrame) & 1)) {
    1890               0 :     textFlags |= gfxTextRunFactory::TEXT_IS_RTL;
    1891                 :   }
    1892               0 :   if (mNextRunContextInfo & nsTextFrameUtils::INCOMING_WHITESPACE) {
    1893               0 :     textFlags |= nsTextFrameUtils::TEXT_TRAILING_WHITESPACE;
    1894                 :   }
    1895               0 :   if (mNextRunContextInfo & nsTextFrameUtils::INCOMING_ARABICCHAR) {
    1896               0 :     textFlags |= gfxTextRunFactory::TEXT_TRAILING_ARABICCHAR;
    1897                 :   }
    1898                 :   // ContinueTextRunAcrossFrames guarantees that it doesn't matter which
    1899                 :   // frame's style is used, so use the last frame's
    1900                 :   textFlags |= nsLayoutUtils::GetTextRunFlagsForStyle(lastStyleContext,
    1901               0 :       textStyle, fontStyle);
    1902                 :   // XXX this is a bit of a hack. For performance reasons, if we're favouring
    1903                 :   // performance over quality, don't try to get accurate glyph extents.
    1904               0 :   if (!(textFlags & gfxTextRunFactory::TEXT_OPTIMIZE_SPEED)) {
    1905               0 :     textFlags |= gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX;
    1906                 :   }
    1907                 : 
    1908               0 :   gfxSkipChars skipChars;
    1909               0 :   skipChars.TakeFrom(&builder);
    1910                 :   // Convert linebreak coordinates to transformed string offsets
    1911               0 :   NS_ASSERTION(nextBreakIndex == mLineBreakBeforeFrames.Length(),
    1912                 :                "Didn't find all the frames to break-before...");
    1913               0 :   gfxSkipCharsIterator iter(skipChars);
    1914               0 :   nsAutoTArray<PRUint32,50> textBreakPointsAfterTransform;
    1915               0 :   for (i = 0; i < textBreakPoints.Length(); ++i) {
    1916                 :     nsTextFrameUtils::AppendLineBreakOffset(&textBreakPointsAfterTransform, 
    1917               0 :             iter.ConvertOriginalToSkipped(textBreakPoints[i]));
    1918                 :   }
    1919               0 :   if (mStartOfLine) {
    1920                 :     nsTextFrameUtils::AppendLineBreakOffset(&textBreakPointsAfterTransform,
    1921               0 :                                             transformedLength);
    1922                 :   }
    1923                 : 
    1924                 :   // Setup factory chain
    1925               0 :   nsAutoPtr<nsTransformingTextRunFactory> transformingFactory;
    1926               0 :   if (anySmallcapsStyle) {
    1927               0 :     transformingFactory = new nsFontVariantTextRunFactory();
    1928                 :   }
    1929               0 :   if (anyTextTransformStyle) {
    1930                 :     transformingFactory =
    1931               0 :       new nsCaseTransformTextRunFactory(transformingFactory.forget());
    1932                 :   }
    1933               0 :   nsTArray<nsStyleContext*> styles;
    1934               0 :   if (transformingFactory) {
    1935               0 :     iter.SetOriginalOffset(0);
    1936               0 :     for (i = 0; i < mMappedFlows.Length(); ++i) {
    1937               0 :       MappedFlow* mappedFlow = &mMappedFlows[i];
    1938                 :       nsTextFrame* f;
    1939               0 :       for (f = mappedFlow->mStartFrame; f != mappedFlow->mEndFrame;
    1940               0 :            f = static_cast<nsTextFrame*>(f->GetNextContinuation())) {
    1941               0 :         PRUint32 offset = iter.GetSkippedOffset();
    1942               0 :         iter.AdvanceOriginal(f->GetContentLength());
    1943               0 :         PRUint32 end = iter.GetSkippedOffset();
    1944               0 :         nsStyleContext* sc = f->GetStyleContext();
    1945                 :         PRUint32 j;
    1946               0 :         for (j = offset; j < end; ++j) {
    1947               0 :           styles.AppendElement(sc);
    1948                 :         }
    1949                 :       }
    1950                 :     }
    1951               0 :     textFlags |= nsTextFrameUtils::TEXT_IS_TRANSFORMED;
    1952               0 :     NS_ASSERTION(iter.GetSkippedOffset() == transformedLength,
    1953                 :                  "We didn't cover all the characters in the text run!");
    1954                 :   }
    1955                 : 
    1956                 :   gfxTextRun* textRun;
    1957                 :   gfxTextRunFactory::Parameters params =
    1958                 :       { mContext, finalUserData, &skipChars,
    1959               0 :         textBreakPointsAfterTransform.Elements(), textBreakPointsAfterTransform.Length(),
    1960               0 :         firstFrame->PresContext()->AppUnitsPerDevPixel() };
    1961                 : 
    1962               0 :   if (mDoubleByteText) {
    1963               0 :     const PRUnichar* text = static_cast<const PRUnichar*>(textPtr);
    1964               0 :     if (transformingFactory) {
    1965                 :       textRun = transformingFactory->MakeTextRun(text, transformedLength, &params,
    1966               0 :                                                  fontGroup, textFlags, styles.Elements());
    1967               0 :       if (textRun) {
    1968                 :         // ownership of the factory has passed to the textrun
    1969               0 :         transformingFactory.forget();
    1970                 :       }
    1971                 :     } else {
    1972               0 :       textRun = MakeTextRun(text, transformedLength, fontGroup, &params, textFlags);
    1973                 :     }
    1974                 :   } else {
    1975               0 :     const PRUint8* text = static_cast<const PRUint8*>(textPtr);
    1976               0 :     textFlags |= gfxFontGroup::TEXT_IS_8BIT;
    1977               0 :     if (transformingFactory) {
    1978                 :       textRun = transformingFactory->MakeTextRun(text, transformedLength, &params,
    1979               0 :                                                  fontGroup, textFlags, styles.Elements());
    1980               0 :       if (textRun) {
    1981                 :         // ownership of the factory has passed to the textrun
    1982               0 :         transformingFactory.forget();
    1983                 :       }
    1984                 :     } else {
    1985               0 :       textRun = MakeTextRun(text, transformedLength, fontGroup, &params, textFlags);
    1986                 :     }
    1987                 :   }
    1988               0 :   if (!textRun) {
    1989               0 :     DestroyUserData(userDataToDestroy);
    1990               0 :     return nsnull;
    1991                 :   }
    1992                 : 
    1993                 :   // We have to set these up after we've created the textrun, because
    1994                 :   // the breaks may be stored in the textrun during this very call.
    1995                 :   // This is a bit annoying because it requires another loop over the frames
    1996                 :   // making up the textrun, but I don't see a way to avoid this.
    1997               0 :   PRUint32 flags = 0;
    1998               0 :   if (mDoubleByteText) {
    1999               0 :     flags |= SBS_DOUBLE_BYTE;
    2000                 :   }
    2001               0 :   if (mSkipIncompleteTextRuns) {
    2002               0 :     flags |= SBS_SUPPRESS_SINK;
    2003                 :   }
    2004               0 :   SetupBreakSinksForTextRun(textRun, textPtr, flags);
    2005                 : 
    2006               0 :   if (mSkipIncompleteTextRuns) {
    2007                 :     mSkipIncompleteTextRuns = !TextContainsLineBreakerWhiteSpace(textPtr,
    2008               0 :         transformedLength, mDoubleByteText);
    2009                 :     // Arrange for this textrun to be deleted the next time the linebreaker
    2010                 :     // is flushed out
    2011               0 :     mTextRunsToDelete.AppendElement(textRun);
    2012                 :     // Since we're doing to destroy the user data now, avoid a dangling
    2013                 :     // pointer. Strictly speaking we don't need to do this since it should
    2014                 :     // not be used (since this textrun will not be used and will be
    2015                 :     // itself deleted soon), but it's always better to not have dangling
    2016                 :     // pointers around.
    2017               0 :     textRun->SetUserData(nsnull);
    2018               0 :     DestroyUserData(userDataToDestroy);
    2019               0 :     return nsnull;
    2020                 :   }
    2021                 : 
    2022                 :   // Actually wipe out the textruns associated with the mapped frames and associate
    2023                 :   // those frames with this text run.
    2024               0 :   AssignTextRun(textRun);
    2025               0 :   return textRun;
    2026                 : }
    2027                 : 
    2028                 : // This is a cut-down version of BuildTextRunForFrames used to set up
    2029                 : // context for the line-breaker, when the textrun has already been created.
    2030                 : // So it does the same walk over the mMappedFlows, but doesn't actually
    2031                 : // build a new textrun.
    2032                 : bool
    2033               0 : BuildTextRunsScanner::SetupLineBreakerContext(gfxTextRun *aTextRun)
    2034                 : {
    2035               0 :   AutoFallibleTArray<PRUint8,BIG_TEXT_NODE_SIZE> buffer;
    2036               0 :   PRUint32 bufferSize = mMaxTextLength*(mDoubleByteText ? 2 : 1);
    2037               0 :   if (bufferSize < mMaxTextLength || bufferSize == PR_UINT32_MAX) {
    2038               0 :     return false;
    2039                 :   }
    2040               0 :   void *textPtr = buffer.AppendElements(bufferSize);
    2041               0 :   if (!textPtr) {
    2042               0 :     return false;
    2043                 :   }
    2044                 : 
    2045               0 :   gfxSkipCharsBuilder builder;
    2046                 : 
    2047               0 :   nsAutoTArray<PRInt32,50> textBreakPoints;
    2048                 :   TextRunUserData dummyData;
    2049                 :   TextRunMappedFlow dummyMappedFlow;
    2050                 : 
    2051                 :   TextRunUserData* userData;
    2052                 :   TextRunUserData* userDataToDestroy;
    2053                 :   // If the situation is particularly simple (and common) we don't need to
    2054                 :   // allocate userData.
    2055               0 :   if (mMappedFlows.Length() == 1 && !mMappedFlows[0].mEndFrame &&
    2056               0 :       mMappedFlows[0].mStartFrame->GetContentOffset() == 0) {
    2057               0 :     userData = &dummyData;
    2058               0 :     userDataToDestroy = nsnull;
    2059               0 :     dummyData.mMappedFlows = &dummyMappedFlow;
    2060                 :   } else {
    2061                 :     userData = static_cast<TextRunUserData*>
    2062               0 :       (nsMemory::Alloc(sizeof(TextRunUserData) + mMappedFlows.Length()*sizeof(TextRunMappedFlow)));
    2063               0 :     userDataToDestroy = userData;
    2064               0 :     userData->mMappedFlows = reinterpret_cast<TextRunMappedFlow*>(userData + 1);
    2065                 :   }
    2066               0 :   userData->mMappedFlowCount = mMappedFlows.Length();
    2067               0 :   userData->mLastFlowIndex = 0;
    2068                 : 
    2069               0 :   PRUint32 nextBreakIndex = 0;
    2070               0 :   nsTextFrame* nextBreakBeforeFrame = GetNextBreakBeforeFrame(&nextBreakIndex);
    2071                 : 
    2072                 :   PRUint32 i;
    2073               0 :   const nsStyleText* textStyle = nsnull;
    2074               0 :   for (i = 0; i < mMappedFlows.Length(); ++i) {
    2075               0 :     MappedFlow* mappedFlow = &mMappedFlows[i];
    2076               0 :     nsTextFrame* f = mappedFlow->mStartFrame;
    2077                 : 
    2078               0 :     textStyle = f->GetStyleText();
    2079                 :     nsTextFrameUtils::CompressionMode compression =
    2080               0 :       CSSWhitespaceToCompressionMode[textStyle->mWhiteSpace];
    2081                 : 
    2082                 :     // Figure out what content is included in this flow.
    2083               0 :     nsIContent* content = f->GetContent();
    2084               0 :     const nsTextFragment* frag = content->GetText();
    2085               0 :     PRInt32 contentStart = mappedFlow->mStartFrame->GetContentOffset();
    2086               0 :     PRInt32 contentEnd = mappedFlow->GetContentEnd();
    2087               0 :     PRInt32 contentLength = contentEnd - contentStart;
    2088                 : 
    2089               0 :     TextRunMappedFlow* newFlow = &userData->mMappedFlows[i];
    2090               0 :     newFlow->mStartFrame = mappedFlow->mStartFrame;
    2091               0 :     newFlow->mDOMOffsetToBeforeTransformOffset = builder.GetCharCount() -
    2092               0 :       mappedFlow->mStartFrame->GetContentOffset();
    2093               0 :     newFlow->mContentLength = contentLength;
    2094                 : 
    2095               0 :     while (nextBreakBeforeFrame && nextBreakBeforeFrame->GetContent() == content) {
    2096                 :       textBreakPoints.AppendElement(
    2097               0 :           nextBreakBeforeFrame->GetContentOffset() + newFlow->mDOMOffsetToBeforeTransformOffset);
    2098               0 :       nextBreakBeforeFrame = GetNextBreakBeforeFrame(&nextBreakIndex);
    2099                 :     }
    2100                 : 
    2101                 :     PRUint32 analysisFlags;
    2102               0 :     if (frag->Is2b()) {
    2103               0 :       NS_ASSERTION(mDoubleByteText, "Wrong buffer char size!");
    2104               0 :       PRUnichar* bufStart = static_cast<PRUnichar*>(textPtr);
    2105                 :       PRUnichar* bufEnd = nsTextFrameUtils::TransformText(
    2106               0 :           frag->Get2b() + contentStart, contentLength, bufStart,
    2107               0 :           compression, &mNextRunContextInfo, &builder, &analysisFlags);
    2108               0 :       textPtr = bufEnd;
    2109                 :     } else {
    2110               0 :       if (mDoubleByteText) {
    2111                 :         // Need to expand the text. First transform it into a temporary buffer,
    2112                 :         // then expand.
    2113               0 :         AutoFallibleTArray<PRUint8,BIG_TEXT_NODE_SIZE> tempBuf;
    2114               0 :         PRUint8* bufStart = tempBuf.AppendElements(contentLength);
    2115               0 :         if (!bufStart) {
    2116               0 :           DestroyUserData(userDataToDestroy);
    2117               0 :           return false;
    2118                 :         }
    2119                 :         PRUint8* end = nsTextFrameUtils::TransformText(
    2120               0 :             reinterpret_cast<const PRUint8*>(frag->Get1b()) + contentStart, contentLength,
    2121               0 :             bufStart, compression, &mNextRunContextInfo, &builder, &analysisFlags);
    2122                 :         textPtr = ExpandBuffer(static_cast<PRUnichar*>(textPtr),
    2123               0 :                                tempBuf.Elements(), end - tempBuf.Elements());
    2124                 :       } else {
    2125               0 :         PRUint8* bufStart = static_cast<PRUint8*>(textPtr);
    2126                 :         PRUint8* end = nsTextFrameUtils::TransformText(
    2127               0 :             reinterpret_cast<const PRUint8*>(frag->Get1b()) + contentStart, contentLength,
    2128               0 :             bufStart, compression, &mNextRunContextInfo, &builder, &analysisFlags);
    2129               0 :         textPtr = end;
    2130                 :       }
    2131                 :     }
    2132                 :   }
    2133                 : 
    2134                 :   // We have to set these up after we've created the textrun, because
    2135                 :   // the breaks may be stored in the textrun during this very call.
    2136                 :   // This is a bit annoying because it requires another loop over the frames
    2137                 :   // making up the textrun, but I don't see a way to avoid this.
    2138               0 :   PRUint32 flags = 0;
    2139               0 :   if (mDoubleByteText) {
    2140               0 :     flags |= SBS_DOUBLE_BYTE;
    2141                 :   }
    2142               0 :   if (mSkipIncompleteTextRuns) {
    2143               0 :     flags |= SBS_SUPPRESS_SINK;
    2144                 :   }
    2145               0 :   SetupBreakSinksForTextRun(aTextRun, buffer.Elements(), flags);
    2146                 : 
    2147               0 :   DestroyUserData(userDataToDestroy);
    2148                 : 
    2149               0 :   return true;
    2150                 : }
    2151                 : 
    2152                 : static bool
    2153               0 : HasCompressedLeadingWhitespace(nsTextFrame* aFrame, const nsStyleText* aStyleText,
    2154                 :                                PRInt32 aContentEndOffset,
    2155                 :                                const gfxSkipCharsIterator& aIterator)
    2156                 : {
    2157               0 :   if (!aIterator.IsOriginalCharSkipped())
    2158               0 :     return false;
    2159                 : 
    2160               0 :   gfxSkipCharsIterator iter = aIterator;
    2161               0 :   PRInt32 frameContentOffset = aFrame->GetContentOffset();
    2162               0 :   const nsTextFragment* frag = aFrame->GetContent()->GetText();
    2163               0 :   while (frameContentOffset < aContentEndOffset && iter.IsOriginalCharSkipped()) {
    2164               0 :     if (IsTrimmableSpace(frag, frameContentOffset, aStyleText))
    2165               0 :       return true;
    2166               0 :     ++frameContentOffset;
    2167               0 :     iter.AdvanceOriginal(1);
    2168                 :   }
    2169               0 :   return false;
    2170                 : }
    2171                 : 
    2172                 : void
    2173               0 : BuildTextRunsScanner::SetupBreakSinksForTextRun(gfxTextRun* aTextRun,
    2174                 :                                                 const void* aTextPtr,
    2175                 :                                                 PRUint32    aFlags)
    2176                 : {
    2177                 :   // textruns have uniform language
    2178               0 :   nsIAtom* language = mMappedFlows[0].mStartFrame->GetStyleFont()->mLanguage;
    2179                 :   // We keep this pointed at the skip-chars data for the current mappedFlow.
    2180                 :   // This lets us cheaply check whether the flow has compressed initial
    2181                 :   // whitespace...
    2182               0 :   gfxSkipCharsIterator iter(aTextRun->GetSkipChars());
    2183                 : 
    2184                 :   PRUint32 i;
    2185               0 :   for (i = 0; i < mMappedFlows.Length(); ++i) {
    2186               0 :     MappedFlow* mappedFlow = &mMappedFlows[i];
    2187               0 :     PRUint32 offset = iter.GetSkippedOffset();
    2188               0 :     gfxSkipCharsIterator iterNext = iter;
    2189               0 :     iterNext.AdvanceOriginal(mappedFlow->GetContentEnd() -
    2190               0 :             mappedFlow->mStartFrame->GetContentOffset());
    2191                 : 
    2192                 :     nsAutoPtr<BreakSink>* breakSink = mBreakSinks.AppendElement(
    2193                 :       new BreakSink(aTextRun, mContext, offset,
    2194               0 :                     (aFlags & SBS_EXISTING_TEXTRUN) != 0));
    2195               0 :     if (!breakSink || !*breakSink)
    2196               0 :       return;
    2197                 : 
    2198               0 :     PRUint32 length = iterNext.GetSkippedOffset() - offset;
    2199               0 :     PRUint32 flags = 0;
    2200               0 :     nsIFrame* initialBreakController = mappedFlow->mAncestorControllingInitialBreak;
    2201               0 :     if (!initialBreakController) {
    2202               0 :       initialBreakController = mLineContainer;
    2203                 :     }
    2204               0 :     if (!initialBreakController->GetStyleText()->WhiteSpaceCanWrap()) {
    2205               0 :       flags |= nsLineBreaker::BREAK_SUPPRESS_INITIAL;
    2206                 :     }
    2207               0 :     nsTextFrame* startFrame = mappedFlow->mStartFrame;
    2208               0 :     const nsStyleText* textStyle = startFrame->GetStyleText();
    2209               0 :     if (!textStyle->WhiteSpaceCanWrap()) {
    2210               0 :       flags |= nsLineBreaker::BREAK_SUPPRESS_INSIDE;
    2211                 :     }
    2212               0 :     if (aTextRun->GetFlags() & nsTextFrameUtils::TEXT_NO_BREAKS) {
    2213               0 :       flags |= nsLineBreaker::BREAK_SKIP_SETTING_NO_BREAKS;
    2214                 :     }
    2215               0 :     if (textStyle->mTextTransform == NS_STYLE_TEXT_TRANSFORM_CAPITALIZE) {
    2216               0 :       flags |= nsLineBreaker::BREAK_NEED_CAPITALIZATION;
    2217                 :     }
    2218               0 :     if (textStyle->mHyphens == NS_STYLE_HYPHENS_AUTO) {
    2219               0 :       flags |= nsLineBreaker::BREAK_USE_AUTO_HYPHENATION;
    2220                 :     }
    2221                 : 
    2222               0 :     if (HasCompressedLeadingWhitespace(startFrame, textStyle,
    2223               0 :                                        mappedFlow->GetContentEnd(), iter)) {
    2224               0 :       mLineBreaker.AppendInvisibleWhitespace(flags);
    2225                 :     }
    2226                 : 
    2227               0 :     if (length > 0) {
    2228                 :       BreakSink* sink =
    2229               0 :         (aFlags & SBS_SUPPRESS_SINK) ? nsnull : (*breakSink).get();
    2230               0 :       if (aFlags & SBS_DOUBLE_BYTE) {
    2231               0 :         const PRUnichar* text = reinterpret_cast<const PRUnichar*>(aTextPtr);
    2232                 :         mLineBreaker.AppendText(language, text + offset,
    2233               0 :                                 length, flags, sink);
    2234                 :       } else {
    2235               0 :         const PRUint8* text = reinterpret_cast<const PRUint8*>(aTextPtr);
    2236                 :         mLineBreaker.AppendText(language, text + offset,
    2237               0 :                                 length, flags, sink);
    2238                 :       }
    2239                 :     }
    2240                 :     
    2241               0 :     iter = iterNext;
    2242                 :   }
    2243                 : }
    2244                 : 
    2245                 : // Find the flow corresponding to aContent in aUserData
    2246                 : static inline TextRunMappedFlow*
    2247               0 : FindFlowForContent(TextRunUserData* aUserData, nsIContent* aContent)
    2248                 : {
    2249                 :   // Find the flow that contains us
    2250               0 :   PRInt32 i = aUserData->mLastFlowIndex;
    2251               0 :   PRInt32 delta = 1;
    2252               0 :   PRInt32 sign = 1;
    2253                 :   // Search starting at the current position and examine close-by
    2254                 :   // positions first, moving further and further away as we go.
    2255               0 :   while (i >= 0 && PRUint32(i) < aUserData->mMappedFlowCount) {
    2256               0 :     TextRunMappedFlow* flow = &aUserData->mMappedFlows[i];
    2257               0 :     if (flow->mStartFrame->GetContent() == aContent) {
    2258               0 :       return flow;
    2259                 :     }
    2260                 : 
    2261               0 :     i += delta;
    2262               0 :     sign = -sign;
    2263               0 :     delta = -delta + sign;
    2264                 :   }
    2265                 : 
    2266                 :   // We ran into an array edge.  Add |delta| to |i| once more to get
    2267                 :   // back to the side where we still need to search, then step in
    2268                 :   // the |sign| direction.
    2269               0 :   i += delta;
    2270               0 :   if (sign > 0) {
    2271               0 :     for (; i < PRInt32(aUserData->mMappedFlowCount); ++i) {
    2272               0 :       TextRunMappedFlow* flow = &aUserData->mMappedFlows[i];
    2273               0 :       if (flow->mStartFrame->GetContent() == aContent) {
    2274               0 :         return flow;
    2275                 :       }
    2276                 :     }
    2277                 :   } else {
    2278               0 :     for (; i >= 0; --i) {
    2279               0 :       TextRunMappedFlow* flow = &aUserData->mMappedFlows[i];
    2280               0 :       if (flow->mStartFrame->GetContent() == aContent) {
    2281               0 :         return flow;
    2282                 :       }
    2283                 :     }
    2284                 :   }
    2285                 : 
    2286               0 :   return nsnull;
    2287                 : }
    2288                 : 
    2289                 : void
    2290               0 : BuildTextRunsScanner::AssignTextRun(gfxTextRun* aTextRun)
    2291                 : {
    2292                 :   PRUint32 i;
    2293               0 :   for (i = 0; i < mMappedFlows.Length(); ++i) {
    2294               0 :     MappedFlow* mappedFlow = &mMappedFlows[i];
    2295               0 :     nsTextFrame* startFrame = mappedFlow->mStartFrame;
    2296               0 :     nsTextFrame* endFrame = mappedFlow->mEndFrame;
    2297                 :     nsTextFrame* f;
    2298               0 :     for (f = startFrame; f != endFrame;
    2299               0 :          f = static_cast<nsTextFrame*>(f->GetNextContinuation())) {
    2300                 : #ifdef DEBUG_roc
    2301                 :       if (f->GetTextRun(mWhichTextRun)) {
    2302                 :         gfxTextRun* textRun = f->GetTextRun(mWhichTextRun);
    2303                 :         if (textRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW) {
    2304                 :           if (mMappedFlows[0].mStartFrame != static_cast<nsTextFrame*>(textRun->GetUserData())) {
    2305                 :             NS_WARNING("REASSIGNING SIMPLE FLOW TEXT RUN!");
    2306                 :           }
    2307                 :         } else {
    2308                 :           TextRunUserData* userData =
    2309                 :             static_cast<TextRunUserData*>(textRun->GetUserData());
    2310                 :          
    2311                 :           if (userData->mMappedFlowCount >= mMappedFlows.Length() ||
    2312                 :               userData->mMappedFlows[userData->mMappedFlowCount - 1].mStartFrame !=
    2313                 :               mMappedFlows[userData->mMappedFlowCount - 1].mStartFrame) {
    2314                 :             NS_WARNING("REASSIGNING MULTIFLOW TEXT RUN (not append)!");
    2315                 :           }
    2316                 :         }
    2317                 :       }
    2318                 : #endif
    2319                 : 
    2320               0 :       gfxTextRun* oldTextRun = f->GetTextRun(mWhichTextRun);
    2321               0 :       if (oldTextRun) {
    2322               0 :         nsTextFrame* firstFrame = nsnull;
    2323               0 :         PRUint32 startOffset = 0;
    2324               0 :         if (oldTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW) {
    2325               0 :           firstFrame = static_cast<nsTextFrame*>(oldTextRun->GetUserData());
    2326                 :         }
    2327                 :         else {
    2328               0 :           TextRunUserData* userData = static_cast<TextRunUserData*>(oldTextRun->GetUserData());
    2329               0 :           firstFrame = userData->mMappedFlows[0].mStartFrame;
    2330               0 :           if (NS_UNLIKELY(f != firstFrame)) {
    2331               0 :             TextRunMappedFlow* flow = FindFlowForContent(userData, f->GetContent());
    2332               0 :             if (flow) {
    2333               0 :               startOffset = flow->mDOMOffsetToBeforeTransformOffset;
    2334                 :             }
    2335                 :             else {
    2336               0 :               NS_ERROR("Can't find flow containing frame 'f'");
    2337                 :             }
    2338                 :           }
    2339                 :         }
    2340                 : 
    2341                 :         // Optimization: if |f| is the first frame in the flow then there are no
    2342                 :         // prev-continuations that use |oldTextRun|.
    2343               0 :         nsTextFrame* clearFrom = nsnull;
    2344               0 :         if (NS_UNLIKELY(f != firstFrame)) {
    2345                 :           // If all the frames in the mapped flow starting at |f| (inclusive)
    2346                 :           // are empty then we let the prev-continuations keep the old text run.
    2347               0 :           gfxSkipCharsIterator iter(oldTextRun->GetSkipChars(), startOffset, f->GetContentOffset());
    2348               0 :           PRUint32 textRunOffset = iter.ConvertOriginalToSkipped(f->GetContentOffset());
    2349               0 :           clearFrom = textRunOffset == oldTextRun->GetLength() ? f : nsnull;
    2350                 :         }
    2351               0 :         f->ClearTextRun(clearFrom, mWhichTextRun);
    2352                 : 
    2353                 : #ifdef DEBUG
    2354               0 :         if (firstFrame && !firstFrame->GetTextRun(mWhichTextRun)) {
    2355                 :           // oldTextRun was destroyed - assert that we don't reference it.
    2356               0 :           for (PRUint32 i = 0; i < mBreakSinks.Length(); ++i) {
    2357               0 :             NS_ASSERTION(oldTextRun != mBreakSinks[i]->mTextRun,
    2358                 :                          "destroyed text run is still in use");
    2359                 :           }
    2360                 :         }
    2361                 : #endif
    2362                 :       }
    2363               0 :       f->SetTextRun(aTextRun, mWhichTextRun, mInflation);
    2364                 :     }
    2365                 :     // Set this bit now; we can't set it any earlier because
    2366                 :     // f->ClearTextRun() might clear it out.
    2367               0 :     startFrame->AddStateBits(TEXT_IN_TEXTRUN_USER_DATA);
    2368                 :   }
    2369               0 : }
    2370                 : 
    2371                 : gfxSkipCharsIterator
    2372               0 : nsTextFrame::EnsureTextRun(TextRunType aWhichTextRun,
    2373                 :                            float aInflation,
    2374                 :                            gfxContext* aReferenceContext,
    2375                 :                            nsIFrame* aLineContainer,
    2376                 :                            const nsLineList::iterator* aLine,
    2377                 :                            PRUint32* aFlowEndInTextRun)
    2378                 : {
    2379               0 :   gfxTextRun *textRun = GetTextRun(aWhichTextRun);
    2380               0 :   if (textRun && (!aLine || !(*aLine)->GetInvalidateTextRuns())) {
    2381               0 :     if (textRun->GetExpirationState()->IsTracked()) {
    2382               0 :       gTextRuns->MarkUsed(textRun);
    2383                 :     }
    2384                 :   } else {
    2385               0 :     nsRefPtr<gfxContext> ctx = aReferenceContext;
    2386               0 :     if (!ctx) {
    2387               0 :       ctx = GetReferenceRenderingContext(this, nsnull);
    2388                 :     }
    2389               0 :     if (ctx) {
    2390                 :       BuildTextRuns(ctx, this, aLineContainer, aLine, aWhichTextRun,
    2391               0 :                     aInflation);
    2392                 :     }
    2393               0 :     textRun = GetTextRun(aWhichTextRun);
    2394               0 :     if (!textRun) {
    2395                 :       // A text run was not constructed for this frame. This is bad. The caller
    2396                 :       // will check mTextRun.
    2397               0 :       static const gfxSkipChars emptySkipChars;
    2398               0 :       return gfxSkipCharsIterator(emptySkipChars, 0);
    2399                 :     }
    2400                 :   }
    2401                 : 
    2402               0 :   if (textRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SIMPLE_FLOW) {
    2403               0 :     if (aFlowEndInTextRun) {
    2404               0 :       *aFlowEndInTextRun = textRun->GetLength();
    2405                 :     }
    2406               0 :     return gfxSkipCharsIterator(textRun->GetSkipChars(), 0, mContentOffset);
    2407                 :   }
    2408                 : 
    2409               0 :   TextRunUserData* userData = static_cast<TextRunUserData*>(textRun->GetUserData());
    2410               0 :   TextRunMappedFlow* flow = FindFlowForContent(userData, mContent);
    2411               0 :   if (flow) {
    2412                 :     // Since textruns can only contain one flow for a given content element,
    2413                 :     // this must be our flow.
    2414               0 :     PRUint32 flowIndex = flow - userData->mMappedFlows;
    2415               0 :     userData->mLastFlowIndex = flowIndex;
    2416               0 :     gfxSkipCharsIterator iter(textRun->GetSkipChars(),
    2417               0 :                               flow->mDOMOffsetToBeforeTransformOffset, mContentOffset);
    2418               0 :     if (aFlowEndInTextRun) {
    2419               0 :       if (flowIndex + 1 < userData->mMappedFlowCount) {
    2420               0 :         gfxSkipCharsIterator end(textRun->GetSkipChars());
    2421                 :         *aFlowEndInTextRun = end.ConvertOriginalToSkipped(
    2422               0 :               flow[1].mStartFrame->GetContentOffset() + flow[1].mDOMOffsetToBeforeTransformOffset);
    2423                 :       } else {
    2424               0 :         *aFlowEndInTextRun = textRun->GetLength();
    2425                 :       }
    2426                 :     }
    2427               0 :     return iter;
    2428                 :   }
    2429                 : 
    2430               0 :   NS_ERROR("Can't find flow containing this frame???");
    2431               0 :   static const gfxSkipChars emptySkipChars;
    2432               0 :   return gfxSkipCharsIterator(emptySkipChars, 0);
    2433                 : }
    2434                 : 
    2435                 : static PRUint32
    2436               0 : GetEndOfTrimmedText(const nsTextFragment* aFrag, const nsStyleText* aStyleText,
    2437                 :                     PRUint32 aStart, PRUint32 aEnd,
    2438                 :                     gfxSkipCharsIterator* aIterator)
    2439                 : {
    2440               0 :   aIterator->SetSkippedOffset(aEnd);
    2441               0 :   while (aIterator->GetSkippedOffset() > aStart) {
    2442               0 :     aIterator->AdvanceSkipped(-1);
    2443               0 :     if (!IsTrimmableSpace(aFrag, aIterator->GetOriginalOffset(), aStyleText))
    2444               0 :       return aIterator->GetSkippedOffset() + 1;
    2445                 :   }
    2446               0 :   return aStart;
    2447                 : }
    2448                 : 
    2449                 : nsTextFrame::TrimmedOffsets
    2450               0 : nsTextFrame::GetTrimmedOffsets(const nsTextFragment* aFrag,
    2451                 :                                bool aTrimAfter)
    2452                 : {
    2453               0 :   NS_ASSERTION(mTextRun, "Need textrun here");
    2454                 :   // This should not be used during reflow. We need our TEXT_REFLOW_FLAGS
    2455                 :   // to be set correctly.  If our parent wasn't reflowed due to the frame
    2456                 :   // tree being too deep then the return value doesn't matter.
    2457               0 :   NS_ASSERTION(!(GetStateBits() & NS_FRAME_FIRST_REFLOW) ||
    2458                 :                (GetParent()->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE),
    2459                 :                "Can only call this on frames that have been reflowed");
    2460               0 :   NS_ASSERTION(!(GetStateBits() & NS_FRAME_IN_REFLOW),
    2461                 :                "Can only call this on frames that are not being reflowed");
    2462                 : 
    2463               0 :   TrimmedOffsets offsets = { GetContentOffset(), GetContentLength() };
    2464               0 :   const nsStyleText* textStyle = GetStyleText();
    2465                 :   // Note that pre-line newlines should still allow us to trim spaces
    2466                 :   // for display
    2467               0 :   if (textStyle->WhiteSpaceIsSignificant())
    2468               0 :     return offsets;
    2469                 : 
    2470               0 :   if (GetStateBits() & TEXT_START_OF_LINE) {
    2471                 :     PRInt32 whitespaceCount =
    2472                 :       GetTrimmableWhitespaceCount(aFrag,
    2473               0 :                                   offsets.mStart, offsets.mLength, 1);
    2474               0 :     offsets.mStart += whitespaceCount;
    2475               0 :     offsets.mLength -= whitespaceCount;
    2476                 :   }
    2477                 : 
    2478               0 :   if (aTrimAfter && (GetStateBits() & TEXT_END_OF_LINE)) {
    2479                 :     // This treats a trailing 'pre-line' newline as trimmable. That's fine,
    2480                 :     // it's actually what we want since we want whitespace before it to
    2481                 :     // be trimmed.
    2482                 :     PRInt32 whitespaceCount =
    2483                 :       GetTrimmableWhitespaceCount(aFrag,
    2484               0 :                                   offsets.GetEnd() - 1, offsets.mLength, -1);
    2485               0 :     offsets.mLength -= whitespaceCount;
    2486                 :   }
    2487               0 :   return offsets;
    2488                 : }
    2489                 : 
    2490                 : /*
    2491                 :  * Currently only Unicode characters below 0x10000 have their spacing modified
    2492                 :  * by justification. If characters above 0x10000 turn out to need
    2493                 :  * justification spacing, that will require extra work. Currently,
    2494                 :  * this function must not include 0xd800 to 0xdbff because these characters
    2495                 :  * are surrogates.
    2496                 :  */
    2497               0 : static bool IsJustifiableCharacter(const nsTextFragment* aFrag, PRInt32 aPos,
    2498                 :                                      bool aLangIsCJ)
    2499                 : {
    2500               0 :   PRUnichar ch = aFrag->CharAt(aPos);
    2501               0 :   if (ch == '\n' || ch == '\t' || ch == '\r')
    2502               0 :     return true;
    2503               0 :   if (ch == ' ' || ch == CH_NBSP) {
    2504                 :     // Don't justify spaces that are combined with diacriticals
    2505               0 :     if (!aFrag->Is2b())
    2506               0 :       return true;
    2507                 :     return !nsTextFrameUtils::IsSpaceCombiningSequenceTail(
    2508               0 :         aFrag->Get2b() + aPos + 1, aFrag->GetLength() - (aPos + 1));
    2509                 :   }
    2510               0 :   if (ch < 0x2150u)
    2511               0 :     return false;
    2512               0 :   if (aLangIsCJ && (
    2513                 :        (0x2150u <= ch && ch <= 0x22ffu) || // Number Forms, Arrows, Mathematical Operators
    2514                 :        (0x2460u <= ch && ch <= 0x24ffu) || // Enclosed Alphanumerics
    2515                 :        (0x2580u <= ch && ch <= 0x27bfu) || // Block Elements, Geometric Shapes, Miscellaneous Symbols, Dingbats
    2516                 :        (0x27f0u <= ch && ch <= 0x2bffu) || // Supplemental Arrows-A, Braille Patterns, Supplemental Arrows-B,
    2517                 :                                            // Miscellaneous Mathematical Symbols-B, Supplemental Mathematical Operators,
    2518                 :                                            // Miscellaneous Symbols and Arrows
    2519                 :        (0x2e80u <= ch && ch <= 0x312fu) || // CJK Radicals Supplement, CJK Radicals Supplement,
    2520                 :                                            // Ideographic Description Characters, CJK Symbols and Punctuation,
    2521                 :                                            // Hiragana, Katakana, Bopomofo
    2522                 :        (0x3190u <= ch && ch <= 0xabffu) || // Kanbun, Bopomofo Extended, Katakana Phonetic Extensions,
    2523                 :                                            // Enclosed CJK Letters and Months, CJK Compatibility,
    2524                 :                                            // CJK Unified Ideographs Extension A, Yijing Hexagram Symbols,
    2525                 :                                            // CJK Unified Ideographs, Yi Syllables, Yi Radicals
    2526                 :        (0xf900u <= ch && ch <= 0xfaffu) || // CJK Compatibility Ideographs
    2527                 :        (0xff5eu <= ch && ch <= 0xff9fu)    // Halfwidth and Fullwidth Forms(a part)
    2528                 :      ))
    2529               0 :     return true;
    2530               0 :   return false;
    2531                 : }
    2532                 : 
    2533                 : void
    2534               0 : nsTextFrame::ClearMetrics(nsHTMLReflowMetrics& aMetrics)
    2535                 : {
    2536               0 :   aMetrics.width = 0;
    2537               0 :   aMetrics.height = 0;
    2538               0 :   aMetrics.ascent = 0;
    2539               0 :   mAscent = 0;
    2540               0 : }
    2541                 : 
    2542               0 : static PRInt32 FindChar(const nsTextFragment* frag,
    2543                 :                         PRInt32 aOffset, PRInt32 aLength, PRUnichar ch)
    2544                 : {
    2545               0 :   PRInt32 i = 0;
    2546               0 :   if (frag->Is2b()) {
    2547               0 :     const PRUnichar* str = frag->Get2b() + aOffset;
    2548               0 :     for (; i < aLength; ++i) {
    2549               0 :       if (*str == ch)
    2550               0 :         return i + aOffset;
    2551               0 :       ++str;
    2552                 :     }
    2553                 :   } else {
    2554               0 :     if (PRUint16(ch) <= 0xFF) {
    2555               0 :       const char* str = frag->Get1b() + aOffset;
    2556               0 :       const void* p = memchr(str, ch, aLength);
    2557               0 :       if (p)
    2558               0 :         return (static_cast<const char*>(p) - str) + aOffset;
    2559                 :     }
    2560                 :   }
    2561               0 :   return -1;
    2562                 : }
    2563                 : 
    2564               0 : static bool IsChineseOrJapanese(nsIFrame* aFrame)
    2565                 : {
    2566               0 :   nsIAtom* language = aFrame->GetStyleFont()->mLanguage;
    2567               0 :   if (!language) {
    2568               0 :     return false;
    2569                 :   }
    2570               0 :   const PRUnichar *lang = language->GetUTF16String();
    2571               0 :   return (!nsCRT::strncmp(lang, NS_LITERAL_STRING("ja").get(), 2) ||
    2572               0 :           !nsCRT::strncmp(lang, NS_LITERAL_STRING("zh").get(), 2)) &&
    2573               0 :          (language->GetLength() == 2 || lang[2] == '-');
    2574                 : }
    2575                 : 
    2576                 : #ifdef DEBUG
    2577               0 : static bool IsInBounds(const gfxSkipCharsIterator& aStart, PRInt32 aContentLength,
    2578                 :                          PRUint32 aOffset, PRUint32 aLength) {
    2579               0 :   if (aStart.GetSkippedOffset() > aOffset)
    2580               0 :     return false;
    2581               0 :   if (aContentLength == PR_INT32_MAX)
    2582               0 :     return true;
    2583               0 :   gfxSkipCharsIterator iter(aStart);
    2584               0 :   iter.AdvanceOriginal(aContentLength);
    2585               0 :   return iter.GetSkippedOffset() >= aOffset + aLength;
    2586                 : }
    2587                 : #endif
    2588                 : 
    2589               0 : class NS_STACK_CLASS PropertyProvider : public gfxTextRun::PropertyProvider {
    2590                 : public:
    2591                 :   /**
    2592                 :    * Use this constructor for reflow, when we don't know what text is
    2593                 :    * really mapped by the frame and we have a lot of other data around.
    2594                 :    * 
    2595                 :    * @param aLength can be PR_INT32_MAX to indicate we cover all the text
    2596                 :    * associated with aFrame up to where its flow chain ends in the given
    2597                 :    * textrun. If PR_INT32_MAX is passed, justification and hyphen-related methods
    2598                 :    * cannot be called, nor can GetOriginalLength().
    2599                 :    */
    2600               0 :   PropertyProvider(gfxTextRun* aTextRun, const nsStyleText* aTextStyle,
    2601                 :                    const nsTextFragment* aFrag, nsTextFrame* aFrame,
    2602                 :                    const gfxSkipCharsIterator& aStart, PRInt32 aLength,
    2603                 :                    nsIFrame* aLineContainer,
    2604                 :                    nscoord aOffsetFromBlockOriginForTabs,
    2605                 :                    nsTextFrame::TextRunType aWhichTextRun)
    2606                 :     : mTextRun(aTextRun), mFontGroup(nsnull),
    2607                 :       mTextStyle(aTextStyle), mFrag(aFrag),
    2608                 :       mLineContainer(aLineContainer),
    2609                 :       mFrame(aFrame), mStart(aStart), mTempIterator(aStart),
    2610                 :       mTabWidths(nsnull), mTabWidthsAnalyzedLimit(0),
    2611                 :       mLength(aLength),
    2612                 :       mWordSpacing(mTextStyle->mWordSpacing),
    2613               0 :       mLetterSpacing(StyleToCoord(mTextStyle->mLetterSpacing)),
    2614                 :       mJustificationSpacing(0),
    2615                 :       mHyphenWidth(-1),
    2616                 :       mOffsetFromBlockOriginForTabs(aOffsetFromBlockOriginForTabs),
    2617                 :       mReflowing(true),
    2618               0 :       mWhichTextRun(aWhichTextRun)
    2619                 :   {
    2620               0 :     NS_ASSERTION(mStart.IsInitialized(), "Start not initialized?");
    2621               0 :   }
    2622                 : 
    2623                 :   /**
    2624                 :    * Use this constructor after the frame has been reflowed and we don't
    2625                 :    * have other data around. Gets everything from the frame. EnsureTextRun
    2626                 :    * *must* be called before this!!!
    2627                 :    */
    2628               0 :   PropertyProvider(nsTextFrame* aFrame, const gfxSkipCharsIterator& aStart,
    2629                 :                    nsTextFrame::TextRunType aWhichTextRun)
    2630               0 :     : mTextRun(aFrame->GetTextRun(aWhichTextRun)), mFontGroup(nsnull),
    2631               0 :       mTextStyle(aFrame->GetStyleText()),
    2632               0 :       mFrag(aFrame->GetContent()->GetText()),
    2633                 :       mLineContainer(nsnull),
    2634                 :       mFrame(aFrame), mStart(aStart), mTempIterator(aStart),
    2635                 :       mTabWidths(nsnull), mTabWidthsAnalyzedLimit(0),
    2636               0 :       mLength(aFrame->GetContentLength()),
    2637                 :       mWordSpacing(mTextStyle->mWordSpacing),
    2638               0 :       mLetterSpacing(StyleToCoord(mTextStyle->mLetterSpacing)),
    2639                 :       mJustificationSpacing(0),
    2640                 :       mHyphenWidth(-1),
    2641                 :       mOffsetFromBlockOriginForTabs(0),
    2642                 :       mReflowing(false),
    2643               0 :       mWhichTextRun(aWhichTextRun)
    2644                 :   {
    2645               0 :     NS_ASSERTION(mTextRun, "Textrun not initialized!");
    2646               0 :   }
    2647                 : 
    2648                 :   // Call this after construction if you're not going to reflow the text
    2649                 :   void InitializeForDisplay(bool aTrimAfter);
    2650                 : 
    2651                 :   virtual void GetSpacing(PRUint32 aStart, PRUint32 aLength, Spacing* aSpacing);
    2652                 :   virtual gfxFloat GetHyphenWidth();
    2653                 :   virtual void GetHyphenationBreaks(PRUint32 aStart, PRUint32 aLength,
    2654                 :                                     bool* aBreakBefore);
    2655               0 :   virtual PRInt8 GetHyphensOption() {
    2656               0 :     return mTextStyle->mHyphens;
    2657                 :   }
    2658                 : 
    2659                 :   void GetSpacingInternal(PRUint32 aStart, PRUint32 aLength, Spacing* aSpacing,
    2660                 :                           bool aIgnoreTabs);
    2661                 : 
    2662                 :   /**
    2663                 :    * Count the number of justifiable characters in the given DOM range
    2664                 :    */
    2665                 :   PRUint32 ComputeJustifiableCharacters(PRInt32 aOffset, PRInt32 aLength);
    2666                 :   /**
    2667                 :    * Find the start and end of the justifiable characters. Does not depend on the
    2668                 :    * position of aStart or aEnd, although it's most efficient if they are near the
    2669                 :    * start and end of the text frame.
    2670                 :    */
    2671                 :   void FindJustificationRange(gfxSkipCharsIterator* aStart,
    2672                 :                               gfxSkipCharsIterator* aEnd);
    2673                 : 
    2674                 :   const nsStyleText* GetStyleText() { return mTextStyle; }
    2675               0 :   nsTextFrame* GetFrame() { return mFrame; }
    2676                 :   // This may not be equal to the frame offset/length in because we may have
    2677                 :   // adjusted for whitespace trimming according to the state bits set in the frame
    2678                 :   // (for the static provider)
    2679               0 :   const gfxSkipCharsIterator& GetStart() { return mStart; }
    2680                 :   // May return PR_INT32_MAX if that was given to the constructor
    2681               0 :   PRUint32 GetOriginalLength() {
    2682               0 :     NS_ASSERTION(mLength != PR_INT32_MAX, "Length not known");
    2683               0 :     return mLength;
    2684                 :   }
    2685               0 :   const nsTextFragment* GetFragment() { return mFrag; }
    2686                 : 
    2687               0 :   gfxFontGroup* GetFontGroup() {
    2688               0 :     if (!mFontGroup)
    2689               0 :       InitFontGroupAndFontMetrics();
    2690               0 :     return mFontGroup;
    2691                 :   }
    2692                 : 
    2693               0 :   nsFontMetrics* GetFontMetrics() {
    2694               0 :     if (!mFontMetrics)
    2695               0 :       InitFontGroupAndFontMetrics();
    2696               0 :     return mFontMetrics;
    2697                 :   }
    2698                 : 
    2699                 :   void CalcTabWidths(PRUint32 aTransformedStart, PRUint32 aTransformedLength);
    2700                 : 
    2701               0 :   const gfxSkipCharsIterator& GetEndHint() { return mTempIterator; }
    2702                 : 
    2703                 : protected:
    2704                 :   void SetupJustificationSpacing();
    2705                 : 
    2706               0 :   void InitFontGroupAndFontMetrics() {
    2707                 :     float inflation = (mWhichTextRun == nsTextFrame::eInflated)
    2708               0 :       ? mFrame->GetFontSizeInflation() : 1.0f;
    2709                 :     mFontGroup = GetFontGroupForFrame(mFrame, inflation,
    2710               0 :                                       getter_AddRefs(mFontMetrics));
    2711               0 :   }
    2712                 : 
    2713                 :   gfxTextRun*           mTextRun;
    2714                 :   gfxFontGroup*         mFontGroup;
    2715                 :   nsRefPtr<nsFontMetrics> mFontMetrics;
    2716                 :   const nsStyleText*    mTextStyle;
    2717                 :   const nsTextFragment* mFrag;
    2718                 :   nsIFrame*             mLineContainer;
    2719                 :   nsTextFrame*          mFrame;
    2720                 :   gfxSkipCharsIterator  mStart;  // Offset in original and transformed string
    2721                 :   gfxSkipCharsIterator  mTempIterator;
    2722                 :   
    2723                 :   // Either null, or pointing to the frame's tabWidthProperty.
    2724                 :   TabWidthStore*        mTabWidths;
    2725                 :   // how far we've done tab-width calculation; this is ONLY valid
    2726                 :   // when mTabWidths is NULL (otherwise rely on mTabWidths->mLimit instead)
    2727                 :   PRUint32              mTabWidthsAnalyzedLimit;
    2728                 : 
    2729                 :   PRInt32               mLength; // DOM string length, may be PR_INT32_MAX
    2730                 :   gfxFloat              mWordSpacing;     // space for each whitespace char
    2731                 :   gfxFloat              mLetterSpacing;   // space for each letter
    2732                 :   gfxFloat              mJustificationSpacing;
    2733                 :   gfxFloat              mHyphenWidth;
    2734                 :   gfxFloat              mOffsetFromBlockOriginForTabs;
    2735                 :   bool                  mReflowing;
    2736                 :   nsTextFrame::TextRunType mWhichTextRun;
    2737                 : };
    2738                 : 
    2739                 : PRUint32
    2740               0 : PropertyProvider::ComputeJustifiableCharacters(PRInt32 aOffset, PRInt32 aLength)
    2741                 : {
    2742                 :   // Scan non-skipped characters and count justifiable chars.
    2743                 :   nsSkipCharsRunIterator
    2744               0 :     run(mStart, nsSkipCharsRunIterator::LENGTH_INCLUDES_SKIPPED, aLength);
    2745               0 :   run.SetOriginalOffset(aOffset);
    2746               0 :   PRUint32 justifiableChars = 0;
    2747               0 :   bool isCJK = IsChineseOrJapanese(mFrame);
    2748               0 :   while (run.NextRun()) {
    2749                 :     PRInt32 i;
    2750               0 :     for (i = 0; i < run.GetRunLength(); ++i) {
    2751                 :       justifiableChars +=
    2752               0 :         IsJustifiableCharacter(mFrag, run.GetOriginalOffset() + i, isCJK);
    2753                 :     }
    2754                 :   }
    2755               0 :   return justifiableChars;
    2756                 : }
    2757                 : 
    2758                 : /**
    2759                 :  * Finds the offset of the first character of the cluster containing aPos
    2760                 :  */
    2761               0 : static void FindClusterStart(gfxTextRun* aTextRun, PRInt32 aOriginalStart,
    2762                 :                              gfxSkipCharsIterator* aPos)
    2763                 : {
    2764               0 :   while (aPos->GetOriginalOffset() > aOriginalStart) {
    2765               0 :     if (aPos->IsOriginalCharSkipped() ||
    2766               0 :         aTextRun->IsClusterStart(aPos->GetSkippedOffset())) {
    2767               0 :       break;
    2768                 :     }
    2769               0 :     aPos->AdvanceOriginal(-1);
    2770                 :   }
    2771               0 : }
    2772                 : 
    2773                 : /**
    2774                 :  * Finds the offset of the last character of the cluster containing aPos
    2775                 :  */
    2776               0 : static void FindClusterEnd(gfxTextRun* aTextRun, PRInt32 aOriginalEnd,
    2777                 :                            gfxSkipCharsIterator* aPos)
    2778                 : {
    2779               0 :   NS_PRECONDITION(aPos->GetOriginalOffset() < aOriginalEnd,
    2780                 :                   "character outside string");
    2781               0 :   aPos->AdvanceOriginal(1);
    2782               0 :   while (aPos->GetOriginalOffset() < aOriginalEnd) {
    2783               0 :     if (aPos->IsOriginalCharSkipped() ||
    2784               0 :         aTextRun->IsClusterStart(aPos->GetSkippedOffset())) {
    2785               0 :       break;
    2786                 :     }
    2787               0 :     aPos->AdvanceOriginal(1);
    2788                 :   }
    2789               0 :   aPos->AdvanceOriginal(-1);
    2790               0 : }
    2791                 : 
    2792                 : // aStart, aLength in transformed string offsets
    2793                 : void
    2794               0 : PropertyProvider::GetSpacing(PRUint32 aStart, PRUint32 aLength,
    2795                 :                              Spacing* aSpacing)
    2796                 : {
    2797                 :   GetSpacingInternal(aStart, aLength, aSpacing,
    2798               0 :                      (mTextRun->GetFlags() & nsTextFrameUtils::TEXT_HAS_TAB) == 0);
    2799               0 : }
    2800                 : 
    2801                 : static bool
    2802               0 : CanAddSpacingAfter(gfxTextRun* aTextRun, PRUint32 aOffset)
    2803                 : {
    2804               0 :   if (aOffset + 1 >= aTextRun->GetLength())
    2805               0 :     return true;
    2806               0 :   return aTextRun->IsClusterStart(aOffset + 1) &&
    2807               0 :     aTextRun->IsLigatureGroupStart(aOffset + 1);
    2808                 : }
    2809                 : 
    2810                 : void
    2811               0 : PropertyProvider::GetSpacingInternal(PRUint32 aStart, PRUint32 aLength,
    2812                 :                                      Spacing* aSpacing, bool aIgnoreTabs)
    2813                 : {
    2814               0 :   NS_PRECONDITION(IsInBounds(mStart, mLength, aStart, aLength), "Range out of bounds");
    2815                 : 
    2816                 :   PRUint32 index;
    2817               0 :   for (index = 0; index < aLength; ++index) {
    2818               0 :     aSpacing[index].mBefore = 0.0;
    2819               0 :     aSpacing[index].mAfter = 0.0;
    2820                 :   }
    2821                 : 
    2822                 :   // Find our offset into the original+transformed string
    2823               0 :   gfxSkipCharsIterator start(mStart);
    2824               0 :   start.SetSkippedOffset(aStart);
    2825                 : 
    2826                 :   // First, compute the word and letter spacing
    2827               0 :   if (mWordSpacing || mLetterSpacing) {
    2828                 :     // Iterate over non-skipped characters
    2829                 :     nsSkipCharsRunIterator
    2830               0 :       run(start, nsSkipCharsRunIterator::LENGTH_UNSKIPPED_ONLY, aLength);
    2831               0 :     while (run.NextRun()) {
    2832               0 :       PRUint32 runOffsetInSubstring = run.GetSkippedOffset() - aStart;
    2833                 :       PRInt32 i;
    2834               0 :       gfxSkipCharsIterator iter = run.GetPos();
    2835               0 :       for (i = 0; i < run.GetRunLength(); ++i) {
    2836               0 :         if (CanAddSpacingAfter(mTextRun, run.GetSkippedOffset() + i)) {
    2837                 :           // End of a cluster, not in a ligature: put letter-spacing after it
    2838               0 :           aSpacing[runOffsetInSubstring + i].mAfter += mLetterSpacing;
    2839                 :         }
    2840               0 :         if (IsCSSWordSpacingSpace(mFrag, i + run.GetOriginalOffset(),
    2841               0 :                                   mTextStyle)) {
    2842                 :           // It kinda sucks, but space characters can be part of clusters,
    2843                 :           // and even still be whitespace (I think!)
    2844               0 :           iter.SetSkippedOffset(run.GetSkippedOffset() + i);
    2845               0 :           FindClusterEnd(mTextRun, run.GetOriginalOffset() + run.GetRunLength(),
    2846               0 :                          &iter);
    2847               0 :           aSpacing[iter.GetSkippedOffset() - aStart].mAfter += mWordSpacing;
    2848                 :         }
    2849                 :       }
    2850                 :     }
    2851                 :   }
    2852                 : 
    2853                 :   // Ignore tab spacing rather than computing it, if the tab size is 0
    2854               0 :   if (!aIgnoreTabs)
    2855               0 :     aIgnoreTabs = mFrame->GetStyleText()->mTabSize == 0;
    2856                 : 
    2857                 :   // Now add tab spacing, if there is any
    2858               0 :   if (!aIgnoreTabs) {
    2859               0 :     CalcTabWidths(aStart, aLength);
    2860               0 :     if (mTabWidths) {
    2861                 :       mTabWidths->ApplySpacing(aSpacing,
    2862               0 :                                aStart - mStart.GetSkippedOffset(), aLength);
    2863                 :     }
    2864                 :   }
    2865                 : 
    2866                 :   // Now add in justification spacing
    2867               0 :   if (mJustificationSpacing) {
    2868               0 :     gfxFloat halfJustificationSpace = mJustificationSpacing/2;
    2869                 :     // Scan non-skipped characters and adjust justifiable chars, adding
    2870                 :     // justification space on either side of the cluster
    2871               0 :     bool isCJK = IsChineseOrJapanese(mFrame);
    2872               0 :     gfxSkipCharsIterator justificationStart(mStart), justificationEnd(mStart);
    2873               0 :     FindJustificationRange(&justificationStart, &justificationEnd);
    2874                 : 
    2875                 :     nsSkipCharsRunIterator
    2876               0 :       run(start, nsSkipCharsRunIterator::LENGTH_UNSKIPPED_ONLY, aLength);
    2877               0 :     while (run.NextRun()) {
    2878                 :       PRInt32 i;
    2879               0 :       gfxSkipCharsIterator iter = run.GetPos();
    2880               0 :       PRInt32 runOriginalOffset = run.GetOriginalOffset();
    2881               0 :       for (i = 0; i < run.GetRunLength(); ++i) {
    2882               0 :         PRInt32 iterOriginalOffset = runOriginalOffset + i;
    2883               0 :         if (IsJustifiableCharacter(mFrag, iterOriginalOffset, isCJK)) {
    2884               0 :           iter.SetOriginalOffset(iterOriginalOffset);
    2885               0 :           FindClusterStart(mTextRun, runOriginalOffset, &iter);
    2886               0 :           PRUint32 clusterFirstChar = iter.GetSkippedOffset();
    2887               0 :           FindClusterEnd(mTextRun, runOriginalOffset + run.GetRunLength(), &iter);
    2888               0 :           PRUint32 clusterLastChar = iter.GetSkippedOffset();
    2889                 :           // Only apply justification to characters before justificationEnd
    2890               0 :           if (clusterFirstChar >= justificationStart.GetSkippedOffset() &&
    2891               0 :               clusterLastChar < justificationEnd.GetSkippedOffset()) {
    2892               0 :             aSpacing[clusterFirstChar - aStart].mBefore += halfJustificationSpace;
    2893               0 :             aSpacing[clusterLastChar - aStart].mAfter += halfJustificationSpace;
    2894                 :           }
    2895                 :         }
    2896                 :       }
    2897                 :     }
    2898                 :   }
    2899               0 : }
    2900                 : 
    2901                 : static gfxFloat
    2902               0 : ComputeTabWidthAppUnits(nsIFrame* aFrame, gfxTextRun* aTextRun)
    2903                 : {
    2904                 :   // Get the number of spaces from CSS -moz-tab-size
    2905               0 :   const nsStyleText* textStyle = aFrame->GetStyleText();
    2906                 :   
    2907                 :   // Round the space width when converting to appunits the same way
    2908                 :   // textruns do
    2909                 :   gfxFloat spaceWidthAppUnits =
    2910               0 :     NS_round(GetFirstFontMetrics(aTextRun->GetFontGroup()).spaceWidth *
    2911               0 :               aTextRun->GetAppUnitsPerDevUnit());
    2912               0 :   return textStyle->mTabSize * spaceWidthAppUnits;
    2913                 : }
    2914                 : 
    2915                 : // aX and the result are in whole appunits.
    2916                 : static gfxFloat
    2917               0 : AdvanceToNextTab(gfxFloat aX, nsIFrame* aFrame,
    2918                 :                  gfxTextRun* aTextRun, gfxFloat* aCachedTabWidth)
    2919                 : {
    2920               0 :   if (*aCachedTabWidth < 0) {
    2921               0 :     *aCachedTabWidth = ComputeTabWidthAppUnits(aFrame, aTextRun);
    2922                 :   }
    2923                 : 
    2924                 :   // Advance aX to the next multiple of *aCachedTabWidth. We must advance
    2925                 :   // by at least 1 appunit.
    2926                 :   // XXX should we make this 1 CSS pixel?
    2927               0 :   return ceil((aX + 1)/(*aCachedTabWidth))*(*aCachedTabWidth);
    2928                 : }
    2929                 : 
    2930                 : void
    2931               0 : PropertyProvider::CalcTabWidths(PRUint32 aStart, PRUint32 aLength)
    2932                 : {
    2933               0 :   if (!mTabWidths) {
    2934               0 :     if (mReflowing && !mLineContainer) {
    2935                 :       // Intrinsic width computation does its own tab processing. We
    2936                 :       // just don't do anything here.
    2937               0 :       return;
    2938                 :     }
    2939               0 :     if (!mReflowing) {
    2940                 :       mTabWidths = static_cast<TabWidthStore*>
    2941               0 :         (mFrame->Properties().Get(TabWidthProperty()));
    2942                 : #ifdef DEBUG
    2943                 :       // If we're not reflowing, we should have already computed the
    2944                 :       // tab widths; check that they're available as far as the last
    2945                 :       // tab character present (if any)
    2946               0 :       for (PRUint32 i = aStart + aLength; i > aStart; --i) {
    2947               0 :         if (mTextRun->CharIsTab(i - 1)) {
    2948               0 :           NS_ASSERTION(mTabWidths && mTabWidths->mLimit >= i,
    2949                 :                        "Precomputed tab widths are missing!");
    2950               0 :           break;
    2951                 :         }
    2952                 :       }
    2953                 : #endif
    2954               0 :       return;
    2955                 :     }
    2956                 :   }
    2957                 : 
    2958               0 :   PRUint32 startOffset = mStart.GetSkippedOffset();
    2959                 :   PRUint32 tabsEnd = mTabWidths ?
    2960               0 :     mTabWidths->mLimit : NS_MAX(mTabWidthsAnalyzedLimit, startOffset);
    2961                 : 
    2962               0 :   if (tabsEnd < aStart + aLength) {
    2963               0 :     NS_ASSERTION(mReflowing,
    2964                 :                  "We need precomputed tab widths, but don't have enough.");
    2965                 : 
    2966               0 :     gfxFloat tabWidth = -1;
    2967               0 :     for (PRUint32 i = tabsEnd; i < aStart + aLength; ++i) {
    2968                 :       Spacing spacing;
    2969               0 :       GetSpacingInternal(i, 1, &spacing, true);
    2970               0 :       mOffsetFromBlockOriginForTabs += spacing.mBefore;
    2971                 : 
    2972               0 :       if (!mTextRun->CharIsTab(i)) {
    2973               0 :         if (mTextRun->IsClusterStart(i)) {
    2974               0 :           PRUint32 clusterEnd = i + 1;
    2975               0 :           while (clusterEnd < mTextRun->GetLength() &&
    2976               0 :                  !mTextRun->IsClusterStart(clusterEnd)) {
    2977               0 :             ++clusterEnd;
    2978                 :           }
    2979                 :           mOffsetFromBlockOriginForTabs +=
    2980               0 :             mTextRun->GetAdvanceWidth(i, clusterEnd - i, nsnull);
    2981                 :         }
    2982                 :       } else {
    2983               0 :         if (!mTabWidths) {
    2984               0 :           mTabWidths = new TabWidthStore();
    2985               0 :           mFrame->Properties().Set(TabWidthProperty(), mTabWidths);
    2986                 :         }
    2987                 :         double nextTab = AdvanceToNextTab(mOffsetFromBlockOriginForTabs,
    2988               0 :                 mFrame, mTextRun, &tabWidth);
    2989                 :         mTabWidths->mWidths.AppendElement(TabWidth(i - startOffset, 
    2990               0 :                 NSToIntRound(nextTab - mOffsetFromBlockOriginForTabs)));
    2991               0 :         mOffsetFromBlockOriginForTabs = nextTab;
    2992                 :       }
    2993                 : 
    2994               0 :       mOffsetFromBlockOriginForTabs += spacing.mAfter;
    2995                 :     }
    2996                 : 
    2997               0 :     if (mTabWidths) {
    2998               0 :       mTabWidths->mLimit = aStart + aLength;
    2999                 :     }
    3000                 :   }
    3001                 : 
    3002               0 :   if (!mTabWidths) {
    3003                 :     // Delete any stale property that may be left on the frame
    3004               0 :     mFrame->Properties().Delete(TabWidthProperty());
    3005                 :     mTabWidthsAnalyzedLimit = NS_MAX(mTabWidthsAnalyzedLimit,
    3006               0 :                                      aStart + aLength);
    3007                 :   }
    3008                 : }
    3009                 : 
    3010                 : gfxFloat
    3011               0 : PropertyProvider::GetHyphenWidth()
    3012                 : {
    3013               0 :   if (mHyphenWidth < 0) {
    3014               0 :     nsAutoPtr<gfxTextRun> hyphenTextRun(GetHyphenTextRun(mTextRun, nsnull, mFrame));
    3015               0 :     mHyphenWidth = mLetterSpacing;
    3016               0 :     if (hyphenTextRun.get()) {
    3017               0 :       mHyphenWidth += hyphenTextRun->GetAdvanceWidth(0, hyphenTextRun->GetLength(), nsnull);
    3018                 :     }
    3019                 :   }
    3020               0 :   return mHyphenWidth;
    3021                 : }
    3022                 : 
    3023                 : void
    3024               0 : PropertyProvider::GetHyphenationBreaks(PRUint32 aStart, PRUint32 aLength,
    3025                 :                                        bool* aBreakBefore)
    3026                 : {
    3027               0 :   NS_PRECONDITION(IsInBounds(mStart, mLength, aStart, aLength), "Range out of bounds");
    3028               0 :   NS_PRECONDITION(mLength != PR_INT32_MAX, "Can't call this with undefined length");
    3029                 : 
    3030               0 :   if (!mTextStyle->WhiteSpaceCanWrap() ||
    3031                 :       mTextStyle->mHyphens == NS_STYLE_HYPHENS_NONE)
    3032                 :   {
    3033               0 :     memset(aBreakBefore, false, aLength*sizeof(bool));
    3034               0 :     return;
    3035                 :   }
    3036                 : 
    3037                 :   // Iterate through the original-string character runs
    3038                 :   nsSkipCharsRunIterator
    3039               0 :     run(mStart, nsSkipCharsRunIterator::LENGTH_UNSKIPPED_ONLY, aLength);
    3040               0 :   run.SetSkippedOffset(aStart);
    3041                 :   // We need to visit skipped characters so that we can detect SHY
    3042               0 :   run.SetVisitSkipped();
    3043                 : 
    3044               0 :   PRInt32 prevTrailingCharOffset = run.GetPos().GetOriginalOffset() - 1;
    3045                 :   bool allowHyphenBreakBeforeNextChar =
    3046               0 :     prevTrailingCharOffset >= mStart.GetOriginalOffset() &&
    3047               0 :     prevTrailingCharOffset < mStart.GetOriginalOffset() + mLength &&
    3048               0 :     mFrag->CharAt(prevTrailingCharOffset) == CH_SHY;
    3049                 : 
    3050               0 :   while (run.NextRun()) {
    3051               0 :     NS_ASSERTION(run.GetRunLength() > 0, "Shouldn't return zero-length runs");
    3052               0 :     if (run.IsSkipped()) {
    3053                 :       // Check if there's a soft hyphen which would let us hyphenate before
    3054                 :       // the next non-skipped character. Don't look at soft hyphens followed
    3055                 :       // by other skipped characters, we won't use them.
    3056                 :       allowHyphenBreakBeforeNextChar =
    3057               0 :         mFrag->CharAt(run.GetOriginalOffset() + run.GetRunLength() - 1) == CH_SHY;
    3058                 :     } else {
    3059               0 :       PRInt32 runOffsetInSubstring = run.GetSkippedOffset() - aStart;
    3060               0 :       memset(aBreakBefore + runOffsetInSubstring, false, run.GetRunLength()*sizeof(bool));
    3061                 :       // Don't allow hyphen breaks at the start of the line
    3062               0 :       aBreakBefore[runOffsetInSubstring] = allowHyphenBreakBeforeNextChar &&
    3063               0 :           (!(mFrame->GetStateBits() & TEXT_START_OF_LINE) ||
    3064               0 :            run.GetSkippedOffset() > mStart.GetSkippedOffset());
    3065               0 :       allowHyphenBreakBeforeNextChar = false;
    3066                 :     }
    3067                 :   }
    3068                 : 
    3069               0 :   if (mTextStyle->mHyphens == NS_STYLE_HYPHENS_AUTO) {
    3070               0 :     for (PRUint32 i = 0; i < aLength; ++i) {
    3071               0 :       if (mTextRun->CanHyphenateBefore(aStart + i)) {
    3072               0 :         aBreakBefore[i] = true;
    3073                 :       }
    3074                 :     }
    3075                 :   }
    3076                 : }
    3077                 : 
    3078                 : void
    3079               0 : PropertyProvider::InitializeForDisplay(bool aTrimAfter)
    3080                 : {
    3081                 :   nsTextFrame::TrimmedOffsets trimmed =
    3082               0 :     mFrame->GetTrimmedOffsets(mFrag, aTrimAfter);
    3083               0 :   mStart.SetOriginalOffset(trimmed.mStart);
    3084               0 :   mLength = trimmed.mLength;
    3085               0 :   SetupJustificationSpacing();
    3086               0 : }
    3087                 : 
    3088               0 : static PRUint32 GetSkippedDistance(const gfxSkipCharsIterator& aStart,
    3089                 :                                    const gfxSkipCharsIterator& aEnd)
    3090                 : {
    3091               0 :   return aEnd.GetSkippedOffset() - aStart.GetSkippedOffset();
    3092                 : }
    3093                 : 
    3094                 : void
    3095               0 : PropertyProvider::FindJustificationRange(gfxSkipCharsIterator* aStart,
    3096                 :                                          gfxSkipCharsIterator* aEnd)
    3097                 : {
    3098               0 :   NS_PRECONDITION(mLength != PR_INT32_MAX, "Can't call this with undefined length");
    3099               0 :   NS_ASSERTION(aStart && aEnd, "aStart or/and aEnd is null");
    3100                 : 
    3101               0 :   aStart->SetOriginalOffset(mStart.GetOriginalOffset());
    3102               0 :   aEnd->SetOriginalOffset(mStart.GetOriginalOffset() + mLength);
    3103                 : 
    3104                 :   // Ignore first cluster at start of line for justification purposes
    3105               0 :   if (mFrame->GetStateBits() & TEXT_START_OF_LINE) {
    3106               0 :     while (aStart->GetOriginalOffset() < aEnd->GetOriginalOffset()) {
    3107               0 :       aStart->AdvanceOriginal(1);
    3108               0 :       if (!aStart->IsOriginalCharSkipped() &&
    3109               0 :           mTextRun->IsClusterStart(aStart->GetSkippedOffset()))
    3110               0 :         break;
    3111                 :     }
    3112                 :   }
    3113                 : 
    3114                 :   // Ignore trailing cluster at end of line for justification purposes
    3115               0 :   if (mFrame->GetStateBits() & TEXT_END_OF_LINE) {
    3116               0 :     while (aEnd->GetOriginalOffset() > aStart->GetOriginalOffset()) {
    3117               0 :       aEnd->AdvanceOriginal(-1);
    3118               0 :       if (!aEnd->IsOriginalCharSkipped() &&
    3119               0 :           mTextRun->IsClusterStart(aEnd->GetSkippedOffset()))
    3120               0 :         break;
    3121                 :     }
    3122                 :   }
    3123               0 : }
    3124                 : 
    3125                 : void
    3126               0 : PropertyProvider::SetupJustificationSpacing()
    3127                 : {
    3128               0 :   NS_PRECONDITION(mLength != PR_INT32_MAX, "Can't call this with undefined length");
    3129                 : 
    3130               0 :   if (!(mFrame->GetStateBits() & TEXT_JUSTIFICATION_ENABLED))
    3131               0 :     return;
    3132                 : 
    3133               0 :   gfxSkipCharsIterator start(mStart), end(mStart);
    3134                 :   // We can't just use our mLength here; when InitializeForDisplay is
    3135                 :   // called with false for aTrimAfter, we still shouldn't be assigning
    3136                 :   // justification space to any trailing whitespace.
    3137                 :   nsTextFrame::TrimmedOffsets trimmed =
    3138               0 :     mFrame->GetTrimmedOffsets(mFrag, true);
    3139               0 :   end.AdvanceOriginal(trimmed.mLength);
    3140               0 :   gfxSkipCharsIterator realEnd(end);
    3141               0 :   FindJustificationRange(&start, &end);
    3142                 : 
    3143                 :   PRInt32 justifiableCharacters =
    3144                 :     ComputeJustifiableCharacters(start.GetOriginalOffset(),
    3145               0 :                                  end.GetOriginalOffset() - start.GetOriginalOffset());
    3146               0 :   if (justifiableCharacters == 0) {
    3147                 :     // Nothing to do, nothing is justifiable and we shouldn't have any
    3148                 :     // justification space assigned
    3149               0 :     return;
    3150                 :   }
    3151                 : 
    3152                 :   gfxFloat naturalWidth =
    3153                 :     mTextRun->GetAdvanceWidth(mStart.GetSkippedOffset(),
    3154               0 :                               GetSkippedDistance(mStart, realEnd), this);
    3155               0 :   if (mFrame->GetStateBits() & TEXT_HYPHEN_BREAK) {
    3156               0 :     nsAutoPtr<gfxTextRun> hyphenTextRun(GetHyphenTextRun(mTextRun, nsnull, mFrame));
    3157               0 :     if (hyphenTextRun.get()) {
    3158                 :       naturalWidth +=
    3159               0 :         hyphenTextRun->GetAdvanceWidth(0, hyphenTextRun->GetLength(), nsnull);
    3160                 :     }
    3161                 :   }
    3162               0 :   gfxFloat totalJustificationSpace = mFrame->GetSize().width - naturalWidth;
    3163               0 :   if (totalJustificationSpace <= 0) {
    3164                 :     // No space available
    3165               0 :     return;
    3166                 :   }
    3167                 :   
    3168               0 :   mJustificationSpacing = totalJustificationSpace/justifiableCharacters;
    3169                 : }
    3170                 : 
    3171                 : //----------------------------------------------------------------------
    3172                 : 
    3173                 : // Helper class for managing blinking text
    3174                 : 
    3175                 : class nsBlinkTimer : public nsITimerCallback
    3176                 : {
    3177                 : public:
    3178                 :   nsBlinkTimer();
    3179                 :   virtual ~nsBlinkTimer();
    3180                 : 
    3181                 :   NS_DECL_ISUPPORTS
    3182                 : 
    3183                 :   void AddFrame(nsPresContext* aPresContext, nsIFrame* aFrame);
    3184                 : 
    3185                 :   bool RemoveFrame(nsIFrame* aFrame);
    3186                 : 
    3187                 :   PRInt32 FrameCount();
    3188                 : 
    3189                 :   void Start();
    3190                 : 
    3191                 :   void Stop();
    3192                 : 
    3193                 :   NS_DECL_NSITIMERCALLBACK
    3194                 : 
    3195                 :   static void AddBlinkFrame(nsPresContext* aPresContext, nsIFrame* aFrame);
    3196                 :   static void RemoveBlinkFrame(nsIFrame* aFrame);
    3197                 :   
    3198               0 :   static bool     GetBlinkIsOff() { return sState == 3; }
    3199                 :   
    3200                 : protected:
    3201                 : 
    3202               0 :   struct FrameData {
    3203                 :     nsPresContext* mPresContext;  // pres context associated with the frame
    3204                 :     nsIFrame*       mFrame;
    3205                 : 
    3206                 : 
    3207               0 :     FrameData(nsPresContext* aPresContext,
    3208                 :               nsIFrame*       aFrame)
    3209               0 :       : mPresContext(aPresContext), mFrame(aFrame) {}
    3210                 :   };
    3211                 : 
    3212                 :   class FrameDataComparator {
    3213                 :     public:
    3214               0 :       bool Equals(const FrameData& aTimer, nsIFrame* const& aFrame) const {
    3215               0 :         return aTimer.mFrame == aFrame;
    3216                 :       }
    3217                 :   };
    3218                 : 
    3219                 :   nsCOMPtr<nsITimer> mTimer;
    3220                 :   nsTArray<FrameData> mFrames;
    3221                 :   nsPresContext* mPresContext;
    3222                 : 
    3223                 : protected:
    3224                 : 
    3225                 :   static nsBlinkTimer* sTextBlinker;
    3226                 :   static PRUint32      sState; // 0-2 == on; 3 == off
    3227                 :   
    3228                 : };
    3229                 : 
    3230                 : nsBlinkTimer* nsBlinkTimer::sTextBlinker = nsnull;
    3231                 : PRUint32      nsBlinkTimer::sState = 0;
    3232                 : 
    3233                 : #ifdef NOISY_BLINK
    3234                 : static PRTime gLastTick;
    3235                 : #endif
    3236                 : 
    3237               0 : nsBlinkTimer::nsBlinkTimer()
    3238                 : {
    3239               0 : }
    3240                 : 
    3241               0 : nsBlinkTimer::~nsBlinkTimer()
    3242                 : {
    3243               0 :   Stop();
    3244               0 :   sTextBlinker = nsnull;
    3245               0 : }
    3246                 : 
    3247               0 : void nsBlinkTimer::Start()
    3248                 : {
    3249                 :   nsresult rv;
    3250               0 :   mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
    3251               0 :   if (NS_OK == rv) {
    3252               0 :     mTimer->InitWithCallback(this, 250, nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP);
    3253                 :   }
    3254               0 : }
    3255                 : 
    3256               0 : void nsBlinkTimer::Stop()
    3257                 : {
    3258               0 :   if (nsnull != mTimer) {
    3259               0 :     mTimer->Cancel();
    3260               0 :     mTimer = nsnull;
    3261                 :   }
    3262               0 : }
    3263                 : 
    3264               0 : NS_IMPL_ISUPPORTS1(nsBlinkTimer, nsITimerCallback)
    3265                 : 
    3266               0 : void nsBlinkTimer::AddFrame(nsPresContext* aPresContext, nsIFrame* aFrame) {
    3267               0 :   mFrames.AppendElement(FrameData(aPresContext, aFrame));
    3268               0 :   if (1 == mFrames.Length()) {
    3269               0 :     Start();
    3270                 :   }
    3271               0 : }
    3272                 : 
    3273               0 : bool nsBlinkTimer::RemoveFrame(nsIFrame* aFrame) {
    3274               0 :   mFrames.RemoveElement(aFrame, FrameDataComparator());
    3275                 :   
    3276               0 :   if (mFrames.IsEmpty()) {
    3277               0 :     Stop();
    3278                 :   }
    3279               0 :   return true;
    3280                 : }
    3281                 : 
    3282               0 : PRInt32 nsBlinkTimer::FrameCount() {
    3283               0 :   return PRInt32(mFrames.Length());
    3284                 : }
    3285                 : 
    3286               0 : NS_IMETHODIMP nsBlinkTimer::Notify(nsITimer *timer)
    3287                 : {
    3288                 :   // Toggle blink state bit so that text code knows whether or not to
    3289                 :   // render. All text code shares the same flag so that they all blink
    3290                 :   // in unison.
    3291               0 :   sState = (sState + 1) % 4;
    3292               0 :   if (sState == 1 || sState == 2)
    3293                 :     // States 0, 1, and 2 are all the same.
    3294               0 :     return NS_OK;
    3295                 : 
    3296                 : #ifdef NOISY_BLINK
    3297                 :   PRTime now = PR_Now();
    3298                 :   char buf[50];
    3299                 :   PRTime delta;
    3300                 :   LL_SUB(delta, now, gLastTick);
    3301                 :   gLastTick = now;
    3302                 :   PR_snprintf(buf, sizeof(buf), "%lldusec", delta);
    3303                 :   printf("%s\n", buf);
    3304                 : #endif
    3305                 : 
    3306               0 :   PRUint32 i, n = mFrames.Length();
    3307               0 :   for (i = 0; i < n; i++) {
    3308               0 :     FrameData& frameData = mFrames.ElementAt(i);
    3309                 : 
    3310                 :     // Determine damaged area and tell view manager to redraw it
    3311                 :     // blink doesn't blink outline ... I hope
    3312               0 :     nsRect bounds(nsPoint(0, 0), frameData.mFrame->GetSize());
    3313               0 :     frameData.mFrame->Invalidate(bounds);
    3314                 :   }
    3315               0 :   return NS_OK;
    3316                 : }
    3317                 : 
    3318                 : 
    3319                 : // static
    3320               0 : void nsBlinkTimer::AddBlinkFrame(nsPresContext* aPresContext, nsIFrame* aFrame)
    3321                 : {
    3322               0 :   if (!sTextBlinker)
    3323                 :   {
    3324               0 :     sTextBlinker = new nsBlinkTimer;
    3325                 :   }
    3326                 : 
    3327               0 :   NS_ADDREF(sTextBlinker);
    3328                 : 
    3329               0 :   sTextBlinker->AddFrame(aPresContext, aFrame);
    3330               0 : }
    3331                 : 
    3332                 : 
    3333                 : // static
    3334               0 : void nsBlinkTimer::RemoveBlinkFrame(nsIFrame* aFrame)
    3335                 : {
    3336               0 :   NS_ASSERTION(sTextBlinker, "Should have blink timer here");
    3337                 : 
    3338               0 :   nsBlinkTimer* blinkTimer = sTextBlinker;    // copy so we can call NS_RELEASE on it
    3339                 : 
    3340               0 :   blinkTimer->RemoveFrame(aFrame);  
    3341               0 :   NS_RELEASE(blinkTimer);
    3342               0 : }
    3343                 : 
    3344                 : //----------------------------------------------------------------------
    3345                 : 
    3346                 : static nscolor
    3347               0 : EnsureDifferentColors(nscolor colorA, nscolor colorB)
    3348                 : {
    3349               0 :   if (colorA == colorB) {
    3350                 :     nscolor res;
    3351                 :     res = NS_RGB(NS_GET_R(colorA) ^ 0xff,
    3352                 :                  NS_GET_G(colorA) ^ 0xff,
    3353               0 :                  NS_GET_B(colorA) ^ 0xff);
    3354               0 :     return res;
    3355                 :   }
    3356               0 :   return colorA;
    3357                 : }
    3358                 : 
    3359                 : //-----------------------------------------------------------------------------
    3360                 : 
    3361               0 : nsTextPaintStyle::nsTextPaintStyle(nsTextFrame* aFrame)
    3362                 :   : mFrame(aFrame),
    3363               0 :     mPresContext(aFrame->PresContext()),
    3364                 :     mInitCommonColors(false),
    3365               0 :     mInitSelectionColors(false)
    3366                 : {
    3367               0 :   for (PRUint32 i = 0; i < ArrayLength(mSelectionStyle); i++)
    3368               0 :     mSelectionStyle[i].mInit = false;
    3369               0 : }
    3370                 : 
    3371                 : bool
    3372               0 : nsTextPaintStyle::EnsureSufficientContrast(nscolor *aForeColor, nscolor *aBackColor)
    3373                 : {
    3374               0 :   InitCommonColors();
    3375                 : 
    3376                 :   // If the combination of selection background color and frame background color
    3377                 :   // is sufficient contrast, don't exchange the selection colors.
    3378                 :   PRInt32 backLuminosityDifference =
    3379               0 :             NS_LUMINOSITY_DIFFERENCE(*aBackColor, mFrameBackgroundColor);
    3380               0 :   if (backLuminosityDifference >= mSufficientContrast)
    3381               0 :     return false;
    3382                 : 
    3383                 :   // Otherwise, we should use the higher-contrast color for the selection
    3384                 :   // background color.
    3385                 :   PRInt32 foreLuminosityDifference =
    3386               0 :             NS_LUMINOSITY_DIFFERENCE(*aForeColor, mFrameBackgroundColor);
    3387               0 :   if (backLuminosityDifference < foreLuminosityDifference) {
    3388               0 :     nscolor tmpColor = *aForeColor;
    3389               0 :     *aForeColor = *aBackColor;
    3390               0 :     *aBackColor = tmpColor;
    3391               0 :     return true;
    3392                 :   }
    3393               0 :   return false;
    3394                 : }
    3395                 : 
    3396                 : nscolor
    3397               0 : nsTextPaintStyle::GetTextColor()
    3398                 : {
    3399               0 :   return nsLayoutUtils::GetColor(mFrame, eCSSProperty_color);
    3400                 : }
    3401                 : 
    3402                 : bool
    3403               0 : nsTextPaintStyle::GetSelectionColors(nscolor* aForeColor,
    3404                 :                                      nscolor* aBackColor)
    3405                 : {
    3406               0 :   NS_ASSERTION(aForeColor, "aForeColor is null");
    3407               0 :   NS_ASSERTION(aBackColor, "aBackColor is null");
    3408                 : 
    3409               0 :   if (!InitSelectionColors())
    3410               0 :     return false;
    3411                 : 
    3412               0 :   *aForeColor = mSelectionTextColor;
    3413               0 :   *aBackColor = mSelectionBGColor;
    3414               0 :   return true;
    3415                 : }
    3416                 : 
    3417                 : void
    3418               0 : nsTextPaintStyle::GetHighlightColors(nscolor* aForeColor,
    3419                 :                                      nscolor* aBackColor)
    3420                 : {
    3421               0 :   NS_ASSERTION(aForeColor, "aForeColor is null");
    3422               0 :   NS_ASSERTION(aBackColor, "aBackColor is null");
    3423                 :   
    3424                 :   nscolor backColor =
    3425               0 :     LookAndFeel::GetColor(LookAndFeel::eColorID_TextHighlightBackground);
    3426                 :   nscolor foreColor =
    3427               0 :     LookAndFeel::GetColor(LookAndFeel::eColorID_TextHighlightForeground);
    3428               0 :   EnsureSufficientContrast(&foreColor, &backColor);
    3429               0 :   *aForeColor = foreColor;
    3430               0 :   *aBackColor = backColor;
    3431               0 : }
    3432                 : 
    3433                 : void
    3434               0 : nsTextPaintStyle::GetURLSecondaryColor(nscolor* aForeColor)
    3435                 : {
    3436               0 :   NS_ASSERTION(aForeColor, "aForeColor is null");
    3437                 : 
    3438               0 :   nscolor textColor = GetTextColor();
    3439                 :   textColor = NS_RGBA(NS_GET_R(textColor),
    3440                 :                       NS_GET_G(textColor),
    3441                 :                       NS_GET_B(textColor),
    3442               0 :                       (PRUint8)(255 * 0.5f));
    3443                 :   // Don't use true alpha color for readability.
    3444               0 :   InitCommonColors();
    3445               0 :   *aForeColor = NS_ComposeColors(mFrameBackgroundColor, textColor);
    3446               0 : }
    3447                 : 
    3448                 : void
    3449               0 : nsTextPaintStyle::GetIMESelectionColors(PRInt32  aIndex,
    3450                 :                                         nscolor* aForeColor,
    3451                 :                                         nscolor* aBackColor)
    3452                 : {
    3453               0 :   NS_ASSERTION(aForeColor, "aForeColor is null");
    3454               0 :   NS_ASSERTION(aBackColor, "aBackColor is null");
    3455               0 :   NS_ASSERTION(aIndex >= 0 && aIndex < 5, "Index out of range");
    3456                 : 
    3457               0 :   nsSelectionStyle* selectionStyle = GetSelectionStyle(aIndex);
    3458               0 :   *aForeColor = selectionStyle->mTextColor;
    3459               0 :   *aBackColor = selectionStyle->mBGColor;
    3460               0 : }
    3461                 : 
    3462                 : bool
    3463               0 : nsTextPaintStyle::GetSelectionUnderlineForPaint(PRInt32  aIndex,
    3464                 :                                                 nscolor* aLineColor,
    3465                 :                                                 float*   aRelativeSize,
    3466                 :                                                 PRUint8* aStyle)
    3467                 : {
    3468               0 :   NS_ASSERTION(aLineColor, "aLineColor is null");
    3469               0 :   NS_ASSERTION(aRelativeSize, "aRelativeSize is null");
    3470               0 :   NS_ASSERTION(aIndex >= 0 && aIndex < 5, "Index out of range");
    3471                 : 
    3472               0 :   nsSelectionStyle* selectionStyle = GetSelectionStyle(aIndex);
    3473               0 :   if (selectionStyle->mUnderlineStyle == NS_STYLE_BORDER_STYLE_NONE ||
    3474                 :       selectionStyle->mUnderlineColor == NS_TRANSPARENT ||
    3475                 :       selectionStyle->mUnderlineRelativeSize <= 0.0f)
    3476               0 :     return false;
    3477                 : 
    3478               0 :   *aLineColor = selectionStyle->mUnderlineColor;
    3479               0 :   *aRelativeSize = selectionStyle->mUnderlineRelativeSize;
    3480               0 :   *aStyle = selectionStyle->mUnderlineStyle;
    3481               0 :   return true;
    3482                 : }
    3483                 : 
    3484                 : void
    3485               0 : nsTextPaintStyle::InitCommonColors()
    3486                 : {
    3487               0 :   if (mInitCommonColors)
    3488               0 :     return;
    3489                 : 
    3490                 :   nsIFrame* bgFrame =
    3491               0 :     nsCSSRendering::FindNonTransparentBackgroundFrame(mFrame);
    3492               0 :   NS_ASSERTION(bgFrame, "Cannot find NonTransparentBackgroundFrame.");
    3493                 :   nscolor bgColor =
    3494               0 :     bgFrame->GetVisitedDependentColor(eCSSProperty_background_color);
    3495                 : 
    3496               0 :   nscolor defaultBgColor = mPresContext->DefaultBackgroundColor();
    3497               0 :   mFrameBackgroundColor = NS_ComposeColors(defaultBgColor, bgColor);
    3498                 : 
    3499               0 :   if (bgFrame->IsThemed()) {
    3500                 :     // Assume a native widget has sufficient contrast always
    3501               0 :     mSufficientContrast = 0;
    3502               0 :     mInitCommonColors = true;
    3503               0 :     return;
    3504                 :   }
    3505                 : 
    3506               0 :   NS_ASSERTION(NS_GET_A(defaultBgColor) == 255,
    3507                 :                "default background color is not opaque");
    3508                 : 
    3509                 :   nscolor defaultWindowBackgroundColor =
    3510               0 :     LookAndFeel::GetColor(LookAndFeel::eColorID_WindowBackground);
    3511                 :   nscolor selectionTextColor =
    3512               0 :     LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectForeground);
    3513                 :   nscolor selectionBGColor =
    3514               0 :     LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectBackground);
    3515                 : 
    3516                 :   mSufficientContrast =
    3517                 :     NS_MIN(NS_MIN(NS_SUFFICIENT_LUMINOSITY_DIFFERENCE,
    3518               0 :                   NS_LUMINOSITY_DIFFERENCE(selectionTextColor,
    3519               0 :                                            selectionBGColor)),
    3520               0 :                   NS_LUMINOSITY_DIFFERENCE(defaultWindowBackgroundColor,
    3521               0 :                                            selectionBGColor));
    3522                 : 
    3523               0 :   mInitCommonColors = true;
    3524                 : }
    3525                 : 
    3526                 : static Element*
    3527               0 : FindElementAncestorForMozSelection(nsIContent* aContent)
    3528                 : {
    3529               0 :   NS_ENSURE_TRUE(aContent, nsnull);
    3530               0 :   while (aContent && aContent->IsInNativeAnonymousSubtree()) {
    3531               0 :     aContent = aContent->GetBindingParent();
    3532                 :   }
    3533               0 :   NS_ASSERTION(aContent, "aContent isn't in non-anonymous tree?");
    3534               0 :   while (aContent && !aContent->IsElement()) {
    3535               0 :     aContent = aContent->GetParent();
    3536                 :   }
    3537               0 :   return aContent ? aContent->AsElement() : nsnull;
    3538                 : }
    3539                 : 
    3540                 : bool
    3541               0 : nsTextPaintStyle::InitSelectionColors()
    3542                 : {
    3543               0 :   if (mInitSelectionColors)
    3544               0 :     return true;
    3545                 : 
    3546                 :   PRInt16 selectionFlags;
    3547               0 :   PRInt16 selectionStatus = mFrame->GetSelectionStatus(&selectionFlags);
    3548               0 :   if (!(selectionFlags & nsISelectionDisplay::DISPLAY_TEXT) ||
    3549                 :       selectionStatus < nsISelectionController::SELECTION_ON) {
    3550                 :     // Not displaying the normal selection.
    3551                 :     // We're not caching this fact, so every call to GetSelectionColors
    3552                 :     // will come through here. We could avoid this, but it's not really worth it.
    3553               0 :     return false;
    3554                 :   }
    3555                 : 
    3556               0 :   mInitSelectionColors = true;
    3557                 : 
    3558               0 :   nsIFrame* nonGeneratedAncestor = nsLayoutUtils::GetNonGeneratedAncestor(mFrame);
    3559                 :   Element* selectionElement =
    3560               0 :     FindElementAncestorForMozSelection(nonGeneratedAncestor->GetContent());
    3561                 : 
    3562               0 :   if (selectionElement &&
    3563                 :       selectionStatus == nsISelectionController::SELECTION_ON) {
    3564               0 :     nsRefPtr<nsStyleContext> sc = nsnull;
    3565                 :     sc = mPresContext->StyleSet()->
    3566                 :       ProbePseudoElementStyle(selectionElement,
    3567                 :                               nsCSSPseudoElements::ePseudo_mozSelection,
    3568               0 :                               mFrame->GetStyleContext());
    3569                 :     // Use -moz-selection pseudo class.
    3570               0 :     if (sc) {
    3571                 :       mSelectionBGColor =
    3572               0 :         sc->GetVisitedDependentColor(eCSSProperty_background_color);
    3573               0 :       mSelectionTextColor = sc->GetVisitedDependentColor(eCSSProperty_color);
    3574               0 :       return true;
    3575                 :     }
    3576                 :   }
    3577                 : 
    3578                 :   nscolor selectionBGColor =
    3579               0 :     LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectBackground);
    3580                 : 
    3581               0 :   if (selectionStatus == nsISelectionController::SELECTION_ATTENTION) {
    3582                 :     mSelectionBGColor =
    3583                 :       LookAndFeel::GetColor(
    3584               0 :         LookAndFeel::eColorID_TextSelectBackgroundAttention);
    3585                 :     mSelectionBGColor  = EnsureDifferentColors(mSelectionBGColor,
    3586               0 :                                                selectionBGColor);
    3587               0 :   } else if (selectionStatus != nsISelectionController::SELECTION_ON) {
    3588                 :     mSelectionBGColor =
    3589               0 :       LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectBackgroundDisabled);
    3590                 :     mSelectionBGColor  = EnsureDifferentColors(mSelectionBGColor,
    3591               0 :                                                selectionBGColor);
    3592                 :   } else {
    3593               0 :     mSelectionBGColor = selectionBGColor;
    3594                 :   }
    3595                 : 
    3596                 :   mSelectionTextColor =
    3597               0 :     LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectForeground);
    3598                 : 
    3599                 :   // On MacOS X, we don't exchange text color and BG color.
    3600               0 :   if (mSelectionTextColor == NS_DONT_CHANGE_COLOR) {
    3601               0 :     nscoord frameColor = mFrame->GetVisitedDependentColor(eCSSProperty_color);
    3602               0 :     mSelectionTextColor = EnsureDifferentColors(frameColor, mSelectionBGColor);
    3603                 :   } else {
    3604               0 :     EnsureSufficientContrast(&mSelectionTextColor, &mSelectionBGColor);
    3605                 :   }
    3606               0 :   return true;
    3607                 : }
    3608                 : 
    3609                 : nsTextPaintStyle::nsSelectionStyle*
    3610               0 : nsTextPaintStyle::GetSelectionStyle(PRInt32 aIndex)
    3611                 : {
    3612               0 :   InitSelectionStyle(aIndex);
    3613               0 :   return &mSelectionStyle[aIndex];
    3614                 : }
    3615                 : 
    3616                 : struct StyleIDs {
    3617                 :   LookAndFeel::ColorID mForeground, mBackground, mLine;
    3618                 :   LookAndFeel::IntID mLineStyle;
    3619                 :   LookAndFeel::FloatID mLineRelativeSize;
    3620                 : };
    3621                 : static StyleIDs SelectionStyleIDs[] = {
    3622                 :   { LookAndFeel::eColorID_IMERawInputForeground,
    3623                 :     LookAndFeel::eColorID_IMERawInputBackground,
    3624                 :     LookAndFeel::eColorID_IMERawInputUnderline,
    3625                 :     LookAndFeel::eIntID_IMERawInputUnderlineStyle,
    3626                 :     LookAndFeel::eFloatID_IMEUnderlineRelativeSize },
    3627                 :   { LookAndFeel::eColorID_IMESelectedRawTextForeground,
    3628                 :     LookAndFeel::eColorID_IMESelectedRawTextBackground,
    3629                 :     LookAndFeel::eColorID_IMESelectedRawTextUnderline,
    3630                 :     LookAndFeel::eIntID_IMESelectedRawTextUnderlineStyle,
    3631                 :     LookAndFeel::eFloatID_IMEUnderlineRelativeSize },
    3632                 :   { LookAndFeel::eColorID_IMEConvertedTextForeground,
    3633                 :     LookAndFeel::eColorID_IMEConvertedTextBackground,
    3634                 :     LookAndFeel::eColorID_IMEConvertedTextUnderline,
    3635                 :     LookAndFeel::eIntID_IMEConvertedTextUnderlineStyle,
    3636                 :     LookAndFeel::eFloatID_IMEUnderlineRelativeSize },
    3637                 :   { LookAndFeel::eColorID_IMESelectedConvertedTextForeground,
    3638                 :     LookAndFeel::eColorID_IMESelectedConvertedTextBackground,
    3639                 :     LookAndFeel::eColorID_IMESelectedConvertedTextUnderline,
    3640                 :     LookAndFeel::eIntID_IMESelectedConvertedTextUnderline,
    3641                 :     LookAndFeel::eFloatID_IMEUnderlineRelativeSize },
    3642                 :   { LookAndFeel::eColorID_LAST_COLOR,
    3643                 :     LookAndFeel::eColorID_LAST_COLOR,
    3644                 :     LookAndFeel::eColorID_SpellCheckerUnderline,
    3645                 :     LookAndFeel::eIntID_SpellCheckerUnderlineStyle,
    3646                 :     LookAndFeel::eFloatID_SpellCheckerUnderlineRelativeSize }
    3647                 : };
    3648                 : 
    3649                 : void
    3650               0 : nsTextPaintStyle::InitSelectionStyle(PRInt32 aIndex)
    3651                 : {
    3652               0 :   NS_ASSERTION(aIndex >= 0 && aIndex < 5, "aIndex is invalid");
    3653               0 :   nsSelectionStyle* selectionStyle = &mSelectionStyle[aIndex];
    3654               0 :   if (selectionStyle->mInit)
    3655               0 :     return;
    3656                 : 
    3657               0 :   StyleIDs* styleIDs = &SelectionStyleIDs[aIndex];
    3658                 : 
    3659                 :   nscolor foreColor, backColor;
    3660               0 :   if (styleIDs->mForeground == LookAndFeel::eColorID_LAST_COLOR) {
    3661               0 :     foreColor = NS_SAME_AS_FOREGROUND_COLOR;
    3662                 :   } else {
    3663               0 :     foreColor = LookAndFeel::GetColor(styleIDs->mForeground);
    3664                 :   }
    3665               0 :   if (styleIDs->mBackground == LookAndFeel::eColorID_LAST_COLOR) {
    3666               0 :     backColor = NS_TRANSPARENT;
    3667                 :   } else {
    3668               0 :     backColor = LookAndFeel::GetColor(styleIDs->mBackground);
    3669                 :   }
    3670                 : 
    3671                 :   // Convert special color to actual color
    3672               0 :   NS_ASSERTION(foreColor != NS_TRANSPARENT,
    3673                 :                "foreColor cannot be NS_TRANSPARENT");
    3674               0 :   NS_ASSERTION(backColor != NS_SAME_AS_FOREGROUND_COLOR,
    3675                 :                "backColor cannot be NS_SAME_AS_FOREGROUND_COLOR");
    3676               0 :   NS_ASSERTION(backColor != NS_40PERCENT_FOREGROUND_COLOR,
    3677                 :                "backColor cannot be NS_40PERCENT_FOREGROUND_COLOR");
    3678                 : 
    3679               0 :   foreColor = GetResolvedForeColor(foreColor, GetTextColor(), backColor);
    3680                 : 
    3681               0 :   if (NS_GET_A(backColor) > 0)
    3682               0 :     EnsureSufficientContrast(&foreColor, &backColor);
    3683                 : 
    3684                 :   nscolor lineColor;
    3685                 :   float relativeSize;
    3686                 :   PRUint8 lineStyle;
    3687                 :   GetSelectionUnderline(mPresContext, aIndex,
    3688               0 :                         &lineColor, &relativeSize, &lineStyle);
    3689               0 :   lineColor = GetResolvedForeColor(lineColor, foreColor, backColor);
    3690                 : 
    3691               0 :   selectionStyle->mTextColor       = foreColor;
    3692               0 :   selectionStyle->mBGColor         = backColor;
    3693               0 :   selectionStyle->mUnderlineColor  = lineColor;
    3694               0 :   selectionStyle->mUnderlineStyle  = lineStyle;
    3695               0 :   selectionStyle->mUnderlineRelativeSize = relativeSize;
    3696               0 :   selectionStyle->mInit            = true;
    3697                 : }
    3698                 : 
    3699                 : /* static */ bool
    3700               0 : nsTextPaintStyle::GetSelectionUnderline(nsPresContext* aPresContext,
    3701                 :                                         PRInt32 aIndex,
    3702                 :                                         nscolor* aLineColor,
    3703                 :                                         float* aRelativeSize,
    3704                 :                                         PRUint8* aStyle)
    3705                 : {
    3706               0 :   NS_ASSERTION(aPresContext, "aPresContext is null");
    3707               0 :   NS_ASSERTION(aRelativeSize, "aRelativeSize is null");
    3708               0 :   NS_ASSERTION(aStyle, "aStyle is null");
    3709               0 :   NS_ASSERTION(aIndex >= 0 && aIndex < 5, "Index out of range");
    3710                 : 
    3711               0 :   StyleIDs& styleID = SelectionStyleIDs[aIndex];
    3712                 : 
    3713               0 :   nscolor color = LookAndFeel::GetColor(styleID.mLine);
    3714               0 :   PRInt32 style = LookAndFeel::GetInt(styleID.mLineStyle);
    3715               0 :   if (style > NS_STYLE_TEXT_DECORATION_STYLE_MAX) {
    3716               0 :     NS_ERROR("Invalid underline style value is specified");
    3717               0 :     style = NS_STYLE_TEXT_DECORATION_STYLE_SOLID;
    3718                 :   }
    3719               0 :   float size = LookAndFeel::GetFloat(styleID.mLineRelativeSize);
    3720                 : 
    3721               0 :   NS_ASSERTION(size, "selection underline relative size must be larger than 0");
    3722                 : 
    3723               0 :   if (aLineColor) {
    3724               0 :     *aLineColor = color;
    3725                 :   }
    3726               0 :   *aRelativeSize = size;
    3727               0 :   *aStyle = style;
    3728                 : 
    3729                 :   return style != NS_STYLE_TEXT_DECORATION_STYLE_NONE &&
    3730                 :          color != NS_TRANSPARENT &&
    3731               0 :          size > 0.0f;
    3732                 : }
    3733                 : 
    3734               0 : inline nscolor Get40PercentColor(nscolor aForeColor, nscolor aBackColor)
    3735                 : {
    3736               0 :   nscolor foreColor = NS_RGBA(NS_GET_R(aForeColor),
    3737                 :                               NS_GET_G(aForeColor),
    3738                 :                               NS_GET_B(aForeColor),
    3739                 :                               (PRUint8)(255 * 0.4f));
    3740                 :   // Don't use true alpha color for readability.
    3741               0 :   return NS_ComposeColors(aBackColor, foreColor);
    3742                 : }
    3743                 : 
    3744                 : nscolor
    3745               0 : nsTextPaintStyle::GetResolvedForeColor(nscolor aColor,
    3746                 :                                        nscolor aDefaultForeColor,
    3747                 :                                        nscolor aBackColor)
    3748                 : {
    3749               0 :   if (aColor == NS_SAME_AS_FOREGROUND_COLOR)
    3750               0 :     return aDefaultForeColor;
    3751                 : 
    3752               0 :   if (aColor != NS_40PERCENT_FOREGROUND_COLOR)
    3753               0 :     return aColor;
    3754                 : 
    3755                 :   // Get actual background color
    3756               0 :   nscolor actualBGColor = aBackColor;
    3757               0 :   if (actualBGColor == NS_TRANSPARENT) {
    3758               0 :     InitCommonColors();
    3759               0 :     actualBGColor = mFrameBackgroundColor;
    3760                 :   }
    3761               0 :   return Get40PercentColor(aDefaultForeColor, actualBGColor);
    3762                 : }
    3763                 : 
    3764                 : //-----------------------------------------------------------------------------
    3765                 : 
    3766                 : #ifdef ACCESSIBILITY
    3767                 : already_AddRefed<nsAccessible>
    3768               0 : nsTextFrame::CreateAccessible()
    3769                 : {
    3770               0 :   if (IsEmpty()) {
    3771               0 :     nsAutoString renderedWhitespace;
    3772               0 :     GetRenderedText(&renderedWhitespace, nsnull, nsnull, 0, 1);
    3773               0 :     if (renderedWhitespace.IsEmpty()) {
    3774               0 :       return nsnull;
    3775                 :     }
    3776                 :   }
    3777                 : 
    3778               0 :   nsAccessibilityService* accService = nsIPresShell::AccService();
    3779               0 :   if (accService) {
    3780                 :     return accService->CreateHTMLTextAccessible(mContent,
    3781               0 :                                                 PresContext()->PresShell());
    3782                 :   }
    3783               0 :   return nsnull;
    3784                 : }
    3785                 : #endif
    3786                 : 
    3787                 : 
    3788                 : //-----------------------------------------------------------------------------
    3789                 : NS_IMETHODIMP
    3790               0 : nsTextFrame::Init(nsIContent*      aContent,
    3791                 :                   nsIFrame*        aParent,
    3792                 :                   nsIFrame*        aPrevInFlow)
    3793                 : {
    3794               0 :   NS_ASSERTION(!aPrevInFlow, "Can't be a continuation!");
    3795               0 :   NS_PRECONDITION(aContent->IsNodeOfType(nsINode::eTEXT),
    3796                 :                   "Bogus content!");
    3797                 : 
    3798                 :   // Remove any NewlineOffsetProperty or InFlowContentLengthProperty since they
    3799                 :   // might be invalid if the content was modified while there was no frame
    3800               0 :   aContent->DeleteProperty(nsGkAtoms::newline);
    3801               0 :   if (PresContext()->BidiEnabled()) {
    3802               0 :     aContent->DeleteProperty(nsGkAtoms::flowlength);
    3803                 :   }
    3804                 : 
    3805                 :   // Since our content has a frame now, this flag is no longer needed.
    3806               0 :   aContent->UnsetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE);
    3807                 : 
    3808                 :   // We're not a continuing frame.
    3809                 :   // mContentOffset = 0; not necessary since we get zeroed out at init
    3810               0 :   return nsFrame::Init(aContent, aParent, aPrevInFlow);
    3811                 : }
    3812                 : 
    3813                 : void
    3814               0 : nsTextFrame::ClearFrameOffsetCache()
    3815                 : {
    3816                 :   // See if we need to remove ourselves from the offset cache
    3817               0 :   if (GetStateBits() & TEXT_IN_OFFSET_CACHE) {
    3818               0 :     nsIFrame* primaryFrame = mContent->GetPrimaryFrame();
    3819               0 :     if (primaryFrame) {
    3820                 :       // The primary frame might be null here.  For example, nsLineBox::DeleteLineList
    3821                 :       // just destroys the frames in order, which means that the primary frame is already
    3822                 :       // dead if we're a continuing text frame, in which case, all of its properties are
    3823                 :       // gone, and we don't need to worry about deleting this property here.
    3824               0 :       primaryFrame->Properties().Delete(OffsetToFrameProperty());
    3825                 :     }
    3826               0 :     RemoveStateBits(TEXT_IN_OFFSET_CACHE);
    3827                 :   }
    3828               0 : }
    3829                 : 
    3830                 : void
    3831               0 : nsTextFrame::DestroyFrom(nsIFrame* aDestructRoot)
    3832                 : {
    3833               0 :   ClearFrameOffsetCache();
    3834                 : 
    3835                 :   // We might want to clear NS_CREATE_FRAME_IF_NON_WHITESPACE or
    3836                 :   // NS_REFRAME_IF_WHITESPACE on mContent here, since our parent frame
    3837                 :   // type might be changing.  Not clear whether it's worth it.
    3838               0 :   ClearTextRuns();
    3839               0 :   if (mNextContinuation) {
    3840               0 :     mNextContinuation->SetPrevInFlow(nsnull);
    3841                 :   }
    3842                 :   // Let the base class destroy the frame
    3843               0 :   nsFrame::DestroyFrom(aDestructRoot);
    3844               0 : }
    3845                 : 
    3846               0 : class nsContinuingTextFrame : public nsTextFrame {
    3847                 : public:
    3848                 :   NS_DECL_FRAMEARENA_HELPERS
    3849                 : 
    3850                 :   friend nsIFrame* NS_NewContinuingTextFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
    3851                 : 
    3852                 :   NS_IMETHOD Init(nsIContent*      aContent,
    3853                 :                   nsIFrame*        aParent,
    3854                 :                   nsIFrame*        aPrevInFlow);
    3855                 : 
    3856                 :   virtual void DestroyFrom(nsIFrame* aDestructRoot);
    3857                 : 
    3858               0 :   virtual nsIFrame* GetPrevContinuation() const {
    3859               0 :     return mPrevContinuation;
    3860                 :   }
    3861               0 :   NS_IMETHOD SetPrevContinuation(nsIFrame* aPrevContinuation) {
    3862               0 :     NS_ASSERTION (!aPrevContinuation || GetType() == aPrevContinuation->GetType(),
    3863                 :                   "setting a prev continuation with incorrect type!");
    3864               0 :     NS_ASSERTION (!nsSplittableFrame::IsInPrevContinuationChain(aPrevContinuation, this),
    3865                 :                   "creating a loop in continuation chain!");
    3866               0 :     mPrevContinuation = aPrevContinuation;
    3867               0 :     RemoveStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
    3868               0 :     return NS_OK;
    3869                 :   }
    3870               0 :   virtual nsIFrame* GetPrevInFlowVirtual() const { return GetPrevInFlow(); }
    3871               0 :   nsIFrame* GetPrevInFlow() const {
    3872               0 :     return (GetStateBits() & NS_FRAME_IS_FLUID_CONTINUATION) ? mPrevContinuation : nsnull;
    3873                 :   }
    3874               0 :   NS_IMETHOD SetPrevInFlow(nsIFrame* aPrevInFlow) {
    3875               0 :     NS_ASSERTION (!aPrevInFlow || GetType() == aPrevInFlow->GetType(),
    3876                 :                   "setting a prev in flow with incorrect type!");
    3877               0 :     NS_ASSERTION (!nsSplittableFrame::IsInPrevContinuationChain(aPrevInFlow, this),
    3878                 :                   "creating a loop in continuation chain!");
    3879               0 :     mPrevContinuation = aPrevInFlow;
    3880               0 :     AddStateBits(NS_FRAME_IS_FLUID_CONTINUATION);
    3881               0 :     return NS_OK;
    3882                 :   }
    3883                 :   virtual nsIFrame* GetFirstInFlow() const;
    3884                 :   virtual nsIFrame* GetFirstContinuation() const;
    3885                 : 
    3886                 :   virtual void AddInlineMinWidth(nsRenderingContext *aRenderingContext,
    3887                 :                                  InlineMinWidthData *aData);
    3888                 :   virtual void AddInlinePrefWidth(nsRenderingContext *aRenderingContext,
    3889                 :                                   InlinePrefWidthData *aData);
    3890                 :   
    3891               0 :   virtual nsresult GetRenderedText(nsAString* aString = nsnull,
    3892                 :                                    gfxSkipChars* aSkipChars = nsnull,
    3893                 :                                    gfxSkipCharsIterator* aSkipIter = nsnull,
    3894                 :                                    PRUint32 aSkippedStartOffset = 0,
    3895                 :                                    PRUint32 aSkippedMaxLength = PR_UINT32_MAX)
    3896               0 :   { return NS_ERROR_NOT_IMPLEMENTED; } // Call on a primary text frame only
    3897                 : 
    3898                 : protected:
    3899               0 :   nsContinuingTextFrame(nsStyleContext* aContext) : nsTextFrame(aContext) {}
    3900                 :   nsIFrame* mPrevContinuation;
    3901                 : };
    3902                 : 
    3903                 : NS_IMETHODIMP
    3904               0 : nsContinuingTextFrame::Init(nsIContent* aContent,
    3905                 :                             nsIFrame*   aParent,
    3906                 :                             nsIFrame*   aPrevInFlow)
    3907                 : {
    3908               0 :   NS_ASSERTION(aPrevInFlow, "Must be a continuation!");
    3909                 :   // NOTE: bypassing nsTextFrame::Init!!!
    3910               0 :   nsresult rv = nsFrame::Init(aContent, aParent, aPrevInFlow);
    3911                 : 
    3912                 : #ifdef IBMBIDI
    3913                 :   nsTextFrame* nextContinuation =
    3914               0 :     static_cast<nsTextFrame*>(aPrevInFlow->GetNextContinuation());
    3915                 : #endif // IBMBIDI
    3916                 :   // Hook the frame into the flow
    3917               0 :   SetPrevInFlow(aPrevInFlow);
    3918               0 :   aPrevInFlow->SetNextInFlow(this);
    3919               0 :   nsTextFrame* prev = static_cast<nsTextFrame*>(aPrevInFlow);
    3920               0 :   mContentOffset = prev->GetContentOffset() + prev->GetContentLengthHint();
    3921               0 :   NS_ASSERTION(mContentOffset < PRInt32(aContent->GetText()->GetLength()),
    3922                 :                "Creating ContinuingTextFrame, but there is no more content");
    3923               0 :   if (prev->GetStyleContext() != GetStyleContext()) {
    3924                 :     // We're taking part of prev's text, and its style may be different
    3925                 :     // so clear its textrun which may no longer be valid (and don't set ours)
    3926               0 :     prev->ClearTextRuns();
    3927                 :   } else {
    3928               0 :     float inflation = prev->GetFontSizeInflation();
    3929               0 :     SetFontSizeInflation(inflation);
    3930               0 :     mTextRun = prev->GetTextRun(nsTextFrame::eInflated);
    3931               0 :     if (inflation != 1.0f) {
    3932                 :       gfxTextRun *uninflatedTextRun =
    3933               0 :         prev->GetTextRun(nsTextFrame::eNotInflated);
    3934               0 :       if (uninflatedTextRun) {
    3935               0 :         SetTextRun(uninflatedTextRun, nsTextFrame::eNotInflated, 1.0f);
    3936                 :       }
    3937                 :     }
    3938                 :   }
    3939                 : #ifdef IBMBIDI
    3940               0 :   if (aPrevInFlow->GetStateBits() & NS_FRAME_IS_BIDI) {
    3941               0 :     FramePropertyTable *propTable = PresContext()->PropertyTable();
    3942                 :     // Get all the properties from the prev-in-flow first to take
    3943                 :     // advantage of the propTable's cache and simplify the assertion below
    3944               0 :     void* embeddingLevel = propTable->Get(aPrevInFlow, EmbeddingLevelProperty());
    3945               0 :     void* baseLevel = propTable->Get(aPrevInFlow, BaseLevelProperty());
    3946               0 :     propTable->Set(this, EmbeddingLevelProperty(), embeddingLevel);
    3947               0 :     propTable->Set(this, BaseLevelProperty(), baseLevel);
    3948                 : 
    3949               0 :     if (nextContinuation) {
    3950               0 :       SetNextContinuation(nextContinuation);
    3951               0 :       nextContinuation->SetPrevContinuation(this);
    3952                 :       // Adjust next-continuations' content offset as needed.
    3953               0 :       while (nextContinuation &&
    3954               0 :              nextContinuation->GetContentOffset() < mContentOffset) {
    3955               0 :         NS_ASSERTION(
    3956                 :           embeddingLevel == propTable->Get(nextContinuation, EmbeddingLevelProperty()) &&
    3957                 :           baseLevel == propTable->Get(nextContinuation, BaseLevelProperty()),
    3958                 :           "stealing text from different type of BIDI continuation");
    3959               0 :         nextContinuation->mContentOffset = mContentOffset;
    3960               0 :         nextContinuation = static_cast<nsTextFrame*>(nextContinuation->GetNextContinuation());
    3961                 :       }
    3962                 :     }
    3963               0 :     mState |= NS_FRAME_IS_BIDI;
    3964                 :   } // prev frame is bidi
    3965                 : #endif // IBMBIDI
    3966                 : 
    3967               0 :   return rv;
    3968                 : }
    3969                 : 
    3970                 : void
    3971               0 : nsContinuingTextFrame::DestroyFrom(nsIFrame* aDestructRoot)
    3972                 : {
    3973               0 :   ClearFrameOffsetCache();
    3974                 : 
    3975                 :   // The text associated with this frame will become associated with our
    3976                 :   // prev-continuation. If that means the text has changed style, then
    3977                 :   // we need to wipe out the text run for the text.
    3978                 :   // Note that mPrevContinuation can be null if we're destroying the whole
    3979                 :   // frame chain from the start to the end.
    3980                 :   // If this frame is mentioned in the userData for a textrun (say
    3981                 :   // because there's a direction change at the start of this frame), then
    3982                 :   // we have to clear the textrun because we're going away and the
    3983                 :   // textrun had better not keep a dangling reference to us.
    3984               0 :   if ((GetStateBits() & TEXT_IN_TEXTRUN_USER_DATA) ||
    3985               0 :       (!mPrevContinuation &&
    3986               0 :        !(GetStateBits() & TEXT_STYLE_MATCHES_PREV_CONTINUATION)) ||
    3987                 :       (mPrevContinuation &&
    3988               0 :        mPrevContinuation->GetStyleContext() != GetStyleContext())) {
    3989               0 :     ClearTextRuns();
    3990                 :     // Clear the previous continuation's text run also, so that it can rebuild
    3991                 :     // the text run to include our text.
    3992               0 :     if (mPrevContinuation) {
    3993                 :       nsTextFrame *prevContinuationText =
    3994               0 :         static_cast<nsTextFrame*>(mPrevContinuation);
    3995               0 :       prevContinuationText->ClearTextRuns();
    3996                 :     }
    3997                 :   }
    3998               0 :   nsSplittableFrame::RemoveFromFlow(this);
    3999                 :   // Let the base class destroy the frame
    4000               0 :   nsFrame::DestroyFrom(aDestructRoot);
    4001               0 : }
    4002                 : 
    4003                 : nsIFrame*
    4004               0 : nsContinuingTextFrame::GetFirstInFlow() const
    4005                 : {
    4006                 :   // Can't cast to |nsContinuingTextFrame*| because the first one isn't.
    4007                 :   nsIFrame *firstInFlow,
    4008                 :            *previous = const_cast<nsIFrame*>
    4009               0 :                                  (static_cast<const nsIFrame*>(this));
    4010               0 :   do {
    4011               0 :     firstInFlow = previous;
    4012               0 :     previous = firstInFlow->GetPrevInFlow();
    4013                 :   } while (previous);
    4014               0 :   return firstInFlow;
    4015                 : }
    4016                 : 
    4017                 : nsIFrame*
    4018               0 : nsContinuingTextFrame::GetFirstContinuation() const
    4019                 : {
    4020                 :   // Can't cast to |nsContinuingTextFrame*| because the first one isn't.
    4021                 :   nsIFrame *firstContinuation,
    4022                 :   *previous = const_cast<nsIFrame*>
    4023               0 :                         (static_cast<const nsIFrame*>(mPrevContinuation));
    4024                 : 
    4025               0 :   NS_ASSERTION(previous, "How can an nsContinuingTextFrame be the first continuation?");
    4026                 : 
    4027               0 :   do {
    4028               0 :     firstContinuation = previous;
    4029               0 :     previous = firstContinuation->GetPrevContinuation();
    4030                 :   } while (previous);
    4031               0 :   return firstContinuation;
    4032                 : }
    4033                 : 
    4034                 : // XXX Do we want to do all the work for the first-in-flow or do the
    4035                 : // work for each part?  (Be careful of first-letter / first-line, though,
    4036                 : // especially first-line!)  Doing all the work on the first-in-flow has
    4037                 : // the advantage of avoiding the potential for incremental reflow bugs,
    4038                 : // but depends on our maintining the frame tree in reasonable ways even
    4039                 : // for edge cases (block-within-inline splits, nextBidi, etc.)
    4040                 : 
    4041                 : // XXX We really need to make :first-letter happen during frame
    4042                 : // construction.
    4043                 : 
    4044                 : // Needed for text frames in XUL.
    4045                 : /* virtual */ nscoord
    4046               0 : nsTextFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
    4047                 : {
    4048               0 :   return nsLayoutUtils::MinWidthFromInline(this, aRenderingContext);
    4049                 : }
    4050                 : 
    4051                 : // Needed for text frames in XUL.
    4052                 : /* virtual */ nscoord
    4053               0 : nsTextFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
    4054                 : {
    4055               0 :   return nsLayoutUtils::PrefWidthFromInline(this, aRenderingContext);
    4056                 : }
    4057                 : 
    4058                 : /* virtual */ void
    4059               0 : nsContinuingTextFrame::AddInlineMinWidth(nsRenderingContext *aRenderingContext,
    4060                 :                                          InlineMinWidthData *aData)
    4061                 : {
    4062                 :   // Do nothing, since the first-in-flow accounts for everything.
    4063                 :   return;
    4064                 : }
    4065                 : 
    4066                 : /* virtual */ void
    4067               0 : nsContinuingTextFrame::AddInlinePrefWidth(nsRenderingContext *aRenderingContext,
    4068                 :                                           InlinePrefWidthData *aData)
    4069                 : {
    4070                 :   // Do nothing, since the first-in-flow accounts for everything.
    4071                 :   return;
    4072                 : }
    4073                 : 
    4074                 : static void 
    4075               0 : DestroySelectionDetails(SelectionDetails* aDetails)
    4076                 : {
    4077               0 :   while (aDetails) {
    4078               0 :     SelectionDetails* next = aDetails->mNext;
    4079               0 :     delete aDetails;
    4080               0 :     aDetails = next;
    4081                 :   }
    4082               0 : }
    4083                 : 
    4084                 : //----------------------------------------------------------------------
    4085                 : 
    4086                 : #if defined(DEBUG_rbs) || defined(DEBUG_bzbarsky)
    4087                 : static void
    4088                 : VerifyNotDirty(nsFrameState state)
    4089                 : {
    4090                 :   bool isZero = state & NS_FRAME_FIRST_REFLOW;
    4091                 :   bool isDirty = state & NS_FRAME_IS_DIRTY;
    4092                 :   if (!isZero && isDirty)
    4093                 :     NS_WARNING("internal offsets may be out-of-sync");
    4094                 : }
    4095                 : #define DEBUG_VERIFY_NOT_DIRTY(state) \
    4096                 : VerifyNotDirty(state)
    4097                 : #else
    4098                 : #define DEBUG_VERIFY_NOT_DIRTY(state)
    4099                 : #endif
    4100                 : 
    4101                 : nsIFrame*
    4102               0 : NS_NewTextFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
    4103                 : {
    4104               0 :   return new (aPresShell) nsTextFrame(aContext);
    4105                 : }
    4106                 : 
    4107               0 : NS_IMPL_FRAMEARENA_HELPERS(nsTextFrame)
    4108                 : 
    4109                 : nsIFrame*
    4110               0 : NS_NewContinuingTextFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
    4111                 : {
    4112               0 :   return new (aPresShell) nsContinuingTextFrame(aContext);
    4113                 : }
    4114                 : 
    4115               0 : NS_IMPL_FRAMEARENA_HELPERS(nsContinuingTextFrame)
    4116                 : 
    4117               0 : nsTextFrame::~nsTextFrame()
    4118                 : {
    4119               0 :   if (0 != (mState & TEXT_BLINK_ON))
    4120                 :   {
    4121               0 :     nsBlinkTimer::RemoveBlinkFrame(this);
    4122                 :   }
    4123               0 : }
    4124                 : 
    4125                 : NS_IMETHODIMP
    4126               0 : nsTextFrame::GetCursor(const nsPoint& aPoint,
    4127                 :                        nsIFrame::Cursor& aCursor)
    4128                 : {
    4129               0 :   FillCursorInformationFromStyle(GetStyleUserInterface(), aCursor);  
    4130               0 :   if (NS_STYLE_CURSOR_AUTO == aCursor.mCursor) {
    4131               0 :     aCursor.mCursor = NS_STYLE_CURSOR_TEXT;
    4132                 : 
    4133                 :     // If tabindex >= 0, use default cursor to indicate it's not selectable
    4134               0 :     nsIFrame *ancestorFrame = this;
    4135               0 :     while ((ancestorFrame = ancestorFrame->GetParent()) != nsnull) {
    4136               0 :       nsIContent *ancestorContent = ancestorFrame->GetContent();
    4137               0 :       if (ancestorContent && ancestorContent->HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)) {
    4138               0 :         nsAutoString tabIndexStr;
    4139               0 :         ancestorContent->GetAttr(kNameSpaceID_None, nsGkAtoms::tabindex, tabIndexStr);
    4140               0 :         if (!tabIndexStr.IsEmpty()) {
    4141               0 :           PRInt32 rv, tabIndexVal = tabIndexStr.ToInteger(&rv);
    4142               0 :           if (NS_SUCCEEDED(rv) && tabIndexVal >= 0) {
    4143               0 :             aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT;
    4144                 :             break;
    4145                 :           }
    4146                 :         }
    4147                 :       }
    4148                 :     }
    4149                 :   }
    4150                 : 
    4151               0 :   return NS_OK;
    4152                 : }
    4153                 : 
    4154                 : nsIFrame*
    4155               0 : nsTextFrame::GetLastInFlow() const
    4156                 : {
    4157               0 :   nsTextFrame* lastInFlow = const_cast<nsTextFrame*>(this);
    4158               0 :   while (lastInFlow->GetNextInFlow())  {
    4159               0 :     lastInFlow = static_cast<nsTextFrame*>(lastInFlow->GetNextInFlow());
    4160                 :   }
    4161               0 :   NS_POSTCONDITION(lastInFlow, "illegal state in flow chain.");
    4162               0 :   return lastInFlow;
    4163                 : }
    4164                 : nsIFrame*
    4165               0 : nsTextFrame::GetLastContinuation() const
    4166                 : {
    4167               0 :   nsTextFrame* lastInFlow = const_cast<nsTextFrame*>(this);
    4168               0 :   while (lastInFlow->mNextContinuation)  {
    4169               0 :     lastInFlow = static_cast<nsTextFrame*>(lastInFlow->mNextContinuation);
    4170                 :   }
    4171               0 :   NS_POSTCONDITION(lastInFlow, "illegal state in continuation chain.");
    4172               0 :   return lastInFlow;
    4173                 : }
    4174                 : 
    4175                 : gfxTextRun*
    4176               0 : nsTextFrame::GetUninflatedTextRun()
    4177                 : {
    4178                 :   return static_cast<gfxTextRun*>(
    4179               0 :            Properties().Get(UninflatedTextRunProperty()));
    4180                 : }
    4181                 : 
    4182                 : void
    4183               0 : nsTextFrame::SetTextRun(gfxTextRun* aTextRun, TextRunType aWhichTextRun,
    4184                 :                         float aInflation)
    4185                 : {
    4186               0 :   NS_ASSERTION(aTextRun, "must have text run");
    4187                 : 
    4188                 :   // Our inflated text run is always stored in mTextRun.  In the cases
    4189                 :   // where our current inflation is not 1.0, however, we store two text
    4190                 :   // runs, and the uninflated one goes in a frame property.  We never
    4191                 :   // store a single text run in both.
    4192               0 :   if (aWhichTextRun == eInflated) {
    4193               0 :     if (HasFontSizeInflation() && aInflation == 1.0f) {
    4194                 :       // FIXME: Probably shouldn't do this within each SetTextRun
    4195                 :       // method, but it doesn't hurt.
    4196               0 :       ClearTextRun(nsnull, nsTextFrame::eNotInflated);
    4197                 :     }
    4198               0 :     SetFontSizeInflation(aInflation);
    4199                 :   } else {
    4200               0 :     NS_ABORT_IF_FALSE(aInflation == 1.0f, "unexpected inflation");
    4201               0 :     if (HasFontSizeInflation()) {
    4202               0 :       Properties().Set(UninflatedTextRunProperty(), aTextRun);
    4203               0 :       return;
    4204                 :     }
    4205                 :     // fall through to setting mTextRun
    4206                 :   }
    4207                 : 
    4208               0 :   mTextRun = aTextRun;
    4209                 : 
    4210                 :   // FIXME: Add assertions testing the relationship between
    4211                 :   // GetFontSizeInflation() and whether we have an uninflated text run
    4212                 :   // (but be aware that text runs can go away).
    4213                 : }
    4214                 : 
    4215                 : bool
    4216               0 : nsTextFrame::RemoveTextRun(gfxTextRun* aTextRun)
    4217                 : {
    4218               0 :   if (aTextRun == mTextRun) {
    4219               0 :     mTextRun = nsnull;
    4220               0 :     return true;
    4221                 :   }
    4222               0 :   FrameProperties props = Properties();
    4223               0 :   if ((GetStateBits() & TEXT_HAS_FONT_INFLATION) &&
    4224               0 :       props.Get(UninflatedTextRunProperty()) == aTextRun) {
    4225               0 :     props.Delete(UninflatedTextRunProperty());
    4226               0 :     return true;
    4227                 :   }
    4228               0 :   return false;
    4229                 : }
    4230                 : 
    4231                 : void
    4232               0 : nsTextFrame::ClearTextRun(nsTextFrame* aStartContinuation,
    4233                 :                           TextRunType aWhichTextRun)
    4234                 : {
    4235                 :   // save textrun because ClearAllTextRunReferences may clear ours
    4236               0 :   gfxTextRun* textRun = GetTextRun(aWhichTextRun);
    4237                 : 
    4238               0 :   if (!textRun)
    4239               0 :     return;
    4240                 : 
    4241               0 :   UnhookTextRunFromFrames(textRun, aStartContinuation);
    4242                 :   // see comments in BuildTextRunForFrames...
    4243                 : //  if (textRun->GetFlags() & gfxFontGroup::TEXT_IS_PERSISTENT) {
    4244                 : //    NS_ERROR("Shouldn't reach here for now...");
    4245                 : //    // the textrun's text may be referencing a DOM node that has changed,
    4246                 : //    // so we'd better kill this textrun now.
    4247                 : //    if (textRun->GetExpirationState()->IsTracked()) {
    4248                 : //      gTextRuns->RemoveFromCache(textRun);
    4249                 : //    }
    4250                 : //    delete textRun;
    4251                 : //    return;
    4252                 : //  }
    4253                 : 
    4254               0 :   if (!textRun->GetUserData()) {
    4255                 :     // Remove it now because it's not doing anything useful
    4256               0 :     gTextRuns->RemoveFromCache(textRun);
    4257               0 :     delete textRun;
    4258                 :   }
    4259                 : }
    4260                 : 
    4261                 : NS_IMETHODIMP
    4262               0 : nsTextFrame::CharacterDataChanged(CharacterDataChangeInfo* aInfo)
    4263                 : {
    4264               0 :   mContent->DeleteProperty(nsGkAtoms::newline);
    4265               0 :   if (PresContext()->BidiEnabled()) {
    4266               0 :     mContent->DeleteProperty(nsGkAtoms::flowlength);
    4267                 :   }
    4268                 : 
    4269                 :   // Find the first frame whose text has changed. Frames that are entirely
    4270                 :   // before the text change are completely unaffected.
    4271                 :   nsTextFrame* next;
    4272               0 :   nsTextFrame* textFrame = this;
    4273               0 :   while (true) {
    4274               0 :     next = static_cast<nsTextFrame*>(textFrame->GetNextContinuation());
    4275               0 :     if (!next || next->GetContentOffset() > PRInt32(aInfo->mChangeStart))
    4276                 :       break;
    4277               0 :     textFrame = next;
    4278                 :   }
    4279                 : 
    4280               0 :   PRInt32 endOfChangedText = aInfo->mChangeStart + aInfo->mReplaceLength;
    4281               0 :   nsTextFrame* lastDirtiedFrame = nsnull;
    4282                 : 
    4283               0 :   nsIPresShell* shell = PresContext()->GetPresShell();
    4284               0 :   do {
    4285                 :     // textFrame contained deleted text (or the insertion point,
    4286                 :     // if this was a pure insertion).
    4287               0 :     textFrame->mState &= ~TEXT_WHITESPACE_FLAGS;
    4288               0 :     textFrame->ClearTextRuns();
    4289               0 :     if (!lastDirtiedFrame ||
    4290               0 :         lastDirtiedFrame->GetParent() != textFrame->GetParent()) {
    4291                 :       // Ask the parent frame to reflow me.
    4292                 :       shell->FrameNeedsReflow(textFrame, nsIPresShell::eStyleChange,
    4293               0 :                               NS_FRAME_IS_DIRTY);
    4294               0 :       lastDirtiedFrame = textFrame;
    4295                 :     } else {
    4296                 :       // if the parent is a block, we're cheating here because we should
    4297                 :       // be marking our line dirty, but we're not. nsTextFrame::SetLength
    4298                 :       // will do that when it gets called during reflow.
    4299               0 :       textFrame->AddStateBits(NS_FRAME_IS_DIRTY);
    4300                 :     }
    4301                 : 
    4302                 :     // Below, frames that start after the deleted text will be adjusted so that
    4303                 :     // their offsets move with the trailing unchanged text. If this change
    4304                 :     // deletes more text than it inserts, those frame offsets will decrease.
    4305                 :     // We need to maintain the invariant that mContentOffset is non-decreasing
    4306                 :     // along the continuation chain. So we need to ensure that frames that
    4307                 :     // started in the deleted text are all still starting before the
    4308                 :     // unchanged text.
    4309               0 :     if (textFrame->mContentOffset > endOfChangedText) {
    4310               0 :       textFrame->mContentOffset = endOfChangedText;
    4311                 :     }
    4312                 : 
    4313               0 :     textFrame = static_cast<nsTextFrame*>(textFrame->GetNextContinuation());
    4314               0 :   } while (textFrame && textFrame->GetContentOffset() < PRInt32(aInfo->mChangeEnd));
    4315                 : 
    4316                 :   // This is how much the length of the string changed by --- i.e.,
    4317                 :   // how much the trailing unchanged text moved.
    4318                 :   PRInt32 sizeChange =
    4319               0 :     aInfo->mChangeStart + aInfo->mReplaceLength - aInfo->mChangeEnd;
    4320                 : 
    4321               0 :   if (sizeChange) {
    4322                 :     // Fix the offsets of the text frames that start in the trailing
    4323                 :     // unchanged text.
    4324               0 :     while (textFrame) {
    4325               0 :       textFrame->mContentOffset += sizeChange;
    4326                 :       // XXX we could rescue some text runs by adjusting their user data
    4327                 :       // to reflect the change in DOM offsets
    4328               0 :       textFrame->ClearTextRuns();
    4329               0 :       textFrame = static_cast<nsTextFrame*>(textFrame->GetNextContinuation());
    4330                 :     }
    4331                 :   }
    4332                 : 
    4333               0 :   return NS_OK;
    4334                 : }
    4335                 : 
    4336                 : /* virtual */ void
    4337               0 : nsTextFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
    4338                 : {
    4339               0 :   nsFrame::DidSetStyleContext(aOldStyleContext);
    4340               0 :   ClearTextRuns();
    4341               0 : } 
    4342                 : 
    4343                 : class nsDisplayText : public nsCharClipDisplayItem {
    4344                 : public:
    4345               0 :   nsDisplayText(nsDisplayListBuilder* aBuilder, nsTextFrame* aFrame) :
    4346                 :     nsCharClipDisplayItem(aBuilder, aFrame),
    4347               0 :     mDisableSubpixelAA(false) {
    4348               0 :     MOZ_COUNT_CTOR(nsDisplayText);
    4349               0 :   }
    4350                 : #ifdef NS_BUILD_REFCNT_LOGGING
    4351               0 :   virtual ~nsDisplayText() {
    4352               0 :     MOZ_COUNT_DTOR(nsDisplayText);
    4353               0 :   }
    4354                 : #endif
    4355                 : 
    4356               0 :   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder) {
    4357               0 :     return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
    4358                 :   }
    4359               0 :   virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
    4360                 :                        HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) {
    4361               0 :     if (nsRect(ToReferenceFrame(), mFrame->GetSize()).Intersects(aRect)) {
    4362               0 :       aOutFrames->AppendElement(mFrame);
    4363                 :     }
    4364               0 :   }
    4365                 :   virtual void Paint(nsDisplayListBuilder* aBuilder,
    4366                 :                      nsRenderingContext* aCtx);
    4367               0 :   NS_DISPLAY_DECL_NAME("Text", TYPE_TEXT)
    4368                 : 
    4369               0 :   virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder)
    4370                 :   {
    4371               0 :     return GetBounds(aBuilder);
    4372                 :   }
    4373                 : 
    4374               0 :   virtual void DisableComponentAlpha() { mDisableSubpixelAA = true; }
    4375                 : 
    4376                 :   bool mDisableSubpixelAA;
    4377                 : };
    4378                 : 
    4379                 : void
    4380               0 : nsDisplayText::Paint(nsDisplayListBuilder* aBuilder,
    4381                 :                      nsRenderingContext* aCtx) {
    4382                 :   // Add 1 pixel of dirty area around mVisibleRect to allow us to paint
    4383                 :   // antialiased pixels beyond the measured text extents.
    4384                 :   // This is temporary until we do this in the actual calculation of text extents.
    4385               0 :   nsRect extraVisible = mVisibleRect;
    4386               0 :   nscoord appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
    4387               0 :   extraVisible.Inflate(appUnitsPerDevPixel, appUnitsPerDevPixel);
    4388               0 :   nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
    4389                 : 
    4390                 :   gfxContextAutoDisableSubpixelAntialiasing disable(aCtx->ThebesContext(),
    4391               0 :                                                     mDisableSubpixelAA);
    4392               0 :   NS_ASSERTION(mLeftEdge >= 0, "illegal left edge");
    4393               0 :   NS_ASSERTION(mRightEdge >= 0, "illegal right edge");
    4394               0 :   f->PaintText(aCtx, ToReferenceFrame(), extraVisible, *this);
    4395               0 : }
    4396                 : 
    4397                 : NS_IMETHODIMP
    4398               0 : nsTextFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
    4399                 :                               const nsRect&           aDirtyRect,
    4400                 :                               const nsDisplayListSet& aLists)
    4401                 : {
    4402               0 :   if (!IsVisibleForPainting(aBuilder))
    4403               0 :     return NS_OK;
    4404                 :   
    4405               0 :   DO_GLOBAL_REFLOW_COUNT_DSP("nsTextFrame");
    4406                 : 
    4407               0 :   if ((0 != (mState & TEXT_BLINK_ON)) && nsBlinkTimer::GetBlinkIsOff() &&
    4408               0 :       PresContext()->IsDynamic() && !aBuilder->IsForEventDelivery())
    4409               0 :     return NS_OK;
    4410                 :     
    4411                 :   return aLists.Content()->AppendNewToTop(
    4412               0 :       new (aBuilder) nsDisplayText(aBuilder, this));
    4413                 : }
    4414                 : 
    4415                 : static nsIFrame*
    4416               0 : GetGeneratedContentOwner(nsIFrame* aFrame, bool* aIsBefore)
    4417                 : {
    4418               0 :   *aIsBefore = false;
    4419               0 :   while (aFrame && (aFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT)) {
    4420               0 :     if (aFrame->GetStyleContext()->GetPseudo() == nsCSSPseudoElements::before) {
    4421               0 :       *aIsBefore = true;
    4422                 :     }
    4423               0 :     aFrame = aFrame->GetParent();
    4424                 :   }
    4425               0 :   return aFrame;
    4426                 : }
    4427                 : 
    4428                 : SelectionDetails*
    4429               0 : nsTextFrame::GetSelectionDetails()
    4430                 : {
    4431               0 :   const nsFrameSelection* frameSelection = GetConstFrameSelection();
    4432               0 :   if (frameSelection->GetTableCellSelection()) {
    4433               0 :     return nsnull;
    4434                 :   }
    4435               0 :   if (!(GetStateBits() & NS_FRAME_GENERATED_CONTENT)) {
    4436                 :     SelectionDetails* details =
    4437                 :       frameSelection->LookUpSelection(mContent, GetContentOffset(),
    4438               0 :                                       GetContentLength(), false);
    4439                 :     SelectionDetails* sd;
    4440               0 :     for (sd = details; sd; sd = sd->mNext) {
    4441               0 :       sd->mStart += mContentOffset;
    4442               0 :       sd->mEnd += mContentOffset;
    4443                 :     }
    4444               0 :     return details;
    4445                 :   }
    4446                 : 
    4447                 :   // Check if the beginning or end of the element is selected, depending on
    4448                 :   // whether we're :before content or :after content.
    4449                 :   bool isBefore;
    4450               0 :   nsIFrame* owner = GetGeneratedContentOwner(this, &isBefore);
    4451               0 :   if (!owner || !owner->GetContent())
    4452               0 :     return nsnull;
    4453                 : 
    4454                 :   SelectionDetails* details =
    4455                 :     frameSelection->LookUpSelection(owner->GetContent(),
    4456               0 :         isBefore ? 0 : owner->GetContent()->GetChildCount(), 0, false);
    4457                 :   SelectionDetails* sd;
    4458               0 :   for (sd = details; sd; sd = sd->mNext) {
    4459                 :     // The entire text is selected!
    4460               0 :     sd->mStart = GetContentOffset();
    4461               0 :     sd->mEnd = GetContentEnd();
    4462                 :   }
    4463               0 :   return details;
    4464                 : }
    4465                 : 
    4466                 : static void
    4467               0 : FillClippedRect(gfxContext* aCtx, nsPresContext* aPresContext,
    4468                 :                 nscolor aColor, const gfxRect& aDirtyRect, const gfxRect& aRect)
    4469                 : {
    4470               0 :   gfxRect r = aRect.Intersect(aDirtyRect);
    4471                 :   // For now, we need to put this in pixel coordinates
    4472               0 :   PRInt32 app = aPresContext->AppUnitsPerDevPixel();
    4473               0 :   aCtx->NewPath();
    4474                 :   // pixel-snap
    4475               0 :   aCtx->Rectangle(gfxRect(r.X() / app, r.Y() / app,
    4476               0 :                           r.Width() / app, r.Height() / app), true);
    4477               0 :   aCtx->SetColor(gfxRGBA(aColor));
    4478               0 :   aCtx->Fill();
    4479               0 : }
    4480                 : 
    4481                 : void
    4482               0 : nsTextFrame::GetTextDecorations(nsPresContext* aPresContext,
    4483                 :                                 nsTextFrame::TextDecorations& aDecorations)
    4484                 : {
    4485               0 :   const nsCompatibility compatMode = aPresContext->CompatibilityMode();
    4486                 : 
    4487               0 :   bool useOverride = false;
    4488                 :   nscolor overrideColor;
    4489                 : 
    4490                 :   // frameTopOffset represents the offset to f's top from our baseline in our
    4491                 :   // coordinate space
    4492                 :   // baselineOffset represents the offset from our baseline to f's baseline or
    4493                 :   // the nearest block's baseline, in our coordinate space, whichever is closest
    4494                 :   // during the particular iteration
    4495               0 :   nscoord frameTopOffset = mAscent,
    4496               0 :           baselineOffset = 0;
    4497                 : 
    4498               0 :   bool nearestBlockFound = false;
    4499                 : 
    4500               0 :   for (nsIFrame* f = this, *fChild = nsnull;
    4501                 :        f;
    4502                 :        fChild = f,
    4503                 :        f = nsLayoutUtils::GetParentOrPlaceholderFor(
    4504               0 :              aPresContext->FrameManager(), f))
    4505                 :   {
    4506               0 :     nsStyleContext *const context = f->GetStyleContext();
    4507               0 :     if (!context->HasTextDecorationLines()) {
    4508               0 :       break;
    4509                 :     }
    4510                 : 
    4511               0 :     const nsStyleTextReset *const styleText = context->GetStyleTextReset();
    4512               0 :     const PRUint8 textDecorations = styleText->mTextDecorationLine;
    4513                 : 
    4514               0 :     if (!useOverride &&
    4515                 :         (NS_STYLE_TEXT_DECORATION_LINE_OVERRIDE_ALL & textDecorations)) {
    4516                 :       // This handles the <a href="blah.html"><font color="green">La 
    4517                 :       // la la</font></a> case. The link underline should be green.
    4518               0 :       useOverride = true;
    4519                 :       overrideColor =
    4520               0 :         nsLayoutUtils::GetColor(f, eCSSProperty_text_decoration_color);
    4521                 :     }
    4522                 : 
    4523               0 :     const bool firstBlock = !nearestBlockFound && nsLayoutUtils::GetAsBlock(f);
    4524                 : 
    4525                 :     // Not updating positions once we hit a parent block is equivalent to
    4526                 :     // the CSS 2.1 spec that blocks should propagate decorations down to their
    4527                 :     // children (albeit the style should be preserved)
    4528                 :     // However, if we're vertically aligned within a block, then we need to
    4529                 :     // recover the right baseline from the line by querying the FrameProperty
    4530                 :     // that should be set (see nsLineLayout::VerticalAlignLine).
    4531               0 :     if (firstBlock) {
    4532                 :       // At this point, fChild can't be null since TextFrames can't be blocks
    4533                 :       const nsStyleCoord& vAlign =
    4534               0 :         fChild->GetStyleContext()->GetStyleTextReset()->mVerticalAlign;
    4535               0 :       if (vAlign.GetUnit() != eStyleUnit_Enumerated ||
    4536               0 :           vAlign.GetIntValue() != NS_STYLE_VERTICAL_ALIGN_BASELINE)
    4537                 :       {
    4538                 :         // Since offset is the offset in the child's coordinate space, we have
    4539                 :         // to undo the accumulation to bring the transform out of the block's
    4540                 :         // coordinate space
    4541                 :         baselineOffset =
    4542               0 :           frameTopOffset - (fChild->GetRect().y - fChild->GetRelativeOffset().y)
    4543               0 :           - NS_PTR_TO_INT32(
    4544               0 :               fChild->Properties().Get(nsIFrame::LineBaselineOffset()));
    4545                 :       }
    4546                 :     }
    4547               0 :     else if (!nearestBlockFound) {
    4548               0 :       baselineOffset = frameTopOffset - f->GetBaseline();
    4549                 :     }
    4550                 : 
    4551               0 :     nearestBlockFound = nearestBlockFound || firstBlock;
    4552               0 :     frameTopOffset += f->GetRect().y - f->GetRelativeOffset().y;
    4553                 : 
    4554               0 :     const PRUint8 style = styleText->GetDecorationStyle();
    4555                 :     // Accumulate only elements that have decorations with a genuine style
    4556               0 :     if (textDecorations && style != NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
    4557                 :       const nscolor color = useOverride ? overrideColor
    4558               0 :         : nsLayoutUtils::GetColor(f, eCSSProperty_text_decoration_color);
    4559                 : 
    4560               0 :       if (textDecorations & NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE) {
    4561                 :         aDecorations.mUnderlines.AppendElement(
    4562               0 :           nsTextFrame::LineDecoration(f, baselineOffset, color, style));
    4563                 :       }
    4564               0 :       if (textDecorations & NS_STYLE_TEXT_DECORATION_LINE_OVERLINE) {
    4565                 :         aDecorations.mOverlines.AppendElement(
    4566               0 :           nsTextFrame::LineDecoration(f, baselineOffset, color, style));
    4567                 :       }
    4568               0 :       if (textDecorations & NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH) {
    4569                 :         aDecorations.mStrikes.AppendElement(
    4570               0 :           nsTextFrame::LineDecoration(f, baselineOffset, color, style));
    4571                 :       }
    4572                 :     }
    4573                 : 
    4574                 :     // In all modes, if we're on an inline-block or inline-table (or
    4575                 :     // inline-stack, inline-box, inline-grid), we're done.
    4576               0 :     const nsStyleDisplay *disp = context->GetStyleDisplay();
    4577               0 :     if (disp->mDisplay != NS_STYLE_DISPLAY_INLINE &&
    4578               0 :         disp->IsInlineOutside()) {
    4579               0 :       break;
    4580                 :     }
    4581                 : 
    4582               0 :     if (compatMode == eCompatibility_NavQuirks) {
    4583                 :       // In quirks mode, if we're on an HTML table element, we're done.
    4584               0 :       if (f->GetContent()->IsHTML(nsGkAtoms::table)) {
    4585               0 :         break;
    4586                 :       }
    4587                 :     } else {
    4588                 :       // In standards/almost-standards mode, if we're on an
    4589                 :       // absolutely-positioned element or a floating element, we're done.
    4590               0 :       if (disp->IsFloating() || disp->IsAbsolutelyPositioned()) {
    4591               0 :         break;
    4592                 :       }
    4593                 :     }
    4594                 :   }
    4595               0 : }
    4596                 : 
    4597                 : void
    4598               0 : nsTextFrame::UnionAdditionalOverflow(nsPresContext* aPresContext,
    4599                 :                                      const nsHTMLReflowState& aBlockReflowState,
    4600                 :                                      PropertyProvider& aProvider,
    4601                 :                                      nsRect* aVisualOverflowRect,
    4602                 :                                      bool aIncludeTextDecorations)
    4603                 : {
    4604                 :   // Text-shadow overflows
    4605                 :   nsRect shadowRect =
    4606               0 :     nsLayoutUtils::GetTextShadowRectsUnion(*aVisualOverflowRect, this);
    4607               0 :   aVisualOverflowRect->UnionRect(*aVisualOverflowRect, shadowRect);
    4608                 : 
    4609               0 :   if (IsFloatingFirstLetterChild()) {
    4610                 :     // The underline/overline drawable area must be contained in the overflow
    4611                 :     // rect when this is in floating first letter frame at *both* modes.
    4612               0 :     nsFontMetrics* fm = aProvider.GetFontMetrics();
    4613               0 :     nscoord fontAscent = fm->MaxAscent();
    4614               0 :     nscoord fontHeight = fm->MaxHeight();
    4615               0 :     nsRect fontRect(0, mAscent - fontAscent, GetSize().width, fontHeight);
    4616               0 :     aVisualOverflowRect->UnionRect(*aVisualOverflowRect, fontRect);
    4617                 :   }
    4618               0 :   if (aIncludeTextDecorations) {
    4619                 :     // Since CSS 2.1 requires that text-decoration defined on ancestors maintain
    4620                 :     // style and position, they can be drawn at virtually any y-offset, so
    4621                 :     // maxima and minima are required to reliably generate the rectangle for
    4622                 :     // them
    4623               0 :     TextDecorations textDecs;
    4624               0 :     GetTextDecorations(aPresContext, textDecs);
    4625               0 :     if (textDecs.HasDecorationLines()) {
    4626                 :       nscoord inflationMinFontSize =
    4627                 :         nsLayoutUtils::InflationMinFontSizeFor(aBlockReflowState.frame,
    4628               0 :                                                nsLayoutUtils::eInReflow);
    4629                 : 
    4630               0 :       const nscoord width = GetSize().width;
    4631               0 :       const gfxFloat appUnitsPerDevUnit = aPresContext->AppUnitsPerDevPixel(),
    4632               0 :                      gfxWidth = width / appUnitsPerDevUnit,
    4633               0 :                      ascent = gfxFloat(mAscent) / appUnitsPerDevUnit;
    4634               0 :       nscoord top(nscoord_MAX), bottom(nscoord_MIN);
    4635                 :       // Below we loop through all text decorations and compute the rectangle
    4636                 :       // containing all of them, in this frame's coordinate space
    4637               0 :       for (PRUint32 i = 0; i < textDecs.mUnderlines.Length(); ++i) {
    4638               0 :         const LineDecoration& dec = textDecs.mUnderlines[i];
    4639                 : 
    4640                 :         float inflation = nsLayoutUtils::FontSizeInflationInner(dec.mFrame,
    4641               0 :                             inflationMinFontSize);
    4642                 :         const gfxFont::Metrics metrics =
    4643               0 :           GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation));
    4644                 : 
    4645                 :         const nsRect decorationRect =
    4646                 :           nsCSSRendering::GetTextDecorationRect(aPresContext,
    4647                 :             gfxSize(gfxWidth, metrics.underlineSize),
    4648                 :             ascent, metrics.underlineOffset,
    4649               0 :             NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE, dec.mStyle) +
    4650               0 :           nsPoint(0, -dec.mBaselineOffset);
    4651                 : 
    4652               0 :         top = NS_MIN(decorationRect.y, top);
    4653               0 :         bottom = NS_MAX(decorationRect.YMost(), bottom);
    4654                 :       }
    4655               0 :       for (PRUint32 i = 0; i < textDecs.mOverlines.Length(); ++i) {
    4656               0 :         const LineDecoration& dec = textDecs.mOverlines[i];
    4657                 : 
    4658                 :         float inflation = nsLayoutUtils::FontSizeInflationInner(dec.mFrame,
    4659               0 :                             inflationMinFontSize);
    4660                 :         const gfxFont::Metrics metrics =
    4661               0 :           GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation));
    4662                 : 
    4663                 :         const nsRect decorationRect =
    4664                 :           nsCSSRendering::GetTextDecorationRect(aPresContext,
    4665                 :             gfxSize(gfxWidth, metrics.underlineSize),
    4666                 :             ascent, metrics.maxAscent,
    4667               0 :             NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, dec.mStyle) +
    4668               0 :           nsPoint(0, -dec.mBaselineOffset);
    4669                 : 
    4670               0 :         top = NS_MIN(decorationRect.y, top);
    4671               0 :         bottom = NS_MAX(decorationRect.YMost(), bottom);
    4672                 :       }
    4673               0 :       for (PRUint32 i = 0; i < textDecs.mStrikes.Length(); ++i) {
    4674               0 :         const LineDecoration& dec = textDecs.mStrikes[i];
    4675                 : 
    4676                 :         float inflation = nsLayoutUtils::FontSizeInflationInner(dec.mFrame,
    4677               0 :                             inflationMinFontSize);
    4678                 :         const gfxFont::Metrics metrics =
    4679               0 :           GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation));
    4680                 : 
    4681                 :         const nsRect decorationRect =
    4682                 :           nsCSSRendering::GetTextDecorationRect(aPresContext,
    4683                 :             gfxSize(gfxWidth, metrics.strikeoutSize),
    4684                 :             ascent, metrics.strikeoutOffset,
    4685               0 :             NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH, dec.mStyle) +
    4686               0 :           nsPoint(0, -dec.mBaselineOffset);
    4687               0 :         top = NS_MIN(decorationRect.y, top);
    4688               0 :         bottom = NS_MAX(decorationRect.YMost(), bottom);
    4689                 :       }
    4690                 : 
    4691                 :       aVisualOverflowRect->UnionRect(*aVisualOverflowRect,
    4692               0 :                                      nsRect(0, top, width, bottom - top));
    4693                 :     }
    4694                 :   }
    4695                 :   // When this frame is not selected, the text-decoration area must be in
    4696                 :   // frame bounds.
    4697               0 :   if (!IsSelected() ||
    4698               0 :       !CombineSelectionUnderlineRect(aPresContext, *aVisualOverflowRect))
    4699                 :     return;
    4700               0 :   AddStateBits(TEXT_SELECTION_UNDERLINE_OVERFLOWED);
    4701                 : }
    4702                 : 
    4703                 : static gfxFloat
    4704               0 : ComputeDescentLimitForSelectionUnderline(nsPresContext* aPresContext,
    4705                 :                                          nsTextFrame* aFrame,
    4706                 :                                          const gfxFont::Metrics& aFontMetrics)
    4707                 : {
    4708               0 :   gfxFloat app = aPresContext->AppUnitsPerDevPixel();
    4709                 :   nscoord lineHeightApp =
    4710                 :     nsHTMLReflowState::CalcLineHeight(aFrame->GetStyleContext(), NS_AUTOHEIGHT,
    4711               0 :                                       aFrame->GetFontSizeInflation());
    4712               0 :   gfxFloat lineHeight = gfxFloat(lineHeightApp) / app;
    4713               0 :   if (lineHeight <= aFontMetrics.maxHeight) {
    4714               0 :     return aFontMetrics.maxDescent;
    4715                 :   }
    4716               0 :   return aFontMetrics.maxDescent + (lineHeight - aFontMetrics.maxHeight) / 2;
    4717                 : }
    4718                 : 
    4719                 : 
    4720                 : // Make sure this stays in sync with DrawSelectionDecorations below
    4721                 : static const SelectionType SelectionTypesWithDecorations =
    4722                 :   nsISelectionController::SELECTION_SPELLCHECK |
    4723                 :   nsISelectionController::SELECTION_IME_RAWINPUT |
    4724                 :   nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT |
    4725                 :   nsISelectionController::SELECTION_IME_CONVERTEDTEXT |
    4726                 :   nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT;
    4727                 : 
    4728                 : static gfxFloat
    4729               0 : ComputeSelectionUnderlineHeight(nsPresContext* aPresContext,
    4730                 :                                 const gfxFont::Metrics& aFontMetrics,
    4731                 :                                 SelectionType aSelectionType)
    4732                 : {
    4733               0 :   switch (aSelectionType) {
    4734                 :     case nsISelectionController::SELECTION_IME_RAWINPUT:
    4735                 :     case nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT:
    4736                 :     case nsISelectionController::SELECTION_IME_CONVERTEDTEXT:
    4737                 :     case nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT:
    4738               0 :       return aFontMetrics.underlineSize;
    4739                 :     case nsISelectionController::SELECTION_SPELLCHECK: {
    4740                 :       // The thickness of the spellchecker underline shouldn't honor the font
    4741                 :       // metrics.  It should be constant pixels value which is decided from the
    4742                 :       // default font size.  Note that if the actual font size is smaller than
    4743                 :       // the default font size, we should use the actual font size because the
    4744                 :       // computed value from the default font size can be too thick for the
    4745                 :       // current font size.
    4746                 :       PRInt32 defaultFontSize =
    4747               0 :         aPresContext->AppUnitsToDevPixels(nsStyleFont(aPresContext).mFont.size);
    4748                 :       gfxFloat fontSize = NS_MIN(gfxFloat(defaultFontSize),
    4749               0 :                                  aFontMetrics.emHeight);
    4750               0 :       fontSize = NS_MAX(fontSize, 1.0);
    4751               0 :       return ceil(fontSize / 20);
    4752                 :     }
    4753                 :     default:
    4754               0 :       NS_WARNING("Requested underline style is not valid");
    4755               0 :       return aFontMetrics.underlineSize;
    4756                 :   }
    4757                 : }
    4758                 : 
    4759                 : /**
    4760                 :  * This, plus SelectionTypesWithDecorations, encapsulates all knowledge about
    4761                 :  * drawing text decoration for selections.
    4762                 :  */
    4763               0 : static void DrawSelectionDecorations(gfxContext* aContext,
    4764                 :     const gfxRect& aDirtyRect,
    4765                 :     SelectionType aType,
    4766                 :     nsTextFrame* aFrame,
    4767                 :     nsTextPaintStyle& aTextPaintStyle,
    4768                 :     const nsTextRangeStyle &aRangeStyle,
    4769                 :     const gfxPoint& aPt, gfxFloat aWidth,
    4770                 :     gfxFloat aAscent, const gfxFont::Metrics& aFontMetrics)
    4771                 : {
    4772               0 :   gfxPoint pt(aPt);
    4773                 :   gfxSize size(aWidth,
    4774                 :                ComputeSelectionUnderlineHeight(aTextPaintStyle.PresContext(),
    4775               0 :                                                aFontMetrics, aType));
    4776                 :   gfxFloat descentLimit =
    4777                 :     ComputeDescentLimitForSelectionUnderline(aTextPaintStyle.PresContext(),
    4778               0 :                                              aFrame, aFontMetrics);
    4779                 : 
    4780                 :   float relativeSize;
    4781                 :   PRUint8 style;
    4782                 :   nscolor color;
    4783                 :   PRInt32 index =
    4784               0 :     nsTextPaintStyle::GetUnderlineStyleIndexForSelectionType(aType);
    4785                 :   bool weDefineSelectionUnderline =
    4786                 :     aTextPaintStyle.GetSelectionUnderlineForPaint(index, &color,
    4787               0 :                                                   &relativeSize, &style);
    4788                 : 
    4789               0 :   switch (aType) {
    4790                 :     case nsISelectionController::SELECTION_IME_RAWINPUT:
    4791                 :     case nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT:
    4792                 :     case nsISelectionController::SELECTION_IME_CONVERTEDTEXT:
    4793                 :     case nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT: {
    4794                 :       // IME decoration lines should not be drawn on the both ends, i.e., we
    4795                 :       // need to cut both edges of the decoration lines.  Because same style
    4796                 :       // IME selections can adjoin, but the users need to be able to know
    4797                 :       // where are the boundaries of the selections.
    4798                 :       //
    4799                 :       //  X: underline
    4800                 :       //
    4801                 :       //     IME selection #1        IME selection #2      IME selection #3
    4802                 :       //  |                     |                      |                    
    4803                 :       //  | XXXXXXXXXXXXXXXXXXX | XXXXXXXXXXXXXXXXXXXX | XXXXXXXXXXXXXXXXXXX
    4804                 :       //  +---------------------+----------------------+--------------------
    4805                 :       //   ^                   ^ ^                    ^ ^
    4806                 :       //  gap                  gap                    gap
    4807               0 :       pt.x += 1.0;
    4808               0 :       size.width -= 2.0;
    4809               0 :       if (aRangeStyle.IsDefined()) {
    4810                 :         // If IME defines the style, that should override our definition.
    4811               0 :         if (aRangeStyle.IsLineStyleDefined()) {
    4812               0 :           if (aRangeStyle.mLineStyle == nsTextRangeStyle::LINESTYLE_NONE) {
    4813               0 :             return;
    4814                 :           }
    4815               0 :           style = aRangeStyle.mLineStyle;
    4816               0 :           relativeSize = aRangeStyle.mIsBoldLine ? 2.0f : 1.0f;
    4817               0 :         } else if (!weDefineSelectionUnderline) {
    4818                 :           // There is no underline style definition.
    4819               0 :           return;
    4820                 :         }
    4821               0 :         if (aRangeStyle.IsUnderlineColorDefined()) {
    4822               0 :           color = aRangeStyle.mUnderlineColor;
    4823               0 :         } else if (aRangeStyle.IsForegroundColorDefined()) {
    4824               0 :           color = aRangeStyle.mForegroundColor;
    4825                 :         } else {
    4826               0 :           NS_ASSERTION(!aRangeStyle.IsBackgroundColorDefined(),
    4827                 :                        "Only the background color is defined");
    4828               0 :           color = aTextPaintStyle.GetTextColor();
    4829                 :         }
    4830               0 :       } else if (!weDefineSelectionUnderline) {
    4831                 :         // IME doesn't specify the selection style and we don't define selection
    4832                 :         // underline.
    4833               0 :         return;
    4834                 :       }
    4835               0 :       break;
    4836                 :     }
    4837                 :     case nsISelectionController::SELECTION_SPELLCHECK:
    4838               0 :       if (!weDefineSelectionUnderline)
    4839               0 :         return;
    4840               0 :       break;
    4841                 :     default:
    4842               0 :       NS_WARNING("Requested selection decorations when there aren't any");
    4843               0 :       return;
    4844                 :   }
    4845               0 :   size.height *= relativeSize;
    4846                 :   nsCSSRendering::PaintDecorationLine(
    4847                 :     aContext, aDirtyRect, color, pt, size, aAscent, aFontMetrics.underlineOffset,
    4848               0 :     NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE, style, descentLimit);
    4849                 : }
    4850                 : 
    4851                 : /**
    4852                 :  * This function encapsulates all knowledge of how selections affect foreground
    4853                 :  * and background colors.
    4854                 :  * @return true if the selection affects colors, false otherwise
    4855                 :  * @param aForeground the foreground color to use
    4856                 :  * @param aBackground the background color to use, or RGBA(0,0,0,0) if no
    4857                 :  * background should be painted
    4858                 :  */
    4859               0 : static bool GetSelectionTextColors(SelectionType aType,
    4860                 :                                      nsTextPaintStyle& aTextPaintStyle,
    4861                 :                                      const nsTextRangeStyle &aRangeStyle,
    4862                 :                                      nscolor* aForeground, nscolor* aBackground)
    4863                 : {
    4864               0 :   switch (aType) {
    4865                 :     case nsISelectionController::SELECTION_NORMAL:
    4866               0 :       return aTextPaintStyle.GetSelectionColors(aForeground, aBackground);
    4867                 :     case nsISelectionController::SELECTION_FIND:
    4868               0 :       aTextPaintStyle.GetHighlightColors(aForeground, aBackground);
    4869               0 :       return true;
    4870                 :     case nsISelectionController::SELECTION_URLSECONDARY:
    4871               0 :       aTextPaintStyle.GetURLSecondaryColor(aForeground);
    4872               0 :       *aBackground = NS_RGBA(0,0,0,0);
    4873               0 :       return true;
    4874                 :     case nsISelectionController::SELECTION_IME_RAWINPUT:
    4875                 :     case nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT:
    4876                 :     case nsISelectionController::SELECTION_IME_CONVERTEDTEXT:
    4877                 :     case nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT:
    4878               0 :       if (aRangeStyle.IsDefined()) {
    4879               0 :         *aForeground = aTextPaintStyle.GetTextColor();
    4880               0 :         *aBackground = NS_RGBA(0,0,0,0);
    4881               0 :         if (!aRangeStyle.IsForegroundColorDefined() &&
    4882               0 :             !aRangeStyle.IsBackgroundColorDefined()) {
    4883               0 :           return false;
    4884                 :         }
    4885               0 :         if (aRangeStyle.IsForegroundColorDefined()) {
    4886               0 :           *aForeground = aRangeStyle.mForegroundColor;
    4887                 :         }
    4888               0 :         if (aRangeStyle.IsBackgroundColorDefined()) {
    4889               0 :           *aBackground = aRangeStyle.mBackgroundColor;
    4890                 :         }
    4891               0 :         return true;
    4892                 :       }
    4893                 :       aTextPaintStyle.GetIMESelectionColors(
    4894                 :         nsTextPaintStyle::GetUnderlineStyleIndexForSelectionType(aType),
    4895               0 :         aForeground, aBackground);
    4896               0 :       return true;
    4897                 :     default:
    4898               0 :       *aForeground = aTextPaintStyle.GetTextColor();
    4899               0 :       *aBackground = NS_RGBA(0,0,0,0);
    4900               0 :       return false;
    4901                 :   }
    4902                 : }
    4903                 : 
    4904                 : /**
    4905                 :  * This class lets us iterate over chunks of text in a uniform selection state,
    4906                 :  * observing cluster boundaries, in content order, maintaining the current
    4907                 :  * x-offset as we go, and telling whether the text chunk has a hyphen after
    4908                 :  * it or not. The caller is responsible for actually computing the advance
    4909                 :  * width of each chunk.
    4910                 :  */
    4911                 : class SelectionIterator {
    4912                 : public:
    4913                 :   /**
    4914                 :    * aStart and aLength are in the original string. aSelectionDetails is
    4915                 :    * according to the original string.
    4916                 :    * @param aXOffset the offset from the origin of the frame to the start
    4917                 :    * of the text (the left baseline origin for LTR, the right baseline origin
    4918                 :    * for RTL)
    4919                 :    */
    4920                 :   SelectionIterator(SelectionDetails** aSelectionDetails,
    4921                 :                     PRInt32 aStart, PRInt32 aLength,
    4922                 :                     PropertyProvider& aProvider, gfxTextRun* aTextRun,
    4923                 :                     gfxFloat aXOffset);
    4924                 : 
    4925                 :   /**
    4926                 :    * Returns the next segment of uniformly selected (or not) text.
    4927                 :    * @param aXOffset the offset from the origin of the frame to the start
    4928                 :    * of the text (the left baseline origin for LTR, the right baseline origin
    4929                 :    * for RTL)
    4930                 :    * @param aOffset the transformed string offset of the text for this segment
    4931                 :    * @param aLength the transformed string length of the text for this segment
    4932                 :    * @param aHyphenWidth if a hyphen is to be rendered after the text, the
    4933                 :    * width of the hyphen, otherwise zero
    4934                 :    * @param aType the selection type for this segment
    4935                 :    * @param aStyle the selection style for this segment
    4936                 :    * @return false if there are no more segments
    4937                 :    */
    4938                 :   bool GetNextSegment(gfxFloat* aXOffset, PRUint32* aOffset, PRUint32* aLength,
    4939                 :                         gfxFloat* aHyphenWidth, SelectionType* aType,
    4940                 :                         nsTextRangeStyle* aStyle);
    4941               0 :   void UpdateWithAdvance(gfxFloat aAdvance) {
    4942               0 :     mXOffset += aAdvance*mTextRun->GetDirection();
    4943               0 :   }
    4944                 : 
    4945                 : private:
    4946                 :   SelectionDetails**      mSelectionDetails;
    4947                 :   PropertyProvider&       mProvider;
    4948                 :   gfxTextRun*             mTextRun;
    4949                 :   gfxSkipCharsIterator    mIterator;
    4950                 :   PRInt32                 mOriginalStart;
    4951                 :   PRInt32                 mOriginalEnd;
    4952                 :   gfxFloat                mXOffset;
    4953                 : };
    4954                 : 
    4955               0 : SelectionIterator::SelectionIterator(SelectionDetails** aSelectionDetails,
    4956                 :     PRInt32 aStart, PRInt32 aLength, PropertyProvider& aProvider,
    4957                 :     gfxTextRun* aTextRun, gfxFloat aXOffset)
    4958                 :   : mSelectionDetails(aSelectionDetails), mProvider(aProvider),
    4959               0 :     mTextRun(aTextRun), mIterator(aProvider.GetStart()),
    4960                 :     mOriginalStart(aStart), mOriginalEnd(aStart + aLength),
    4961               0 :     mXOffset(aXOffset)
    4962                 : {
    4963               0 :   mIterator.SetOriginalOffset(aStart);
    4964               0 : }
    4965                 : 
    4966               0 : bool SelectionIterator::GetNextSegment(gfxFloat* aXOffset,
    4967                 :     PRUint32* aOffset, PRUint32* aLength, gfxFloat* aHyphenWidth,
    4968                 :     SelectionType* aType, nsTextRangeStyle* aStyle)
    4969                 : {
    4970               0 :   if (mIterator.GetOriginalOffset() >= mOriginalEnd)
    4971               0 :     return false;
    4972                 :   
    4973                 :   // save offset into transformed string now
    4974               0 :   PRUint32 runOffset = mIterator.GetSkippedOffset();
    4975                 :   
    4976               0 :   PRInt32 index = mIterator.GetOriginalOffset() - mOriginalStart;
    4977               0 :   SelectionDetails* sdptr = mSelectionDetails[index];
    4978                 :   SelectionType type =
    4979               0 :     sdptr ? sdptr->mType : nsISelectionController::SELECTION_NONE;
    4980               0 :   nsTextRangeStyle style;
    4981               0 :   if (sdptr) {
    4982               0 :     style = sdptr->mTextRangeStyle;
    4983                 :   }
    4984               0 :   for (++index; mOriginalStart + index < mOriginalEnd; ++index) {
    4985               0 :     if (sdptr != mSelectionDetails[index])
    4986               0 :       break;
    4987                 :   }
    4988               0 :   mIterator.SetOriginalOffset(index + mOriginalStart);
    4989                 : 
    4990                 :   // Advance to the next cluster boundary
    4991               0 :   while (mIterator.GetOriginalOffset() < mOriginalEnd &&
    4992               0 :          !mIterator.IsOriginalCharSkipped() &&
    4993               0 :          !mTextRun->IsClusterStart(mIterator.GetSkippedOffset())) {
    4994               0 :     mIterator.AdvanceOriginal(1);
    4995                 :   }
    4996                 : 
    4997                 :   bool haveHyphenBreak =
    4998               0 :     (mProvider.GetFrame()->GetStateBits() & TEXT_HYPHEN_BREAK) != 0;
    4999               0 :   *aOffset = runOffset;
    5000               0 :   *aLength = mIterator.GetSkippedOffset() - runOffset;
    5001               0 :   *aXOffset = mXOffset;
    5002               0 :   *aHyphenWidth = 0;
    5003               0 :   if (mIterator.GetOriginalOffset() == mOriginalEnd && haveHyphenBreak) {
    5004               0 :     *aHyphenWidth = mProvider.GetHyphenWidth();
    5005                 :   }
    5006               0 :   *aType = type;
    5007               0 :   *aStyle = style;
    5008               0 :   return true;
    5009                 : }
    5010                 : 
    5011                 : static void
    5012               0 : AddHyphenToMetrics(nsTextFrame* aTextFrame, gfxTextRun* aBaseTextRun,
    5013                 :                    gfxTextRun::Metrics* aMetrics,
    5014                 :                    gfxFont::BoundingBoxType aBoundingBoxType,
    5015                 :                    gfxContext* aContext)
    5016                 : {
    5017                 :   // Fix up metrics to include hyphen
    5018                 :   nsAutoPtr<gfxTextRun> hyphenTextRun(
    5019               0 :     GetHyphenTextRun(aBaseTextRun, aContext, aTextFrame));
    5020               0 :   if (!hyphenTextRun.get())
    5021                 :     return;
    5022                 : 
    5023                 :   gfxTextRun::Metrics hyphenMetrics =
    5024                 :     hyphenTextRun->MeasureText(0, hyphenTextRun->GetLength(),
    5025               0 :                                aBoundingBoxType, aContext, nsnull);
    5026               0 :   aMetrics->CombineWith(hyphenMetrics, aBaseTextRun->IsRightToLeft());
    5027                 : }
    5028                 : 
    5029                 : void
    5030               0 : nsTextFrame::PaintOneShadow(PRUint32 aOffset, PRUint32 aLength,
    5031                 :                             nsCSSShadowItem* aShadowDetails,
    5032                 :                             PropertyProvider* aProvider, const nsRect& aDirtyRect,
    5033                 :                             const gfxPoint& aFramePt, const gfxPoint& aTextBaselinePt,
    5034                 :                             gfxContext* aCtx, const nscolor& aForegroundColor,
    5035                 :                             const nsCharClipDisplayItem::ClipEdges& aClipEdges,
    5036                 :                             nscoord aLeftSideOffset)
    5037                 : {
    5038               0 :   gfxPoint shadowOffset(aShadowDetails->mXOffset, aShadowDetails->mYOffset);
    5039               0 :   nscoord blurRadius = NS_MAX(aShadowDetails->mRadius, 0);
    5040                 : 
    5041                 :   gfxTextRun::Metrics shadowMetrics =
    5042                 :     mTextRun->MeasureText(aOffset, aLength, gfxFont::LOOSE_INK_EXTENTS,
    5043               0 :                           nsnull, aProvider);
    5044               0 :   if (GetStateBits() & TEXT_HYPHEN_BREAK) {
    5045               0 :     AddHyphenToMetrics(this, mTextRun, &shadowMetrics, gfxFont::LOOSE_INK_EXTENTS, aCtx);
    5046                 :   }
    5047                 : 
    5048                 :   // This rect is the box which is equivalent to where the shadow will be painted.
    5049                 :   // The origin of mBoundingBox is the text baseline left, so we must translate it by
    5050                 :   // that much in order to make the origin the top-left corner of the text bounding box.
    5051                 :   gfxRect shadowGfxRect = shadowMetrics.mBoundingBox +
    5052               0 :     gfxPoint(aFramePt.x + aLeftSideOffset, aTextBaselinePt.y) + shadowOffset;
    5053                 :   nsRect shadowRect(NSToCoordRound(shadowGfxRect.X()),
    5054                 :                     NSToCoordRound(shadowGfxRect.Y()),
    5055                 :                     NSToCoordRound(shadowGfxRect.Width()),
    5056               0 :                     NSToCoordRound(shadowGfxRect.Height()));
    5057                 : 
    5058               0 :   nsContextBoxBlur contextBoxBlur;
    5059                 :   gfxContext* shadowContext = contextBoxBlur.Init(shadowRect, 0, blurRadius,
    5060               0 :                                                   PresContext()->AppUnitsPerDevPixel(),
    5061               0 :                                                   aCtx, aDirtyRect, nsnull);
    5062               0 :   if (!shadowContext)
    5063                 :     return;
    5064                 : 
    5065                 :   nscolor shadowColor;
    5066                 :   const nscolor* decorationOverrideColor;
    5067               0 :   if (aShadowDetails->mHasColor) {
    5068               0 :     shadowColor = aShadowDetails->mColor;
    5069               0 :     decorationOverrideColor = &shadowColor;
    5070                 :   } else {
    5071               0 :     shadowColor = aForegroundColor;
    5072               0 :     decorationOverrideColor = nsnull;
    5073                 :   }
    5074                 : 
    5075               0 :   aCtx->Save();
    5076               0 :   aCtx->NewPath();
    5077               0 :   aCtx->SetColor(gfxRGBA(shadowColor));
    5078                 : 
    5079                 :   // Draw the text onto our alpha-only surface to capture the alpha values.
    5080                 :   // Remember that the box blur context has a device offset on it, so we don't need to
    5081                 :   // translate any coordinates to fit on the surface.
    5082                 :   gfxFloat advanceWidth;
    5083                 :   gfxRect dirtyRect(aDirtyRect.x, aDirtyRect.y,
    5084               0 :                     aDirtyRect.width, aDirtyRect.height);
    5085               0 :   DrawText(shadowContext, dirtyRect, aFramePt + shadowOffset,
    5086               0 :            aTextBaselinePt + shadowOffset, aOffset, aLength, *aProvider,
    5087                 :            nsTextPaintStyle(this), aClipEdges, advanceWidth,
    5088               0 :            (GetStateBits() & TEXT_HYPHEN_BREAK) != 0, decorationOverrideColor);
    5089                 : 
    5090               0 :   contextBoxBlur.DoPaint();
    5091               0 :   aCtx->Restore();
    5092                 : }
    5093                 : 
    5094                 : // Paints selection backgrounds and text in the correct colors. Also computes
    5095                 : // aAllTypes, the union of all selection types that are applying to this text.
    5096                 : bool
    5097               0 : nsTextFrame::PaintTextWithSelectionColors(gfxContext* aCtx,
    5098                 :     const gfxPoint& aFramePt, const gfxPoint& aTextBaselinePt,
    5099                 :     const gfxRect& aDirtyRect,
    5100                 :     PropertyProvider& aProvider,
    5101                 :     PRUint32 aContentOffset, PRUint32 aContentLength,
    5102                 :     nsTextPaintStyle& aTextPaintStyle, SelectionDetails* aDetails,
    5103                 :     SelectionType* aAllTypes,
    5104                 :     const nsCharClipDisplayItem::ClipEdges& aClipEdges)
    5105                 : {
    5106                 :   // Figure out which selections control the colors to use for each character.
    5107               0 :   AutoFallibleTArray<SelectionDetails*,BIG_TEXT_NODE_SIZE> prevailingSelectionsBuffer;
    5108                 :   SelectionDetails** prevailingSelections =
    5109               0 :     prevailingSelectionsBuffer.AppendElements(aContentLength);
    5110               0 :   if (!prevailingSelections) {
    5111               0 :     return false;
    5112                 :   }
    5113                 : 
    5114               0 :   SelectionType allTypes = 0;
    5115               0 :   for (PRUint32 i = 0; i < aContentLength; ++i) {
    5116               0 :     prevailingSelections[i] = nsnull;
    5117                 :   }
    5118                 : 
    5119               0 :   SelectionDetails *sdptr = aDetails;
    5120               0 :   bool anyBackgrounds = false;
    5121               0 :   while (sdptr) {
    5122               0 :     PRInt32 start = NS_MAX(0, sdptr->mStart - PRInt32(aContentOffset));
    5123                 :     PRInt32 end = NS_MIN(PRInt32(aContentLength),
    5124               0 :                          sdptr->mEnd - PRInt32(aContentOffset));
    5125               0 :     SelectionType type = sdptr->mType;
    5126               0 :     if (start < end) {
    5127               0 :       allTypes |= type;
    5128                 :       // Ignore selections that don't set colors
    5129                 :       nscolor foreground, background;
    5130               0 :       if (GetSelectionTextColors(type, aTextPaintStyle, sdptr->mTextRangeStyle,
    5131               0 :                                  &foreground, &background)) {
    5132               0 :         if (NS_GET_A(background) > 0) {
    5133               0 :           anyBackgrounds = true;
    5134                 :         }
    5135               0 :         for (PRInt32 i = start; i < end; ++i) {
    5136                 :           // Favour normal selection over IME selections
    5137               0 :           if (!prevailingSelections[i] ||
    5138               0 :               type < prevailingSelections[i]->mType) {
    5139               0 :             prevailingSelections[i] = sdptr;
    5140                 :           }
    5141                 :         }
    5142                 :       }
    5143                 :     }
    5144               0 :     sdptr = sdptr->mNext;
    5145                 :   }
    5146               0 :   *aAllTypes = allTypes;
    5147                 : 
    5148               0 :   if (!allTypes) {
    5149                 :     // Nothing is selected in the given text range. XXX can this still occur?
    5150               0 :     return false;
    5151                 :   }
    5152                 : 
    5153               0 :   const gfxFloat startXOffset = aTextBaselinePt.x - aFramePt.x;
    5154                 :   gfxFloat xOffset, hyphenWidth;
    5155                 :   PRUint32 offset, length; // in transformed string
    5156                 :   SelectionType type;
    5157               0 :   nsTextRangeStyle rangeStyle;
    5158                 :   // Draw background colors
    5159               0 :   if (anyBackgrounds) {
    5160                 :     SelectionIterator iterator(prevailingSelections, aContentOffset, aContentLength,
    5161               0 :                                aProvider, mTextRun, startXOffset);
    5162               0 :     while (iterator.GetNextSegment(&xOffset, &offset, &length, &hyphenWidth,
    5163                 :                                    &type, &rangeStyle)) {
    5164                 :       nscolor foreground, background;
    5165                 :       GetSelectionTextColors(type, aTextPaintStyle, rangeStyle,
    5166               0 :                              &foreground, &background);
    5167                 :       // Draw background color
    5168                 :       gfxFloat advance = hyphenWidth +
    5169               0 :         mTextRun->GetAdvanceWidth(offset, length, &aProvider);
    5170               0 :       if (NS_GET_A(background) > 0) {
    5171               0 :         gfxFloat x = xOffset - (mTextRun->IsRightToLeft() ? advance : 0);
    5172                 :         FillClippedRect(aCtx, aTextPaintStyle.PresContext(),
    5173                 :                         background, aDirtyRect,
    5174               0 :                         gfxRect(aFramePt.x + x, aFramePt.y, advance, GetSize().height));
    5175                 :       }
    5176               0 :       iterator.UpdateWithAdvance(advance);
    5177                 :     }
    5178                 :   }
    5179                 :   
    5180                 :   // Draw text
    5181               0 :   const nsStyleText* textStyle = GetStyleText();
    5182                 :   nsRect dirtyRect(aDirtyRect.x, aDirtyRect.y,
    5183               0 :                    aDirtyRect.width, aDirtyRect.height);
    5184                 :   SelectionIterator iterator(prevailingSelections, aContentOffset, aContentLength,
    5185               0 :                              aProvider, mTextRun, startXOffset);
    5186               0 :   while (iterator.GetNextSegment(&xOffset, &offset, &length, &hyphenWidth,
    5187                 :                                  &type, &rangeStyle)) {
    5188                 :     nscolor foreground, background;
    5189                 :     GetSelectionTextColors(type, aTextPaintStyle, rangeStyle,
    5190               0 :                            &foreground, &background);
    5191               0 :     gfxPoint textBaselinePt(aFramePt.x + xOffset, aTextBaselinePt.y);
    5192                 : 
    5193                 :     // Draw shadows, if any
    5194               0 :     if (textStyle->mTextShadow) {
    5195               0 :       for (PRUint32 i = textStyle->mTextShadow->Length(); i > 0; --i) {
    5196                 :         PaintOneShadow(offset, length,
    5197                 :                        textStyle->mTextShadow->ShadowAt(i - 1), &aProvider,
    5198                 :                        dirtyRect, aFramePt, textBaselinePt, aCtx,
    5199               0 :                        foreground, aClipEdges, xOffset);
    5200                 :       }
    5201                 :     }
    5202                 : 
    5203                 :     // Draw text segment
    5204               0 :     aCtx->SetColor(gfxRGBA(foreground));
    5205                 :     gfxFloat advance;
    5206                 : 
    5207                 :     DrawText(aCtx, aDirtyRect, aFramePt, textBaselinePt,
    5208                 :              offset, length, aProvider, aTextPaintStyle, aClipEdges, advance,
    5209               0 :              hyphenWidth > 0);
    5210               0 :     if (hyphenWidth) {
    5211               0 :       advance += hyphenWidth;
    5212                 :     }
    5213               0 :     iterator.UpdateWithAdvance(advance);
    5214                 :   }
    5215               0 :   return true;
    5216                 : }
    5217                 : 
    5218                 : void
    5219               0 : nsTextFrame::PaintTextSelectionDecorations(gfxContext* aCtx,
    5220                 :     const gfxPoint& aFramePt,
    5221                 :     const gfxPoint& aTextBaselinePt, const gfxRect& aDirtyRect,
    5222                 :     PropertyProvider& aProvider,
    5223                 :     PRUint32 aContentOffset, PRUint32 aContentLength,
    5224                 :     nsTextPaintStyle& aTextPaintStyle, SelectionDetails* aDetails,
    5225                 :     SelectionType aSelectionType)
    5226                 : {
    5227                 :   // Hide text decorations if we're currently hiding @font-face fallback text
    5228               0 :   if (aProvider.GetFontGroup()->ShouldSkipDrawing())
    5229               0 :     return;
    5230                 : 
    5231                 :   // Figure out which characters will be decorated for this selection.
    5232               0 :   AutoFallibleTArray<SelectionDetails*, BIG_TEXT_NODE_SIZE> selectedCharsBuffer;
    5233                 :   SelectionDetails** selectedChars =
    5234               0 :     selectedCharsBuffer.AppendElements(aContentLength);
    5235               0 :   if (!selectedChars) {
    5236                 :     return;
    5237                 :   }
    5238               0 :   for (PRUint32 i = 0; i < aContentLength; ++i) {
    5239               0 :     selectedChars[i] = nsnull;
    5240                 :   }
    5241                 : 
    5242               0 :   SelectionDetails *sdptr = aDetails;
    5243               0 :   while (sdptr) {
    5244               0 :     if (sdptr->mType == aSelectionType) {
    5245               0 :       PRInt32 start = NS_MAX(0, sdptr->mStart - PRInt32(aContentOffset));
    5246                 :       PRInt32 end = NS_MIN(PRInt32(aContentLength),
    5247               0 :                            sdptr->mEnd - PRInt32(aContentOffset));
    5248               0 :       for (PRInt32 i = start; i < end; ++i) {
    5249               0 :         selectedChars[i] = sdptr;
    5250                 :       }
    5251                 :     }
    5252               0 :     sdptr = sdptr->mNext;
    5253                 :   }
    5254                 : 
    5255               0 :   gfxFont* firstFont = aProvider.GetFontGroup()->GetFontAt(0);
    5256               0 :   if (!firstFont)
    5257                 :     return; // OOM
    5258               0 :   gfxFont::Metrics decorationMetrics(firstFont->GetMetrics());
    5259                 :   decorationMetrics.underlineOffset =
    5260               0 :     aProvider.GetFontGroup()->GetUnderlineOffset();
    5261                 : 
    5262               0 :   gfxFloat startXOffset = aTextBaselinePt.x - aFramePt.x;
    5263                 :   SelectionIterator iterator(selectedChars, aContentOffset, aContentLength,
    5264               0 :                              aProvider, mTextRun, startXOffset);
    5265                 :   gfxFloat xOffset, hyphenWidth;
    5266                 :   PRUint32 offset, length;
    5267               0 :   PRInt32 app = aTextPaintStyle.PresContext()->AppUnitsPerDevPixel();
    5268                 :   // XXX aTextBaselinePt is in AppUnits, shouldn't it be nsFloatPoint?
    5269               0 :   gfxPoint pt(0.0, (aTextBaselinePt.y - mAscent) / app);
    5270                 :   gfxRect dirtyRect(aDirtyRect.x / app, aDirtyRect.y / app,
    5271               0 :                     aDirtyRect.width / app, aDirtyRect.height / app);
    5272                 :   SelectionType type;
    5273               0 :   nsTextRangeStyle selectedStyle;
    5274               0 :   while (iterator.GetNextSegment(&xOffset, &offset, &length, &hyphenWidth,
    5275                 :                                  &type, &selectedStyle)) {
    5276                 :     gfxFloat advance = hyphenWidth +
    5277               0 :       mTextRun->GetAdvanceWidth(offset, length, &aProvider);
    5278               0 :     if (type == aSelectionType) {
    5279                 :       pt.x = (aFramePt.x + xOffset -
    5280               0 :              (mTextRun->IsRightToLeft() ? advance : 0)) / app;
    5281               0 :       gfxFloat width = NS_ABS(advance) / app;
    5282                 :       DrawSelectionDecorations(aCtx, dirtyRect, aSelectionType, this, aTextPaintStyle,
    5283                 :                                selectedStyle,
    5284               0 :                                pt, width, mAscent / app, decorationMetrics);
    5285                 :     }
    5286               0 :     iterator.UpdateWithAdvance(advance);
    5287                 :   }
    5288                 : }
    5289                 : 
    5290                 : bool
    5291               0 : nsTextFrame::PaintTextWithSelection(gfxContext* aCtx,
    5292                 :     const gfxPoint& aFramePt,
    5293                 :     const gfxPoint& aTextBaselinePt, const gfxRect& aDirtyRect,
    5294                 :     PropertyProvider& aProvider,
    5295                 :     PRUint32 aContentOffset, PRUint32 aContentLength,
    5296                 :     nsTextPaintStyle& aTextPaintStyle,
    5297                 :     const nsCharClipDisplayItem::ClipEdges& aClipEdges)
    5298                 : {
    5299               0 :   NS_ASSERTION(GetContent()->IsSelectionDescendant(), "wrong paint path");
    5300                 : 
    5301               0 :   SelectionDetails* details = GetSelectionDetails();
    5302               0 :   if (!details) {
    5303               0 :     return false;
    5304                 :   }
    5305                 : 
    5306                 :   SelectionType allTypes;
    5307               0 :   if (!PaintTextWithSelectionColors(aCtx, aFramePt, aTextBaselinePt, aDirtyRect,
    5308                 :                                     aProvider, aContentOffset, aContentLength,
    5309                 :                                     aTextPaintStyle, details, &allTypes,
    5310               0 :                                     aClipEdges)) {
    5311               0 :     DestroySelectionDetails(details);
    5312               0 :     return false;
    5313                 :   }
    5314                 :   PRInt32 i;
    5315                 :   // Iterate through just the selection types that paint decorations and
    5316                 :   // paint decorations for any that actually occur in this frame. Paint
    5317                 :   // higher-numbered selection types below lower-numered ones on the
    5318                 :   // general principal that lower-numbered selections are higher priority.
    5319               0 :   allTypes &= SelectionTypesWithDecorations;
    5320               0 :   for (i = nsISelectionController::NUM_SELECTIONTYPES - 1; i >= 1; --i) {
    5321               0 :     SelectionType type = 1 << (i - 1);
    5322               0 :     if (allTypes & type) {
    5323                 :       // There is some selection of this type. Try to paint its decorations
    5324                 :       // (there might not be any for this type but that's OK,
    5325                 :       // PaintTextSelectionDecorations will exit early).
    5326                 :       PaintTextSelectionDecorations(aCtx, aFramePt, aTextBaselinePt, aDirtyRect,
    5327                 :                                     aProvider, aContentOffset, aContentLength,
    5328               0 :                                     aTextPaintStyle, details, type);
    5329                 :     }
    5330                 :   }
    5331                 : 
    5332               0 :   DestroySelectionDetails(details);
    5333               0 :   return true;
    5334                 : }
    5335                 : 
    5336                 : nscolor
    5337               0 : nsTextFrame::GetCaretColorAt(PRInt32 aOffset)
    5338                 : {
    5339               0 :   NS_PRECONDITION(aOffset >= 0, "aOffset must be positive");
    5340                 : 
    5341               0 :   gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
    5342               0 :   PropertyProvider provider(this, iter, nsTextFrame::eInflated);
    5343               0 :   PRInt32 contentOffset = provider.GetStart().GetOriginalOffset();
    5344               0 :   PRInt32 contentLength = provider.GetOriginalLength();
    5345               0 :   NS_PRECONDITION(aOffset >= contentOffset &&
    5346                 :                   aOffset <= contentOffset + contentLength,
    5347                 :                   "aOffset must be in the frame's range");
    5348               0 :   PRInt32 offsetInFrame = aOffset - contentOffset;
    5349               0 :   if (offsetInFrame < 0 || offsetInFrame >= contentLength) {
    5350               0 :     return nsFrame::GetCaretColorAt(aOffset);
    5351                 :   }
    5352                 : 
    5353               0 :   nsTextPaintStyle textPaintStyle(this);
    5354               0 :   SelectionDetails* details = GetSelectionDetails();
    5355               0 :   SelectionDetails* sdptr = details;
    5356               0 :   nscolor result = nsFrame::GetCaretColorAt(aOffset);
    5357               0 :   SelectionType type = 0;
    5358               0 :   while (sdptr) {
    5359               0 :     PRInt32 start = NS_MAX(0, sdptr->mStart - contentOffset);
    5360               0 :     PRInt32 end = NS_MIN(contentLength, sdptr->mEnd - contentOffset);
    5361               0 :     if (start <= offsetInFrame && offsetInFrame < end &&
    5362                 :         (type == 0 || sdptr->mType < type)) {
    5363                 :       nscolor foreground, background;
    5364               0 :       if (GetSelectionTextColors(sdptr->mType, textPaintStyle,
    5365                 :                                  sdptr->mTextRangeStyle,
    5366               0 :                                  &foreground, &background)) {
    5367               0 :         result = foreground;
    5368               0 :         type = sdptr->mType;
    5369                 :       }
    5370                 :     }
    5371               0 :     sdptr = sdptr->mNext;
    5372                 :   }
    5373                 : 
    5374               0 :   DestroySelectionDetails(details);
    5375               0 :   return result;
    5376                 : }
    5377                 : 
    5378                 : static PRUint32
    5379               0 : ComputeTransformedLength(PropertyProvider& aProvider)
    5380                 : {
    5381               0 :   gfxSkipCharsIterator iter(aProvider.GetStart());
    5382               0 :   PRUint32 start = iter.GetSkippedOffset();
    5383               0 :   iter.AdvanceOriginal(aProvider.GetOriginalLength());
    5384               0 :   return iter.GetSkippedOffset() - start;
    5385                 : }
    5386                 : 
    5387                 : bool
    5388               0 : nsTextFrame::MeasureCharClippedText(nscoord aLeftEdge, nscoord aRightEdge,
    5389                 :                                     nscoord* aSnappedLeftEdge,
    5390                 :                                     nscoord* aSnappedRightEdge)
    5391                 : {
    5392                 :   // We need a *reference* rendering context (not one that might have a
    5393                 :   // transform), so we don't have a rendering context argument.
    5394                 :   // XXX get the block and line passed to us somehow! This is slow!
    5395               0 :   gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
    5396               0 :   if (!mTextRun)
    5397               0 :     return false;
    5398                 : 
    5399               0 :   PropertyProvider provider(this, iter, nsTextFrame::eInflated);
    5400                 :   // Trim trailing whitespace
    5401               0 :   provider.InitializeForDisplay(true);
    5402                 : 
    5403               0 :   PRUint32 startOffset = provider.GetStart().GetSkippedOffset();
    5404               0 :   PRUint32 maxLength = ComputeTransformedLength(provider);
    5405                 :   return MeasureCharClippedText(provider, aLeftEdge, aRightEdge,
    5406                 :                                 &startOffset, &maxLength,
    5407               0 :                                 aSnappedLeftEdge, aSnappedRightEdge);
    5408                 : }
    5409                 : 
    5410               0 : static PRUint32 GetClusterLength(gfxTextRun* aTextRun,
    5411                 :                                  PRUint32    aStartOffset,
    5412                 :                                  PRUint32    aMaxLength,
    5413                 :                                  bool        aIsRTL)
    5414                 : {
    5415               0 :   PRUint32 clusterLength = aIsRTL ? 0 : 1;
    5416               0 :   while (clusterLength < aMaxLength) {
    5417               0 :     if (aTextRun->IsClusterStart(aStartOffset + clusterLength)) {
    5418               0 :       if (aIsRTL) {
    5419               0 :         ++clusterLength;
    5420                 :       }
    5421               0 :       break;
    5422                 :     }
    5423               0 :     ++clusterLength;
    5424                 :   }
    5425               0 :   return clusterLength;
    5426                 : }
    5427                 : 
    5428                 : bool
    5429               0 : nsTextFrame::MeasureCharClippedText(PropertyProvider& aProvider,
    5430                 :                                     nscoord aLeftEdge, nscoord aRightEdge,
    5431                 :                                     PRUint32* aStartOffset,
    5432                 :                                     PRUint32* aMaxLength,
    5433                 :                                     nscoord*  aSnappedLeftEdge,
    5434                 :                                     nscoord*  aSnappedRightEdge)
    5435                 : {
    5436               0 :   *aSnappedLeftEdge = 0;
    5437               0 :   *aSnappedRightEdge = 0;
    5438               0 :   if (aLeftEdge <= 0 && aRightEdge <= 0) {
    5439               0 :     return true;
    5440                 :   }
    5441                 : 
    5442               0 :   PRUint32 offset = *aStartOffset;
    5443               0 :   PRUint32 maxLength = *aMaxLength;
    5444               0 :   const nscoord frameWidth = GetSize().width;
    5445               0 :   const bool rtl = mTextRun->IsRightToLeft();
    5446               0 :   gfxFloat advanceWidth = 0;
    5447               0 :   const nscoord startEdge = rtl ? aRightEdge : aLeftEdge;
    5448               0 :   if (startEdge > 0) {
    5449               0 :     const gfxFloat maxAdvance = gfxFloat(startEdge);
    5450               0 :     while (maxLength > 0) {
    5451                 :       PRUint32 clusterLength =
    5452               0 :         GetClusterLength(mTextRun, offset, maxLength, rtl);
    5453                 :       advanceWidth +=
    5454               0 :         mTextRun->GetAdvanceWidth(offset, clusterLength, &aProvider);
    5455               0 :       maxLength -= clusterLength;
    5456               0 :       offset += clusterLength;
    5457               0 :       if (advanceWidth >= maxAdvance) {
    5458               0 :         break;
    5459                 :       }
    5460                 :     }
    5461               0 :     nscoord* snappedStartEdge = rtl ? aSnappedRightEdge : aSnappedLeftEdge;
    5462               0 :     *snappedStartEdge = NSToCoordFloor(advanceWidth);
    5463               0 :     *aStartOffset = offset;
    5464                 :   }
    5465                 : 
    5466               0 :   const nscoord endEdge = rtl ? aLeftEdge : aRightEdge;
    5467               0 :   if (endEdge > 0) {
    5468               0 :     const gfxFloat maxAdvance = gfxFloat(frameWidth - endEdge);
    5469               0 :     while (maxLength > 0) {
    5470                 :       PRUint32 clusterLength =
    5471               0 :         GetClusterLength(mTextRun, offset, maxLength, rtl);
    5472                 :       gfxFloat nextAdvance = advanceWidth +
    5473               0 :         mTextRun->GetAdvanceWidth(offset, clusterLength, &aProvider);
    5474               0 :       if (nextAdvance > maxAdvance) {
    5475               0 :         break;
    5476                 :       }
    5477                 :       // This cluster fits, include it.
    5478               0 :       advanceWidth = nextAdvance;
    5479               0 :       maxLength -= clusterLength;
    5480               0 :       offset += clusterLength;
    5481                 :     }
    5482               0 :     maxLength = offset - *aStartOffset;
    5483               0 :     nscoord* snappedEndEdge = rtl ? aSnappedLeftEdge : aSnappedRightEdge;
    5484               0 :     *snappedEndEdge = NSToCoordFloor(gfxFloat(frameWidth) - advanceWidth);
    5485                 :   }
    5486               0 :   *aMaxLength = maxLength;
    5487               0 :   return maxLength != 0;
    5488                 : }
    5489                 : 
    5490                 : void
    5491               0 : nsTextFrame::PaintText(nsRenderingContext* aRenderingContext, nsPoint aPt,
    5492                 :                        const nsRect& aDirtyRect,
    5493                 :                        const nsCharClipDisplayItem& aItem)
    5494                 : {
    5495                 :   // Don't pass in aRenderingContext here, because we need a *reference*
    5496                 :   // context and aRenderingContext might have some transform in it
    5497                 :   // XXX get the block and line passed to us somehow! This is slow!
    5498               0 :   gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
    5499               0 :   if (!mTextRun)
    5500               0 :     return;
    5501                 : 
    5502               0 :   PropertyProvider provider(this, iter, nsTextFrame::eInflated);
    5503                 :   // Trim trailing whitespace
    5504               0 :   provider.InitializeForDisplay(true);
    5505                 : 
    5506               0 :   gfxContext* ctx = aRenderingContext->ThebesContext();
    5507               0 :   const bool rtl = mTextRun->IsRightToLeft();
    5508               0 :   const nscoord frameWidth = GetSize().width;
    5509               0 :   gfxPoint framePt(aPt.x, aPt.y);
    5510                 :   gfxPoint textBaselinePt(rtl ? gfxFloat(aPt.x + frameWidth) : framePt.x,
    5511               0 :              nsLayoutUtils::GetSnappedBaselineY(this, ctx, aPt.y, mAscent));
    5512               0 :   PRUint32 startOffset = provider.GetStart().GetSkippedOffset();
    5513               0 :   PRUint32 maxLength = ComputeTransformedLength(provider);
    5514                 :   nscoord snappedLeftEdge, snappedRightEdge;
    5515               0 :   if (!MeasureCharClippedText(provider, aItem.mLeftEdge, aItem.mRightEdge,
    5516               0 :          &startOffset, &maxLength, &snappedLeftEdge, &snappedRightEdge)) {
    5517                 :     return;
    5518                 :   }
    5519               0 :   textBaselinePt.x += rtl ? -snappedRightEdge : snappedLeftEdge;
    5520                 :   nsCharClipDisplayItem::ClipEdges clipEdges(aItem, snappedLeftEdge,
    5521               0 :                                              snappedRightEdge);
    5522               0 :   nsTextPaintStyle textPaintStyle(this);
    5523                 : 
    5524                 :   gfxRect dirtyRect(aDirtyRect.x, aDirtyRect.y,
    5525               0 :                     aDirtyRect.width, aDirtyRect.height);
    5526                 :   // Fork off to the (slower) paint-with-selection path if necessary.
    5527               0 :   if (IsSelected()) {
    5528               0 :     gfxSkipCharsIterator tmp(provider.GetStart());
    5529               0 :     PRInt32 contentOffset = tmp.ConvertSkippedToOriginal(startOffset);
    5530                 :     PRInt32 contentLength =
    5531               0 :       tmp.ConvertSkippedToOriginal(startOffset + maxLength) - contentOffset;
    5532               0 :     if (PaintTextWithSelection(ctx, framePt, textBaselinePt, dirtyRect,
    5533                 :                                provider, contentOffset, contentLength,
    5534               0 :                                textPaintStyle, clipEdges)) {
    5535                 :       return;
    5536                 :     }
    5537                 :   }
    5538                 : 
    5539               0 :   nscolor foregroundColor = textPaintStyle.GetTextColor();
    5540               0 :   const nsStyleText* textStyle = GetStyleText();
    5541               0 :   if (textStyle->mTextShadow) {
    5542                 :     // Text shadow happens with the last value being painted at the back,
    5543                 :     // ie. it is painted first.
    5544               0 :     for (PRUint32 i = textStyle->mTextShadow->Length(); i > 0; --i) {
    5545                 :       PaintOneShadow(startOffset, maxLength,
    5546                 :                      textStyle->mTextShadow->ShadowAt(i - 1), &provider,
    5547                 :                      aDirtyRect, framePt, textBaselinePt, ctx,
    5548               0 :                      foregroundColor, clipEdges, snappedLeftEdge);
    5549                 :     }
    5550                 :   }
    5551                 : 
    5552               0 :   ctx->SetColor(gfxRGBA(foregroundColor));
    5553                 : 
    5554                 :   gfxFloat advanceWidth;
    5555                 :   DrawText(ctx, dirtyRect, framePt, textBaselinePt, startOffset, maxLength, provider,
    5556                 :            textPaintStyle, clipEdges, advanceWidth,
    5557               0 :            (GetStateBits() & TEXT_HYPHEN_BREAK) != 0);
    5558                 : }
    5559                 : 
    5560                 : void
    5561               0 : nsTextFrame::DrawTextRun(gfxContext* const aCtx,
    5562                 :                          const gfxPoint& aTextBaselinePt,
    5563                 :                          PRUint32 aOffset, PRUint32 aLength,
    5564                 :                          PropertyProvider& aProvider,
    5565                 :                          gfxFloat& aAdvanceWidth,
    5566                 :                          bool aDrawSoftHyphen)
    5567                 : {
    5568                 :   mTextRun->Draw(aCtx, aTextBaselinePt, gfxFont::GLYPH_FILL, aOffset, aLength,
    5569               0 :                  &aProvider, &aAdvanceWidth, nsnull);
    5570                 : 
    5571               0 :   if (aDrawSoftHyphen) {
    5572                 :     // Don't use ctx as the context, because we need a reference context here,
    5573                 :     // ctx may be transformed.
    5574               0 :     nsAutoPtr<gfxTextRun> hyphenTextRun(GetHyphenTextRun(mTextRun, nsnull, this));
    5575               0 :     if (hyphenTextRun.get()) {
    5576                 :       // For right-to-left text runs, the soft-hyphen is positioned at the left
    5577                 :       // of the text, minus its own width
    5578               0 :       gfxFloat hyphenBaselineX = aTextBaselinePt.x + mTextRun->GetDirection() * aAdvanceWidth -
    5579               0 :         (mTextRun->IsRightToLeft() ? hyphenTextRun->GetAdvanceWidth(0, hyphenTextRun->GetLength(), nsnull) : 0);
    5580                 :       hyphenTextRun->Draw(aCtx, gfxPoint(hyphenBaselineX, aTextBaselinePt.y),
    5581                 :                           gfxFont::GLYPH_FILL, 0, hyphenTextRun->GetLength(),
    5582               0 :                           nsnull, nsnull, nsnull);
    5583                 :     }
    5584                 :   }
    5585               0 : }
    5586                 : 
    5587                 : void
    5588               0 : nsTextFrame::DrawTextRunAndDecorations(
    5589                 :     gfxContext* const aCtx, const gfxRect& aDirtyRect,
    5590                 :     const gfxPoint& aFramePt, const gfxPoint& aTextBaselinePt,
    5591                 :     PRUint32 aOffset, PRUint32 aLength,
    5592                 :     PropertyProvider& aProvider,
    5593                 :     const nsTextPaintStyle& aTextStyle,
    5594                 :     const nsCharClipDisplayItem::ClipEdges& aClipEdges,
    5595                 :     gfxFloat& aAdvanceWidth,
    5596                 :     bool aDrawSoftHyphen,
    5597                 :     const TextDecorations& aDecorations,
    5598                 :     const nscolor* const aDecorationOverrideColor)
    5599                 : {
    5600               0 :     const gfxFloat app = aTextStyle.PresContext()->AppUnitsPerDevPixel();
    5601                 : 
    5602                 :     // XXX aFramePt is in AppUnits, shouldn't it be nsFloatPoint?
    5603               0 :     nscoord x = NSToCoordRound(aFramePt.x);
    5604               0 :     nscoord width = GetRect().width;
    5605               0 :     aClipEdges.Intersect(&x, &width);
    5606                 : 
    5607               0 :     gfxPoint decPt(x / app, 0);
    5608               0 :     gfxSize decSize(width / app, 0);
    5609               0 :     const gfxFloat ascent = gfxFloat(mAscent) / app;
    5610               0 :     const gfxFloat frameTop = aFramePt.y;
    5611                 : 
    5612                 :     gfxRect dirtyRect(aDirtyRect.x / app, aDirtyRect.y / app,
    5613               0 :                       aDirtyRect.Width() / app, aDirtyRect.Height() / app);
    5614                 : 
    5615                 :     nscoord inflationMinFontSize =
    5616               0 :       nsLayoutUtils::InflationMinFontSizeFor(this, nsLayoutUtils::eNotInReflow);
    5617                 : 
    5618                 :     // Underlines
    5619               0 :     for (PRUint32 i = aDecorations.mUnderlines.Length(); i-- > 0; ) {
    5620               0 :       const LineDecoration& dec = aDecorations.mUnderlines[i];
    5621                 : 
    5622                 :       float inflation = nsLayoutUtils::FontSizeInflationInner(dec.mFrame,
    5623               0 :                           inflationMinFontSize);
    5624                 :       const gfxFont::Metrics metrics =
    5625               0 :         GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation));
    5626                 : 
    5627               0 :       decSize.height = metrics.underlineSize;
    5628               0 :       decPt.y = (frameTop - dec.mBaselineOffset) / app;
    5629                 : 
    5630               0 :       const nscolor lineColor = aDecorationOverrideColor ? *aDecorationOverrideColor : dec.mColor;
    5631                 :       nsCSSRendering::PaintDecorationLine(aCtx, dirtyRect, lineColor, decPt, decSize, ascent,
    5632                 :         metrics.underlineOffset, NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE,
    5633               0 :         dec.mStyle);
    5634                 :     }
    5635                 :     // Overlines
    5636               0 :     for (PRUint32 i = aDecorations.mOverlines.Length(); i-- > 0; ) {
    5637               0 :       const LineDecoration& dec = aDecorations.mOverlines[i];
    5638                 : 
    5639                 :       float inflation = nsLayoutUtils::FontSizeInflationInner(dec.mFrame,
    5640               0 :                           inflationMinFontSize);
    5641                 :       const gfxFont::Metrics metrics =
    5642               0 :         GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation));
    5643                 : 
    5644               0 :       decSize.height = metrics.underlineSize;
    5645               0 :       decPt.y = (frameTop - dec.mBaselineOffset) / app;
    5646                 : 
    5647               0 :       const nscolor lineColor = aDecorationOverrideColor ? *aDecorationOverrideColor : dec.mColor;
    5648                 :       nsCSSRendering::PaintDecorationLine(aCtx, dirtyRect, lineColor, decPt, decSize, ascent,
    5649               0 :         metrics.maxAscent, NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, dec.mStyle);
    5650                 :     }
    5651                 : 
    5652                 :     // CSS 2.1 mandates that text be painted after over/underlines, and *then*
    5653                 :     // line-throughs
    5654                 :     DrawTextRun(aCtx, aTextBaselinePt, aOffset, aLength, aProvider, aAdvanceWidth,
    5655               0 :                 aDrawSoftHyphen);
    5656                 : 
    5657                 :     // Line-throughs
    5658               0 :     for (PRUint32 i = aDecorations.mStrikes.Length(); i-- > 0; ) {
    5659               0 :       const LineDecoration& dec = aDecorations.mStrikes[i];
    5660                 : 
    5661                 :       float inflation = nsLayoutUtils::FontSizeInflationInner(dec.mFrame,
    5662               0 :                           inflationMinFontSize);
    5663                 :       const gfxFont::Metrics metrics =
    5664               0 :         GetFirstFontMetrics(GetFontGroupForFrame(dec.mFrame, inflation));
    5665                 : 
    5666               0 :       decSize.height = metrics.strikeoutSize;
    5667               0 :       decPt.y = (frameTop - dec.mBaselineOffset) / app;
    5668                 : 
    5669               0 :       const nscolor lineColor = aDecorationOverrideColor ? *aDecorationOverrideColor : dec.mColor;
    5670                 :       nsCSSRendering::PaintDecorationLine(aCtx, dirtyRect, lineColor, decPt, decSize, ascent,
    5671                 :         metrics.strikeoutOffset, NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH,
    5672               0 :         dec.mStyle);
    5673                 :     }
    5674               0 : }
    5675                 : 
    5676                 : void
    5677               0 : nsTextFrame::DrawText(
    5678                 :     gfxContext* const aCtx, const gfxRect& aDirtyRect,
    5679                 :     const gfxPoint& aFramePt, const gfxPoint& aTextBaselinePt,
    5680                 :     PRUint32 aOffset, PRUint32 aLength,
    5681                 :     PropertyProvider& aProvider,
    5682                 :     const nsTextPaintStyle& aTextStyle,
    5683                 :     const nsCharClipDisplayItem::ClipEdges& aClipEdges,
    5684                 :     gfxFloat& aAdvanceWidth,
    5685                 :     bool aDrawSoftHyphen,
    5686                 :     const nscolor* const aDecorationOverrideColor)
    5687                 : {
    5688               0 :   TextDecorations decorations;
    5689               0 :   GetTextDecorations(aTextStyle.PresContext(), decorations);
    5690                 : 
    5691                 :   // Hide text decorations if we're currently hiding @font-face fallback text
    5692               0 :   const bool drawDecorations = !aProvider.GetFontGroup()->ShouldSkipDrawing() &&
    5693               0 :                                decorations.HasDecorationLines();
    5694               0 :   if (drawDecorations) {
    5695                 :     DrawTextRunAndDecorations(aCtx, aDirtyRect, aFramePt, aTextBaselinePt, aOffset, aLength,
    5696                 :                               aProvider, aTextStyle, aClipEdges, aAdvanceWidth,
    5697                 :                               aDrawSoftHyphen, decorations,
    5698               0 :                               aDecorationOverrideColor);
    5699                 :   } else {
    5700                 :     DrawTextRun(aCtx, aTextBaselinePt, aOffset, aLength, aProvider,
    5701               0 :                 aAdvanceWidth, aDrawSoftHyphen);
    5702                 :   }
    5703               0 : }
    5704                 : 
    5705                 : PRInt16
    5706               0 : nsTextFrame::GetSelectionStatus(PRInt16* aSelectionFlags)
    5707                 : {
    5708                 :   // get the selection controller
    5709               0 :   nsCOMPtr<nsISelectionController> selectionController;
    5710                 :   nsresult rv = GetSelectionController(PresContext(),
    5711               0 :                                        getter_AddRefs(selectionController));
    5712               0 :   if (NS_FAILED(rv) || !selectionController)
    5713               0 :     return nsISelectionController::SELECTION_OFF;
    5714                 : 
    5715               0 :   selectionController->GetSelectionFlags(aSelectionFlags);
    5716                 : 
    5717                 :   PRInt16 selectionValue;
    5718               0 :   selectionController->GetDisplaySelection(&selectionValue);
    5719                 : 
    5720               0 :   return selectionValue;
    5721                 : }
    5722                 : 
    5723                 : bool
    5724               0 : nsTextFrame::IsVisibleInSelection(nsISelection* aSelection)
    5725                 : {
    5726                 :   // Check the quick way first
    5727               0 :   if (!GetContent()->IsSelectionDescendant())
    5728               0 :     return false;
    5729                 :     
    5730               0 :   SelectionDetails* details = GetSelectionDetails();
    5731               0 :   bool found = false;
    5732                 :     
    5733                 :   // where are the selection points "really"
    5734               0 :   SelectionDetails *sdptr = details;
    5735               0 :   while (sdptr) {
    5736               0 :     if (sdptr->mEnd > GetContentOffset() &&
    5737               0 :         sdptr->mStart < GetContentEnd() &&
    5738                 :         sdptr->mType == nsISelectionController::SELECTION_NORMAL) {
    5739               0 :       found = true;
    5740               0 :       break;
    5741                 :     }
    5742               0 :     sdptr = sdptr->mNext;
    5743                 :   }
    5744               0 :   DestroySelectionDetails(details);
    5745                 : 
    5746               0 :   return found;
    5747                 : }
    5748                 : 
    5749                 : /**
    5750                 :  * Compute the longest prefix of text whose width is <= aWidth. Return
    5751                 :  * the length of the prefix. Also returns the width of the prefix in aFitWidth.
    5752                 :  */
    5753                 : static PRUint32
    5754               0 : CountCharsFit(gfxTextRun* aTextRun, PRUint32 aStart, PRUint32 aLength,
    5755                 :               gfxFloat aWidth, PropertyProvider* aProvider,
    5756                 :               gfxFloat* aFitWidth)
    5757                 : {
    5758               0 :   PRUint32 last = 0;
    5759               0 :   gfxFloat width = 0;
    5760                 :   PRUint32 i;
    5761               0 :   for (i = 1; i <= aLength; ++i) {
    5762               0 :     if (i == aLength || aTextRun->IsClusterStart(aStart + i)) {
    5763                 :       gfxFloat nextWidth = width +
    5764               0 :           aTextRun->GetAdvanceWidth(aStart + last, i - last, aProvider);
    5765               0 :       if (nextWidth > aWidth)
    5766               0 :         break;
    5767               0 :       last = i;
    5768               0 :       width = nextWidth;
    5769                 :     }
    5770                 :   }
    5771               0 :   *aFitWidth = width;
    5772               0 :   return last;
    5773                 : }
    5774                 : 
    5775                 : nsIFrame::ContentOffsets
    5776               0 : nsTextFrame::CalcContentOffsetsFromFramePoint(nsPoint aPoint)
    5777                 : {
    5778               0 :   return GetCharacterOffsetAtFramePointInternal(aPoint, true);
    5779                 : }
    5780                 : 
    5781                 : nsIFrame::ContentOffsets
    5782               0 : nsTextFrame::GetCharacterOffsetAtFramePoint(const nsPoint &aPoint)
    5783                 : {
    5784               0 :   return GetCharacterOffsetAtFramePointInternal(aPoint, false);
    5785                 : }
    5786                 : 
    5787                 : nsIFrame::ContentOffsets
    5788               0 : nsTextFrame::GetCharacterOffsetAtFramePointInternal(const nsPoint &aPoint,
    5789                 :                                                     bool aForInsertionPoint)
    5790                 : {
    5791               0 :   ContentOffsets offsets;
    5792                 :   
    5793               0 :   gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
    5794               0 :   if (!mTextRun)
    5795               0 :     return offsets;
    5796                 :   
    5797               0 :   PropertyProvider provider(this, iter, nsTextFrame::eInflated);
    5798                 :   // Trim leading but not trailing whitespace if possible
    5799               0 :   provider.InitializeForDisplay(false);
    5800               0 :   gfxFloat width = mTextRun->IsRightToLeft() ? mRect.width - aPoint.x : aPoint.x;
    5801                 :   gfxFloat fitWidth;
    5802               0 :   PRUint32 skippedLength = ComputeTransformedLength(provider);
    5803                 : 
    5804                 :   PRUint32 charsFit = CountCharsFit(mTextRun,
    5805               0 :       provider.GetStart().GetSkippedOffset(), skippedLength, width, &provider, &fitWidth);
    5806                 : 
    5807                 :   PRInt32 selectedOffset;
    5808               0 :   if (charsFit < skippedLength) {
    5809                 :     // charsFit characters fitted, but no more could fit. See if we're
    5810                 :     // more than halfway through the cluster.. If we are, choose the next
    5811                 :     // cluster.
    5812               0 :     gfxSkipCharsIterator extraCluster(provider.GetStart());
    5813               0 :     extraCluster.AdvanceSkipped(charsFit);
    5814               0 :     gfxSkipCharsIterator extraClusterLastChar(extraCluster);
    5815                 :     FindClusterEnd(mTextRun,
    5816               0 :                    provider.GetStart().GetOriginalOffset() + provider.GetOriginalLength(),
    5817               0 :                    &extraClusterLastChar);
    5818                 :     gfxFloat charWidth =
    5819                 :         mTextRun->GetAdvanceWidth(extraCluster.GetSkippedOffset(),
    5820               0 :                                   GetSkippedDistance(extraCluster, extraClusterLastChar) + 1,
    5821               0 :                                   &provider);
    5822               0 :     selectedOffset = !aForInsertionPoint || width <= fitWidth + charWidth/2
    5823                 :         ? extraCluster.GetOriginalOffset()
    5824               0 :         : extraClusterLastChar.GetOriginalOffset() + 1;
    5825                 :   } else {
    5826                 :     // All characters fitted, we're at (or beyond) the end of the text.
    5827                 :     // XXX This could be some pathological situation where negative spacing
    5828                 :     // caused characters to move backwards. We can't really handle that
    5829                 :     // in the current frame system because frames can't have negative
    5830                 :     // intrinsic widths.
    5831                 :     selectedOffset =
    5832               0 :         provider.GetStart().GetOriginalOffset() + provider.GetOriginalLength();
    5833                 :     // If we're at the end of a preformatted line which has a terminating
    5834                 :     // linefeed, we want to reduce the offset by one to make sure that the
    5835                 :     // selection is placed before the linefeed character.
    5836               0 :     if (GetStyleText()->NewlineIsSignificant() &&
    5837               0 :         HasTerminalNewline()) {
    5838               0 :       --selectedOffset;
    5839                 :     }
    5840                 :   }
    5841                 : 
    5842               0 :   offsets.content = GetContent();
    5843               0 :   offsets.offset = offsets.secondaryOffset = selectedOffset;
    5844               0 :   offsets.associateWithNext = mContentOffset == offsets.offset;
    5845                 :   return offsets;
    5846                 : }
    5847                 : 
    5848                 : bool
    5849               0 : nsTextFrame::CombineSelectionUnderlineRect(nsPresContext* aPresContext,
    5850                 :                                            nsRect& aRect)
    5851                 : {
    5852               0 :   if (aRect.IsEmpty())
    5853               0 :     return false;
    5854                 : 
    5855               0 :   nsRect givenRect = aRect;
    5856                 : 
    5857               0 :   nsRefPtr<nsFontMetrics> fm;
    5858                 :   nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
    5859               0 :                                         GetFontSizeInflation());
    5860               0 :   gfxFontGroup* fontGroup = fm->GetThebesFontGroup();
    5861               0 :   gfxFont* firstFont = fontGroup->GetFontAt(0);
    5862               0 :   if (!firstFont)
    5863               0 :     return false; // OOM
    5864               0 :   const gfxFont::Metrics& metrics = firstFont->GetMetrics();
    5865               0 :   gfxFloat underlineOffset = fontGroup->GetUnderlineOffset();
    5866               0 :   gfxFloat ascent = aPresContext->AppUnitsToGfxUnits(mAscent);
    5867                 :   gfxFloat descentLimit =
    5868               0 :     ComputeDescentLimitForSelectionUnderline(aPresContext, this, metrics);
    5869                 : 
    5870               0 :   SelectionDetails *details = GetSelectionDetails();
    5871               0 :   for (SelectionDetails *sd = details; sd; sd = sd->mNext) {
    5872               0 :     if (sd->mStart == sd->mEnd || !(sd->mType & SelectionTypesWithDecorations))
    5873               0 :       continue;
    5874                 : 
    5875                 :     PRUint8 style;
    5876                 :     float relativeSize;
    5877                 :     PRInt32 index =
    5878               0 :       nsTextPaintStyle::GetUnderlineStyleIndexForSelectionType(sd->mType);
    5879               0 :     if (sd->mType == nsISelectionController::SELECTION_SPELLCHECK) {
    5880               0 :       if (!nsTextPaintStyle::GetSelectionUnderline(aPresContext, index, nsnull,
    5881               0 :                                                    &relativeSize, &style)) {
    5882               0 :         continue;
    5883                 :       }
    5884                 :     } else {
    5885                 :       // IME selections
    5886               0 :       nsTextRangeStyle& rangeStyle = sd->mTextRangeStyle;
    5887               0 :       if (rangeStyle.IsDefined()) {
    5888               0 :         if (!rangeStyle.IsLineStyleDefined() ||
    5889                 :             rangeStyle.mLineStyle == nsTextRangeStyle::LINESTYLE_NONE) {
    5890               0 :           continue;
    5891                 :         }
    5892               0 :         style = rangeStyle.mLineStyle;
    5893               0 :         relativeSize = rangeStyle.mIsBoldLine ? 2.0f : 1.0f;
    5894               0 :       } else if (!nsTextPaintStyle::GetSelectionUnderline(aPresContext, index,
    5895                 :                                                           nsnull, &relativeSize,
    5896               0 :                                                           &style)) {
    5897               0 :         continue;
    5898                 :       }
    5899                 :     }
    5900               0 :     nsRect decorationArea;
    5901                 :     gfxSize size(aPresContext->AppUnitsToGfxUnits(aRect.width),
    5902                 :                  ComputeSelectionUnderlineHeight(aPresContext,
    5903               0 :                                                  metrics, sd->mType));
    5904               0 :     relativeSize = NS_MAX(relativeSize, 1.0f);
    5905               0 :     size.height *= relativeSize;
    5906                 :     decorationArea =
    5907                 :       nsCSSRendering::GetTextDecorationRect(aPresContext, size,
    5908                 :         ascent, underlineOffset, NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE,
    5909               0 :         style, descentLimit);
    5910               0 :     aRect.UnionRect(aRect, decorationArea);
    5911                 :   }
    5912               0 :   DestroySelectionDetails(details);
    5913                 : 
    5914               0 :   return !aRect.IsEmpty() && !givenRect.Contains(aRect);
    5915                 : }
    5916                 : 
    5917                 : bool
    5918               0 : nsTextFrame::IsFrameSelected() const
    5919                 : {
    5920               0 :   NS_ASSERTION(!GetContent() || GetContent()->IsSelectionDescendant(),
    5921                 :                "use the public IsSelected() instead");
    5922               0 :   return nsRange::IsNodeSelected(GetContent(), GetContentOffset(),
    5923               0 :                                  GetContentEnd());
    5924                 : }
    5925                 : 
    5926                 : void
    5927               0 : nsTextFrame::SetSelectedRange(PRUint32 aStart, PRUint32 aEnd, bool aSelected,
    5928                 :                               SelectionType aType)
    5929                 : {
    5930               0 :   NS_ASSERTION(!GetPrevContinuation(), "Should only be called for primary frame");
    5931                 :   DEBUG_VERIFY_NOT_DIRTY(mState);
    5932                 : 
    5933                 :   // Selection is collapsed, which can't affect text frame rendering
    5934               0 :   if (aStart == aEnd)
    5935               0 :     return;
    5936                 : 
    5937               0 :   nsTextFrame* f = this;
    5938               0 :   while (f && f->GetContentEnd() <= PRInt32(aStart)) {
    5939               0 :     f = static_cast<nsTextFrame*>(f->GetNextContinuation());
    5940                 :   }
    5941                 : 
    5942               0 :   nsPresContext* presContext = PresContext();
    5943               0 :   while (f && f->GetContentOffset() < PRInt32(aEnd)) {
    5944                 :     // We may need to reflow to recompute the overflow area for
    5945                 :     // spellchecking or IME underline if their underline is thicker than
    5946                 :     // the normal decoration line.
    5947               0 :     if (aType & SelectionTypesWithDecorations) {
    5948                 :       bool didHaveOverflowingSelection =
    5949               0 :         (f->GetStateBits() & TEXT_SELECTION_UNDERLINE_OVERFLOWED) != 0;
    5950               0 :       nsRect r(nsPoint(0, 0), GetSize());
    5951                 :       bool willHaveOverflowingSelection =
    5952               0 :         aSelected && f->CombineSelectionUnderlineRect(presContext, r);
    5953               0 :       if (didHaveOverflowingSelection || willHaveOverflowingSelection) {
    5954               0 :         presContext->PresShell()->FrameNeedsReflow(f,
    5955                 :                                                    nsIPresShell::eStyleChange,
    5956               0 :                                                    NS_FRAME_IS_DIRTY);
    5957                 :       }
    5958                 :     }
    5959                 :     // Selection might change anything. Invalidate the overflow area.
    5960               0 :     f->InvalidateOverflowRect();
    5961                 : 
    5962               0 :     f = static_cast<nsTextFrame*>(f->GetNextContinuation());
    5963                 :   }
    5964                 : }
    5965                 : 
    5966                 : NS_IMETHODIMP
    5967               0 : nsTextFrame::GetPointFromOffset(PRInt32 inOffset,
    5968                 :                                 nsPoint* outPoint)
    5969                 : {
    5970               0 :   if (!outPoint)
    5971               0 :     return NS_ERROR_NULL_POINTER;
    5972                 : 
    5973               0 :   outPoint->x = 0;
    5974               0 :   outPoint->y = 0;
    5975                 : 
    5976                 :   DEBUG_VERIFY_NOT_DIRTY(mState);
    5977               0 :   if (mState & NS_FRAME_IS_DIRTY)
    5978               0 :     return NS_ERROR_UNEXPECTED;
    5979                 : 
    5980               0 :   if (GetContentLength() <= 0) {
    5981               0 :     return NS_OK;
    5982                 :   }
    5983                 : 
    5984               0 :   gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
    5985               0 :   if (!mTextRun)
    5986               0 :     return NS_ERROR_FAILURE;
    5987                 : 
    5988               0 :   PropertyProvider properties(this, iter, nsTextFrame::eInflated);
    5989                 :   // Don't trim trailing whitespace, we want the caret to appear in the right
    5990                 :   // place if it's positioned there
    5991               0 :   properties.InitializeForDisplay(false);  
    5992                 : 
    5993               0 :   if (inOffset < GetContentOffset()){
    5994               0 :     NS_WARNING("offset before this frame's content");
    5995               0 :     inOffset = GetContentOffset();
    5996               0 :   } else if (inOffset > GetContentEnd()) {
    5997               0 :     NS_WARNING("offset after this frame's content");
    5998               0 :     inOffset = GetContentEnd();
    5999                 :   }
    6000               0 :   PRInt32 trimmedOffset = properties.GetStart().GetOriginalOffset();
    6001               0 :   PRInt32 trimmedEnd = trimmedOffset + properties.GetOriginalLength();
    6002               0 :   inOffset = NS_MAX(inOffset, trimmedOffset);
    6003               0 :   inOffset = NS_MIN(inOffset, trimmedEnd);
    6004                 : 
    6005               0 :   iter.SetOriginalOffset(inOffset);
    6006                 : 
    6007               0 :   if (inOffset < trimmedEnd &&
    6008               0 :       !iter.IsOriginalCharSkipped() &&
    6009               0 :       !mTextRun->IsClusterStart(iter.GetSkippedOffset())) {
    6010               0 :     NS_WARNING("GetPointFromOffset called for non-cluster boundary");
    6011               0 :     FindClusterStart(mTextRun, trimmedOffset, &iter);
    6012                 :   }
    6013                 : 
    6014                 :   gfxFloat advanceWidth =
    6015               0 :     mTextRun->GetAdvanceWidth(properties.GetStart().GetSkippedOffset(),
    6016               0 :                               GetSkippedDistance(properties.GetStart(), iter),
    6017               0 :                               &properties);
    6018               0 :   nscoord width = NSToCoordCeilClamped(advanceWidth);
    6019                 : 
    6020               0 :   if (mTextRun->IsRightToLeft()) {
    6021               0 :     outPoint->x = mRect.width - width;
    6022                 :   } else {
    6023               0 :     outPoint->x = width;
    6024                 :   }
    6025               0 :   outPoint->y = 0;
    6026                 : 
    6027               0 :   return NS_OK;
    6028                 : }
    6029                 : 
    6030                 : NS_IMETHODIMP
    6031               0 : nsTextFrame::GetChildFrameContainingOffset(PRInt32   aContentOffset,
    6032                 :                                            bool      aHint,
    6033                 :                                            PRInt32*  aOutOffset,
    6034                 :                                            nsIFrame**aOutFrame)
    6035                 : {
    6036                 :   DEBUG_VERIFY_NOT_DIRTY(mState);
    6037                 : #if 0 //XXXrbs disable due to bug 310227
    6038                 :   if (mState & NS_FRAME_IS_DIRTY)
    6039                 :     return NS_ERROR_UNEXPECTED;
    6040                 : #endif
    6041                 : 
    6042               0 :   NS_ASSERTION(aOutOffset && aOutFrame, "Bad out parameters");
    6043               0 :   NS_ASSERTION(aContentOffset >= 0, "Negative content offset, existing code was very broken!");
    6044               0 :   nsIFrame* primaryFrame = mContent->GetPrimaryFrame();
    6045               0 :   if (this != primaryFrame) {
    6046                 :     // This call needs to happen on the primary frame
    6047                 :     return primaryFrame->GetChildFrameContainingOffset(aContentOffset, aHint,
    6048               0 :                                                        aOutOffset, aOutFrame);
    6049                 :   }
    6050                 : 
    6051               0 :   nsTextFrame* f = this;
    6052               0 :   PRInt32 offset = mContentOffset;
    6053                 : 
    6054                 :   // Try to look up the offset to frame property
    6055                 :   nsTextFrame* cachedFrame = static_cast<nsTextFrame*>
    6056               0 :     (Properties().Get(OffsetToFrameProperty()));
    6057                 : 
    6058               0 :   if (cachedFrame) {
    6059               0 :     f = cachedFrame;
    6060               0 :     offset = f->GetContentOffset();
    6061                 : 
    6062               0 :     f->RemoveStateBits(TEXT_IN_OFFSET_CACHE);
    6063                 :   }
    6064                 : 
    6065               0 :   if ((aContentOffset >= offset) &&
    6066                 :       (aHint || aContentOffset != offset)) {
    6067               0 :     while (true) {
    6068               0 :       nsTextFrame* next = static_cast<nsTextFrame*>(f->GetNextContinuation());
    6069               0 :       if (!next || aContentOffset < next->GetContentOffset())
    6070               0 :         break;
    6071               0 :       if (aContentOffset == next->GetContentOffset()) {
    6072               0 :         if (aHint) {
    6073               0 :           f = next;
    6074                 :         }
    6075               0 :         break;
    6076                 :       }
    6077               0 :       f = next;
    6078               0 :     }
    6079                 :   } else {
    6080               0 :     while (true) {
    6081               0 :       nsTextFrame* prev = static_cast<nsTextFrame*>(f->GetPrevContinuation());
    6082               0 :       if (!prev || aContentOffset > f->GetContentOffset())
    6083               0 :         break;
    6084               0 :       if (aContentOffset == f->GetContentOffset()) {
    6085               0 :         if (!aHint) {
    6086               0 :           f = prev;
    6087                 :         }
    6088               0 :         break;
    6089                 :       }
    6090               0 :       f = prev;
    6091                 :     }
    6092                 :   }
    6093                 :   
    6094               0 :   *aOutOffset = aContentOffset - f->GetContentOffset();
    6095               0 :   *aOutFrame = f;
    6096                 : 
    6097                 :   // cache the frame we found
    6098               0 :   Properties().Set(OffsetToFrameProperty(), f);
    6099               0 :   f->AddStateBits(TEXT_IN_OFFSET_CACHE);
    6100                 : 
    6101               0 :   return NS_OK;
    6102                 : }
    6103                 : 
    6104                 : bool
    6105               0 : nsTextFrame::PeekOffsetNoAmount(bool aForward, PRInt32* aOffset)
    6106                 : {
    6107               0 :   NS_ASSERTION(aOffset && *aOffset <= GetContentLength(), "aOffset out of range");
    6108                 : 
    6109               0 :   gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
    6110               0 :   if (!mTextRun)
    6111               0 :     return false;
    6112                 : 
    6113               0 :   TrimmedOffsets trimmed = GetTrimmedOffsets(mContent->GetText(), true);
    6114                 :   // Check whether there are nonskipped characters in the trimmmed range
    6115               0 :   return iter.ConvertOriginalToSkipped(trimmed.GetEnd()) >
    6116               0 :          iter.ConvertOriginalToSkipped(trimmed.mStart);
    6117                 : }
    6118                 : 
    6119                 : /**
    6120                 :  * This class iterates through the clusters before or after the given
    6121                 :  * aPosition (which is a content offset). You can test each cluster
    6122                 :  * to see if it's whitespace (as far as selection/caret movement is concerned),
    6123                 :  * or punctuation, or if there is a word break before the cluster. ("Before"
    6124                 :  * is interpreted according to aDirection, so if aDirection is -1, "before"
    6125                 :  * means actually *after* the cluster content.)
    6126                 :  */
    6127               0 : class NS_STACK_CLASS ClusterIterator {
    6128                 : public:
    6129                 :   ClusterIterator(nsTextFrame* aTextFrame, PRInt32 aPosition, PRInt32 aDirection,
    6130                 :                   nsString& aContext);
    6131                 : 
    6132                 :   bool NextCluster();
    6133                 :   bool IsWhitespace();
    6134                 :   bool IsPunctuation();
    6135               0 :   bool HaveWordBreakBefore() { return mHaveWordBreak; }
    6136                 :   PRInt32 GetAfterOffset();
    6137                 :   PRInt32 GetBeforeOffset();
    6138                 : 
    6139                 : private:
    6140                 :   gfxSkipCharsIterator        mIterator;
    6141                 :   const nsTextFragment*       mFrag;
    6142                 :   nsTextFrame*                mTextFrame;
    6143                 :   PRInt32                     mDirection;
    6144                 :   PRInt32                     mCharIndex;
    6145                 :   nsTextFrame::TrimmedOffsets mTrimmed;
    6146                 :   nsTArray<bool>      mWordBreaks;
    6147                 :   bool                        mHaveWordBreak;
    6148                 : };
    6149                 : 
    6150                 : static bool
    6151               0 : IsAcceptableCaretPosition(const gfxSkipCharsIterator& aIter,
    6152                 :                           bool aRespectClusters,
    6153                 :                           gfxTextRun* aTextRun,
    6154                 :                           nsIFrame* aFrame)
    6155                 : {
    6156               0 :   if (aIter.IsOriginalCharSkipped())
    6157               0 :     return false;
    6158               0 :   PRUint32 index = aIter.GetSkippedOffset();
    6159               0 :   if (aRespectClusters && !aTextRun->IsClusterStart(index))
    6160               0 :     return false;
    6161               0 :   if (index > 0) {
    6162                 :     // Check whether the proposed position is in between the two halves of a
    6163                 :     // surrogate pair; if so, this is not a valid character boundary.
    6164                 :     // (In the case where we are respecting clusters, we won't actually get
    6165                 :     // this far because the low surrogate is also marked as non-clusterStart
    6166                 :     // so we'll return FALSE above.)
    6167               0 :     if (aTextRun->CharIsLowSurrogate(index)) {
    6168               0 :       return false;
    6169                 :     }
    6170                 :   }
    6171               0 :   return true;
    6172                 : }
    6173                 : 
    6174                 : bool
    6175               0 : nsTextFrame::PeekOffsetCharacter(bool aForward, PRInt32* aOffset,
    6176                 :                                  bool aRespectClusters)
    6177                 : {
    6178               0 :   PRInt32 contentLength = GetContentLength();
    6179               0 :   NS_ASSERTION(aOffset && *aOffset <= contentLength, "aOffset out of range");
    6180                 : 
    6181                 :   bool selectable;
    6182                 :   PRUint8 selectStyle;  
    6183               0 :   IsSelectable(&selectable, &selectStyle);
    6184               0 :   if (selectStyle == NS_STYLE_USER_SELECT_ALL)
    6185               0 :     return false;
    6186                 : 
    6187               0 :   gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
    6188               0 :   if (!mTextRun)
    6189               0 :     return false;
    6190                 : 
    6191               0 :   TrimmedOffsets trimmed = GetTrimmedOffsets(mContent->GetText(), false);
    6192                 : 
    6193                 :   // A negative offset means "end of frame".
    6194               0 :   PRInt32 startOffset = GetContentOffset() + (*aOffset < 0 ? contentLength : *aOffset);
    6195                 : 
    6196               0 :   if (!aForward) {
    6197                 :     // If at the beginning of the line, look at the previous continuation
    6198               0 :     for (PRInt32 i = NS_MIN(trimmed.GetEnd(), startOffset) - 1;
    6199                 :          i >= trimmed.mStart; --i) {
    6200               0 :       iter.SetOriginalOffset(i);
    6201               0 :       if (IsAcceptableCaretPosition(iter, aRespectClusters, mTextRun, this)) {
    6202               0 :         *aOffset = i - mContentOffset;
    6203               0 :         return true;
    6204                 :       }
    6205                 :     }
    6206               0 :     *aOffset = 0;
    6207                 :   } else {
    6208                 :     // If we're at the end of a line, look at the next continuation
    6209               0 :     iter.SetOriginalOffset(startOffset);
    6210               0 :     if (startOffset <= trimmed.GetEnd() &&
    6211               0 :         !(startOffset < trimmed.GetEnd() &&
    6212               0 :           GetStyleText()->NewlineIsSignificant() &&
    6213               0 :           iter.GetSkippedOffset() < mTextRun->GetLength() &&
    6214               0 :           mTextRun->CharIsNewline(iter.GetSkippedOffset()))) {
    6215               0 :       for (PRInt32 i = startOffset + 1; i <= trimmed.GetEnd(); ++i) {
    6216               0 :         iter.SetOriginalOffset(i);
    6217               0 :         if (i == trimmed.GetEnd() ||
    6218               0 :             IsAcceptableCaretPosition(iter, aRespectClusters, mTextRun, this)) {
    6219               0 :           *aOffset = i - mContentOffset;
    6220               0 :           return true;
    6221                 :         }
    6222                 :       }
    6223                 :     }
    6224               0 :     *aOffset = contentLength;
    6225                 :   }
    6226                 :   
    6227               0 :   return false;
    6228                 : }
    6229                 : 
    6230                 : bool
    6231               0 : ClusterIterator::IsWhitespace()
    6232                 : {
    6233               0 :   NS_ASSERTION(mCharIndex >= 0, "No cluster selected");
    6234               0 :   return IsSelectionSpace(mFrag, mCharIndex);
    6235                 : }
    6236                 : 
    6237                 : bool
    6238               0 : ClusterIterator::IsPunctuation()
    6239                 : {
    6240               0 :   NS_ASSERTION(mCharIndex >= 0, "No cluster selected");
    6241                 :   nsIUGenCategory::nsUGenCategory c =
    6242               0 :     mozilla::unicode::GetGenCategory(mFrag->CharAt(mCharIndex));
    6243               0 :   return c == nsIUGenCategory::kPunctuation || c == nsIUGenCategory::kSymbol;
    6244                 : }
    6245                 : 
    6246                 : PRInt32
    6247               0 : ClusterIterator::GetBeforeOffset()
    6248                 : {
    6249               0 :   NS_ASSERTION(mCharIndex >= 0, "No cluster selected");
    6250               0 :   return mCharIndex + (mDirection > 0 ? 0 : 1);
    6251                 : }
    6252                 : 
    6253                 : PRInt32
    6254               0 : ClusterIterator::GetAfterOffset()
    6255                 : {
    6256               0 :   NS_ASSERTION(mCharIndex >= 0, "No cluster selected");
    6257               0 :   return mCharIndex + (mDirection > 0 ? 1 : 0);
    6258                 : }
    6259                 : 
    6260                 : bool
    6261               0 : ClusterIterator::NextCluster()
    6262                 : {
    6263               0 :   if (!mDirection)
    6264               0 :     return false;
    6265               0 :   gfxTextRun* textRun = mTextFrame->GetTextRun(nsTextFrame::eInflated);
    6266                 : 
    6267               0 :   mHaveWordBreak = false;
    6268               0 :   while (true) {
    6269               0 :     bool keepGoing = false;
    6270               0 :     if (mDirection > 0) {
    6271               0 :       if (mIterator.GetOriginalOffset() >= mTrimmed.GetEnd())
    6272               0 :         return false;
    6273               0 :       keepGoing = mIterator.IsOriginalCharSkipped() ||
    6274               0 :           mIterator.GetOriginalOffset() < mTrimmed.mStart ||
    6275               0 :           !textRun->IsClusterStart(mIterator.GetSkippedOffset());
    6276               0 :       mCharIndex = mIterator.GetOriginalOffset();
    6277               0 :       mIterator.AdvanceOriginal(1);
    6278                 :     } else {
    6279               0 :       if (mIterator.GetOriginalOffset() <= mTrimmed.mStart)
    6280               0 :         return false;
    6281               0 :       mIterator.AdvanceOriginal(-1);
    6282               0 :       keepGoing = mIterator.IsOriginalCharSkipped() ||
    6283               0 :           mIterator.GetOriginalOffset() >= mTrimmed.GetEnd() ||
    6284               0 :           !textRun->IsClusterStart(mIterator.GetSkippedOffset());
    6285               0 :       mCharIndex = mIterator.GetOriginalOffset();
    6286                 :     }
    6287                 : 
    6288               0 :     if (mWordBreaks[GetBeforeOffset() - mTextFrame->GetContentOffset()]) {
    6289               0 :       mHaveWordBreak = true;
    6290                 :     }
    6291               0 :     if (!keepGoing)
    6292               0 :       return true;
    6293                 :   }
    6294                 : }
    6295                 : 
    6296               0 : ClusterIterator::ClusterIterator(nsTextFrame* aTextFrame, PRInt32 aPosition,
    6297                 :                                  PRInt32 aDirection, nsString& aContext)
    6298               0 :   : mTextFrame(aTextFrame), mDirection(aDirection), mCharIndex(-1)
    6299                 : {
    6300               0 :   mIterator = aTextFrame->EnsureTextRun(nsTextFrame::eInflated);
    6301               0 :   if (!aTextFrame->GetTextRun(nsTextFrame::eInflated)) {
    6302               0 :     mDirection = 0; // signal failure
    6303               0 :     return;
    6304                 :   }
    6305               0 :   mIterator.SetOriginalOffset(aPosition);
    6306                 : 
    6307               0 :   mFrag = aTextFrame->GetContent()->GetText();
    6308               0 :   mTrimmed = aTextFrame->GetTrimmedOffsets(mFrag, true);
    6309                 : 
    6310               0 :   PRInt32 textOffset = aTextFrame->GetContentOffset();
    6311               0 :   PRInt32 textLen = aTextFrame->GetContentLength();
    6312               0 :   if (!mWordBreaks.AppendElements(textLen + 1)) {
    6313               0 :     mDirection = 0; // signal failure
    6314               0 :     return;
    6315                 :   }
    6316               0 :   memset(mWordBreaks.Elements(), false, (textLen + 1)*sizeof(bool));
    6317                 :   PRInt32 textStart;
    6318               0 :   if (aDirection > 0) {
    6319               0 :     if (aContext.IsEmpty()) {
    6320                 :       // No previous context, so it must be the start of a line or text run
    6321               0 :       mWordBreaks[0] = true;
    6322                 :     }
    6323               0 :     textStart = aContext.Length();
    6324               0 :     mFrag->AppendTo(aContext, textOffset, textLen);
    6325                 :   } else {
    6326               0 :     if (aContext.IsEmpty()) {
    6327                 :       // No following context, so it must be the end of a line or text run
    6328               0 :       mWordBreaks[textLen] = true;
    6329                 :     }
    6330               0 :     textStart = 0;
    6331               0 :     nsAutoString str;
    6332               0 :     mFrag->AppendTo(str, textOffset, textLen);
    6333               0 :     aContext.Insert(str, 0);
    6334                 :   }
    6335               0 :   nsIWordBreaker* wordBreaker = nsContentUtils::WordBreaker();
    6336                 :   PRInt32 i;
    6337               0 :   for (i = 0; i <= textLen; ++i) {
    6338               0 :     PRInt32 indexInText = i + textStart;
    6339               0 :     mWordBreaks[i] |=
    6340                 :       wordBreaker->BreakInBetween(aContext.get(), indexInText,
    6341               0 :                                   aContext.get() + indexInText,
    6342               0 :                                   aContext.Length() - indexInText);
    6343                 :   }
    6344                 : }
    6345                 : 
    6346                 : bool
    6347               0 : nsTextFrame::PeekOffsetWord(bool aForward, bool aWordSelectEatSpace, bool aIsKeyboardSelect,
    6348                 :                             PRInt32* aOffset, PeekWordState* aState)
    6349                 : {
    6350               0 :   PRInt32 contentLength = GetContentLength();
    6351               0 :   NS_ASSERTION (aOffset && *aOffset <= contentLength, "aOffset out of range");
    6352                 : 
    6353                 :   bool selectable;
    6354                 :   PRUint8 selectStyle;
    6355               0 :   IsSelectable(&selectable, &selectStyle);
    6356               0 :   if (selectStyle == NS_STYLE_USER_SELECT_ALL)
    6357               0 :     return false;
    6358                 : 
    6359               0 :   PRInt32 offset = GetContentOffset() + (*aOffset < 0 ? contentLength : *aOffset);
    6360               0 :   ClusterIterator cIter(this, offset, aForward ? 1 : -1, aState->mContext);
    6361                 : 
    6362               0 :   if (!cIter.NextCluster())
    6363               0 :     return false;
    6364                 : 
    6365               0 :   do {
    6366               0 :     bool isPunctuation = cIter.IsPunctuation();
    6367               0 :     bool isWhitespace = cIter.IsWhitespace();
    6368               0 :     bool isWordBreakBefore = cIter.HaveWordBreakBefore();
    6369               0 :     if (aWordSelectEatSpace == isWhitespace && !aState->mSawBeforeType) {
    6370               0 :       aState->SetSawBeforeType();
    6371               0 :       aState->Update(isPunctuation, isWhitespace);
    6372               0 :       continue;
    6373                 :     }
    6374                 :     // See if we can break before the current cluster
    6375               0 :     if (!aState->mAtStart) {
    6376                 :       bool canBreak;
    6377               0 :       if (isPunctuation != aState->mLastCharWasPunctuation) {
    6378                 :         canBreak = BreakWordBetweenPunctuation(aState, aForward,
    6379               0 :                      isPunctuation, isWhitespace, aIsKeyboardSelect);
    6380               0 :       } else if (!aState->mLastCharWasWhitespace &&
    6381               0 :                  !isWhitespace && !isPunctuation && isWordBreakBefore) {
    6382                 :         // if both the previous and the current character are not white
    6383                 :         // space but this can be word break before, we don't need to eat
    6384                 :         // a white space in this case. This case happens in some languages
    6385                 :         // that their words are not separated by white spaces. E.g.,
    6386                 :         // Japanese and Chinese.
    6387               0 :         canBreak = true;
    6388                 :       } else {
    6389               0 :         canBreak = isWordBreakBefore && aState->mSawBeforeType;
    6390                 :       }
    6391               0 :       if (canBreak) {
    6392               0 :         *aOffset = cIter.GetBeforeOffset() - mContentOffset;
    6393               0 :         return true;
    6394                 :       }
    6395                 :     }
    6396               0 :     aState->Update(isPunctuation, isWhitespace);
    6397                 :   } while (cIter.NextCluster());
    6398                 : 
    6399               0 :   *aOffset = cIter.GetAfterOffset() - mContentOffset;
    6400               0 :   return false;
    6401                 : }
    6402                 : 
    6403                 :  // TODO this needs to be deCOMtaminated with the interface fixed in
    6404                 : // nsIFrame.h, but we won't do that until the old textframe is gone.
    6405                 : NS_IMETHODIMP
    6406               0 : nsTextFrame::CheckVisibility(nsPresContext* aContext, PRInt32 aStartIndex,
    6407                 :     PRInt32 aEndIndex, bool aRecurse, bool *aFinished, bool *aRetval)
    6408                 : {
    6409               0 :   if (!aRetval)
    6410               0 :     return NS_ERROR_NULL_POINTER;
    6411                 : 
    6412                 :   // Text in the range is visible if there is at least one character in the range
    6413                 :   // that is not skipped and is mapped by this frame (which is the primary frame)
    6414                 :   // or one of its continuations.
    6415               0 :   for (nsTextFrame* f = this; f;
    6416               0 :        f = static_cast<nsTextFrame*>(GetNextContinuation())) {
    6417               0 :     PRInt32 dummyOffset = 0;
    6418               0 :     if (f->PeekOffsetNoAmount(true, &dummyOffset)) {
    6419               0 :       *aRetval = true;
    6420               0 :       return NS_OK;
    6421                 :     }
    6422                 :   }
    6423                 : 
    6424               0 :   *aRetval = false;
    6425               0 :   return NS_OK;
    6426                 : }
    6427                 : 
    6428                 : NS_IMETHODIMP
    6429               0 : nsTextFrame::GetOffsets(PRInt32 &start, PRInt32 &end) const
    6430                 : {
    6431               0 :   start = GetContentOffset();
    6432               0 :   end = GetContentEnd();
    6433               0 :   return NS_OK;
    6434                 : }
    6435                 : 
    6436                 : static PRInt32
    6437               0 : FindEndOfPunctuationRun(const nsTextFragment* aFrag,
    6438                 :                         gfxTextRun* aTextRun,
    6439                 :                         gfxSkipCharsIterator* aIter,
    6440                 :                         PRInt32 aOffset,
    6441                 :                         PRInt32 aStart,
    6442                 :                         PRInt32 aEnd)
    6443                 : {
    6444                 :   PRInt32 i;
    6445                 : 
    6446               0 :   for (i = aStart; i < aEnd - aOffset; ++i) {
    6447               0 :     if (nsContentUtils::IsFirstLetterPunctuationAt(aFrag, aOffset + i)) {
    6448               0 :       aIter->SetOriginalOffset(aOffset + i);
    6449               0 :       FindClusterEnd(aTextRun, aEnd, aIter);
    6450               0 :       i = aIter->GetOriginalOffset() - aOffset;
    6451                 :     } else {
    6452               0 :       break;
    6453                 :     }
    6454                 :   }
    6455               0 :   return i;
    6456                 : }
    6457                 : 
    6458                 : /**
    6459                 :  * Returns true if this text frame completes the first-letter, false
    6460                 :  * if it does not contain a true "letter".
    6461                 :  * If returns true, then it also updates aLength to cover just the first-letter
    6462                 :  * text.
    6463                 :  *
    6464                 :  * XXX :first-letter should be handled during frame construction
    6465                 :  * (and it has a good bit in common with nextBidi)
    6466                 :  * 
    6467                 :  * @param aLength an in/out parameter: on entry contains the maximum length to
    6468                 :  * return, on exit returns length of the first-letter fragment (which may
    6469                 :  * include leading and trailing punctuation, for example)
    6470                 :  */
    6471                 : static bool
    6472               0 : FindFirstLetterRange(const nsTextFragment* aFrag,
    6473                 :                      gfxTextRun* aTextRun,
    6474                 :                      PRInt32 aOffset, const gfxSkipCharsIterator& aIter,
    6475                 :                      PRInt32* aLength)
    6476                 : {
    6477                 :   PRInt32 i;
    6478               0 :   PRInt32 length = *aLength;
    6479               0 :   PRInt32 endOffset = aOffset + length;
    6480               0 :   gfxSkipCharsIterator iter(aIter);
    6481                 : 
    6482                 :   // skip leading whitespace, then consume clusters that start with punctuation
    6483                 :   i = FindEndOfPunctuationRun(aFrag, aTextRun, &iter, aOffset, 
    6484               0 :                               GetTrimmableWhitespaceCount(aFrag, aOffset, length, 1),
    6485               0 :                               endOffset);
    6486               0 :   if (i == length)
    6487               0 :     return false;
    6488                 : 
    6489                 :   // If the next character is not a letter or number, there is no first-letter.
    6490                 :   // Return true so that we don't go on looking, but set aLength to 0.
    6491               0 :   if (!nsContentUtils::IsAlphanumericAt(aFrag, aOffset + i)) {
    6492               0 :     *aLength = 0;
    6493               0 :     return true;
    6494                 :   }
    6495                 : 
    6496                 :   // consume another cluster (the actual first letter)
    6497               0 :   iter.SetOriginalOffset(aOffset + i);
    6498               0 :   FindClusterEnd(aTextRun, endOffset, &iter);
    6499               0 :   i = iter.GetOriginalOffset() - aOffset;
    6500               0 :   if (i + 1 == length)
    6501               0 :     return true;
    6502                 : 
    6503                 :   // consume clusters that start with punctuation
    6504               0 :   i = FindEndOfPunctuationRun(aFrag, aTextRun, &iter, aOffset, i + 1, endOffset);
    6505               0 :   if (i < length)
    6506               0 :     *aLength = i;
    6507               0 :   return true;
    6508                 : }
    6509                 : 
    6510                 : static PRUint32
    6511               0 : FindStartAfterSkippingWhitespace(PropertyProvider* aProvider,
    6512                 :                                  nsIFrame::InlineIntrinsicWidthData* aData,
    6513                 :                                  const nsStyleText* aTextStyle,
    6514                 :                                  gfxSkipCharsIterator* aIterator,
    6515                 :                                  PRUint32 aFlowEndInTextRun)
    6516                 : {
    6517               0 :   if (aData->skipWhitespace) {
    6518               0 :     while (aIterator->GetSkippedOffset() < aFlowEndInTextRun &&
    6519               0 :            IsTrimmableSpace(aProvider->GetFragment(), aIterator->GetOriginalOffset(), aTextStyle)) {
    6520               0 :       aIterator->AdvanceOriginal(1);
    6521                 :     }
    6522                 :   }
    6523               0 :   return aIterator->GetSkippedOffset();
    6524                 : }
    6525                 : 
    6526                 : union VoidPtrOrFloat {
    6527               0 :   VoidPtrOrFloat() : p(nsnull) {}
    6528                 : 
    6529                 :   void *p;
    6530                 :   float f;
    6531                 : };
    6532                 : 
    6533                 : float
    6534               0 : nsTextFrame::GetFontSizeInflation() const
    6535                 : {
    6536               0 :   if (!HasFontSizeInflation()) {
    6537               0 :     return 1.0f;
    6538                 :   }
    6539               0 :   VoidPtrOrFloat u;
    6540               0 :   u.p = Properties().Get(FontSizeInflationProperty());
    6541               0 :   return u.f;
    6542                 : }
    6543                 : 
    6544                 : void
    6545               0 : nsTextFrame::SetFontSizeInflation(float aInflation)
    6546                 : {
    6547               0 :   if (aInflation == 1.0f) {
    6548               0 :     if (HasFontSizeInflation()) {
    6549               0 :       RemoveStateBits(TEXT_HAS_FONT_INFLATION);
    6550               0 :       Properties().Delete(FontSizeInflationProperty());
    6551                 :     }
    6552               0 :     return;
    6553                 :   }
    6554                 : 
    6555               0 :   AddStateBits(TEXT_HAS_FONT_INFLATION);
    6556               0 :   VoidPtrOrFloat u;
    6557               0 :   u.f = aInflation;
    6558               0 :   Properties().Set(FontSizeInflationProperty(), u.p);
    6559                 : }
    6560                 : 
    6561                 : /* virtual */ 
    6562               0 : void nsTextFrame::MarkIntrinsicWidthsDirty()
    6563                 : {
    6564               0 :   ClearTextRuns();
    6565               0 :   nsFrame::MarkIntrinsicWidthsDirty();
    6566               0 : }
    6567                 : 
    6568                 : // XXX this doesn't handle characters shaped by line endings. We need to
    6569                 : // temporarily override the "current line ending" settings.
    6570                 : void
    6571               0 : nsTextFrame::AddInlineMinWidthForFlow(nsRenderingContext *aRenderingContext,
    6572                 :                                       nsIFrame::InlineMinWidthData *aData,
    6573                 :                                       float aInflation,
    6574                 :                                       TextRunType aTextRunType)
    6575                 : {
    6576                 :   PRUint32 flowEndInTextRun;
    6577               0 :   gfxContext* ctx = aRenderingContext->ThebesContext();
    6578                 :   gfxSkipCharsIterator iter =
    6579                 :     EnsureTextRun(aTextRunType, aInflation, ctx, aData->lineContainer,
    6580               0 :                   aData->line, &flowEndInTextRun);
    6581               0 :   gfxTextRun *textRun = GetTextRun(aTextRunType);
    6582               0 :   if (!textRun)
    6583               0 :     return;
    6584                 : 
    6585                 :   // Pass null for the line container. This will disable tab spacing, but that's
    6586                 :   // OK since we can't really handle tabs for intrinsic sizing anyway.
    6587               0 :   const nsStyleText* textStyle = GetStyleText();
    6588               0 :   const nsTextFragment* frag = mContent->GetText();
    6589                 : 
    6590                 :   // If we're hyphenating, the PropertyProvider needs the actual length;
    6591                 :   // otherwise we can just pass PR_INT32_MAX to mean "all the text"
    6592               0 :   PRInt32 len = PR_INT32_MAX;
    6593               0 :   bool hyphenating = frag->GetLength() > 0 &&
    6594                 :     (textStyle->mHyphens == NS_STYLE_HYPHENS_AUTO ||
    6595                 :      (textStyle->mHyphens == NS_STYLE_HYPHENS_MANUAL &&
    6596               0 :       (textRun->GetFlags() & gfxTextRunFactory::TEXT_ENABLE_HYPHEN_BREAKS) != 0));
    6597               0 :   if (hyphenating) {
    6598               0 :     gfxSkipCharsIterator tmp(iter);
    6599               0 :     len = NS_MIN<PRInt32>(GetContentOffset() + GetInFlowContentLength(),
    6600               0 :                  tmp.ConvertSkippedToOriginal(flowEndInTextRun)) - iter.GetOriginalOffset();
    6601                 :   }
    6602                 :   PropertyProvider provider(textRun, textStyle, frag, this,
    6603               0 :                             iter, len, nsnull, 0, aTextRunType);
    6604                 : 
    6605               0 :   bool collapseWhitespace = !textStyle->WhiteSpaceIsSignificant();
    6606               0 :   bool preformatNewlines = textStyle->NewlineIsSignificant();
    6607               0 :   bool preformatTabs = textStyle->WhiteSpaceIsSignificant();
    6608               0 :   gfxFloat tabWidth = -1;
    6609                 :   PRUint32 start =
    6610               0 :     FindStartAfterSkippingWhitespace(&provider, aData, textStyle, &iter, flowEndInTextRun);
    6611                 : 
    6612               0 :   AutoFallibleTArray<bool,BIG_TEXT_NODE_SIZE> hyphBuffer;
    6613               0 :   bool *hyphBreakBefore = nsnull;
    6614               0 :   if (hyphenating) {
    6615               0 :     hyphBreakBefore = hyphBuffer.AppendElements(flowEndInTextRun - start);
    6616               0 :     if (hyphBreakBefore) {
    6617                 :       provider.GetHyphenationBreaks(start, flowEndInTextRun - start,
    6618               0 :                                     hyphBreakBefore);
    6619                 :     }
    6620                 :   }
    6621                 : 
    6622               0 :   for (PRUint32 i = start, wordStart = start; i <= flowEndInTextRun; ++i) {
    6623               0 :     bool preformattedNewline = false;
    6624               0 :     bool preformattedTab = false;
    6625               0 :     if (i < flowEndInTextRun) {
    6626                 :       // XXXldb Shouldn't we be including the newline as part of the
    6627                 :       // segment that it ends rather than part of the segment that it
    6628                 :       // starts?
    6629               0 :       preformattedNewline = preformatNewlines && textRun->CharIsNewline(i);
    6630               0 :       preformattedTab = preformatTabs && textRun->CharIsTab(i);
    6631               0 :       if (!textRun->CanBreakLineBefore(i) &&
    6632               0 :           !preformattedNewline &&
    6633               0 :           !preformattedTab &&
    6634               0 :           (!hyphBreakBefore || !hyphBreakBefore[i - start]))
    6635                 :       {
    6636                 :         // we can't break here (and it's not the end of the flow)
    6637               0 :         continue;
    6638                 :       }
    6639                 :     }
    6640                 : 
    6641               0 :     if (i > wordStart) {
    6642                 :       nscoord width =
    6643               0 :         NSToCoordCeilClamped(textRun->GetAdvanceWidth(wordStart, i - wordStart, &provider));
    6644               0 :       aData->currentLine = NSCoordSaturatingAdd(aData->currentLine, width);
    6645               0 :       aData->atStartOfLine = false;
    6646                 : 
    6647               0 :       if (collapseWhitespace) {
    6648               0 :         PRUint32 trimStart = GetEndOfTrimmedText(frag, textStyle, wordStart, i, &iter);
    6649               0 :         if (trimStart == start) {
    6650                 :           // This is *all* trimmable whitespace, so whatever trailingWhitespace
    6651                 :           // we saw previously is still trailing...
    6652               0 :           aData->trailingWhitespace += width;
    6653                 :         } else {
    6654                 :           // Some non-whitespace so the old trailingWhitespace is no longer trailing
    6655                 :           aData->trailingWhitespace =
    6656               0 :             NSToCoordCeilClamped(textRun->GetAdvanceWidth(trimStart, i - trimStart, &provider));
    6657                 :         }
    6658                 :       } else {
    6659               0 :         aData->trailingWhitespace = 0;
    6660                 :       }
    6661                 :     }
    6662                 : 
    6663               0 :     if (preformattedTab) {
    6664                 :       PropertyProvider::Spacing spacing;
    6665               0 :       provider.GetSpacing(i, 1, &spacing);
    6666               0 :       aData->currentLine += nscoord(spacing.mBefore);
    6667                 :       gfxFloat afterTab =
    6668                 :         AdvanceToNextTab(aData->currentLine, this,
    6669               0 :                          textRun, &tabWidth);
    6670               0 :       aData->currentLine = nscoord(afterTab + spacing.mAfter);
    6671               0 :       wordStart = i + 1;
    6672               0 :     } else if (i < flowEndInTextRun ||
    6673               0 :         (i == textRun->GetLength() &&
    6674               0 :          (textRun->GetFlags() & nsTextFrameUtils::TEXT_HAS_TRAILING_BREAK))) {
    6675               0 :       if (preformattedNewline) {
    6676               0 :         aData->ForceBreak(aRenderingContext);
    6677               0 :       } else if (i < flowEndInTextRun && hyphBreakBefore &&
    6678               0 :                  hyphBreakBefore[i - start])
    6679                 :       {
    6680                 :         aData->OptionallyBreak(aRenderingContext, 
    6681               0 :                                NSToCoordRound(provider.GetHyphenWidth()));
    6682                 :       } {
    6683               0 :         aData->OptionallyBreak(aRenderingContext);
    6684                 :       }
    6685               0 :       wordStart = i;
    6686                 :     }
    6687                 :   }
    6688                 : 
    6689               0 :   if (start < flowEndInTextRun) {
    6690                 :     // Check if we have collapsible whitespace at the end
    6691                 :     aData->skipWhitespace =
    6692                 :       IsTrimmableSpace(provider.GetFragment(),
    6693                 :                        iter.ConvertSkippedToOriginal(flowEndInTextRun - 1),
    6694               0 :                        textStyle);
    6695                 :   }
    6696                 : }
    6697                 : 
    6698                 : // XXX Need to do something here to avoid incremental reflow bugs due to
    6699                 : // first-line and first-letter changing min-width
    6700                 : /* virtual */ void
    6701               0 : nsTextFrame::AddInlineMinWidth(nsRenderingContext *aRenderingContext,
    6702                 :                                nsIFrame::InlineMinWidthData *aData)
    6703                 : {
    6704                 :   float inflation =
    6705               0 :     nsLayoutUtils::FontSizeInflationFor(this, nsLayoutUtils::eInReflow);
    6706               0 :   TextRunType trtype = (inflation == 1.0f) ? eNotInflated : eInflated;
    6707                 : 
    6708                 :   nsTextFrame* f;
    6709               0 :   gfxTextRun* lastTextRun = nsnull;
    6710                 :   // nsContinuingTextFrame does nothing for AddInlineMinWidth; all text frames
    6711                 :   // in the flow are handled right here.
    6712               0 :   for (f = this; f; f = static_cast<nsTextFrame*>(f->GetNextContinuation())) {
    6713                 :     // f->GetTextRun(nsTextFrame::eNotInflated) could be null if we
    6714                 :     // haven't set up textruns yet for f.  Except in OOM situations,
    6715                 :     // lastTextRun will only be null for the first text frame.
    6716               0 :     if (f == this || f->GetTextRun(trtype) != lastTextRun) {
    6717                 :       nsIFrame* lc;
    6718               0 :       if (aData->lineContainer &&
    6719               0 :           aData->lineContainer != (lc = FindLineContainer(f))) {
    6720               0 :         NS_ASSERTION(f != this, "wrong InlineMinWidthData container"
    6721                 :                                 " for first continuation");
    6722               0 :         aData->line = nsnull;
    6723               0 :         aData->lineContainer = lc;
    6724                 :       }
    6725                 : 
    6726                 :       // This will process all the text frames that share the same textrun as f.
    6727                 :       f->AddInlineMinWidthForFlow(aRenderingContext, aData,
    6728               0 :                                   inflation, trtype);
    6729               0 :       lastTextRun = f->GetTextRun(trtype);
    6730                 :     }
    6731                 :   }
    6732               0 : }
    6733                 : 
    6734                 : // XXX this doesn't handle characters shaped by line endings. We need to
    6735                 : // temporarily override the "current line ending" settings.
    6736                 : void
    6737               0 : nsTextFrame::AddInlinePrefWidthForFlow(nsRenderingContext *aRenderingContext,
    6738                 :                                        nsIFrame::InlinePrefWidthData *aData,
    6739                 :                                        float aInflation,
    6740                 :                                        TextRunType aTextRunType)
    6741                 : {
    6742                 :   PRUint32 flowEndInTextRun;
    6743               0 :   gfxContext* ctx = aRenderingContext->ThebesContext();
    6744                 :   gfxSkipCharsIterator iter =
    6745                 :     EnsureTextRun(aTextRunType, aInflation, ctx, aData->lineContainer,
    6746               0 :                   aData->line, &flowEndInTextRun);
    6747               0 :   gfxTextRun *textRun = GetTextRun(aTextRunType);
    6748               0 :   if (!textRun)
    6749               0 :     return;
    6750                 : 
    6751                 :   // Pass null for the line container. This will disable tab spacing, but that's
    6752                 :   // OK since we can't really handle tabs for intrinsic sizing anyway.
    6753                 :   
    6754               0 :   const nsStyleText* textStyle = GetStyleText();
    6755               0 :   const nsTextFragment* frag = mContent->GetText();
    6756                 :   PropertyProvider provider(textRun, textStyle, frag, this,
    6757               0 :                             iter, PR_INT32_MAX, nsnull, 0, aTextRunType);
    6758                 : 
    6759               0 :   bool collapseWhitespace = !textStyle->WhiteSpaceIsSignificant();
    6760               0 :   bool preformatNewlines = textStyle->NewlineIsSignificant();
    6761               0 :   bool preformatTabs = textStyle->WhiteSpaceIsSignificant();
    6762               0 :   gfxFloat tabWidth = -1;
    6763                 :   PRUint32 start =
    6764               0 :     FindStartAfterSkippingWhitespace(&provider, aData, textStyle, &iter, flowEndInTextRun);
    6765                 : 
    6766                 :   // XXX Should we consider hyphenation here?
    6767                 :   // If newlines and tabs aren't preformatted, nothing to do inside
    6768                 :   // the loop so make i skip to the end
    6769               0 :   PRUint32 loopStart = (preformatNewlines || preformatTabs) ? start : flowEndInTextRun;
    6770               0 :   for (PRUint32 i = loopStart, lineStart = start; i <= flowEndInTextRun; ++i) {
    6771               0 :     bool preformattedNewline = false;
    6772               0 :     bool preformattedTab = false;
    6773               0 :     if (i < flowEndInTextRun) {
    6774                 :       // XXXldb Shouldn't we be including the newline as part of the
    6775                 :       // segment that it ends rather than part of the segment that it
    6776                 :       // starts?
    6777               0 :       NS_ASSERTION(preformatNewlines, "We can't be here unless newlines are hard breaks");
    6778               0 :       preformattedNewline = preformatNewlines && textRun->CharIsNewline(i);
    6779               0 :       preformattedTab = preformatTabs && textRun->CharIsTab(i);
    6780               0 :       if (!preformattedNewline && !preformattedTab) {
    6781                 :         // we needn't break here (and it's not the end of the flow)
    6782               0 :         continue;
    6783                 :       }
    6784                 :     }
    6785                 : 
    6786               0 :     if (i > lineStart) {
    6787                 :       nscoord width =
    6788               0 :         NSToCoordCeilClamped(textRun->GetAdvanceWidth(lineStart, i - lineStart, &provider));
    6789               0 :       aData->currentLine = NSCoordSaturatingAdd(aData->currentLine, width);
    6790                 : 
    6791               0 :       if (collapseWhitespace) {
    6792               0 :         PRUint32 trimStart = GetEndOfTrimmedText(frag, textStyle, lineStart, i, &iter);
    6793               0 :         if (trimStart == start) {
    6794                 :           // This is *all* trimmable whitespace, so whatever trailingWhitespace
    6795                 :           // we saw previously is still trailing...
    6796               0 :           aData->trailingWhitespace += width;
    6797                 :         } else {
    6798                 :           // Some non-whitespace so the old trailingWhitespace is no longer trailing
    6799                 :           aData->trailingWhitespace =
    6800               0 :             NSToCoordCeilClamped(textRun->GetAdvanceWidth(trimStart, i - trimStart, &provider));
    6801                 :         }
    6802                 :       } else {
    6803               0 :         aData->trailingWhitespace = 0;
    6804                 :       }
    6805                 :     }
    6806                 : 
    6807               0 :     if (preformattedTab) {
    6808                 :       PropertyProvider::Spacing spacing;
    6809               0 :       provider.GetSpacing(i, 1, &spacing);
    6810               0 :       aData->currentLine += nscoord(spacing.mBefore);
    6811                 :       gfxFloat afterTab =
    6812                 :         AdvanceToNextTab(aData->currentLine, this,
    6813               0 :                          textRun, &tabWidth);
    6814               0 :       aData->currentLine = nscoord(afterTab + spacing.mAfter);
    6815               0 :       lineStart = i + 1;
    6816               0 :     } else if (preformattedNewline) {
    6817               0 :       aData->ForceBreak(aRenderingContext);
    6818               0 :       lineStart = i;
    6819                 :     }
    6820                 :   }
    6821                 : 
    6822                 :   // Check if we have collapsible whitespace at the end
    6823               0 :   if (start < flowEndInTextRun) {
    6824                 :     aData->skipWhitespace =
    6825                 :       IsTrimmableSpace(provider.GetFragment(),
    6826                 :                        iter.ConvertSkippedToOriginal(flowEndInTextRun - 1),
    6827               0 :                        textStyle);
    6828                 :   }
    6829                 : }
    6830                 : 
    6831                 : // XXX Need to do something here to avoid incremental reflow bugs due to
    6832                 : // first-line and first-letter changing pref-width
    6833                 : /* virtual */ void
    6834               0 : nsTextFrame::AddInlinePrefWidth(nsRenderingContext *aRenderingContext,
    6835                 :                                 nsIFrame::InlinePrefWidthData *aData)
    6836                 : {
    6837                 :   float inflation =
    6838               0 :     nsLayoutUtils::FontSizeInflationFor(this, nsLayoutUtils::eInReflow);
    6839               0 :   TextRunType trtype = (inflation == 1.0f) ? eNotInflated : eInflated;
    6840                 : 
    6841                 :   nsTextFrame* f;
    6842               0 :   gfxTextRun* lastTextRun = nsnull;
    6843                 :   // nsContinuingTextFrame does nothing for AddInlineMinWidth; all text frames
    6844                 :   // in the flow are handled right here.
    6845               0 :   for (f = this; f; f = static_cast<nsTextFrame*>(f->GetNextContinuation())) {
    6846                 :     // f->GetTextRun(nsTextFrame::eNotInflated) could be null if we
    6847                 :     // haven't set up textruns yet for f.  Except in OOM situations,
    6848                 :     // lastTextRun will only be null for the first text frame.
    6849               0 :     if (f == this || f->GetTextRun(trtype) != lastTextRun) {
    6850                 :       nsIFrame* lc;
    6851               0 :       if (aData->lineContainer &&
    6852               0 :           aData->lineContainer != (lc = FindLineContainer(f))) {
    6853               0 :         NS_ASSERTION(f != this, "wrong InlinePrefWidthData container"
    6854                 :                                 " for first continuation");
    6855               0 :         aData->line = nsnull;
    6856               0 :         aData->lineContainer = lc;
    6857                 :       }
    6858                 : 
    6859                 :       // This will process all the text frames that share the same textrun as f.
    6860                 :       f->AddInlinePrefWidthForFlow(aRenderingContext, aData,
    6861               0 :                                    inflation, trtype);
    6862               0 :       lastTextRun = f->GetTextRun(trtype);
    6863                 :     }
    6864                 :   }
    6865               0 : }
    6866                 : 
    6867                 : /* virtual */ nsSize
    6868               0 : nsTextFrame::ComputeSize(nsRenderingContext *aRenderingContext,
    6869                 :                          nsSize aCBSize, nscoord aAvailableWidth,
    6870                 :                          nsSize aMargin, nsSize aBorder, nsSize aPadding,
    6871                 :                          bool aShrinkWrap)
    6872                 : {
    6873                 :   // Inlines and text don't compute size before reflow.
    6874               0 :   return nsSize(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
    6875                 : }
    6876                 : 
    6877                 : static nsRect
    6878               0 : RoundOut(const gfxRect& aRect)
    6879                 : {
    6880               0 :   nsRect r;
    6881               0 :   r.x = NSToCoordFloor(aRect.X());
    6882               0 :   r.y = NSToCoordFloor(aRect.Y());
    6883               0 :   r.width = NSToCoordCeil(aRect.XMost()) - r.x;
    6884               0 :   r.height = NSToCoordCeil(aRect.YMost()) - r.y;
    6885                 :   return r;
    6886                 : }
    6887                 : 
    6888                 : nsRect
    6889               0 : nsTextFrame::ComputeTightBounds(gfxContext* aContext) const
    6890                 : {
    6891               0 :   if (GetStyleContext()->HasTextDecorationLines() ||
    6892               0 :       (GetStateBits() & TEXT_HYPHEN_BREAK)) {
    6893                 :     // This is conservative, but OK.
    6894               0 :     return GetVisualOverflowRect();
    6895                 :   }
    6896                 : 
    6897                 :   gfxSkipCharsIterator iter =
    6898               0 :     const_cast<nsTextFrame*>(this)->EnsureTextRun(nsTextFrame::eInflated);
    6899               0 :   if (!mTextRun)
    6900               0 :     return nsRect(0, 0, 0, 0);
    6901                 : 
    6902                 :   PropertyProvider provider(const_cast<nsTextFrame*>(this), iter,
    6903               0 :                             nsTextFrame::eInflated);
    6904                 :   // Trim trailing whitespace
    6905               0 :   provider.InitializeForDisplay(true);
    6906                 : 
    6907                 :   gfxTextRun::Metrics metrics =
    6908               0 :         mTextRun->MeasureText(provider.GetStart().GetSkippedOffset(),
    6909                 :                               ComputeTransformedLength(provider),
    6910                 :                               gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS,
    6911               0 :                               aContext, &provider);
    6912                 :   // mAscent should be the same as metrics.mAscent, but it's what we use to
    6913                 :   // paint so that's the one we'll use.
    6914               0 :   return RoundOut(metrics.mBoundingBox) + nsPoint(0, mAscent);
    6915                 : }
    6916                 : 
    6917                 : static bool
    6918               0 : HasSoftHyphenBefore(const nsTextFragment* aFrag, gfxTextRun* aTextRun,
    6919                 :                     PRInt32 aStartOffset, const gfxSkipCharsIterator& aIter)
    6920                 : {
    6921               0 :   if (aIter.GetSkippedOffset() < aTextRun->GetLength() &&
    6922               0 :       aTextRun->CanHyphenateBefore(aIter.GetSkippedOffset())) {
    6923               0 :     return true;
    6924                 :   }
    6925               0 :   if (!(aTextRun->GetFlags() & nsTextFrameUtils::TEXT_HAS_SHY))
    6926               0 :     return false;
    6927               0 :   gfxSkipCharsIterator iter = aIter;
    6928               0 :   while (iter.GetOriginalOffset() > aStartOffset) {
    6929               0 :     iter.AdvanceOriginal(-1);
    6930               0 :     if (!iter.IsOriginalCharSkipped())
    6931               0 :       break;
    6932               0 :     if (aFrag->CharAt(iter.GetOriginalOffset()) == CH_SHY)
    6933               0 :       return true;
    6934                 :   }
    6935               0 :   return false;
    6936                 : }
    6937                 : 
    6938                 : static void
    6939               0 : RemoveInFlows(nsIFrame* aFrame, nsIFrame* aFirstToNotRemove)
    6940                 : {
    6941               0 :   NS_PRECONDITION(aFrame != aFirstToNotRemove, "This will go very badly");
    6942                 :   // We have to be careful here, because some RemoveFrame implementations
    6943                 :   // remove and destroy not only the passed-in frame but also all its following
    6944                 :   // in-flows (and sometimes all its following continuations in general).  So
    6945                 :   // we remove |f| and everything up to but not including firstToNotRemove from
    6946                 :   // the flow first, to make sure that only the things we want destroyed are
    6947                 :   // destroyed.
    6948                 : 
    6949                 :   // This sadly duplicates some of the logic from
    6950                 :   // nsSplittableFrame::RemoveFromFlow.  We can get away with not duplicating
    6951                 :   // all of it, because we know that the prev-continuation links of
    6952                 :   // firstToNotRemove and f are fluid, and non-null.
    6953               0 :   NS_ASSERTION(aFirstToNotRemove->GetPrevContinuation() ==
    6954                 :                aFirstToNotRemove->GetPrevInFlow() &&
    6955                 :                aFirstToNotRemove->GetPrevInFlow() != nsnull,
    6956                 :                "aFirstToNotRemove should have a fluid prev continuation");
    6957               0 :   NS_ASSERTION(aFrame->GetPrevContinuation() ==
    6958                 :                aFrame->GetPrevInFlow() &&
    6959                 :                aFrame->GetPrevInFlow() != nsnull,
    6960                 :                "aFrame should have a fluid prev continuation");
    6961                 :   
    6962               0 :   nsIFrame* prevContinuation = aFrame->GetPrevContinuation();
    6963               0 :   nsIFrame* lastRemoved = aFirstToNotRemove->GetPrevContinuation();
    6964                 : 
    6965               0 :   prevContinuation->SetNextInFlow(aFirstToNotRemove);
    6966               0 :   aFirstToNotRemove->SetPrevInFlow(prevContinuation);
    6967                 : 
    6968               0 :   aFrame->SetPrevInFlow(nsnull);
    6969               0 :   lastRemoved->SetNextInFlow(nsnull);
    6970                 : 
    6971               0 :   nsIFrame *parent = aFrame->GetParent();
    6972               0 :   nsBlockFrame *parentBlock = nsLayoutUtils::GetAsBlock(parent);
    6973               0 :   if (parentBlock) {
    6974                 :     // Manually call DoRemoveFrame so we can tell it that we're
    6975                 :     // removing empty frames; this will keep it from blowing away
    6976                 :     // text runs.
    6977               0 :     parentBlock->DoRemoveFrame(aFrame, nsBlockFrame::FRAMES_ARE_EMPTY);
    6978                 :   } else {
    6979                 :     // Just remove it normally; use the nextBidi list to avoid
    6980                 :     // posting new reflows.
    6981               0 :     parent->RemoveFrame(nsIFrame::kNoReflowPrincipalList, aFrame);
    6982                 :   }
    6983               0 : }
    6984                 : 
    6985                 : void
    6986               0 : nsTextFrame::SetLength(PRInt32 aLength, nsLineLayout* aLineLayout,
    6987                 :                        PRUint32 aSetLengthFlags)
    6988                 : {
    6989               0 :   mContentLengthHint = aLength;
    6990               0 :   PRInt32 end = GetContentOffset() + aLength;
    6991               0 :   nsTextFrame* f = static_cast<nsTextFrame*>(GetNextInFlow());
    6992               0 :   if (!f)
    6993               0 :     return;
    6994                 : 
    6995                 :   // If our end offset is moving, then even if frames are not being pushed or
    6996                 :   // pulled, content is moving to or from the next line and the next line
    6997                 :   // must be reflowed.
    6998                 :   // If the next-continuation is dirty, then we should dirty the next line now
    6999                 :   // because we may have skipped doing it if we dirtied it in
    7000                 :   // CharacterDataChanged. This is ugly but teaching FrameNeedsReflow
    7001                 :   // and ChildIsDirty to handle a range of frames would be worse.
    7002               0 :   if (aLineLayout &&
    7003               0 :       (end != f->mContentOffset || (f->GetStateBits() & NS_FRAME_IS_DIRTY))) {
    7004               0 :     aLineLayout->SetDirtyNextLine();
    7005                 :   }
    7006                 : 
    7007               0 :   if (end < f->mContentOffset) {
    7008                 :     // Our frame is shrinking. Give the text to our next in flow.
    7009               0 :     if (aLineLayout &&
    7010               0 :         GetStyleText()->WhiteSpaceIsSignificant() &&
    7011               0 :         HasTerminalNewline() &&
    7012               0 :         GetParent()->GetType() != nsGkAtoms::letterFrame &&
    7013                 :         (aSetLengthFlags & ALLOW_FRAME_CREATION_AND_DESTRUCTION)) {
    7014                 :       // Whatever text we hand to our next-in-flow will end up in a frame all of
    7015                 :       // its own, since it ends in a forced linebreak.  Might as well just put
    7016                 :       // it in a separate frame now.  This is important to prevent text run
    7017                 :       // churn; if we did not do that, then we'd likely end up rebuilding
    7018                 :       // textruns for all our following continuations.
    7019                 :       // We skip this optimization when the parent is a first-letter frame
    7020                 :       // because it doesn't deal well with more than one child frame.
    7021                 :       // We also skip this optimization if we were called during bidi
    7022                 :       // resolution, so as not to create a new frame which doesn't appear in
    7023                 :       // the bidi resolver's list of frames
    7024               0 :       nsPresContext* presContext = PresContext();
    7025                 :       nsIFrame* newFrame;
    7026                 :       nsresult rv = presContext->PresShell()->FrameConstructor()->
    7027               0 :         CreateContinuingFrame(presContext, this, GetParent(), &newFrame);
    7028               0 :       if (NS_SUCCEEDED(rv)) {
    7029               0 :         nsTextFrame* next = static_cast<nsTextFrame*>(newFrame);
    7030               0 :         nsFrameList temp(next, next);
    7031               0 :         GetParent()->InsertFrames(kNoReflowPrincipalList, this, temp);
    7032               0 :         f = next;
    7033                 :       }
    7034                 :     }
    7035                 : 
    7036               0 :     f->mContentOffset = end;
    7037               0 :     if (f->GetTextRun(nsTextFrame::eInflated) != mTextRun) {
    7038               0 :       ClearTextRuns();
    7039               0 :       f->ClearTextRuns();
    7040                 :     }
    7041               0 :     return;
    7042                 :   }
    7043                 :   // Our frame is growing. Take text from our in-flow(s).
    7044                 :   // We can take text from frames in lines beyond just the next line.
    7045                 :   // We don't dirty those lines. That's OK, because when we reflow
    7046                 :   // our empty next-in-flow, it will take text from its next-in-flow and
    7047                 :   // dirty that line.
    7048                 : 
    7049                 :   // Note that in the process we may end up removing some frames from
    7050                 :   // the flow if they end up empty.
    7051               0 :   nsIFrame *framesToRemove = nsnull;
    7052               0 :   while (f && f->mContentOffset < end) {
    7053               0 :     f->mContentOffset = end;
    7054               0 :     if (f->GetTextRun(nsTextFrame::eInflated) != mTextRun) {
    7055               0 :       ClearTextRuns();
    7056               0 :       f->ClearTextRuns();
    7057                 :     }
    7058               0 :     nsTextFrame* next = static_cast<nsTextFrame*>(f->GetNextInFlow());
    7059                 :     // Note: the "f->GetNextSibling() == next" check below is to restrict
    7060                 :     // this optimization to the case where they are on the same child list.
    7061                 :     // Otherwise we might remove the only child of a nsFirstLetterFrame
    7062                 :     // for example and it can't handle that.  See bug 597627 for details.
    7063               0 :     if (next && next->mContentOffset <= end && f->GetNextSibling() == next &&
    7064                 :         (aSetLengthFlags & ALLOW_FRAME_CREATION_AND_DESTRUCTION)) {
    7065                 :       // |f| is now empty.  We may as well remove it, instead of copying all
    7066                 :       // the text from |next| into it instead; the latter leads to use
    7067                 :       // rebuilding textruns for all following continuations.
    7068                 :       // We skip this optimization if we were called during bidi resolution,
    7069                 :       // since the bidi resolver may try to handle the destroyed frame later
    7070                 :       // and crash
    7071               0 :       if (!framesToRemove) {
    7072                 :         // Remember that we have to remove this frame.
    7073               0 :         framesToRemove = f;
    7074                 :       }
    7075                 : 
    7076                 :       // Important: if |f| has the same style context as its prev continuation,
    7077                 :       // mark it accordingly so we can skip clearing textruns as needed.  Note
    7078                 :       // that at this point f always has a prev continuation.
    7079               0 :       if (f->GetStyleContext() == f->GetPrevContinuation()->GetStyleContext()) {
    7080               0 :         f->AddStateBits(TEXT_STYLE_MATCHES_PREV_CONTINUATION);
    7081                 :       }
    7082               0 :     } else if (framesToRemove) {
    7083               0 :       RemoveInFlows(framesToRemove, f);
    7084               0 :       framesToRemove = nsnull;
    7085                 :     }
    7086               0 :     f = next;
    7087                 :   }
    7088               0 :   NS_POSTCONDITION(!framesToRemove || (f && f->mContentOffset == end),
    7089                 :                    "How did we exit the loop if we null out framesToRemove if "
    7090                 :                    "!next || next->mContentOffset > end ?");
    7091               0 :   if (framesToRemove) {
    7092                 :     // We are guaranteed that we exited the loop with f not null, per the
    7093                 :     // postcondition above
    7094               0 :     RemoveInFlows(framesToRemove, f);
    7095                 :   }
    7096                 : 
    7097                 : #ifdef DEBUG
    7098               0 :   f = this;
    7099               0 :   PRInt32 iterations = 0;
    7100               0 :   while (f && iterations < 10) {
    7101               0 :     f->GetContentLength(); // Assert if negative length
    7102               0 :     f = static_cast<nsTextFrame*>(f->GetNextContinuation());
    7103               0 :     ++iterations;
    7104                 :   }
    7105               0 :   f = this;
    7106               0 :   iterations = 0;
    7107               0 :   while (f && iterations < 10) {
    7108               0 :     f->GetContentLength(); // Assert if negative length
    7109               0 :     f = static_cast<nsTextFrame*>(f->GetPrevContinuation());
    7110               0 :     ++iterations;
    7111                 :   }
    7112                 : #endif
    7113                 : }
    7114                 : 
    7115                 : bool
    7116               0 : nsTextFrame::IsFloatingFirstLetterChild() const
    7117                 : {
    7118               0 :   if (!(GetStateBits() & TEXT_FIRST_LETTER))
    7119               0 :     return false;
    7120               0 :   nsIFrame* frame = GetParent();
    7121               0 :   if (!frame || frame->GetType() != nsGkAtoms::letterFrame)
    7122               0 :     return false;
    7123               0 :   return frame->GetStyleDisplay()->IsFloating();
    7124                 : }
    7125                 : 
    7126                 : struct NewlineProperty {
    7127                 :   PRInt32 mStartOffset;
    7128                 :   // The offset of the first \n after mStartOffset, or -1 if there is none
    7129                 :   PRInt32 mNewlineOffset;
    7130                 : 
    7131               0 :   static void Destroy(void* aObject, nsIAtom* aPropertyName,
    7132                 :                       void* aPropertyValue, void* aData)
    7133                 :   {
    7134                 :     delete static_cast<NewlineProperty*>(aPropertyValue);
    7135               0 :   }
    7136                 : };
    7137                 : 
    7138                 : NS_IMETHODIMP
    7139               0 : nsTextFrame::Reflow(nsPresContext*           aPresContext,
    7140                 :                     nsHTMLReflowMetrics&     aMetrics,
    7141                 :                     const nsHTMLReflowState& aReflowState,
    7142                 :                     nsReflowStatus&          aStatus)
    7143                 : {
    7144               0 :   DO_GLOBAL_REFLOW_COUNT("nsTextFrame");
    7145               0 :   DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
    7146                 : 
    7147                 :   // XXX If there's no line layout, we shouldn't even have created this
    7148                 :   // frame. This may happen if, for example, this is text inside a table
    7149                 :   // but not inside a cell. For now, just don't reflow.
    7150               0 :   if (!aReflowState.mLineLayout) {
    7151               0 :     ClearMetrics(aMetrics);
    7152               0 :     aStatus = NS_FRAME_COMPLETE;
    7153               0 :     return NS_OK;
    7154                 :   }
    7155                 : 
    7156                 :   ReflowText(*aReflowState.mLineLayout, aReflowState.availableWidth,
    7157                 :              aReflowState.rendContext, aReflowState.mFlags.mBlinks,
    7158               0 :              aMetrics, aStatus);
    7159                 : 
    7160               0 :   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics);
    7161               0 :   return NS_OK;
    7162                 : }
    7163                 : 
    7164                 : #ifdef ACCESSIBILITY
    7165                 : /**
    7166                 :  * Notifies accessibility about text reflow. Used by nsTextFrame::ReflowText.
    7167                 :  */
    7168                 : class NS_STACK_CLASS ReflowTextA11yNotifier
    7169                 : {
    7170                 : public:
    7171               0 :   ReflowTextA11yNotifier(nsPresContext* aPresContext, nsIContent* aContent) :
    7172               0 :     mContent(aContent), mPresContext(aPresContext)
    7173                 :   {
    7174               0 :   }
    7175               0 :   ~ReflowTextA11yNotifier()
    7176                 :   {
    7177               0 :     nsAccessibilityService* accService = nsIPresShell::AccService();
    7178               0 :     if (accService) {
    7179               0 :       accService->UpdateText(mPresContext->PresShell(), mContent);
    7180                 :     }
    7181               0 :   }
    7182                 : private:
    7183                 :   ReflowTextA11yNotifier();
    7184                 :   ReflowTextA11yNotifier(const ReflowTextA11yNotifier&);
    7185                 :   ReflowTextA11yNotifier& operator =(const ReflowTextA11yNotifier&);
    7186                 : 
    7187                 :   nsIContent* mContent;
    7188                 :   nsPresContext* mPresContext;
    7189                 : };
    7190                 : #endif
    7191                 : 
    7192                 : void
    7193               0 : nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
    7194                 :                         nsRenderingContext* aRenderingContext,
    7195                 :                         bool aShouldBlink,
    7196                 :                         nsHTMLReflowMetrics& aMetrics,
    7197                 :                         nsReflowStatus& aStatus)
    7198                 : {
    7199                 : #ifdef NOISY_REFLOW
    7200                 :   ListTag(stdout);
    7201                 :   printf(": BeginReflow: availableWidth=%d\n", aAvailableWidth);
    7202                 : #endif
    7203                 : 
    7204               0 :   nsPresContext* presContext = PresContext();
    7205                 : 
    7206                 : #ifdef ACCESSIBILITY
    7207                 :   // Schedule the update of accessible tree since rendered text might be changed.
    7208               0 :   ReflowTextA11yNotifier(presContext, mContent);
    7209                 : #endif
    7210                 : 
    7211                 :   /////////////////////////////////////////////////////////////////////
    7212                 :   // Set up flags and clear out state
    7213                 :   /////////////////////////////////////////////////////////////////////
    7214                 : 
    7215                 :   // Clear out the reflow state flags in mState (without destroying
    7216                 :   // the TEXT_BLINK_ON bit). We also clear the whitespace flags because this
    7217                 :   // can change whether the frame maps whitespace-only text or not.
    7218               0 :   RemoveStateBits(TEXT_REFLOW_FLAGS | TEXT_WHITESPACE_FLAGS);
    7219                 : 
    7220                 :   // Temporarily map all possible content while we construct our new textrun.
    7221                 :   // so that when doing reflow our styles prevail over any part of the
    7222                 :   // textrun we look at. Note that next-in-flows may be mapping the same
    7223                 :   // content; gfxTextRun construction logic will ensure that we take priority.
    7224               0 :   PRInt32 maxContentLength = GetInFlowContentLength();
    7225                 : 
    7226                 :   // We don't need to reflow if there is no content.
    7227               0 :   if (!maxContentLength) {
    7228               0 :     ClearMetrics(aMetrics);
    7229               0 :     aStatus = NS_FRAME_COMPLETE;
    7230               0 :     return;
    7231                 :   }
    7232                 : 
    7233               0 :   if (aShouldBlink) {
    7234               0 :     if (0 == (mState & TEXT_BLINK_ON)) {
    7235               0 :       mState |= TEXT_BLINK_ON;
    7236               0 :       nsBlinkTimer::AddBlinkFrame(presContext, this);
    7237                 :     }
    7238                 :   }
    7239                 :   else {
    7240               0 :     if (0 != (mState & TEXT_BLINK_ON)) {
    7241               0 :       mState &= ~TEXT_BLINK_ON;
    7242               0 :       nsBlinkTimer::RemoveBlinkFrame(this);
    7243                 :     }
    7244                 :   }
    7245                 : 
    7246                 : #ifdef NOISY_BIDI
    7247                 :     printf("Reflowed textframe\n");
    7248                 : #endif
    7249                 : 
    7250               0 :   const nsStyleText* textStyle = GetStyleText();
    7251                 : 
    7252               0 :   bool atStartOfLine = aLineLayout.LineAtStart();
    7253               0 :   if (atStartOfLine) {
    7254               0 :     AddStateBits(TEXT_START_OF_LINE);
    7255                 :   }
    7256                 : 
    7257                 :   PRUint32 flowEndInTextRun;
    7258               0 :   nsIFrame* lineContainer = aLineLayout.GetLineContainerFrame();
    7259               0 :   gfxContext* ctx = aRenderingContext->ThebesContext();
    7260               0 :   const nsTextFragment* frag = mContent->GetText();
    7261                 : 
    7262                 :   // DOM offsets of the text range we need to measure, after trimming
    7263                 :   // whitespace, restricting to first-letter, and restricting preformatted text
    7264                 :   // to nearest newline
    7265               0 :   PRInt32 length = maxContentLength;
    7266               0 :   PRInt32 offset = GetContentOffset();
    7267                 : 
    7268                 :   // Restrict preformatted text to the nearest newline
    7269               0 :   PRInt32 newLineOffset = -1; // this will be -1 or a content offset
    7270               0 :   PRInt32 contentNewLineOffset = -1;
    7271                 :   // Pointer to the nsGkAtoms::newline set on this frame's element
    7272               0 :   NewlineProperty* cachedNewlineOffset = nsnull;
    7273               0 :   if (textStyle->NewlineIsSignificant()) {
    7274                 :     cachedNewlineOffset =
    7275               0 :       static_cast<NewlineProperty*>(mContent->GetProperty(nsGkAtoms::newline));
    7276               0 :     if (cachedNewlineOffset && cachedNewlineOffset->mStartOffset <= offset &&
    7277                 :         (cachedNewlineOffset->mNewlineOffset == -1 ||
    7278                 :          cachedNewlineOffset->mNewlineOffset >= offset)) {
    7279               0 :       contentNewLineOffset = cachedNewlineOffset->mNewlineOffset;
    7280                 :     } else {
    7281                 :       contentNewLineOffset = FindChar(frag, offset, 
    7282               0 :                                       mContent->TextLength() - offset, '\n');
    7283                 :     }
    7284               0 :     if (contentNewLineOffset < offset + length) {
    7285                 :       /*
    7286                 :         The new line offset could be outside this frame if the frame has been
    7287                 :         split by bidi resolution. In that case we won't use it in this reflow
    7288                 :         (newLineOffset will remain -1), but we will still cache it in mContent
    7289                 :       */
    7290               0 :       newLineOffset = contentNewLineOffset;
    7291                 :     }
    7292               0 :     if (newLineOffset >= 0) {
    7293               0 :       length = newLineOffset + 1 - offset;
    7294                 :     }
    7295                 :   }
    7296               0 :   if (atStartOfLine && !textStyle->WhiteSpaceIsSignificant()) {
    7297                 :     // Skip leading whitespace. Make sure we don't skip a 'pre-line'
    7298                 :     // newline if there is one.
    7299               0 :     PRInt32 skipLength = newLineOffset >= 0 ? length - 1 : length;
    7300                 :     PRInt32 whitespaceCount =
    7301               0 :       GetTrimmableWhitespaceCount(frag, offset, skipLength, 1);
    7302               0 :     offset += whitespaceCount;
    7303               0 :     length -= whitespaceCount;
    7304                 :   }
    7305                 : 
    7306               0 :   bool completedFirstLetter = false;
    7307                 :   // Layout dependent styles are a problem because we need to reconstruct
    7308                 :   // the gfxTextRun based on our layout.
    7309               0 :   if (aLineLayout.GetInFirstLetter() || aLineLayout.GetInFirstLine()) {
    7310                 :     SetLength(maxContentLength, &aLineLayout,
    7311               0 :               ALLOW_FRAME_CREATION_AND_DESTRUCTION);
    7312                 : 
    7313               0 :     if (aLineLayout.GetInFirstLetter()) {
    7314                 :       // floating first-letter boundaries are significant in textrun
    7315                 :       // construction, so clear the textrun out every time we hit a first-letter
    7316                 :       // and have changed our length (which controls the first-letter boundary)
    7317               0 :       ClearTextRuns();
    7318                 :       // Find the length of the first-letter. We need a textrun for this.
    7319                 :       // REVIEW: maybe-bogus inflation should be ok (fixed below)
    7320                 :       gfxSkipCharsIterator iter =
    7321                 :         EnsureTextRun(nsTextFrame::eInflated, GetFontSizeInflation(),  ctx,
    7322               0 :                       lineContainer, aLineLayout.GetLine(),
    7323               0 :                       &flowEndInTextRun);
    7324                 : 
    7325               0 :       if (mTextRun) {
    7326               0 :         PRInt32 firstLetterLength = length;
    7327               0 :         if (aLineLayout.GetFirstLetterStyleOK()) {
    7328                 :           completedFirstLetter =
    7329               0 :             FindFirstLetterRange(frag, mTextRun, offset, iter, &firstLetterLength);
    7330               0 :           if (newLineOffset >= 0) {
    7331                 :             // Don't allow a preformatted newline to be part of a first-letter.
    7332               0 :             firstLetterLength = NS_MIN(firstLetterLength, length - 1);
    7333               0 :             if (length == 1) {
    7334                 :               // There is no text to be consumed by the first-letter before the
    7335                 :               // preformatted newline. Note that the first letter is therefore
    7336                 :               // complete (FindFirstLetterRange will have returned false).
    7337               0 :               completedFirstLetter = true;
    7338                 :             }
    7339                 :           }
    7340                 :         } else {
    7341                 :           // We're in a first-letter frame's first in flow, so if there
    7342                 :           // was a first-letter, we'd be it. However, for one reason
    7343                 :           // or another (e.g., preformatted line break before this text),
    7344                 :           // we're not actually supposed to have first-letter style. So
    7345                 :           // just make a zero-length first-letter.
    7346               0 :           firstLetterLength = 0;
    7347               0 :           completedFirstLetter = true;
    7348                 :         }
    7349               0 :         length = firstLetterLength;
    7350               0 :         if (length) {
    7351               0 :           AddStateBits(TEXT_FIRST_LETTER);
    7352                 :         }
    7353                 :         // Change this frame's length to the first-letter length right now
    7354                 :         // so that when we rebuild the textrun it will be built with the
    7355                 :         // right first-letter boundary
    7356               0 :         SetLength(offset + length - GetContentOffset(), &aLineLayout,
    7357               0 :                   ALLOW_FRAME_CREATION_AND_DESTRUCTION);
    7358                 :         // Ensure that the textrun will be rebuilt
    7359               0 :         ClearTextRuns();
    7360                 :       }
    7361                 :     } 
    7362                 :   }
    7363                 : 
    7364                 :   float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this,
    7365               0 :                               nsLayoutUtils::eInReflow);
    7366                 : 
    7367               0 :   if (fontSizeInflation != GetFontSizeInflation()) {
    7368                 :     // FIXME: Ideally, if we already have a text run, we'd move it to be
    7369                 :     // the uninflated text run.
    7370               0 :     ClearTextRun(nsnull, nsTextFrame::eInflated);
    7371                 :   }
    7372                 : 
    7373                 :   gfxSkipCharsIterator iter =
    7374                 :     EnsureTextRun(nsTextFrame::eInflated, fontSizeInflation, ctx,
    7375               0 :                   lineContainer, aLineLayout.GetLine(), &flowEndInTextRun);
    7376                 : 
    7377               0 :   NS_ABORT_IF_FALSE(GetFontSizeInflation() == fontSizeInflation,
    7378                 :                     "EnsureTextRun should have set font size inflation");
    7379                 : 
    7380               0 :   if (mTextRun && iter.GetOriginalEnd() < offset + length) {
    7381                 :     // The textrun does not map enough text for this frame. This can happen
    7382                 :     // when the textrun was ended in the middle of a text node because a
    7383                 :     // preformatted newline was encountered, and prev-in-flow frames have
    7384                 :     // consumed all the text of the textrun. We need a new textrun.
    7385               0 :     ClearTextRuns();
    7386                 :     iter = EnsureTextRun(nsTextFrame::eInflated, fontSizeInflation, ctx,
    7387               0 :                          lineContainer, aLineLayout.GetLine(),
    7388               0 :                          &flowEndInTextRun);
    7389                 :   }
    7390                 : 
    7391               0 :   if (!mTextRun) {
    7392               0 :     ClearMetrics(aMetrics);
    7393               0 :     aStatus = NS_FRAME_COMPLETE;
    7394               0 :     return;
    7395                 :   }
    7396                 : 
    7397               0 :   NS_ASSERTION(gfxSkipCharsIterator(iter).ConvertOriginalToSkipped(offset + length)
    7398                 :                     <= mTextRun->GetLength(),
    7399                 :                "Text run does not map enough text for our reflow");
    7400                 : 
    7401                 :   /////////////////////////////////////////////////////////////////////
    7402                 :   // See how much text should belong to this text frame, and measure it
    7403                 :   /////////////////////////////////////////////////////////////////////
    7404                 :   
    7405               0 :   iter.SetOriginalOffset(offset);
    7406               0 :   nscoord xOffsetForTabs = (mTextRun->GetFlags() & nsTextFrameUtils::TEXT_HAS_TAB) ?
    7407               0 :     (aLineLayout.GetCurrentFrameXDistanceFromBlock() -
    7408                 :        lineContainer->GetUsedBorderAndPadding().left)
    7409               0 :     : -1;
    7410                 :   PropertyProvider provider(mTextRun, textStyle, frag, this, iter, length,
    7411               0 :       lineContainer, xOffsetForTabs, nsTextFrame::eInflated);
    7412                 : 
    7413               0 :   PRUint32 transformedOffset = provider.GetStart().GetSkippedOffset();
    7414                 : 
    7415                 :   // The metrics for the text go in here
    7416               0 :   gfxTextRun::Metrics textMetrics;
    7417               0 :   gfxFont::BoundingBoxType boundingBoxType = IsFloatingFirstLetterChild() ?
    7418                 :                                                gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS :
    7419               0 :                                                gfxFont::LOOSE_INK_EXTENTS;
    7420               0 :   NS_ASSERTION(!(NS_REFLOW_CALC_BOUNDING_METRICS & aMetrics.mFlags),
    7421                 :                "We shouldn't be passed NS_REFLOW_CALC_BOUNDING_METRICS anymore");
    7422                 : 
    7423               0 :   PRInt32 limitLength = length;
    7424               0 :   PRInt32 forceBreak = aLineLayout.GetForcedBreakPosition(mContent);
    7425               0 :   bool forceBreakAfter = false;
    7426               0 :   if (forceBreak >= offset + length) {
    7427               0 :     forceBreakAfter = forceBreak == offset + length;
    7428                 :     // The break is not within the text considered for this textframe.
    7429               0 :     forceBreak = -1;
    7430                 :   }
    7431               0 :   if (forceBreak >= 0) {
    7432               0 :     limitLength = forceBreak - offset;
    7433               0 :     NS_ASSERTION(limitLength >= 0, "Weird break found!");
    7434                 :   }
    7435                 :   // This is the heart of text reflow right here! We don't know where
    7436                 :   // to break, so we need to see how much text fits in the available width.
    7437                 :   PRUint32 transformedLength;
    7438               0 :   if (offset + limitLength >= PRInt32(frag->GetLength())) {
    7439               0 :     NS_ASSERTION(offset + limitLength == PRInt32(frag->GetLength()),
    7440                 :                  "Content offset/length out of bounds");
    7441               0 :     NS_ASSERTION(flowEndInTextRun >= transformedOffset,
    7442                 :                  "Negative flow length?");
    7443               0 :     transformedLength = flowEndInTextRun - transformedOffset;
    7444                 :   } else {
    7445                 :     // we're not looking at all the content, so we need to compute the
    7446                 :     // length of the transformed substring we're looking at
    7447               0 :     gfxSkipCharsIterator iter(provider.GetStart());
    7448               0 :     iter.SetOriginalOffset(offset + limitLength);
    7449               0 :     transformedLength = iter.GetSkippedOffset() - transformedOffset;
    7450                 :   }
    7451               0 :   PRUint32 transformedLastBreak = 0;
    7452                 :   bool usedHyphenation;
    7453               0 :   gfxFloat trimmedWidth = 0;
    7454               0 :   gfxFloat availWidth = aAvailableWidth;
    7455               0 :   bool canTrimTrailingWhitespace = !textStyle->WhiteSpaceIsSignificant();
    7456                 :   PRInt32 unusedOffset;  
    7457                 :   gfxBreakPriority breakPriority;
    7458               0 :   aLineLayout.GetLastOptionalBreakPosition(&unusedOffset, &breakPriority);
    7459                 :   PRUint32 transformedCharsFit =
    7460                 :     mTextRun->BreakAndMeasureText(transformedOffset, transformedLength,
    7461               0 :                                   (GetStateBits() & TEXT_START_OF_LINE) != 0,
    7462                 :                                   availWidth,
    7463               0 :                                   &provider, !aLineLayout.LineIsBreakable(),
    7464                 :                                   canTrimTrailingWhitespace ? &trimmedWidth : nsnull,
    7465                 :                                   &textMetrics, boundingBoxType, ctx,
    7466                 :                                   &usedHyphenation, &transformedLastBreak,
    7467               0 :                                   textStyle->WordCanWrap(), &breakPriority);
    7468               0 :   if (!length && !textMetrics.mAscent && !textMetrics.mDescent) {
    7469                 :     // If we're measuring a zero-length piece of text, update
    7470                 :     // the height manually.
    7471               0 :     nsFontMetrics* fm = provider.GetFontMetrics();
    7472               0 :     if (fm) {
    7473               0 :       textMetrics.mAscent = gfxFloat(fm->MaxAscent());
    7474               0 :       textMetrics.mDescent = gfxFloat(fm->MaxDescent());
    7475                 :     }
    7476                 :   }
    7477                 :   // The "end" iterator points to the first character after the string mapped
    7478                 :   // by this frame. Basically, its original-string offset is offset+charsFit
    7479                 :   // after we've computed charsFit.
    7480               0 :   gfxSkipCharsIterator end(provider.GetEndHint());
    7481               0 :   end.SetSkippedOffset(transformedOffset + transformedCharsFit);
    7482               0 :   PRInt32 charsFit = end.GetOriginalOffset() - offset;
    7483               0 :   if (offset + charsFit == newLineOffset) {
    7484                 :     // We broke before a trailing preformatted '\n'. The newline should
    7485                 :     // be assigned to this frame. Note that newLineOffset will be -1 if
    7486                 :     // there was no preformatted newline, so we wouldn't get here in that
    7487                 :     // case.
    7488               0 :     ++charsFit;
    7489                 :   }
    7490                 :   // That might have taken us beyond our assigned content range (because
    7491                 :   // we might have advanced over some skipped chars that extend outside
    7492                 :   // this frame), so get back in.
    7493               0 :   PRInt32 lastBreak = -1;
    7494               0 :   if (charsFit >= limitLength) {
    7495               0 :     charsFit = limitLength;
    7496               0 :     if (transformedLastBreak != PR_UINT32_MAX) {
    7497                 :       // lastBreak is needed.
    7498                 :       // This may set lastBreak greater than 'length', but that's OK
    7499               0 :       lastBreak = end.ConvertSkippedToOriginal(transformedOffset + transformedLastBreak);
    7500                 :     }
    7501               0 :     end.SetOriginalOffset(offset + charsFit);
    7502                 :     // If we were forced to fit, and the break position is after a soft hyphen,
    7503                 :     // note that this is a hyphenation break.
    7504               0 :     if ((forceBreak >= 0 || forceBreakAfter) &&
    7505               0 :         HasSoftHyphenBefore(frag, mTextRun, offset, end)) {
    7506               0 :       usedHyphenation = true;
    7507                 :     }
    7508                 :   }
    7509               0 :   if (usedHyphenation) {
    7510                 :     // Fix up metrics to include hyphen
    7511               0 :     AddHyphenToMetrics(this, mTextRun, &textMetrics, boundingBoxType, ctx);
    7512               0 :     AddStateBits(TEXT_HYPHEN_BREAK | TEXT_HAS_NONCOLLAPSED_CHARACTERS);
    7513                 :   }
    7514                 : 
    7515               0 :   gfxFloat trimmableWidth = 0;
    7516               0 :   bool brokeText = forceBreak >= 0 || transformedCharsFit < transformedLength;
    7517               0 :   if (canTrimTrailingWhitespace) {
    7518                 :     // Optimization: if we trimmed trailing whitespace, and we can be sure
    7519                 :     // this frame will be at the end of the line, then leave it trimmed off.
    7520                 :     // Otherwise we have to undo the trimming, in case we're not at the end of
    7521                 :     // the line. (If we actually do end up at the end of the line, we'll have
    7522                 :     // to trim it off again in TrimTrailingWhiteSpace, and we'd like to avoid
    7523                 :     // having to re-do it.)
    7524               0 :     if (brokeText) {
    7525                 :       // We're definitely going to break so our trailing whitespace should
    7526                 :       // definitely be timmed. Record that we've already done it.
    7527               0 :       AddStateBits(TEXT_TRIMMED_TRAILING_WHITESPACE);
    7528                 :     } else {
    7529                 :       // We might not be at the end of the line. (Note that even if this frame
    7530                 :       // ends in breakable whitespace, it might not be at the end of the line
    7531                 :       // because it might be followed by breakable, but preformatted, whitespace.)
    7532                 :       // Undo the trimming.
    7533               0 :       textMetrics.mAdvanceWidth += trimmedWidth;
    7534               0 :       trimmableWidth = trimmedWidth;
    7535               0 :       if (mTextRun->IsRightToLeft()) {
    7536                 :         // Space comes before text, so the bounding box is moved to the
    7537                 :         // right by trimmdWidth
    7538               0 :         textMetrics.mBoundingBox.MoveBy(gfxPoint(trimmedWidth, 0));
    7539                 :       }
    7540                 :     }
    7541                 :   }
    7542                 : 
    7543               0 :   if (!brokeText && lastBreak >= 0) {
    7544                 :     // Since everything fit and no break was forced,
    7545                 :     // record the last break opportunity
    7546               0 :     NS_ASSERTION(textMetrics.mAdvanceWidth - trimmableWidth <= aAvailableWidth,
    7547                 :                  "If the text doesn't fit, and we have a break opportunity, why didn't MeasureText use it?");
    7548               0 :     aLineLayout.NotifyOptionalBreakPosition(mContent, lastBreak, true, breakPriority);
    7549                 :   }
    7550                 : 
    7551               0 :   PRInt32 contentLength = offset + charsFit - GetContentOffset();
    7552                 : 
    7553                 :   /////////////////////////////////////////////////////////////////////
    7554                 :   // Compute output metrics
    7555                 :   /////////////////////////////////////////////////////////////////////
    7556                 : 
    7557                 :   // first-letter frames should use the tight bounding box metrics for ascent/descent
    7558                 :   // for good drop-cap effects
    7559               0 :   if (GetStateBits() & TEXT_FIRST_LETTER) {
    7560               0 :     textMetrics.mAscent = NS_MAX(gfxFloat(0.0), -textMetrics.mBoundingBox.Y());
    7561               0 :     textMetrics.mDescent = NS_MAX(gfxFloat(0.0), textMetrics.mBoundingBox.YMost());
    7562                 :   }
    7563                 : 
    7564                 :   // Setup metrics for caller
    7565                 :   // Disallow negative widths
    7566               0 :   aMetrics.width = NSToCoordCeil(NS_MAX(gfxFloat(0.0), textMetrics.mAdvanceWidth));
    7567                 : 
    7568               0 :   if (transformedCharsFit == 0 && !usedHyphenation) {
    7569               0 :     aMetrics.ascent = 0;
    7570               0 :     aMetrics.height = 0;
    7571               0 :   } else if (boundingBoxType != gfxFont::LOOSE_INK_EXTENTS) {
    7572                 :     // Use actual text metrics for floating first letter frame.
    7573               0 :     aMetrics.ascent = NSToCoordCeil(textMetrics.mAscent);
    7574               0 :     aMetrics.height = aMetrics.ascent + NSToCoordCeil(textMetrics.mDescent);
    7575                 :   } else {
    7576                 :     // Otherwise, ascent should contain the overline drawable area.
    7577                 :     // And also descent should contain the underline drawable area.
    7578                 :     // nsFontMetrics::GetMaxAscent/GetMaxDescent contains them.
    7579               0 :     nsFontMetrics* fm = provider.GetFontMetrics();
    7580               0 :     nscoord fontAscent = fm->MaxAscent();
    7581               0 :     nscoord fontDescent = fm->MaxDescent();
    7582               0 :     aMetrics.ascent = NS_MAX(NSToCoordCeil(textMetrics.mAscent), fontAscent);
    7583               0 :     nscoord descent = NS_MAX(NSToCoordCeil(textMetrics.mDescent), fontDescent);
    7584               0 :     aMetrics.height = aMetrics.ascent + descent;
    7585                 :   }
    7586                 : 
    7587               0 :   NS_ASSERTION(aMetrics.ascent >= 0, "Negative ascent???");
    7588               0 :   NS_ASSERTION(aMetrics.height - aMetrics.ascent >= 0, "Negative descent???");
    7589                 : 
    7590               0 :   mAscent = aMetrics.ascent;
    7591                 : 
    7592                 :   // Handle text that runs outside its normal bounds.
    7593               0 :   nsRect boundingBox = RoundOut(textMetrics.mBoundingBox) + nsPoint(0, mAscent);
    7594               0 :   aMetrics.SetOverflowAreasToDesiredBounds();
    7595               0 :   aMetrics.VisualOverflow().UnionRect(aMetrics.VisualOverflow(), boundingBox);
    7596                 : 
    7597                 :   // When we have text decorations, we don't need to compute their overflow now
    7598                 :   // because we're guaranteed to do it later
    7599                 :   // (see nsLineLayout::RelativePositionFrames)
    7600               0 :   UnionAdditionalOverflow(presContext, *aLineLayout.GetLineContainerRS(),
    7601               0 :                           provider, &aMetrics.VisualOverflow(), false);
    7602                 : 
    7603                 :   /////////////////////////////////////////////////////////////////////
    7604                 :   // Clean up, update state
    7605                 :   /////////////////////////////////////////////////////////////////////
    7606                 : 
    7607                 :   // If all our characters are discarded or collapsed, then trimmable width
    7608                 :   // from the last textframe should be preserved. Otherwise the trimmable width
    7609                 :   // from this textframe overrides. (Currently in CSS trimmable width can be
    7610                 :   // at most one space so there's no way for trimmable width from a previous
    7611                 :   // frame to accumulate with trimmable width from this frame.)
    7612               0 :   if (transformedCharsFit > 0) {
    7613               0 :     aLineLayout.SetTrimmableWidth(NSToCoordFloor(trimmableWidth));
    7614               0 :     AddStateBits(TEXT_HAS_NONCOLLAPSED_CHARACTERS);
    7615                 :   }
    7616               0 :   if (charsFit > 0 && charsFit == length &&
    7617                 :       textStyle->mHyphens != NS_STYLE_HYPHENS_NONE &&
    7618               0 :       HasSoftHyphenBefore(frag, mTextRun, offset, end)) {
    7619                 :     // Record a potential break after final soft hyphen
    7620                 :     aLineLayout.NotifyOptionalBreakPosition(mContent, offset + length,
    7621               0 :         textMetrics.mAdvanceWidth + provider.GetHyphenWidth() <= availWidth,
    7622               0 :                                            eNormalBreak);
    7623                 :   }
    7624               0 :   bool breakAfter = forceBreakAfter;
    7625                 :   // length == 0 means either the text is empty or it's all collapsed away
    7626               0 :   bool emptyTextAtStartOfLine = atStartOfLine && length == 0;
    7627               0 :   if (!breakAfter && charsFit == length && !emptyTextAtStartOfLine &&
    7628               0 :       transformedOffset + transformedLength == mTextRun->GetLength() &&
    7629               0 :       (mTextRun->GetFlags() & nsTextFrameUtils::TEXT_HAS_TRAILING_BREAK)) {
    7630                 :     // We placed all the text in the textrun and we have a break opportunity at
    7631                 :     // the end of the textrun. We need to record it because the following
    7632                 :     // content may not care about nsLineBreaker.
    7633                 : 
    7634                 :     // Note that because we didn't break, we can be sure that (thanks to the
    7635                 :     // code up above) textMetrics.mAdvanceWidth includes the width of any
    7636                 :     // trailing whitespace. So we need to subtract trimmableWidth here
    7637                 :     // because if we did break at this point, that much width would be trimmed.
    7638               0 :     if (textMetrics.mAdvanceWidth - trimmableWidth > availWidth) {
    7639               0 :       breakAfter = true;
    7640                 :     } else {
    7641                 :       aLineLayout.NotifyOptionalBreakPosition(mContent, offset + length,
    7642               0 :                                               true, eNormalBreak);
    7643                 :     }
    7644                 :   }
    7645                 : 
    7646                 :   // Compute reflow status
    7647                 :   aStatus = contentLength == maxContentLength
    7648               0 :     ? NS_FRAME_COMPLETE : NS_FRAME_NOT_COMPLETE;
    7649                 : 
    7650               0 :   if (charsFit == 0 && length > 0 && !usedHyphenation) {
    7651                 :     // Couldn't place any text
    7652               0 :     aStatus = NS_INLINE_LINE_BREAK_BEFORE();
    7653               0 :   } else if (contentLength > 0 && mContentOffset + contentLength - 1 == newLineOffset) {
    7654                 :     // Ends in \n
    7655               0 :     aStatus = NS_INLINE_LINE_BREAK_AFTER(aStatus);
    7656               0 :     aLineLayout.SetLineEndsInBR(true);
    7657               0 :   } else if (breakAfter) {
    7658               0 :     aStatus = NS_INLINE_LINE_BREAK_AFTER(aStatus);
    7659                 :   }
    7660               0 :   if (completedFirstLetter) {
    7661               0 :     aLineLayout.SetFirstLetterStyleOK(false);
    7662               0 :     aStatus |= NS_INLINE_BREAK_FIRST_LETTER_COMPLETE;
    7663                 :   }
    7664                 : 
    7665                 :   // Updated the cached NewlineProperty, or delete it.
    7666               0 :   if (contentLength < maxContentLength &&
    7667               0 :       textStyle->NewlineIsSignificant() &&
    7668                 :       (contentNewLineOffset < 0 ||
    7669                 :        mContentOffset + contentLength <= contentNewLineOffset)) {
    7670               0 :     if (!cachedNewlineOffset) {
    7671               0 :       cachedNewlineOffset = new NewlineProperty;
    7672               0 :       if (NS_FAILED(mContent->SetProperty(nsGkAtoms::newline, cachedNewlineOffset,
    7673                 :                                           NewlineProperty::Destroy))) {
    7674                 :         delete cachedNewlineOffset;
    7675               0 :         cachedNewlineOffset = nsnull;
    7676                 :       }
    7677                 :     }
    7678               0 :     if (cachedNewlineOffset) {
    7679               0 :       cachedNewlineOffset->mStartOffset = offset;
    7680               0 :       cachedNewlineOffset->mNewlineOffset = contentNewLineOffset;
    7681                 :     }
    7682               0 :   } else if (cachedNewlineOffset) {
    7683               0 :     mContent->DeleteProperty(nsGkAtoms::newline);
    7684                 :   }
    7685                 : 
    7686                 :   // Compute space and letter counts for justification, if required
    7687               0 :   if (!textStyle->WhiteSpaceIsSignificant() &&
    7688               0 :       (lineContainer->GetStyleText()->mTextAlign == NS_STYLE_TEXT_ALIGN_JUSTIFY ||
    7689               0 :        lineContainer->GetStyleText()->mTextAlignLast == NS_STYLE_TEXT_ALIGN_JUSTIFY)) {
    7690               0 :     AddStateBits(TEXT_JUSTIFICATION_ENABLED);    // This will include a space for trailing whitespace, if any is present.
    7691                 :     // This is corrected for in nsLineLayout::TrimWhiteSpaceIn.
    7692                 :     PRInt32 numJustifiableCharacters =
    7693               0 :       provider.ComputeJustifiableCharacters(offset, charsFit);
    7694                 : 
    7695               0 :     NS_ASSERTION(numJustifiableCharacters <= charsFit,
    7696                 :                  "Bad justifiable character count");
    7697                 :     aLineLayout.SetTextJustificationWeights(numJustifiableCharacters,
    7698               0 :         charsFit - numJustifiableCharacters);
    7699                 :   }
    7700                 : 
    7701               0 :   SetLength(contentLength, &aLineLayout, ALLOW_FRAME_CREATION_AND_DESTRUCTION);
    7702                 : 
    7703               0 :   Invalidate(aMetrics.VisualOverflow());
    7704                 : 
    7705                 : #ifdef NOISY_REFLOW
    7706                 :   ListTag(stdout);
    7707                 :   printf(": desiredSize=%d,%d(b=%d) status=%x\n",
    7708                 :          aMetrics.width, aMetrics.height, aMetrics.ascent,
    7709                 :          aStatus);
    7710                 : #endif
    7711                 : }
    7712                 : 
    7713                 : /* virtual */ bool
    7714               0 : nsTextFrame::CanContinueTextRun() const
    7715                 : {
    7716                 :   // We can continue a text run through a text frame
    7717               0 :   return true;
    7718                 : }
    7719                 : 
    7720                 : nsTextFrame::TrimOutput
    7721               0 : nsTextFrame::TrimTrailingWhiteSpace(nsRenderingContext* aRC)
    7722                 : {
    7723                 :   TrimOutput result;
    7724               0 :   result.mChanged = false;
    7725               0 :   result.mLastCharIsJustifiable = false;
    7726               0 :   result.mDeltaWidth = 0;
    7727                 : 
    7728               0 :   AddStateBits(TEXT_END_OF_LINE);
    7729                 : 
    7730               0 :   PRInt32 contentLength = GetContentLength();
    7731               0 :   if (!contentLength)
    7732               0 :     return result;
    7733                 : 
    7734               0 :   gfxContext* ctx = aRC->ThebesContext();
    7735                 :   gfxSkipCharsIterator start =
    7736               0 :     EnsureTextRun(nsTextFrame::eInflated, GetFontSizeInflation(), ctx);
    7737               0 :   NS_ENSURE_TRUE(mTextRun, result);
    7738                 : 
    7739               0 :   PRUint32 trimmedStart = start.GetSkippedOffset();
    7740                 : 
    7741               0 :   const nsTextFragment* frag = mContent->GetText();
    7742               0 :   TrimmedOffsets trimmed = GetTrimmedOffsets(frag, true);
    7743               0 :   gfxSkipCharsIterator trimmedEndIter = start;
    7744               0 :   const nsStyleText* textStyle = GetStyleText();
    7745               0 :   gfxFloat delta = 0;
    7746               0 :   PRUint32 trimmedEnd = trimmedEndIter.ConvertOriginalToSkipped(trimmed.GetEnd());
    7747                 :   
    7748               0 :   if (GetStateBits() & TEXT_TRIMMED_TRAILING_WHITESPACE) {
    7749                 :     // We pre-trimmed this frame, so the last character is justifiable
    7750               0 :     result.mLastCharIsJustifiable = true;
    7751               0 :   } else if (trimmed.GetEnd() < GetContentEnd()) {
    7752               0 :     gfxSkipCharsIterator end = trimmedEndIter;
    7753               0 :     PRUint32 endOffset = end.ConvertOriginalToSkipped(GetContentOffset() + contentLength);
    7754               0 :     if (trimmedEnd < endOffset) {
    7755                 :       // We can't be dealing with tabs here ... they wouldn't be trimmed. So it's
    7756                 :       // OK to pass null for the line container.
    7757                 :       PropertyProvider provider(mTextRun, textStyle, frag, this, start, contentLength,
    7758               0 :                                 nsnull, 0, nsTextFrame::eInflated);
    7759               0 :       delta = mTextRun->GetAdvanceWidth(trimmedEnd, endOffset - trimmedEnd, &provider);
    7760                 :       // non-compressed whitespace being skipped at end of line -> justifiable
    7761                 :       // XXX should we actually *count* justifiable characters that should be
    7762                 :       // removed from the overall count? I think so...
    7763               0 :       result.mLastCharIsJustifiable = true;
    7764               0 :       result.mChanged = true;
    7765                 :     }
    7766                 :   }
    7767                 : 
    7768               0 :   if (!result.mLastCharIsJustifiable &&
    7769               0 :       (GetStateBits() & TEXT_JUSTIFICATION_ENABLED)) {
    7770                 :     // Check if any character in the last cluster is justifiable
    7771                 :     PropertyProvider provider(mTextRun, textStyle, frag, this, start, contentLength,
    7772               0 :                               nsnull, 0, nsTextFrame::eInflated);
    7773               0 :     bool isCJK = IsChineseOrJapanese(this);
    7774               0 :     gfxSkipCharsIterator justificationStart(start), justificationEnd(trimmedEndIter);
    7775               0 :     provider.FindJustificationRange(&justificationStart, &justificationEnd);
    7776                 : 
    7777                 :     PRInt32 i;
    7778               0 :     for (i = justificationEnd.GetOriginalOffset(); i < trimmed.GetEnd(); ++i) {
    7779               0 :       if (IsJustifiableCharacter(frag, i, isCJK)) {
    7780               0 :         result.mLastCharIsJustifiable = true;
    7781                 :       }
    7782                 :     }
    7783                 :   }
    7784                 : 
    7785                 :   gfxFloat advanceDelta;
    7786                 :   mTextRun->SetLineBreaks(trimmedStart, trimmedEnd - trimmedStart,
    7787               0 :                           (GetStateBits() & TEXT_START_OF_LINE) != 0, true,
    7788               0 :                           &advanceDelta, ctx);
    7789               0 :   if (advanceDelta != 0) {
    7790               0 :     result.mChanged = true;
    7791                 :   }
    7792                 : 
    7793                 :   // aDeltaWidth is *subtracted* from our width.
    7794                 :   // If advanceDelta is positive then setting the line break made us longer,
    7795                 :   // so aDeltaWidth could go negative.
    7796               0 :   result.mDeltaWidth = NSToCoordFloor(delta - advanceDelta);
    7797                 :   // If aDeltaWidth goes negative, that means this frame might not actually fit
    7798                 :   // anymore!!! We need higher level line layout to recover somehow.
    7799                 :   // If it's because the frame has a soft hyphen that is now being displayed,
    7800                 :   // this should actually be OK, because our reflow recorded the break
    7801                 :   // opportunity that allowed the soft hyphen to be used, and we wouldn't
    7802                 :   // have recorded the opportunity unless the hyphen fit (or was the first
    7803                 :   // opportunity on the line).
    7804                 :   // Otherwise this can/ really only happen when we have glyphs with special
    7805                 :   // shapes at the end of lines, I think. Breaking inside a kerning pair won't
    7806                 :   // do it because that would mean we broke inside this textrun, and
    7807                 :   // BreakAndMeasureText should make sure the resulting shaped substring fits.
    7808                 :   // Maybe if we passed a maxTextLength? But that only happens at direction
    7809                 :   // changes (so we wouldn't kern across the boundary) or for first-letter
    7810                 :   // (which always fits because it starts the line!).
    7811               0 :   NS_WARN_IF_FALSE(result.mDeltaWidth >= 0,
    7812                 :                    "Negative deltawidth, something odd is happening");
    7813                 : 
    7814                 : #ifdef NOISY_TRIM
    7815                 :   ListTag(stdout);
    7816                 :   printf(": trim => %d\n", result.mDeltaWidth);
    7817                 : #endif
    7818               0 :   return result;
    7819                 : }
    7820                 : 
    7821                 : nsOverflowAreas
    7822               0 : nsTextFrame::RecomputeOverflow(const nsHTMLReflowState& aBlockReflowState)
    7823                 : {
    7824               0 :   nsRect bounds(nsPoint(0, 0), GetSize());
    7825               0 :   nsOverflowAreas result(bounds, bounds);
    7826                 : 
    7827               0 :   gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
    7828               0 :   if (!mTextRun)
    7829                 :     return result;
    7830                 : 
    7831               0 :   PropertyProvider provider(this, iter, nsTextFrame::eInflated);
    7832               0 :   provider.InitializeForDisplay(true);
    7833                 : 
    7834                 :   gfxTextRun::Metrics textMetrics =
    7835               0 :     mTextRun->MeasureText(provider.GetStart().GetSkippedOffset(),
    7836                 :                           ComputeTransformedLength(provider),
    7837                 :                           gfxFont::LOOSE_INK_EXTENTS, nsnull,
    7838               0 :                           &provider);
    7839               0 :   nsRect &vis = result.VisualOverflow();
    7840               0 :   vis.UnionRect(vis, RoundOut(textMetrics.mBoundingBox) + nsPoint(0, mAscent));
    7841                 :   UnionAdditionalOverflow(PresContext(), aBlockReflowState, provider,
    7842               0 :                           &vis, true);
    7843                 :   return result;
    7844                 : }
    7845               0 : static PRUnichar TransformChar(const nsStyleText* aStyle, gfxTextRun* aTextRun,
    7846                 :                                PRUint32 aSkippedOffset, PRUnichar aChar)
    7847                 : {
    7848               0 :   if (aChar == '\n') {
    7849               0 :     return aStyle->NewlineIsSignificant() ? aChar : ' ';
    7850                 :   }
    7851               0 :   switch (aStyle->mTextTransform) {
    7852                 :   case NS_STYLE_TEXT_TRANSFORM_LOWERCASE:
    7853               0 :     aChar = ToLowerCase(aChar);
    7854               0 :     break;
    7855                 :   case NS_STYLE_TEXT_TRANSFORM_UPPERCASE:
    7856               0 :     aChar = ToUpperCase(aChar);
    7857               0 :     break;
    7858                 :   case NS_STYLE_TEXT_TRANSFORM_CAPITALIZE:
    7859               0 :     if (aTextRun->CanBreakLineBefore(aSkippedOffset)) {
    7860               0 :       aChar = ToTitleCase(aChar);
    7861                 :     }
    7862               0 :     break;
    7863                 :   }
    7864                 : 
    7865               0 :   return aChar;
    7866                 : }
    7867                 : 
    7868               0 : nsresult nsTextFrame::GetRenderedText(nsAString* aAppendToString,
    7869                 :                                       gfxSkipChars* aSkipChars,
    7870                 :                                       gfxSkipCharsIterator* aSkipIter,
    7871                 :                                       PRUint32 aSkippedStartOffset,
    7872                 :                                       PRUint32 aSkippedMaxLength)
    7873                 : {
    7874                 :   // The handling of aSkippedStartOffset and aSkippedMaxLength could be more efficient...
    7875               0 :   gfxSkipCharsBuilder skipCharsBuilder;
    7876                 :   nsTextFrame* textFrame;
    7877               0 :   const nsTextFragment* textFrag = mContent->GetText();
    7878               0 :   PRUint32 keptCharsLength = 0;
    7879               0 :   PRUint32 validCharsLength = 0;
    7880                 : 
    7881                 :   // Build skipChars and copy text, for each text frame in this continuation block
    7882               0 :   for (textFrame = this; textFrame;
    7883               0 :        textFrame = static_cast<nsTextFrame*>(textFrame->GetNextContinuation())) {
    7884                 :     // For each text frame continuation in this block ...
    7885                 : 
    7886               0 :     if (textFrame->GetStateBits() & NS_FRAME_IS_DIRTY) {
    7887                 :       // We don't trust dirty frames, expecially when computing rendered text.
    7888               0 :       break;
    7889                 :     }
    7890                 : 
    7891                 :     // Ensure the text run and grab the gfxSkipCharsIterator for it
    7892                 :     gfxSkipCharsIterator iter =
    7893               0 :       textFrame->EnsureTextRun(nsTextFrame::eInflated);
    7894               0 :     if (!textFrame->mTextRun)
    7895               0 :       return NS_ERROR_FAILURE;
    7896                 : 
    7897                 :     // Skip to the start of the text run, past ignored chars at start of line
    7898                 :     // XXX In the future we may decide to trim extra spaces before a hard line
    7899                 :     // break, in which case we need to accurately detect those sitations and 
    7900                 :     // call GetTrimmedOffsets() with true to trim whitespace at the line's end
    7901               0 :     TrimmedOffsets trimmedContentOffsets = textFrame->GetTrimmedOffsets(textFrag, false);
    7902               0 :     PRInt32 startOfLineSkipChars = trimmedContentOffsets.mStart - textFrame->mContentOffset;
    7903               0 :     if (startOfLineSkipChars > 0) {
    7904               0 :       skipCharsBuilder.SkipChars(startOfLineSkipChars);
    7905               0 :       iter.SetOriginalOffset(trimmedContentOffsets.mStart);
    7906                 :     }
    7907                 : 
    7908                 :     // Keep and copy the appropriate chars withing the caller's requested range
    7909               0 :     const nsStyleText* textStyle = textFrame->GetStyleText();
    7910               0 :     while (iter.GetOriginalOffset() < trimmedContentOffsets.GetEnd() &&
    7911                 :            keptCharsLength < aSkippedMaxLength) {
    7912                 :       // For each original char from content text
    7913               0 :       if (iter.IsOriginalCharSkipped() || ++validCharsLength <= aSkippedStartOffset) {
    7914               0 :         skipCharsBuilder.SkipChar();
    7915                 :       } else {
    7916               0 :         ++keptCharsLength;
    7917               0 :         skipCharsBuilder.KeepChar();
    7918               0 :         if (aAppendToString) {
    7919                 :           aAppendToString->Append(
    7920                 :               TransformChar(textStyle, textFrame->mTextRun, iter.GetSkippedOffset(),
    7921               0 :                             textFrag->CharAt(iter.GetOriginalOffset())));
    7922                 :         }
    7923                 :       }
    7924               0 :       iter.AdvanceOriginal(1);
    7925                 :     }
    7926               0 :     if (keptCharsLength >= aSkippedMaxLength) {
    7927               0 :       break; // Already past the end, don't build string or gfxSkipCharsIter anymore
    7928                 :     }
    7929                 :   }
    7930                 :   
    7931               0 :   if (aSkipChars) {
    7932               0 :     aSkipChars->TakeFrom(&skipCharsBuilder); // Copy skipChars into aSkipChars
    7933               0 :     if (aSkipIter) {
    7934                 :       // Caller must provide both pointers in order to retrieve a gfxSkipCharsIterator,
    7935                 :       // because the gfxSkipCharsIterator holds a weak pointer to the gfxSkipCars.
    7936               0 :       *aSkipIter = gfxSkipCharsIterator(*aSkipChars, GetContentLength());
    7937                 :     }
    7938                 :   }
    7939                 : 
    7940               0 :   return NS_OK;
    7941                 : }
    7942                 : 
    7943                 : #ifdef DEBUG
    7944                 : // Translate the mapped content into a string that's printable
    7945                 : void
    7946               0 : nsTextFrame::ToCString(nsCString& aBuf, PRInt32* aTotalContentLength) const
    7947                 : {
    7948                 :   // Get the frames text content
    7949               0 :   const nsTextFragment* frag = mContent->GetText();
    7950               0 :   if (!frag) {
    7951               0 :     return;
    7952                 :   }
    7953                 : 
    7954                 :   // Compute the total length of the text content.
    7955               0 :   *aTotalContentLength = frag->GetLength();
    7956                 : 
    7957               0 :   PRInt32 contentLength = GetContentLength();
    7958                 :   // Set current fragment and current fragment offset
    7959               0 :   if (0 == contentLength) {
    7960               0 :     return;
    7961                 :   }
    7962               0 :   PRInt32 fragOffset = GetContentOffset();
    7963               0 :   PRInt32 n = fragOffset + contentLength;
    7964               0 :   while (fragOffset < n) {
    7965               0 :     PRUnichar ch = frag->CharAt(fragOffset++);
    7966               0 :     if (ch == '\r') {
    7967               0 :       aBuf.AppendLiteral("\\r");
    7968               0 :     } else if (ch == '\n') {
    7969               0 :       aBuf.AppendLiteral("\\n");
    7970               0 :     } else if (ch == '\t') {
    7971               0 :       aBuf.AppendLiteral("\\t");
    7972               0 :     } else if ((ch < ' ') || (ch >= 127)) {
    7973               0 :       aBuf.Append(nsPrintfCString("\\u%04x", ch));
    7974                 :     } else {
    7975               0 :       aBuf.Append(ch);
    7976                 :     }
    7977                 :   }
    7978                 : }
    7979                 : #endif
    7980                 : 
    7981                 : nsIAtom*
    7982               0 : nsTextFrame::GetType() const
    7983                 : {
    7984               0 :   return nsGkAtoms::textFrame;
    7985                 : }
    7986                 : 
    7987                 : /* virtual */ bool
    7988               0 : nsTextFrame::IsEmpty()
    7989                 : {
    7990               0 :   NS_ASSERTION(!(mState & TEXT_IS_ONLY_WHITESPACE) ||
    7991                 :                !(mState & TEXT_ISNOT_ONLY_WHITESPACE),
    7992                 :                "Invalid state");
    7993                 :   
    7994                 :   // XXXldb Should this check compatibility mode as well???
    7995               0 :   const nsStyleText* textStyle = GetStyleText();
    7996               0 :   if (textStyle->WhiteSpaceIsSignificant()) {
    7997                 :     // XXX shouldn't we return true if the length is zero?
    7998               0 :     return false;
    7999                 :   }
    8000                 : 
    8001               0 :   if (mState & TEXT_ISNOT_ONLY_WHITESPACE) {
    8002               0 :     return false;
    8003                 :   }
    8004                 : 
    8005               0 :   if (mState & TEXT_IS_ONLY_WHITESPACE) {
    8006               0 :     return true;
    8007                 :   }
    8008                 :   
    8009               0 :   bool isEmpty = IsAllWhitespace(mContent->GetText(),
    8010               0 :           textStyle->mWhiteSpace != NS_STYLE_WHITESPACE_PRE_LINE);
    8011               0 :   mState |= (isEmpty ? TEXT_IS_ONLY_WHITESPACE : TEXT_ISNOT_ONLY_WHITESPACE);
    8012               0 :   return isEmpty;
    8013                 : }
    8014                 : 
    8015                 : #ifdef DEBUG
    8016                 : NS_IMETHODIMP
    8017               0 : nsTextFrame::GetFrameName(nsAString& aResult) const
    8018                 : {
    8019               0 :   MakeFrameName(NS_LITERAL_STRING("Text"), aResult);
    8020                 :   PRInt32 totalContentLength;
    8021               0 :   nsCAutoString tmp;
    8022               0 :   ToCString(tmp, &totalContentLength);
    8023               0 :   tmp.SetLength(NS_MIN(tmp.Length(), 50u));
    8024               0 :   aResult += NS_LITERAL_STRING("\"") + NS_ConvertASCIItoUTF16(tmp) + NS_LITERAL_STRING("\"");
    8025               0 :   return NS_OK;
    8026                 : }
    8027                 : 
    8028                 : NS_IMETHODIMP_(nsFrameState)
    8029               0 : nsTextFrame::GetDebugStateBits() const
    8030                 : {
    8031                 :   // mask out our emptystate flags; those are just caches
    8032               0 :   return nsFrame::GetDebugStateBits() &
    8033               0 :     ~(TEXT_WHITESPACE_FLAGS | TEXT_REFLOW_FLAGS);
    8034                 : }
    8035                 : 
    8036                 : NS_IMETHODIMP
    8037               0 : nsTextFrame::List(FILE* out, PRInt32 aIndent) const
    8038                 : {
    8039                 :   // Output the tag
    8040               0 :   IndentBy(out, aIndent);
    8041               0 :   ListTag(out);
    8042               0 :   if (HasView()) {
    8043               0 :     fprintf(out, " [view=%p]", static_cast<void*>(GetView()));
    8044                 :   }
    8045               0 :   fprintf(out, " [run=%p]", static_cast<void*>(mTextRun));
    8046                 : 
    8047                 :   // Output the first/last content offset and prev/next in flow info
    8048               0 :   bool isComplete = PRUint32(GetContentEnd()) == GetContent()->TextLength();
    8049                 :   fprintf(out, "[%d,%d,%c] ", 
    8050                 :           GetContentOffset(), GetContentLength(),
    8051               0 :           isComplete ? 'T':'F');
    8052                 :   
    8053               0 :   if (GetNextSibling()) {
    8054               0 :     fprintf(out, " next=%p", static_cast<void*>(GetNextSibling()));
    8055                 :   }
    8056               0 :   nsIFrame* prevContinuation = GetPrevContinuation();
    8057               0 :   if (nsnull != prevContinuation) {
    8058               0 :     fprintf(out, " prev-continuation=%p", static_cast<void*>(prevContinuation));
    8059                 :   }
    8060               0 :   if (nsnull != mNextContinuation) {
    8061               0 :     fprintf(out, " next-continuation=%p", static_cast<void*>(mNextContinuation));
    8062                 :   }
    8063                 : 
    8064                 :   // Output the rect and state
    8065               0 :   fprintf(out, " {%d,%d,%d,%d}", mRect.x, mRect.y, mRect.width, mRect.height);
    8066               0 :   fprintf(out, " [state=%016llx]", (unsigned long long)mState);
    8067               0 :   if (IsSelected()) {
    8068               0 :     fprintf(out, " SELECTED");
    8069                 :   }
    8070               0 :   fprintf(out, " [content=%p]", static_cast<void*>(mContent));
    8071               0 :   if (HasOverflowAreas()) {
    8072               0 :     nsRect overflowArea = GetVisualOverflowRect();
    8073                 :     fprintf(out, " [vis-overflow=%d,%d,%d,%d]",
    8074                 :             overflowArea.x, overflowArea.y,
    8075               0 :             overflowArea.width, overflowArea.height);
    8076               0 :     overflowArea = GetScrollableOverflowRect();
    8077                 :     fprintf(out, " [scr-overflow=%d,%d,%d,%d]",
    8078                 :             overflowArea.x, overflowArea.y,
    8079               0 :             overflowArea.width, overflowArea.height);
    8080                 :   }
    8081               0 :   fprintf(out, " sc=%p", static_cast<void*>(mStyleContext));
    8082               0 :   nsIAtom* pseudoTag = mStyleContext->GetPseudo();
    8083               0 :   if (pseudoTag) {
    8084               0 :     nsAutoString atomString;
    8085               0 :     pseudoTag->ToString(atomString);
    8086                 :     fprintf(out, " pst=%s",
    8087               0 :             NS_LossyConvertUTF16toASCII(atomString).get());
    8088                 :   }
    8089               0 :   fputs("\n", out);
    8090               0 :   return NS_OK;
    8091                 : }
    8092                 : #endif
    8093                 : 
    8094                 : void
    8095               0 : nsTextFrame::AdjustOffsetsForBidi(PRInt32 aStart, PRInt32 aEnd)
    8096                 : {
    8097               0 :   AddStateBits(NS_FRAME_IS_BIDI);
    8098               0 :   mContent->DeleteProperty(nsGkAtoms::flowlength);
    8099                 : 
    8100                 :   /*
    8101                 :    * After Bidi resolution we may need to reassign text runs.
    8102                 :    * This is called during bidi resolution from the block container, so we
    8103                 :    * shouldn't be holding a local reference to a textrun anywhere.
    8104                 :    */
    8105               0 :   ClearTextRuns();
    8106                 : 
    8107               0 :   nsTextFrame* prev = static_cast<nsTextFrame*>(GetPrevContinuation());
    8108               0 :   if (prev) {
    8109                 :     // the bidi resolver can be very evil when columns/pages are involved. Don't
    8110                 :     // let it violate our invariants.
    8111               0 :     PRInt32 prevOffset = prev->GetContentOffset();
    8112               0 :     aStart = NS_MAX(aStart, prevOffset);
    8113               0 :     aEnd = NS_MAX(aEnd, prevOffset);
    8114               0 :     prev->ClearTextRuns();
    8115                 :   }
    8116                 : 
    8117               0 :   mContentOffset = aStart;
    8118               0 :   SetLength(aEnd - aStart, nsnull, 0);
    8119                 : 
    8120                 :   /**
    8121                 :    * After inserting text the caret Bidi level must be set to the level of the
    8122                 :    * inserted text.This is difficult, because we cannot know what the level is
    8123                 :    * until after the Bidi algorithm is applied to the whole paragraph.
    8124                 :    *
    8125                 :    * So we set the caret Bidi level to UNDEFINED here, and the caret code will
    8126                 :    * set it correctly later
    8127                 :    */
    8128               0 :   nsRefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
    8129               0 :   if (frameSelection) {
    8130               0 :     frameSelection->UndefineCaretBidiLevel();
    8131                 :   }
    8132               0 : }
    8133                 : 
    8134                 : /**
    8135                 :  * @return true if this text frame ends with a newline character.  It should return
    8136                 :  * false if it is not a text frame.
    8137                 :  */
    8138                 : bool
    8139               0 : nsTextFrame::HasTerminalNewline() const
    8140                 : {
    8141               0 :   return ::HasTerminalNewline(this);
    8142                 : }
    8143                 : 
    8144                 : bool
    8145               0 : nsTextFrame::IsAtEndOfLine() const
    8146                 : {
    8147               0 :   return (GetStateBits() & TEXT_END_OF_LINE) != 0;
    8148                 : }
    8149                 : 
    8150                 : nscoord
    8151               0 : nsTextFrame::GetBaseline() const
    8152                 : {
    8153               0 :   return mAscent;
    8154                 : }
    8155                 : 
    8156                 : bool
    8157               0 : nsTextFrame::HasAnyNoncollapsedCharacters()
    8158                 : {
    8159               0 :   gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
    8160               0 :   PRInt32 offset = GetContentOffset(),
    8161               0 :           offsetEnd = GetContentEnd();
    8162               0 :   PRInt32 skippedOffset = iter.ConvertOriginalToSkipped(offset);
    8163               0 :   PRInt32 skippedOffsetEnd = iter.ConvertOriginalToSkipped(offsetEnd);
    8164               0 :   return skippedOffset != skippedOffsetEnd;
    8165                 : }

Generated by: LCOV version 1.7