lastWordLen, &lastWordDimensions);
lastWordLen, PR_FALSE, &lastWordDimensions);
nsTextFrame::MeasureText(nsPresContext*          aPresContext,                         const nsHTMLReflowState& aReflowState,                         nsTextTransformer&       aTx,                         nsILineBreaker*          aLb,                         TextStyle&               aTs,                         TextReflowData&          aTextData){  PRBool firstThing = PR_TRUE;  nscoord maxWidth = aReflowState.availableWidth;  nsLineLayout& lineLayout = *aReflowState.mLineLayout;  PRInt32 contentLength = aTx.GetContentLength();  PRInt32 startingOffset = aTextData.mOffset;  PRInt32 prevOffset = -1;  PRInt32 column = mColumn;  PRInt32 prevColumn = column;  nscoord prevMaxWordWidth = 0, prevAscent = 0, prevDescent = 0;  PRInt32 lastWordLen = 0;  PRUnichar* lastWordPtr = nsnull;  PRBool  endsInWhitespace = PR_FALSE;  PRBool  endsInNewline = PR_FALSE;  PRBool  justDidFirstLetter = PR_FALSE;  nsTextDimensions dimensions, lastWordDimensions;  PRBool  measureTextRuns = PR_FALSE;  if (contentLength == 0) {    aTextData.mX = 0;    aTextData.mAscent = 0;    aTextData.mDescent = 0;    return NS_FRAME_COMPLETE;  }#if defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS)  // see if we have implementation for GetTextDimensions()  PRUint32 hints = 0;  aReflowState.rendContext->GetHints(hints);  if (hints & NS_RENDERING_HINT_FAST_MEASURE) {    measureTextRuns = !aTextData.mComputeMaxWordWidth && !aTs.mPreformatted &&                      !aTs.mSmallCaps && !aTs.mWordSpacing && !aTs.mLetterSpacing &&                      aTextData.mWrapping;  }  // Don't measure text runs with letter spacing active, it doesn't work  // it also doesn't work if we are not word-wrapping (bug 42832)#endif /* defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS)*/  TextRun textRun;  PRUint32 estimatedNumChars = EstimateNumChars(maxWidth - aTextData.mX,                                                aTs.mAveCharWidth);#ifdef IBMBIDI  nsTextFrame* nextBidi = nsnull;  PRInt32      start = -1, end;  if (mState & NS_FRAME_IS_BIDI) {    nextBidi = NS_STATIC_CAST(nsTextFrame*,                              aPresContext->PropertyTable()->GetProperty(this,                                                     nsLayoutAtoms::nextBidi));    if (nextBidi) {      if (mContentLength < 1) {        mContentLength = 1;      }      nextBidi->GetOffsets(start, end);      if (start <= mContentOffset) {        nextBidi->AdjustOffsetsForBidi(mContentOffset + mContentLength, end);      }      else {        mContentLength = start - mContentOffset;      }    }  }#endif //IBMBIDI  aTextData.mX = 0;  if (aTextData.mMeasureText) {    aTs.mNormalFont->GetMaxAscent(aTextData.mAscent);    aTs.mNormalFont->GetMaxDescent(aTextData.mDescent);  }  PRBool firstWordDone = PR_FALSE;  for (;;) {#ifdef IBMBIDI    if (nextBidi && (mContentLength <= 0) ) {      if (textRun.IsBuffering()) {        // Measure the remaining text        goto MeasureTextRun;      }      else {        break;      }    }#endif // IBMBIDI    // Get next word/whitespace from the text    PRBool isWhitespace, wasTransformed;    PRInt32 wordLen, contentLen;    union {      char*       bp1;      PRUnichar*  bp2;    };#ifdef IBMBIDI    wordLen = start;#endif // IBMBIDI    bp2 = aTx.GetNextWord(aTextData.mInWord, &wordLen, &contentLen, &isWhitespace,                          &wasTransformed, textRun.mNumSegments == 0);    // We need to set aTextData.mCanBreakBefore to true after 1st word. But we can't set     // aTextData.mCanBreakBefore without seeing the 2nd word. That's because this frame     // may only contain part of one word, the other part is in next frame.     // we don't care if first word is whitespace, that will be addressed later.     if (!aTextData.mCanBreakBefore && !firstThing && !isWhitespace) {      firstWordDone = PR_TRUE;    }#ifdef IBMBIDI    if (nextBidi) {      mContentLength -= contentLen;      if (mContentLength < 0) {        contentLen += mContentLength;        wordLen = PR_MIN(wordLen, contentLen);      }    }#endif // IBMBIDI    // Remember if the text was transformed    if (wasTransformed) {      mState |= TEXT_WAS_TRANSFORMED;    }        if (bp2) {      if (firstWordDone) {        // The first word has been processed, and 2nd word is seen         // we can set it be breakable here after.         aTextData.mCanBreakBefore = PR_TRUE;      }    } else {      if (textRun.IsBuffering()) {        // Measure the remaining text        goto MeasureTextRun;      }      else {        // Advance the offset in case we just consumed a bunch of        // discarded characters. Otherwise, if this is the first piece        // of content for this frame we will attempt to break-before it.        aTextData.mOffset += contentLen;        break;      }    }    lastWordLen = wordLen;    lastWordPtr = bp2;    aTextData.mInWord = PR_FALSE;    // Measure the word/whitespace    PRUnichar firstChar;    if (aTx.TransformedTextIsAscii()) {      firstChar = *bp1;    } else {      firstChar = *bp2;    }    if (isWhitespace) {      if ('\n' == firstChar) {        // We hit a newline. Stop looping.        NS_WARN_IF_FALSE(aTs.mPreformatted, "newline w/o ts.mPreformatted");        prevOffset = aTextData.mOffset;        aTextData.mOffset++;        endsInWhitespace = PR_TRUE;        endsInNewline = PR_TRUE;        break;      }      if (aTextData.mSkipWhitespace) {        aTextData.mOffset += contentLen;        aTextData.mSkipWhitespace = PR_FALSE;        if (wasTransformed) {          // As long as there were no discarded characters, then don't consider          // skipped leading whitespace as being transformed          if (wordLen == contentLen) {            mState &= ~TEXT_WAS_TRANSFORMED;          }        }        // Only set flag when we actually do skip whitespace        mState |= TEXT_SKIP_LEADING_WS;        continue;      }      firstThing = PR_FALSE;      // NOTE: Even if the textRun absorbs the whitespace below, we still      // want to remember that we're breakable.      aTextData.mCanBreakBefore = PR_TRUE;      aTextData.mFirstLetterOK = PR_FALSE;       if ('\t' == firstChar) {        // Expand tabs to the proper width        wordLen = 8 - (7 & column);        // Apply word spacing to every space derived from a tab        dimensions.width = (aTs.mSpaceWidth + aTs.mWordSpacing + aTs.mLetterSpacing)*wordLen;        // Because we have to expand the tab when rendering consider that        // a transformation of the text        mState |= TEXT_WAS_TRANSFORMED;      }      else if (textRun.IsBuffering()) {        // Add a whitespace segment        textRun.AddSegment(wordLen, contentLen, PR_TRUE);        continue;      }      else {        // Apply word spacing to every space, if there's more than one        dimensions.width = wordLen*(aTs.mWordSpacing + aTs.mLetterSpacing + aTs.mSpaceWidth);// XXX simplistic      }      //Even if there is not enough space for this "space", we still put it       //here instead of next line      prevColumn = column;      column += wordLen;      endsInWhitespace = PR_TRUE;      prevOffset = aTextData.mOffset;      aTextData.mOffset += contentLen;      if (aTextData.mMeasureText) {        //if we're wrapping, then don't add the whitespace width to the         // x-offset unless the whitespace will fit within maxWidth.''        if (aTextData.mWrapping) {          if (aTextData.mX + dimensions.width <= maxWidth) {            aTextData.mX += dimensions.width;          }          else {            // since we didn't add the trailing space width, set this flag so that             // we will not trim this non-existing space            aTextData.mTrailingSpaceTrimmed = PR_TRUE;            break;          }        }        else {          //if we're not wrapping, then always advance           // the x-offset regardless of maxWidth          aTextData.mX += dimensions.width;        }      } //(aTextData.mMeasureText)    }    else {      firstThing = PR_FALSE;      aTextData.mSkipWhitespace = PR_FALSE;      if (aTextData.mFirstLetterOK) {        if (IsPunctuationMark(firstChar)) {          if (contentLen > 1)          {            wordLen = 2;            contentLen = 2;          }        }        else {          wordLen = 1;          contentLen = 1;        }        justDidFirstLetter = PR_TRUE;      }            if (aTextData.mMeasureText) {        if (measureTextRuns && !justDidFirstLetter) {          // Add another word to the text run          textRun.AddSegment(wordLen, contentLen, PR_FALSE);          // See if we should measure the text          if ((textRun.mTotalNumChars >= estimatedNumChars) ||              (textRun.mNumSegments >= (TEXT_MAX_NUM_SEGMENTS - 1))) {            goto MeasureTextRun;          }        }        else {          if (aTs.mSmallCaps) {            MeasureSmallCapsText(aReflowState, aTs, bp2, wordLen, &dimensions);          }          else {            // Measure just the one word            if (aTx.TransformedTextIsAscii()) {              aReflowState.rendContext->GetTextDimensions(bp1, wordLen, dimensions);            } else {              aReflowState.rendContext->GetTextDimensions(bp2, wordLen, dimensions);            }#ifdef MOZ_MATHML            // If GetBoundingMetrics is available, use the exact glyph metrics            // for ::first-letter            // XXX remove the #ifdef if GetBoundingMetrics becomes mainstream            if (justDidFirstLetter) {              nsresult res;              nsBoundingMetrics bm;              if (aTx.TransformedTextIsAscii()) {                res = aReflowState.rendContext->GetBoundingMetrics(bp1, wordLen, bm);              } else {                res = aReflowState.rendContext->GetBoundingMetrics(bp2, wordLen, bm);              }              if (NS_SUCCEEDED(res)) {                aTextData.mAscent = dimensions.ascent = bm.ascent;                aTextData.mDescent = dimensions.descent = bm.descent;              }            }#endif            if (aTs.mLetterSpacing) {              dimensions.width += aTs.mLetterSpacing * wordLen;            }          }          lastWordDimensions = dimensions;          // See if there is room for the text          if ((0 != aTextData.mX) && aTextData.mWrapping && (aTextData.mX + dimensions.width > maxWidth)) {            // The text will not fit.            break;          }          prevMaxWordWidth = aTextData.mMaxWordWidth;          prevAscent = aTextData.mAscent;          prevDescent =  aTextData.mDescent;          aTextData.mX += dimensions.width;          if (dimensions.width > aTextData.mMaxWordWidth) {            aTextData.mMaxWordWidth = dimensions.width;          }          if (aTextData.mAscent < dimensions.ascent) {            aTextData.mAscent = dimensions.ascent;          }          if (aTextData.mDescent < dimensions.descent) {            aTextData.mDescent = dimensions.descent;          }          prevColumn = column;          column += wordLen;          endsInWhitespace = PR_FALSE;          prevOffset = aTextData.mOffset;          aTextData.mOffset += contentLen;          if (justDidFirstLetter) {            // Time to stop            break;          }        }      }      else {        // We didn't measure the text, but we need to update our state        prevColumn = column;        column += wordLen;        endsInWhitespace = PR_FALSE;        prevOffset = aTextData.mOffset;        aTextData.mOffset += contentLen;        if (justDidFirstLetter) {          // Time to stop          break;        }      }    }    continue;  MeasureTextRun:#if defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS)  // see if we have implementation for GetTextDimensions()  if (hints & NS_RENDERING_HINT_FAST_MEASURE) {    PRInt32 numCharsFit;    // These calls can return numCharsFit not positioned at a break in the textRun. Beware.    if (aTx.TransformedTextIsAscii()) {      aReflowState.rendContext->GetTextDimensions((char*)aTx.GetWordBuffer(), textRun.mTotalNumChars,                                         maxWidth - aTextData.mX,                                         textRun.mBreaks, textRun.mNumSegments,                                         dimensions, numCharsFit, lastWordDimensions);    } else {      aReflowState.rendContext->GetTextDimensions(aTx.GetWordBuffer(), textRun.mTotalNumChars,                                         maxWidth - aTextData.mX,                                         textRun.mBreaks, textRun.mNumSegments,                                         dimensions, numCharsFit, lastWordDimensions);    }    // See how much of the text fit    if ((0 != aTextData.mX) && aTextData.mWrapping && (aTextData.mX + dimensions.width > maxWidth)) {      // None of the text fits#ifdef IBMBIDI      nextBidi = nsnull;#endif // IBMBIDI      break;    }    // Find the index of the last segment that fit    PRInt32 lastSegment;    if (numCharsFit >= textRun.mTotalNumChars) { // fast path, normal case      NS_ASSERTION(numCharsFit == textRun.mTotalNumChars, "shouldn't overshoot");      lastSegment = textRun.mNumSegments - 1;    } else {      for (lastSegment = 0; textRun.mBreaks[lastSegment] < numCharsFit; lastSegment++) ;      NS_ASSERTION(lastSegment < textRun.mNumSegments, "failed to find segment");      // now we have textRun.mBreaks[lastSegment] >= numCharsFit      /* O'Callahan XXX: This snippet together with the snippet below prevents mail from loading         Justification seems to work just fine without these changes.         We get into trouble in a case where lastSegment gets set to -1      if (textRun.mBreaks[lastSegment] > numCharsFit) {        // NOTE: this segment did not actually fit!        lastSegment--;      }      */    }    /* O'Callahan XXX: This snippet together with the snippet above prevents mail from loading    if (lastSegment < 0) {              // no segments fit      break;    } else */    if (lastSegment == 0) {      // Only one segment fit      prevColumn = column;      prevOffset = aTextData.mOffset;    } else {      // The previous state is for the next to last word      // NOTE: The textRun data are relative to the last updated column and offset!      prevColumn = column + textRun.mBreaks[lastSegment - 1];      prevOffset = aTextData.mOffset + textRun.mSegments[lastSegment - 1].ContentLen();    }    aTextData.mX += dimensions.width;    if (aTextData.mAscent < dimensions.ascent) {      aTextData.mAscent = dimensions.ascent;    }    if (aTextData.mDescent < dimensions.descent) {      aTextData.mDescent = dimensions.descent;    }    // this is where to backup if line-breaking happens to push the last word    prevAscent = aTextData.mAscent;    prevDescent = aTextData.mDescent;    // we can now consider the last word since we know where to backup    if (aTextData.mAscent < lastWordDimensions.ascent) {      aTextData.mAscent = lastWordDimensions.ascent;    }    if (aTextData.mDescent < lastWordDimensions.descent) {      aTextData.mDescent = lastWordDimensions.descent;    }    column += numCharsFit;    aTextData.mOffset += textRun.mSegments[lastSegment].ContentLen();    endsInWhitespace = textRun.mSegments[lastSegment].IsWhitespace();    // If all the text didn't fit, then we're done    if (numCharsFit != textRun.mTotalNumChars) {#ifdef IBMBIDI      nextBidi = nsnull;#endif // IBMBIDI      break;    }#ifdef IBMBIDI    if (nextBidi && (mContentLength <= 0) ) {      break;    }#endif // IBMBIDI    if (nsnull == bp2) {      // No more text so we're all finished. Advance the offset in case the last      // call to GetNextWord() discarded characters      aTextData.mOffset += contentLen;      break;    }    // Reset the number of text run segments    textRun.Reset();    // Estimate the remaining number of characters we think will fit    estimatedNumChars = EstimateNumChars(maxWidth - aTextData.mX,                                         aTs.mAveCharWidth);  }#else /* defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS) */    int unused = -1;#endif /* defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS) */  }  // If we didn't actually measure any text, then make sure it looks  // like we did  if (!aTextData.mMeasureText) {    aTextData.mAscent = mAscent;    aTextData.mDescent = mRect.height - aTextData.mAscent;    aTextData.mX = mRect.width;    if (mState & TEXT_TRIMMED_WS) {      // Add back in the width of a space since it was trimmed away last time      // NOTE: Trailing whitespace includes word and letter spacing!      aTextData.mX += aTs.mSpaceWidth + aTs.mWordSpacing + aTs.mLetterSpacing;    }  }    // Post processing logic to deal with word-breaking that spans  // multiple frames.  if (lineLayout.InWord()) {    // We are already in a word. This means a text frame prior to this    // one had a fragment of a nbword that is joined with this    // frame. It also means that the prior frame already found this    // frame and recorded it as part of the word.#ifdef DEBUG_WORD_WRAPPING    ListTag(stdout);    printf(": in word; skipping\n");#endif    lineLayout.ForgetWordFrame(this);  }  if (!lineLayout.InWord()) {    // There is no currently active word. This frame may contain the    // start of one.    if (endsInWhitespace) {      // Nope, this frame doesn't start a word.      lineLayout.ForgetWordFrames();    }    else if ((aTextData.mOffset == contentLength) && (prevOffset >= 0)) {      // Force breakable to false when we aren't wrapping (this      // guarantees that the combined word will stay together)      if (!aTextData.mWrapping) {        aTextData.mCanBreakBefore = PR_FALSE;      }      // This frame does start a word. However, there is no point      // messing around with it if we are already out of room. We      // always have room if we are not breakable.      if (!aTextData.mCanBreakBefore || (aTextData.mX <= maxWidth)) {        // There is room for this word fragment. It's possible that        // this word fragment is the end of the text-run. If it's not        // then we continue with the look-ahead processing.        nsIFrame* next = lineLayout.FindNextText(aPresContext, this);        if (nsnull != next) {#ifdef DEBUG_WORD_WRAPPING          nsAutoString tmp(aTx.GetWordBuffer(), lastWordLen);          ListTag(stdout);          printf(": start='");          fputs(NS_LossyConvertUCS2toASCII(tmp).get(), stdout);          printf("' lastWordLen=%d baseWidth=%d prevOffset=%d offset=%d next=",                 lastWordLen, lastWordDimensions.width, prevOffset, aTextData.mOffset);          ListTag(stdout, next);          printf("\n");#endif          PRUnichar* pWordBuf = lastWordPtr;          PRUint32   wordBufLen = aTx.GetWordBufferLength() -                                  (lastWordPtr - aTx.GetWordBuffer());          if (aTx.TransformedTextIsAscii()) {            // The text transform buffer contains ascii characters, so            // transform it to Unicode            NS_ASSERTION(wordBufLen >= PRUint32(lastWordLen), "no room to transform in place");            TransformTextToUnicode((char*)lastWordPtr, lastWordLen);          }          // Look ahead in the text-run and compute the final word          // width, taking into account any style changes and stopping          // at the first breakable point.          if (!aTextData.mMeasureText || (lastWordDimensions.width == -1)) {            // We either didn't measure any text or we measured multiple words            // at once so either way we don't know lastWordDimensions. We'll have to            // compute it now            if (prevOffset == startingOffset) {              // There's only one word, so we don't have to measure after all              lastWordDimensions.width = aTextData.mX;            }            else if (aTs.mSmallCaps) {              MeasureSmallCapsText(aReflowState, aTs, pWordBuf,                                   lastWordLen, &lastWordDimensions);            }            else {              aReflowState.rendContext->GetTextDimensions(pWordBuf, lastWordLen, lastWordDimensions);              if (aTs.mLetterSpacing) {                lastWordDimensions.width += aTs.mLetterSpacing * lastWordLen;              }            }          }          nsTextDimensions wordDimensions = ComputeTotalWordDimensions(aPresContext, aLb,                                                    lineLayout,                                                    aReflowState, next,                                                    lastWordDimensions,                                                    pWordBuf,                                                    lastWordLen,                                                    wordBufLen,                                                    aTextData.mCanBreakBefore);          if (!aTextData.mCanBreakBefore || (aTextData.mX - lastWordDimensions.width + wordDimensions.width <= maxWidth)) {            // The fully joined word has fit. Account for the joined            // word's affect on the max-element-size here (since the            // joined word is large than it's pieces, the right effect            // will occur from the perspective of the container            // reflowing this frame)            if (wordDimensions.width > aTextData.mMaxWordWidth) {              aTextData.mMaxWordWidth = wordDimensions.width;            }            // Now that we now that we will retain the last word, we should            // account for its ascent and descent            if (aTextData.mAscent < lastWordDimensions.ascent) {              aTextData.mAscent = lastWordDimensions.ascent;            }            if (aTextData.mDescent < lastWordDimensions.descent) {              aTextData.mDescent = lastWordDimensions.descent;            }          }          else {#ifdef NOISY_REFLOW            ListTag(stdout);            printf(": look-ahead (didn't fit) x=%d wordWidth=%d lastWordWidth=%d\n",                   aTextData.mX, wordDimensions.width, lastWordDimensions.width);#endif            // The fully joined word won't fit. We need to reduce our            // size by lastWordDimensions            aTextData.mX -= lastWordDimensions.width;            aTextData.mMaxWordWidth = prevMaxWordWidth;            aTextData.mOffset = prevOffset;            column = prevColumn;            if (aTextData.mMeasureText) {              aTextData.mAscent = prevAscent;              aTextData.mDescent = prevDescent;            }            // else {            // XXX we didn't measure the text, and so we don't know where to back up,            //     we will retain our current height. However, there is a possible            //     edge case that is not handled: since we just chopped the last word,            //     our remaining text could have got shorter.            // }#ifdef DEBUG_WORD_WRAPPING            printf("  x=%d maxWordWidth=%d len=%d\n", aTextData.mX, aTextData.mMaxWordWidth,                   aTextData.mOffset - startingOffset);#endif            lineLayout.ForgetWordFrames();          }        }      }    }  }  // Inform line layout of how this piece of text ends in whitespace  // (only text objects do this). Note that if x is zero then this  // text object collapsed into nothingness which means it shouldn't  // effect the current setting of the ends-in-whitespace flag.  lineLayout.SetColumn(column);  lineLayout.SetUnderstandsWhiteSpace(PR_TRUE);  if (0 != aTextData.mX) {    lineLayout.SetEndsInWhiteSpace(endsInWhitespace);  }  if (justDidFirstLetter) {    lineLayout.SetFirstLetterFrame(this);    lineLayout.SetFirstLetterStyleOK(PR_FALSE);    mState |= TEXT_FIRST_LETTER;  }  // Return our reflow status  nsReflowStatus rs = (aTextData.mOffset == contentLength)#ifdef IBMBIDI                      || (aTextData.mOffset == start)#endif // IBMBIDI    ? NS_FRAME_COMPLETE    : NS_FRAME_NOT_COMPLETE;  if (endsInNewline) {    rs = NS_INLINE_LINE_BREAK_AFTER(rs);    lineLayout.SetLineEndsInBR(PR_TRUE);  }  else if ((aTextData.mOffset != contentLength) && (aTextData.mOffset == startingOffset)) {    // Break-before a long-word that doesn't fit here    rs = NS_INLINE_LINE_BREAK_BEFORE();  }  return rs;}