wordLen = 2; contentLen = 2;
if (contentLen > 1) { wordLen = 2; contentLen = 2; }
nsTextFrame::MeasureText(nsIPresContext*          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;  PRInt32 lastWordLen = 0;  PRInt32 lastWordWidth = 0;  PRUnichar* lastWordPtr = nsnull;  PRBool  textStartsWithNBSP = PR_FALSE;  PRBool  endsInWhitespace = PR_FALSE;  PRBool  endsInNewline = PR_FALSE;  PRBool  justDidFirstLetter = PR_FALSE;#ifdef _WIN32  PRBool  measureTextRuns = !aTextData.mComputeMaxWordWidth && !aTs.mPreformatted &&                            !aTs.mSmallCaps && !aTs.mWordSpacing && !aTs.mLetterSpacing;  // Don't measure text runs with letter spacing active, it doesn't work#else  PRBool  measureTextRuns = PR_FALSE;#endif  TextRun textRun;  PRInt32 estimatedNumChars;     // Estimate the number of characters that will fit. Use 105% of the available  // width divided by the average character width  estimatedNumChars = (maxWidth - aTextData.mX) / aTs.mAveCharWidth;  estimatedNumChars += estimatedNumChars / 20;  aTextData.mX = 0;  for (;;firstThing = PR_FALSE) {    // Get next word/whitespace from the text    PRBool isWhitespace, wasTransformed;    PRInt32 wordLen, contentLen;    union {      char*       bp1;      PRUnichar*  bp2;    };    bp2 = aTx.GetNextWord(aTextData.mInWord, &wordLen, &contentLen, &isWhitespace,                          &wasTransformed, textRun.mNumSegments == 0);    // Remember if the text was transformed    if (wasTransformed) {      mState |= TEXT_WAS_TRANSFORMED;    }    if (nsnull == bp2) {      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    nscoord width;    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;      }      // NOTE: Even if the textRun absorbs the whitespace below, we still      // want to remember that we're breakable.      aTextData.mIsBreakable = 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        width = (aTs.mSpaceWidth + aTs.mWordSpacing)*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        width = wordLen*(aTs.mWordSpacing + aTs.mSpaceWidth);// XXX simplistic      }      if (aTextData.mMeasureText) {        // See if there is room for the text        if ((0 != aTextData.mX) && aTextData.mWrapping && (aTextData.mX + width > maxWidth)) {          // The text will not fit.          break;        }        aTextData.mX += width;      }      prevColumn = column;      column += wordLen;      endsInWhitespace = PR_TRUE;      prevOffset = aTextData.mOffset;      aTextData.mOffset += contentLen;    } else {      // See if the first thing in the section of text is a      // non-breaking space (html nbsp entity). If it is then make      // note of that fact for the line layout logic.      if (aTextData.mWrapping && firstThing && (firstChar == ' ')) {        textStartsWithNBSP = PR_TRUE;      }      aTextData.mSkipWhitespace = PR_FALSE;      if (aTextData.mFirstLetterOK) {        // XXX need a lookup function here; plus look ahead using the        // text-runs        if ((firstChar == '\'') || (firstChar == '\"')) {          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, &width);          }          else {            // Measure just the one word            if (aTx.TransformedTextIsAscii()) {              aReflowState.rendContext->GetWidth(bp1, wordLen, width);            } else {              aReflowState.rendContext->GetWidth(bp2, wordLen, width);            }            if (aTs.mLetterSpacing) {              width += aTs.mLetterSpacing * wordLen;            }          }          lastWordWidth = width;          // See if there is room for the text          if ((0 != aTextData.mX) && aTextData.mWrapping && (aTextData.mX + width > maxWidth)) {            // The text will not fit.            break;          }          aTextData.mX += width;          prevMaxWordWidth = aTextData.mMaxWordWidth;          if (width > aTextData.mMaxWordWidth) {            aTextData.mMaxWordWidth = width;          }          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:#ifdef _WIN32    PRInt32 numCharsFit;    // These calls can return numCharsFit not positioned at a break in the textRun. Beware.    if (aTx.TransformedTextIsAscii()) {      aReflowState.rendContext->GetWidth((char*)aTx.GetWordBuffer(), textRun.mTotalNumChars,                                         maxWidth - aTextData.mX,                                         textRun.mBreaks, textRun.mNumSegments,                                         width, numCharsFit);    } else {      aReflowState.rendContext->GetWidth(aTx.GetWordBuffer(), textRun.mTotalNumChars,                                         maxWidth - aTextData.mX,                                         textRun.mBreaks, textRun.mNumSegments,                                         width, numCharsFit);    }    // See how much of the text fit    if ((0 != aTextData.mX) && aTextData.mWrapping && (aTextData.mX + width > maxWidth)) {      // None of the text fits      break;    }    // Find the index of the last segment that fit    PRInt32 lastSegment;    if (numCharsFit == textRun.mTotalNumChars) { // fast path, normal case      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      prevColumn = textRun.mBreaks[lastSegment - 1];      prevOffset = textRun.mSegments[lastSegment - 1].ContentLen();      // 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 += width;    column += numCharsFit;    aTextData.mOffset += textRun.mSegments[lastSegment].ContentLen();    endsInWhitespace = textRun.mSegments[lastSegment].IsWhitespace();    // Since we measure multiple words we don't know what the last word    // width is    lastWordWidth = -1;    // If all the text didn't fit, then we're done    if (numCharsFit != textRun.mTotalNumChars) {      break;    }    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 = (maxWidth - aTextData.mX) / aTs.mAveCharWidth;    estimatedNumChars += estimatedNumChars / 20;#else    int unused = -1;#endif  }  // If we didn't actually measure any text, then make sure it looks  // like we did  if (!aTextData.mMeasureText) {    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 spacing!      aTextData.mX += aTs.mSpaceWidth + aTs.mWordSpacing;    }  }    // 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.mIsBreakable = 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.mIsBreakable || (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(this);        if (nsnull != next) {#ifdef DEBUG_WORD_WRAPPING          nsAutoString tmp(aTx.GetWordBuffer(), lastWordLen);          ListTag(stdout);          printf(": start='");          fputs(tmp, stdout);          printf("' lastWordLen=%d baseWidth=%d prevOffset=%d offset=%d next=",                 lastWordLen, lastWordWidth, 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 || (lastWordWidth == -1)) {            // We either didn't measure any text or we measured multiple words            // at once so either way we don't know lastWordWidth. We'll have to            // compute it now            if (prevOffset == startingOffset) {              // There's only one word, so we don't have to measure after all              lastWordWidth = aTextData.mX;            }            else if (aTs.mSmallCaps) {              MeasureSmallCapsText(aReflowState, aTs, pWordBuf,                                   lastWordLen, &lastWordWidth);            }            else {              aReflowState.rendContext->GetWidth(pWordBuf, lastWordLen, lastWordWidth);              if (aTs.mLetterSpacing) {                lastWordWidth += aTs.mLetterSpacing * lastWordLen;              }            }          }          nscoord wordWidth = ComputeTotalWordWidth(aPresContext, aLb,                                                    lineLayout,                                                    aReflowState, next,                                                    lastWordWidth,                                                    pWordBuf,                                                    lastWordLen,                                                    wordBufLen);          if (!aTextData.mIsBreakable || (aTextData.mX - lastWordWidth + wordWidth <= 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 (wordWidth > aTextData.mMaxWordWidth) {              aTextData.mMaxWordWidth = wordWidth;            }          }          else {#ifdef NOISY_REFLOW            ListTag(stdout);            printf(": look-ahead (didn't fit) x=%d wordWidth=%d lastWordWidth=%d\n",                   aTextData.mX, wordWidth, lastWordWidth);#endif            // The fully joined word won't fit. We need to reduce our            // size by the lastWordWidth.            aTextData.mX -= lastWordWidth;            aTextData.mMaxWordWidth = prevMaxWordWidth;            aTextData.mOffset = prevOffset;            column = prevColumn;#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);  lineLayout.SetTextStartsWithNBSP(textStartsWithNBSP);  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)    ? NS_FRAME_COMPLETE    : NS_FRAME_NOT_COMPLETE;  if (endsInNewline) {    rs = NS_INLINE_LINE_BREAK_AFTER(rs);  }  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;}