tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

InternetCiter.cpp (10297B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "InternetCiter.h"
      7 
      8 #include "mozilla/Casting.h"
      9 #include "mozilla/intl/Segmenter.h"
     10 #include "HTMLEditUtils.h"
     11 #include "nsAString.h"
     12 #include "nsCOMPtr.h"
     13 #include "nsCRT.h"
     14 #include "nsDebug.h"
     15 #include "nsDependentSubstring.h"
     16 #include "nsError.h"
     17 #include "nsServiceManagerUtils.h"
     18 #include "nsString.h"
     19 #include "nsStringIterator.h"
     20 
     21 namespace mozilla {
     22 
     23 /**
     24 * Mail citations using the Internet style: > This is a citation.
     25 */
     26 
     27 void InternetCiter::GetCiteString(const nsAString& aInString,
     28                                  nsAString& aOutString) {
     29  aOutString.Truncate();
     30  char16_t uch = HTMLEditUtils::kNewLine;
     31 
     32  // Strip trailing new lines which will otherwise turn up
     33  // as ugly quoted empty lines.
     34  nsReadingIterator<char16_t> beginIter, endIter;
     35  aInString.BeginReading(beginIter);
     36  aInString.EndReading(endIter);
     37  while (beginIter != endIter && (*endIter == HTMLEditUtils::kCarriageReturn ||
     38                                  *endIter == HTMLEditUtils::kNewLine)) {
     39    --endIter;
     40  }
     41 
     42  // Loop over the string:
     43  while (beginIter != endIter) {
     44    if (uch == HTMLEditUtils::kNewLine) {
     45      aOutString.Append(HTMLEditUtils::kGreaterThan);
     46      // No space between >: this is ">>> " style quoting, for
     47      // compatibility with RFC 2646 and format=flowed.
     48      if (*beginIter != HTMLEditUtils::kGreaterThan) {
     49        aOutString.Append(HTMLEditUtils::kSpace);
     50      }
     51    }
     52 
     53    uch = *beginIter;
     54    ++beginIter;
     55 
     56    aOutString += uch;
     57  }
     58 
     59  if (uch != HTMLEditUtils::kNewLine) {
     60    aOutString += HTMLEditUtils::kNewLine;
     61  }
     62 }
     63 
     64 static void AddCite(nsAString& aOutString, int32_t citeLevel) {
     65  for (int32_t i = 0; i < citeLevel; ++i) {
     66    aOutString.Append(HTMLEditUtils::kGreaterThan);
     67  }
     68  if (citeLevel > 0) {
     69    aOutString.Append(HTMLEditUtils::kSpace);
     70  }
     71 }
     72 
     73 static inline void BreakLine(nsAString& aOutString, uint32_t& outStringCol,
     74                             uint32_t citeLevel) {
     75  aOutString.Append(HTMLEditUtils::kNewLine);
     76  if (citeLevel > 0) {
     77    AddCite(aOutString, citeLevel);
     78    outStringCol = citeLevel + 1;
     79  } else {
     80    outStringCol = 0;
     81  }
     82 }
     83 
     84 static inline bool IsSpace(char16_t c) {
     85  return (nsCRT::IsAsciiSpace(c) || (c == HTMLEditUtils::kNewLine) ||
     86          (c == HTMLEditUtils::kCarriageReturn) || (c == HTMLEditUtils::kNBSP));
     87 }
     88 
     89 void InternetCiter::Rewrap(const nsAString& aInString, uint32_t aWrapCol,
     90                           uint32_t aFirstLineOffset, bool aRespectNewlines,
     91                           nsAString& aOutString) {
     92  // There shouldn't be returns in this string, only dom newlines.
     93  // Check to make sure:
     94 #ifdef DEBUG
     95  int32_t crPosition = aInString.FindChar(HTMLEditUtils::kCarriageReturn);
     96  NS_ASSERTION(crPosition < 0, "Rewrap: CR in string gotten from DOM!\n");
     97 #endif /* DEBUG */
     98 
     99  aOutString.Truncate();
    100 
    101  // Loop over lines in the input string, rewrapping each one.
    102  uint32_t posInString = 0;
    103  uint32_t outStringCol = 0;
    104  uint32_t citeLevel = 0;
    105  const nsPromiseFlatString& tString = PromiseFlatString(aInString);
    106  const uint32_t length = tString.Length();
    107  while (posInString < length) {
    108    // Get the new cite level here since we're at the beginning of a line
    109    uint32_t newCiteLevel = 0;
    110    while (posInString < length &&
    111           tString[posInString] == HTMLEditUtils::kGreaterThan) {
    112      ++newCiteLevel;
    113      ++posInString;
    114      while (posInString < length &&
    115             tString[posInString] == HTMLEditUtils::kSpace) {
    116        ++posInString;
    117      }
    118    }
    119    if (posInString >= length) {
    120      break;
    121    }
    122 
    123    // Special case: if this is a blank line, maintain a blank line
    124    // (retain the original paragraph breaks)
    125    if (tString[posInString] == HTMLEditUtils::kNewLine &&
    126        !aOutString.IsEmpty()) {
    127      if (aOutString.Last() != HTMLEditUtils::kNewLine) {
    128        aOutString.Append(HTMLEditUtils::kNewLine);
    129      }
    130      AddCite(aOutString, newCiteLevel);
    131      aOutString.Append(HTMLEditUtils::kNewLine);
    132 
    133      ++posInString;
    134      outStringCol = 0;
    135      continue;
    136    }
    137 
    138    // If the cite level has changed, then start a new line with the
    139    // new cite level (but if we're at the beginning of the string,
    140    // don't bother).
    141    if (newCiteLevel != citeLevel && posInString > newCiteLevel + 1 &&
    142        outStringCol) {
    143      BreakLine(aOutString, outStringCol, 0);
    144    }
    145    citeLevel = newCiteLevel;
    146 
    147    // Prepend the quote level to the out string if appropriate
    148    if (!outStringCol) {
    149      AddCite(aOutString, citeLevel);
    150      outStringCol = citeLevel + (citeLevel ? 1 : 0);
    151    }
    152    // If it's not a cite, and we're not at the beginning of a line in
    153    // the output string, add a space to separate new text from the
    154    // previous text.
    155    else if (outStringCol > citeLevel) {
    156      aOutString.Append(HTMLEditUtils::kSpace);
    157      ++outStringCol;
    158    }
    159 
    160    // find the next newline -- don't want to go farther than that
    161    int32_t nextNewline =
    162        tString.FindChar(HTMLEditUtils::kNewLine, posInString);
    163    if (nextNewline < 0) {
    164      nextNewline = length;
    165    }
    166 
    167    // For now, don't wrap unquoted lines at all.
    168    // This is because the plaintext edit window has already wrapped them
    169    // by the time we get them for rewrap, yet when we call the line
    170    // breaker, it will refuse to break backwards, and we'll end up
    171    // with a line that's too long and gets displayed as a lone word
    172    // on a line by itself.  Need special logic to detect this case
    173    // and break it ourselves without resorting to the line breaker.
    174    if (!citeLevel) {
    175      aOutString.Append(
    176          Substring(tString, posInString, nextNewline - posInString));
    177      outStringCol += nextNewline - posInString;
    178      if (nextNewline != (int32_t)length) {
    179        aOutString.Append(HTMLEditUtils::kNewLine);
    180        outStringCol = 0;
    181      }
    182      posInString = nextNewline + 1;
    183      continue;
    184    }
    185 
    186    // Otherwise we have to use the line breaker and loop
    187    // over this line of the input string to get all of it:
    188    while ((int32_t)posInString < nextNewline) {
    189      // Skip over initial spaces:
    190      while ((int32_t)posInString < nextNewline &&
    191             nsCRT::IsAsciiSpace(tString[posInString])) {
    192        ++posInString;
    193      }
    194 
    195      // If this is a short line, just append it and continue:
    196      if (outStringCol + nextNewline - posInString <=
    197          aWrapCol - citeLevel - 1) {
    198        // If this short line is the final one in the in string,
    199        // then we need to include the final newline, if any:
    200        if (nextNewline + 1 == (int32_t)length &&
    201            tString[nextNewline - 1] == HTMLEditUtils::kNewLine) {
    202          ++nextNewline;
    203        }
    204        // Trim trailing spaces:
    205        int32_t lastRealChar = nextNewline;
    206        while ((uint32_t)lastRealChar > posInString &&
    207               nsCRT::IsAsciiSpace(tString[lastRealChar - 1])) {
    208          --lastRealChar;
    209        }
    210 
    211        aOutString +=
    212            Substring(tString, posInString, lastRealChar - posInString);
    213        outStringCol += lastRealChar - posInString;
    214        posInString = nextNewline + 1;
    215        continue;
    216      }
    217 
    218      int32_t eol = posInString + aWrapCol - citeLevel - outStringCol;
    219      // eol is the prospective end of line.
    220      // If it's already less than our current position,
    221      // then our line is already too long, so break now.
    222      if (eol <= (int32_t)posInString) {
    223        BreakLine(aOutString, outStringCol, citeLevel);
    224        continue;  // continue inner loop, with outStringCol now at bol
    225      }
    226 
    227      MOZ_ASSERT(eol >= 0 && eol - posInString > 0);
    228 
    229      uint32_t breakPt = 0;
    230      Maybe<uint32_t> nextBreakPt;
    231      intl::LineBreakIteratorUtf16 lineBreakIter(Span<const char16_t>(
    232          tString.get() + posInString, length - posInString));
    233      while (true) {
    234        nextBreakPt = lineBreakIter.Next();
    235        if (!nextBreakPt ||
    236            *nextBreakPt > AssertedCast<uint32_t>(eol) - posInString) {
    237          break;
    238        }
    239        breakPt = *nextBreakPt;
    240      }
    241 
    242      if (breakPt == 0) {
    243        // If we couldn't find a breakpoint within the eol upper bound, and
    244        // we're not starting a new line, then end this line and loop around
    245        // again:
    246        if (outStringCol > citeLevel + 1) {
    247          BreakLine(aOutString, outStringCol, citeLevel);
    248          continue;  // continue inner loop, with outStringCol now at bol
    249        }
    250 
    251        MOZ_ASSERT(nextBreakPt.isSome(),
    252                   "Next() always treats end-of-text as a break");
    253        breakPt = *nextBreakPt;
    254      }
    255 
    256      // Special case: maybe we should have wrapped last time.
    257      // If the first breakpoint here makes the current line too long,
    258      // then if we already have text on the current line,
    259      // break and loop around again.
    260      // If we're at the beginning of the current line, though,
    261      // don't force a break since the long word might be a url
    262      // and breaking it would make it unclickable on the other end.
    263      const int SLOP = 6;
    264      if (outStringCol + breakPt > aWrapCol + SLOP &&
    265          outStringCol > citeLevel + 1) {
    266        BreakLine(aOutString, outStringCol, citeLevel);
    267        continue;
    268      }
    269 
    270      nsAutoString sub(Substring(tString, posInString, breakPt));
    271      // skip newlines or white-space at the end of the string
    272      int32_t subend = sub.Length();
    273      while (subend > 0 && IsSpace(sub[subend - 1])) {
    274        --subend;
    275      }
    276      sub.Left(sub, subend);
    277      aOutString += sub;
    278      outStringCol += sub.Length();
    279      // Advance past the white-space which caused the wrap:
    280      posInString += breakPt;
    281      while (posInString < length && IsSpace(tString[posInString])) {
    282        ++posInString;
    283      }
    284 
    285      // Add a newline and the quote level to the out string
    286      if (posInString < length) {  // not for the last line, though
    287        BreakLine(aOutString, outStringCol, citeLevel);
    288      }
    289    }  // end inner loop within one line of aInString
    290  }  // end outer loop over lines of aInString
    291 }
    292 
    293 }  // namespace mozilla