LCOV - app.info - layout/generic/nsTextFrameThebes.cpp

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-07-07 | 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