diff --git a/mozilla/content/base/src/nsHTMLContentSerializer.cpp b/mozilla/content/base/src/nsHTMLContentSerializer.cpp index 1ff95fb6df1..90618a49ded 100644 --- a/mozilla/content/base/src/nsHTMLContentSerializer.cpp +++ b/mozilla/content/base/src/nsHTMLContentSerializer.cpp @@ -39,8 +39,10 @@ #include "nsHTMLContentSerializer.h" #include "nsIDOMElement.h" +#include "nsIDOMText.h" #include "nsIContent.h" #include "nsIDocument.h" +#include "nsIDOMDocument.h" #include "nsINameSpaceManager.h" #include "nsString.h" #include "nsUnicharUtils.h" @@ -57,6 +59,8 @@ #include "nsIHTMLContent.h" #include "nsIParserService.h" #include "nsContentUtils.h" +#include "nsILineBreakerFactory.h" +#include "nsLWBrkCIID.h" #define kIndentStr NS_LITERAL_STRING(" ") #define kLessThan NS_LITERAL_STRING("<") @@ -64,6 +68,7 @@ #define kEndTag NS_LITERAL_STRING(" domDoc; + aText->GetOwnerDocument(getter_AddRefs(domDoc)); + nsCOMPtr document = do_QueryInterface(domDoc); + if (document) { + document->GetLineBreaker(getter_AddRefs(mLineBreaker)); + } + + if (!mLineBreaker) { + nsresult rv; + nsCOMPtr lf(do_GetService(kLWBrkCID, &rv)); + if (NS_SUCCEEDED(rv)) { + rv = lf->GetBreaker(nsString(), getter_AddRefs(mLineBreaker)); + // Ignore result value. + // If we are unable to obtain a line breaker, + // we will use our simple fallback logic. + } + } + } + nsAutoString data; nsresult rv; @@ -188,6 +217,236 @@ nsHTMLContentSerializer::AppendText(nsIDOMText* aText, return NS_OK; } +void nsHTMLContentSerializer::AppendWrapped_WhitespaceSequence( + nsASingleFragmentString::const_char_iterator &aPos, + const nsASingleFragmentString::const_char_iterator aEnd, + const nsASingleFragmentString::const_char_iterator aSequenceStart, + PRBool &aMayIgnoreStartOfLineWhitespaceSequence, + nsAString &aOutputStr) +{ + // Handle the complete sequence of whitespace. + // Continue to iterate until we find the first non-whitespace char. + // Updates "aPos" to point to the first unhandled char. + // Also updates the aMayIgnoreStartOfLineWhitespaceSequence flag, + // as well as the other "global" state flags. + + PRBool sawBlankOrTab = PR_FALSE; + PRBool leaveLoop = PR_FALSE; + + do { + switch (*aPos) { + case ' ': + case '\t': + sawBlankOrTab = PR_TRUE; + // no break + case '\n': + ++aPos; + // do not increase mColPos, + // because we will reduce the whitespace to a single char + break; + default: + leaveLoop = PR_TRUE; + break; + } + } while (!leaveLoop && aPos < aEnd); + + if (mAddSpace) { + // if we had previously been asked to add space, + // our situation has not changed + } + else if (!sawBlankOrTab && mMayIgnoreLineBreakSequence) { + // nothing to do + mMayIgnoreLineBreakSequence = PR_FALSE; + } + else if (aMayIgnoreStartOfLineWhitespaceSequence) { + // nothing to do + aMayIgnoreStartOfLineWhitespaceSequence = PR_FALSE; + } + else { + if (sawBlankOrTab) { + if (mColPos + 1 >= mMaxColumn) { + // no much sense in delaying, we only have one slot left, + // let's write a break now + aOutputStr.Append(mLineBreak); + mColPos = 0; + } + else { + // do not write out yet, we may write out either a space or a linebreak + // let's delay writing it out until we know more + + mAddSpace = PR_TRUE; + ++mColPos; // eat a slot of available space + } + } + else { + // Asian text usually does not contain spaces, therefore we should not + // transform a linebreak into a space. + // Since we only saw linebreaks, but no spaces or tabs, + // let's write a linebreak now. + aOutputStr.Append(mLineBreak); + mMayIgnoreLineBreakSequence = PR_TRUE; + mColPos = 0; + } + } +} + +void nsHTMLContentSerializer::AppendWrapped_NonWhitespaceSequence( + nsASingleFragmentString::const_char_iterator &aPos, + const nsASingleFragmentString::const_char_iterator aEnd, + const nsASingleFragmentString::const_char_iterator aSequenceStart, + PRBool &aMayIgnoreStartOfLineWhitespaceSequence, + nsAString& aOutputStr) +{ + mMayIgnoreLineBreakSequence = PR_FALSE; + aMayIgnoreStartOfLineWhitespaceSequence = PR_FALSE; + + // Handle the complete sequence of non-whitespace in this block + // Iterate until we find the first whitespace char or an aEnd condition + // Updates "aPos" to point to the first unhandled char. + // Also updates the aMayIgnoreStartOfLineWhitespaceSequence flag, + // as well as the other "global" state flags. + + PRBool thisSequenceStartsAtBeginningOfLine = !mColPos; + PRBool onceAgainBecauseWeAddedBreakInFront; + PRBool foundWhitespaceInLoop; + + do { + onceAgainBecauseWeAddedBreakInFront = PR_FALSE; + foundWhitespaceInLoop = PR_FALSE; + + do { + if (*aPos == ' ' || *aPos == '\t' || *aPos == '\n') { + foundWhitespaceInLoop = PR_TRUE; + break; + } + + ++aPos; + ++mColPos; + } while (mColPos < mMaxColumn && aPos < aEnd); + + if (aPos == aEnd || foundWhitespaceInLoop) { + // there is enough room for the complete block we found + + if (mAddSpace) { + aOutputStr.Append(PRUnichar(' ')); + mAddSpace = PR_FALSE; + } + + aOutputStr.Append(aSequenceStart, aPos - aSequenceStart); + // We have not yet reached the max column, we will continue to + // fill the current line in the next outer loop iteration. + } + else { // mColPos == mMaxColumn + if (!thisSequenceStartsAtBeginningOfLine && mAddSpace) { + // We can avoid to wrap. + + aOutputStr.Append(mLineBreak); + mAddSpace = PR_FALSE; + aPos = aSequenceStart; + mColPos = 0; + thisSequenceStartsAtBeginningOfLine = PR_TRUE; + onceAgainBecauseWeAddedBreakInFront = PR_TRUE; + } + else { + // we must wrap + + PRBool foundWrapPosition = PR_FALSE; + + if (mLineBreaker) { // we have a line breaker helper object + PRUint32 wrapPosition; + PRBool needMoreText; + nsresult rv; + + rv = mLineBreaker->Prev(aSequenceStart, + (aEnd - aSequenceStart), + (aPos - aSequenceStart) + 1, + &wrapPosition, + &needMoreText); + if (NS_SUCCEEDED(rv) && !needMoreText && wrapPosition > 0) { + foundWrapPosition = PR_TRUE; + } + else { + rv = mLineBreaker->Next(aSequenceStart, + (aEnd - aSequenceStart), + (aPos - aSequenceStart), + &wrapPosition, + &needMoreText); + if (NS_SUCCEEDED(rv) && !needMoreText && wrapPosition > 0) { + foundWrapPosition = PR_TRUE; + } + } + + if (foundWrapPosition) { + if (mAddSpace) { + aOutputStr.Append(PRUnichar(' ')); + mAddSpace = PR_FALSE; + } + + aOutputStr.Append(aSequenceStart, wrapPosition); + aOutputStr.Append(mLineBreak); + aPos = aSequenceStart + wrapPosition; + mColPos = 0; + aMayIgnoreStartOfLineWhitespaceSequence = PR_TRUE; + mMayIgnoreLineBreakSequence = PR_TRUE; + } + } + + if (!mLineBreaker || !foundWrapPosition) { + // try some simple fallback logic + // go forward up to the next whitespace position, + // in the worst case this will be all the rest of the data + + do { + if (*aPos == ' ' || *aPos == '\t' || *aPos == '\n') { + break; + } + + ++aPos; + ++mColPos; + } while (aPos < aEnd); + + if (mAddSpace) { + aOutputStr.Append(PRUnichar(' ')); + mAddSpace = PR_FALSE; + } + + aOutputStr.Append(aSequenceStart, aPos - aSequenceStart); + } + } + } + } while (onceAgainBecauseWeAddedBreakInFront); +} + +void +nsHTMLContentSerializer::AppendToStringWrapped(const nsASingleFragmentString& aStr, + nsAString& aOutputStr, + PRBool aTranslateEntities) +{ + nsASingleFragmentString::const_char_iterator pos, end, sequenceStart; + + aStr.BeginReading(pos); + aStr.EndReading(end); + + // if the current line already has text on it, such as a tag, + // leading whitespace is significant + + PRBool mayIgnoreStartOfLineWhitespaceSequence = !mColPos; + + while (pos < end) { + sequenceStart = pos; + + // if beginning of a whitespace sequence + if (*pos == ' ' || *pos == '\n' || *pos == '\t') { + AppendWrapped_WhitespaceSequence(pos, end, sequenceStart, + mayIgnoreStartOfLineWhitespaceSequence, aOutputStr); + } + else { // any other non-whitespace char + AppendWrapped_NonWhitespaceSequence(pos, end, sequenceStart, + mayIgnoreStartOfLineWhitespaceSequence, aOutputStr); + } + } +} + NS_IMETHODIMP nsHTMLContentSerializer::AppendDocumentStart(nsIDOMDocument *aDocument, nsAString& aStr) @@ -429,6 +688,7 @@ nsHTMLContentSerializer::AppendElementStart(nsIDOMElement *aElement, if (name.get() == nsHTMLAtoms::br && mPreLevel > 0 && (mFlags & nsIDocumentEncoder::OutputNoFormattingInPre)) { AppendToString(mLineBreak, aStr); + mMayIgnoreLineBreakSequence = PR_TRUE; mColPos = 0; return NS_OK; } @@ -439,6 +699,7 @@ nsHTMLContentSerializer::AppendElementStart(nsIDOMElement *aElement, if (LineBreakBeforeOpen(name, hasDirtyAttr)) { AppendToString(mLineBreak, aStr); + mMayIgnoreLineBreakSequence = PR_TRUE; mColPos = 0; mAddSpace = PR_FALSE; } @@ -501,6 +762,7 @@ nsHTMLContentSerializer::AppendElementStart(nsIDOMElement *aElement, if (LineBreakAfterOpen(name, hasDirtyAttr)) { AppendToString(mLineBreak, aStr); + mMayIgnoreLineBreakSequence = PR_TRUE; mColPos = 0; } @@ -570,6 +832,7 @@ nsHTMLContentSerializer::AppendElementEnd(nsIDOMElement *aElement, if (LineBreakBeforeClose(name, hasDirtyAttr)) { AppendToString(mLineBreak, aStr); + mMayIgnoreLineBreakSequence = PR_TRUE; mColPos = 0; mAddSpace = PR_FALSE; } @@ -586,6 +849,7 @@ nsHTMLContentSerializer::AppendElementEnd(nsIDOMElement *aElement, if (LineBreakAfterClose(name, hasDirtyAttr)) { AppendToString(mLineBreak, aStr); + mMayIgnoreLineBreakSequence = PR_TRUE; mColPos = 0; } @@ -623,152 +887,6 @@ nsHTMLContentSerializer::AppendToString(const PRUnichar aChar, aOutputStr.Append(aChar); } -void -nsHTMLContentSerializer::AppendToStringWrapped(const nsASingleFragmentString& aStr, - nsAString& aOutputStr, - PRBool aTranslateEntities) -{ - // indicates a space has been seen, position is stored in lastSpace - PRBool spaceSeen = PR_FALSE; - - // indicates non-whitespace has been seen, position is stored in lastChar - PRBool charSeen = PR_FALSE; - - PRBool addLineBreak = PR_FALSE; - - nsASingleFragmentString::const_char_iterator pos, end, segStart, lastSpace, lastChar; - - aStr.BeginReading(pos); - aStr.EndReading(end); - if (pos == end) { - return; - } - - // if the current line already has text on it, such as a tag, - // leading whitespace is significant, so add a space back - // after skipping over the whitespace - if ((mColPos > 0) && (*pos == ' ' || *pos == '\n')) { - mAddSpace = PR_TRUE; - } - - for (;;) { - - // skip leading spaces - while (*pos == ' ' || *pos == '\n') { - ++pos; - if (pos == end) { - return; - } - lastSpace = pos; - } - segStart = pos; - lastChar = pos; - spaceSeen = PR_FALSE; - charSeen = PR_TRUE; - - if (addLineBreak) { - aOutputStr.Append(mLineBreak); - mAddSpace = PR_FALSE; - mColPos = 0; - } - - while (mColPos < mMaxColumn) { - PRUnichar c = *pos; - - if (c == ' ') { - lastSpace = pos; - spaceSeen = PR_TRUE; - } - else if (c == '\n') { - if (charSeen) { - if (mAddSpace) { - aOutputStr.Append(PRUnichar(' ')); - } - - aOutputStr.Append(segStart, lastChar - segStart + 1); - charSeen = PR_FALSE; - } - mAddSpace = PR_TRUE; - segStart = pos; - spaceSeen = PR_FALSE; - ++segStart; - } - else { - lastChar = pos; - charSeen = PR_TRUE; - } - - ++pos; - ++mColPos; - - if (pos == end) { - if (!charSeen || pos == segStart) { - // nothing to append, or nothing meaningful to append - return; - } - if (mAddSpace) { - aOutputStr.Append(PRUnichar(' ')); - mAddSpace = PR_FALSE; - } - - aOutputStr.Append(segStart, lastChar - segStart + 1); - - // if the string ended in whitespace, set mAddSpace to true - if (pos != lastChar+1) { - mAddSpace = PR_TRUE; - } - return; - } - } - - if (spaceSeen) { - if (mAddSpace) { - aOutputStr.Append(PRUnichar(' ')); - mAddSpace = PR_FALSE; - } - - // write up to the last space encountered - aOutputStr.Append(segStart, lastSpace - segStart); - - // back up to that wrapping point for the next run through the loop - pos = lastSpace; - // add a line break before any more text - addLineBreak = PR_TRUE; - } - else { - - // if we're past the wrapping width with no place to wrap at, - // find the next whitespace and wrap there - while (pos != end && *pos != ' ' && *pos != '\n') { - ++pos; - } - - if (mAddSpace) { - // whitespace was needed before the next segment, so we can put - // a newline instead of a space, and avoid getting a lone line - aOutputStr.Append(mLineBreak); - addLineBreak = PR_FALSE; - - mColPos = pos - segStart; - - // if the string doesn't end in whitespace, set mAddSpace to false - if (pos == end) { - mAddSpace = PR_FALSE; - } - } - else { - // no choice but to write a long line and wrap immediately after it - addLineBreak = PR_TRUE; - } - aOutputStr.Append(segStart, pos - segStart); - - if (pos == end) { - return; - } - } - } -} - static PRUint16 kValNBSP = 160; static const char* kEntityNBSP = "nbsp"; diff --git a/mozilla/content/base/src/nsHTMLContentSerializer.h b/mozilla/content/base/src/nsHTMLContentSerializer.h index 42b556f4a53..975bea7f7ac 100644 --- a/mozilla/content/base/src/nsHTMLContentSerializer.h +++ b/mozilla/content/base/src/nsHTMLContentSerializer.h @@ -42,6 +42,7 @@ #include "nsXMLContentSerializer.h" #include "nsIEntityConverter.h" #include "nsString.h" +#include "nsILineBreaker.h" class nsIContent; class nsIAtom; @@ -97,6 +98,18 @@ class nsHTMLContentSerializer : public nsXMLContentSerializer { PRBool aIncrColumn = PR_TRUE); virtual void AppendToStringConvertLF(const nsAString& aStr, nsAString& aOutputStr); + void AppendWrapped_WhitespaceSequence( + nsASingleFragmentString::const_char_iterator &aPos, + const nsASingleFragmentString::const_char_iterator aEnd, + const nsASingleFragmentString::const_char_iterator aSequenceStart, + PRBool &aMayIgnoreStartOfLineWhitespaceSequence, + nsAString &aOutputStr); + void AppendWrapped_NonWhitespaceSequence( + nsASingleFragmentString::const_char_iterator &aPos, + const nsASingleFragmentString::const_char_iterator aEnd, + const nsASingleFragmentString::const_char_iterator aSequenceStart, + PRBool &aMayIgnoreStartOfLineWhitespaceSequence, + nsAString &aOutputStr); virtual void AppendToStringWrapped(const nsASingleFragmentString& aStr, nsAString& aOutputStr, PRBool aTranslateEntities); @@ -120,6 +133,7 @@ class nsHTMLContentSerializer : public nsXMLContentSerializer { // continued on the same line while serializing source. Otherwise, // the newline character acts as the whitespace and no space is needed. PRPackedBool mAddSpace; + PRPackedBool mMayIgnoreLineBreakSequence; // To keep track of First LI child of OL in selected range PRPackedBool mIsFirstChildOfOL; @@ -135,9 +149,11 @@ class nsHTMLContentSerializer : public nsXMLContentSerializer { * what so ever. */ PRPackedBool mInCDATA; + PRPackedBool mNeedLineBreaker; + + nsCOMPtr mLineBreaker; PRInt32 mMaxColumn; - nsString mLineBreak; nsCOMPtr mCharSet;