Merge inbound to mozilla-central. a=merge
authorGurzau Raul <rgurzau@mozilla.com>
Sat, 01 Dec 2018 16:30:37 +0200
changeset 508304 b3085cd7b7c20a55894eb3dcd8c92ab2bc0f03f3
parent 508299 b12e6afd5831cc692e666d4295cbb260613fd8ad (current diff)
parent 508303 dec172d2c7fd901849b9496601f73483b31f12b0 (diff)
child 508306 ac75e40dd2ab684d8bbf074ec677db2d77c93055
child 508344 e987e55bf874e2147dc799ecd8b686c6c711d722
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
--- a/netwerk/streamconv/converters/mozTXTToHTMLConv.cpp
+++ b/netwerk/streamconv/converters/mozTXTToHTMLConv.cpp
@@ -2,45 +2,43 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/TextUtils.h"
 #include "mozTXTToHTMLConv.h"
 #include "nsNetUtil.h"
 #include "nsUnicharUtils.h"
+#include "nsUnicodeProperties.h"
 #include "nsCRT.h"
 #include "nsIExternalProtocolHandler.h"
 #include "nsIIOService.h"
 #include "nsIURI.h"
 
 #include <algorithm>
 
 #ifdef DEBUG_BenB_Perf
 #include "prtime.h"
 #include "prinrval.h"
 #endif
 
-using mozilla::IsAsciiAlpha;
-using mozilla::IsAsciiDigit;
-
 const double growthRate = 1.2;
 
 // Bug 183111, editor now replaces multiple spaces with leading
 // 0xA0's and a single ending space, so need to treat 0xA0's as spaces.
 // 0xA0 is the Latin1/Unicode character for "non-breaking space (nbsp)"
 // Also recognize the Japanese ideographic space 0x3000 as a space.
 static inline bool IsSpace(const char16_t aChar) {
   return (nsCRT::IsAsciiSpace(aChar) || aChar == 0xA0 || aChar == 0x3000);
 }
 
 // Escape Char will take ch, escape it and append the result to
 // aStringToAppendTo
 void mozTXTToHTMLConv::EscapeChar(const char16_t ch,
-                                  nsString& aStringToAppendTo,
+                                  nsAString& aStringToAppendTo,
                                   bool inAttribute) {
   switch (ch) {
     case '<':
       aStringToAppendTo.AppendLiteral("&lt;");
       break;
     case '>':
       aStringToAppendTo.AppendLiteral("&gt;");
       break;
@@ -326,17 +324,17 @@ void mozTXTToHTMLConv::CalculateURLBound
 
   EscapeStr(desc, false);
 
   txtURL.Append(&aInString[start], end - start + 1);
   txtURL.StripWhitespace();
 
   // FIX ME
   nsAutoString temp2;
-  ScanTXT(&aInString[descstart], pos - descstart,
+  ScanTXT(nsDependentSubstring(aInString, descstart),
           ~kURLs /*prevents loop*/ & whathasbeendone, temp2);
   replaceBefore = temp2.Length();
 }
 
 bool mozTXTToHTMLConv::ShouldLinkify(const nsCString& aURL) {
   if (!mIOService) return false;
 
   nsAutoCString scheme;
@@ -502,16 +500,24 @@ bool mozTXTToHTMLConv::FindURL(const cha
         replaceAfter = resultReplaceAfter;
         state[check] = success;
       }
     }  // if
   }    // for
   return state[check] == success;
 }
 
+static inline bool IsAlpha(const uint32_t aChar) {
+  return mozilla::unicode::GetGenCategory(aChar) == nsUGenCategory::kLetter;
+}
+
+static inline bool IsDigit(const uint32_t aChar) {
+  return mozilla::unicode::GetGenCategory(aChar) == nsUGenCategory::kNumber;
+}
+
 bool mozTXTToHTMLConv::ItMatchesDelimited(const char16_t* aInString,
                                           int32_t aInLength,
                                           const char16_t* rep, int32_t aRepLen,
                                           LIMTYPE before, LIMTYPE after) {
   // this little method gets called a LOT. I found we were spending a
   // lot of time just calculating the length of the variable "rep"
   // over and over again every time we called it. So we're now passing
   // an integer in here.
@@ -520,27 +526,27 @@ bool mozTXTToHTMLConv::ItMatchesDelimite
   if (((before == LT_IGNORE && (after == LT_IGNORE || after == LT_DELIMITER)) &&
        textLen < aRepLen) ||
       ((before != LT_IGNORE || (after != LT_IGNORE && after != LT_DELIMITER)) &&
        textLen < aRepLen + 1) ||
       (before != LT_IGNORE && after != LT_IGNORE && after != LT_DELIMITER &&
        textLen < aRepLen + 2))
     return false;
 
-  char16_t text0 = aInString[0];
-  char16_t textAfterPos = aInString[aRepLen + (before == LT_IGNORE ? 0 : 1)];
+  uint32_t text0 = aInString[0];
+  uint32_t textAfterPos = aInString[aRepLen + (before == LT_IGNORE ? 0 : 1)];
 
-  if ((before == LT_ALPHA && !IsAsciiAlpha(text0)) ||
-      (before == LT_DIGIT && !IsAsciiDigit(text0)) ||
+  if ((before == LT_ALPHA && !IsAlpha(text0)) ||
+      (before == LT_DIGIT && !IsDigit(text0)) ||
       (before == LT_DELIMITER &&
-       (IsAsciiAlpha(text0) || IsAsciiDigit(text0) || text0 == *rep)) ||
-      (after == LT_ALPHA && !IsAsciiAlpha(textAfterPos)) ||
-      (after == LT_DIGIT && !IsAsciiDigit(textAfterPos)) ||
+       (IsAlpha(text0) || IsDigit(text0) || text0 == *rep)) ||
+      (after == LT_ALPHA && !IsAlpha(textAfterPos)) ||
+      (after == LT_DIGIT && !IsDigit(textAfterPos)) ||
       (after == LT_DELIMITER &&
-       (IsAsciiAlpha(textAfterPos) || IsAsciiDigit(textAfterPos) ||
+       (IsAlpha(textAfterPos) || IsDigit(textAfterPos) ||
         textAfterPos == *rep)) ||
       !Substring(Substring(aInString, aInString + aInLength),
                  (before == LT_IGNORE ? 0 : 1), aRepLen)
            .Equals(Substring(rep, rep + aRepLen),
                    nsCaseInsensitiveStringComparator()))
     return false;
 
   return true;
@@ -561,17 +567,17 @@ uint32_t mozTXTToHTMLConv::NumberOfMatch
   return result;
 }
 
 // NOTE: the converted html for the phrase is appended to aOutString
 // tagHTML and attributeHTML are plain ASCII (literal strings, in fact)
 bool mozTXTToHTMLConv::StructPhraseHit(
     const char16_t* aInString, int32_t aInStringLength, bool col0,
     const char16_t* tagTXT, int32_t aTagTXTLen, const char* tagHTML,
-    const char* attributeHTML, nsString& aOutString, uint32_t& openTags) {
+    const char* attributeHTML, nsAString& aOutString, uint32_t& openTags) {
   /* We're searching for the following pattern:
      LT_DELIMITER - "*" - ALPHA -
      [ some text (maybe more "*"-pairs) - ALPHA ] "*" - LT_DELIMITER.
      <strong> is only inserted, if existence of a pair could be verified
      We use the first opening/closing tag, if we can choose */
 
   const char16_t* newOffset = aInString;
   int32_t newLength = aInStringLength;
@@ -654,17 +660,17 @@ bool mozTXTToHTMLConv::SmilyHit(const ch
     return true;
   }
 
   return false;
 }
 
 // the glyph is appended to aOutputString instead of the original string...
 bool mozTXTToHTMLConv::GlyphHit(const char16_t* aInString, int32_t aInLength,
-                                bool col0, nsString& aOutputString,
+                                bool col0, nsAString& aOutputString,
                                 int32_t& glyphTextLen) {
   char16_t text0 = aInString[0];
   char16_t text1 = aInString[1];
   char16_t firstChar = (col0 ? text0 : text1);
 
   // temporary variable used to store the glyph html text
   nsAutoString outputHTML;
   bool bTestSmilie;
@@ -911,47 +917,59 @@ int32_t mozTXTToHTMLConv::CiteLevelTXT(c
       }
     } else
       moreCites = false;
   }
 
   return result;
 }
 
-void mozTXTToHTMLConv::ScanTXT(const char16_t* aInString,
-                               int32_t aInStringLength, uint32_t whattodo,
-                               nsString& aOutString) {
+NS_IMETHODIMP
+mozTXTToHTMLConv::ScanTXT(const nsAString& aInString, uint32_t whattodo,
+                          nsAString& aOutString) {
+  if (aInString.Length() == 0) {
+    aOutString.Truncate();
+    return NS_OK;
+  }
+
+  if (!aOutString.SetCapacity(uint32_t(aInString.Length() * growthRate),
+                              mozilla::fallible)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
   bool doURLs = 0 != (whattodo & kURLs);
   bool doGlyphSubstitution = 0 != (whattodo & kGlyphSubstitution);
   bool doStructPhrase = 0 != (whattodo & kStructPhrase);
 
   uint32_t structPhrase_strong = 0;  // Number of currently open tags
   uint32_t structPhrase_underline = 0;
   uint32_t structPhrase_italic = 0;
   uint32_t structPhrase_code = 0;
 
   nsAutoString outputHTML;  // moved here for performance increase
 
-  for (uint32_t i = 0; int32_t(i) < aInStringLength;) {
+  const char16_t* rawInputString = aInString.BeginReading();
+
+  for (uint32_t i = 0; i < aInString.Length();) {
     if (doGlyphSubstitution) {
       int32_t glyphTextLen;
-      if (GlyphHit(&aInString[i], aInStringLength - i, i == 0, aOutString,
-                   glyphTextLen)) {
+      if (GlyphHit(&rawInputString[i], aInString.Length() - i, i == 0,
+                   aOutString, glyphTextLen)) {
         i += glyphTextLen;
         continue;
       }
     }
 
     if (doStructPhrase) {
-      const char16_t* newOffset = aInString;
-      int32_t newLength = aInStringLength;
+      const char16_t* newOffset = rawInputString;
+      int32_t newLength = aInString.Length();
       if (i > 0)  // skip the first element?
       {
-        newOffset = &aInString[i - 1];
-        newLength = aInStringLength - i + 1;
+        newOffset = &rawInputString[i - 1];
+        newLength = aInString.Length() - i + 1;
       }
 
       switch (aInString[i])  // Performance increase
       {
         case '*':
           if (StructPhraseHit(newOffset, newLength, i == 0, u"*", 1, "b",
                               "class=\"moz-txt-star\"", aOutString,
                               structPhrase_strong)) {
@@ -988,22 +1006,23 @@ void mozTXTToHTMLConv::ScanTXT(const cha
     }
 
     if (doURLs) {
       switch (aInString[i]) {
         case ':':
         case '@':
         case '.':
           if ((i == 0 || ((i > 0) && aInString[i - 1] != ' ')) &&
-              aInString[i + 1] != ' ')  // Performance increase
+              ((i == aInString.Length() - 1) ||
+               (aInString[i + 1] != ' ')))  // Performance increase
           {
             int32_t replaceBefore;
             int32_t replaceAfter;
-            if (FindURL(aInString, aInStringLength, i, whattodo, outputHTML,
-                        replaceBefore, replaceAfter) &&
+            if (FindURL(rawInputString, aInString.Length(), i, whattodo,
+                        outputHTML, replaceBefore, replaceAfter) &&
                 structPhrase_strong + structPhrase_italic +
                         structPhrase_underline + structPhrase_code ==
                     0
                 /* workaround for bug #19445 */) {
               aOutString.Cut(aOutString.Length() - replaceBefore,
                              replaceBefore);
               aOutString += outputHTML;
               i += replaceAfter + 1;
@@ -1024,20 +1043,28 @@ void mozTXTToHTMLConv::ScanTXT(const cha
         break;
       // Normal characters
       default:
         aOutString += aInString[i];
         i++;
         break;
     }
   }
+  return NS_OK;
 }
 
-void mozTXTToHTMLConv::ScanHTML(nsString& aInString, uint32_t whattodo,
-                                nsString& aOutString) {
+NS_IMETHODIMP
+mozTXTToHTMLConv::ScanHTML(const nsAString& input, uint32_t whattodo,
+                           nsAString& aOutString) {
+  const nsPromiseFlatString& aInString = PromiseFlatString(input);
+  if (!aOutString.SetCapacity(uint32_t(aInString.Length() * growthRate),
+                              mozilla::fallible)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
   // some common variables we were recalculating
   // every time inside the for loop...
   int32_t lengthOfInString = aInString.Length();
   const char16_t* uniBuffer = aInString.get();
 
 #ifdef DEBUG_BenB_Perf
   PRTime parsing_start = PR_IntervalNow();
 #endif
@@ -1113,24 +1140,25 @@ void mozTXTToHTMLConv::ScanHTML(nsString
     } else {
       uint32_t start = uint32_t(i);
       i = aInString.FindChar('<', i);
       if (i == kNotFound) i = lengthOfInString;
 
       nsString tempString;
       tempString.SetCapacity(uint32_t((uint32_t(i) - start) * growthRate));
       UnescapeStr(uniBuffer, start, uint32_t(i) - start, tempString);
-      ScanTXT(tempString.get(), tempString.Length(), whattodo, aOutString);
+      ScanTXT(tempString, whattodo, aOutString);
     }
   }
 
 #ifdef DEBUG_BenB_Perf
   printf("ScanHTML time:    %d ms\n",
          PR_IntervalToMilliseconds(PR_IntervalNow() - parsing_start));
 #endif
+  return NS_OK;
 }
 
 /****************************************************************************
   XPCOM Interface
 *****************************************************************************/
 
 NS_IMETHODIMP
 mozTXTToHTMLConv::Convert(nsIInputStream* aFromStream, const char* aFromType,
@@ -1167,56 +1195,16 @@ mozTXTToHTMLConv::OnStopRequest(nsIReque
 NS_IMETHODIMP
 mozTXTToHTMLConv::CiteLevelTXT(const char16_t* line, uint32_t* logLineStart,
                                uint32_t* _retval) {
   if (!logLineStart || !_retval || !line) return NS_ERROR_NULL_POINTER;
   *_retval = CiteLevelTXT(line, *logLineStart);
   return NS_OK;
 }
 
-NS_IMETHODIMP
-mozTXTToHTMLConv::ScanTXT(const char16_t* text, uint32_t whattodo,
-                          char16_t** _retval) {
-  NS_ENSURE_ARG(text);
-
-  // FIX ME!!!
-  nsString outString;
-  int32_t inLength = NS_strlen(text);
-  // by setting a large capacity up front, we save time
-  // when appending characters to the output string because we don't
-  // need to reallocate and re-copy the characters already in the out String.
-  NS_ASSERTION(inLength, "ScanTXT passed 0 length string");
-  if (inLength == 0) {
-    *_retval = NS_xstrdup(text);
-    return NS_OK;
-  }
-
-  outString.SetCapacity(uint32_t(inLength * growthRate));
-  ScanTXT(text, inLength, whattodo, outString);
-
-  *_retval = ToNewUnicode(outString);
-  return *_retval ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
-}
-
-NS_IMETHODIMP
-mozTXTToHTMLConv::ScanHTML(const char16_t* text, uint32_t whattodo,
-                           char16_t** _retval) {
-  NS_ENSURE_ARG(text);
-
-  // FIX ME!!!
-  nsString outString;
-  nsString inString(
-      text);  // look at this nasty extra copy of the entire input buffer!
-  outString.SetCapacity(uint32_t(inString.Length() * growthRate));
-
-  ScanHTML(inString, whattodo, outString);
-  *_retval = ToNewUnicode(outString);
-  return *_retval ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
-}
-
 nsresult MOZ_NewTXTToHTMLConv(mozTXTToHTMLConv** aConv) {
   MOZ_ASSERT(aConv != nullptr, "null ptr");
   if (!aConv) return NS_ERROR_NULL_POINTER;
 
   *aConv = new mozTXTToHTMLConv();
   if (!*aConv) return NS_ERROR_OUT_OF_MEMORY;
 
   NS_ADDREF(*aConv);
--- a/netwerk/streamconv/converters/mozTXTToHTMLConv.h
+++ b/netwerk/streamconv/converters/mozTXTToHTMLConv.h
@@ -28,27 +28,16 @@ class mozTXTToHTMLConv : public mozITXTT
   NS_DECL_ISUPPORTS
 
   NS_DECL_MOZITXTTOHTMLCONV
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSISTREAMLISTENER
   NS_DECL_NSISTREAMCONVERTER
 
   /**
-    see mozITXTToHTMLConv::ScanTXT
-   */
-  void ScanTXT(const char16_t* aInString, int32_t aInStringLength,
-               uint32_t whattodo, nsString& aOutString);
-
-  /**
-    see mozITXTToHTMLConv::ScanHTML. We will modify aInString potentially...
-   */
-  void ScanHTML(nsString& aInString, uint32_t whattodo, nsString& aOutString);
-
-  /**
     see mozITXTToHTMLConv::CiteLevelTXT
    */
   int32_t CiteLevelTXT(const char16_t* line, uint32_t& logLineStart);
 
   //////////////////////////////////////////////////////////
  protected:
   //////////////////////////////////////////////////////////
   nsCOMPtr<nsIIOService>
@@ -107,17 +96,17 @@ class mozTXTToHTMLConv : public mozITXTT
     "Char" in function name to avoid side effects with nsString(ch)
     constructors.
     @param ch (in)
     @param aStringToAppendto (out) - the string to append the escaped
                                      string to.
     @param inAttribute (in) - will escape quotes, too (which is
                               only needed for attribute values)
   */
-  void EscapeChar(const char16_t ch, nsString& aStringToAppendto,
+  void EscapeChar(const char16_t ch, nsAString& aStringToAppendto,
                   bool inAttribute);
 
   /**
     See EscapeChar. Escapes the string in place.
   */
   void EscapeStr(nsString& aInString, bool inAttribute);
 
   /**
@@ -243,17 +232,17 @@ class mozTXTToHTMLConv : public mozITXTT
                 e.g. "class=txt_star"
     @param aOutString: string to APPEND the converted html into
     @param open (in/out): Number of currently open tags of type tagHTML
     @return Conversion succeeded
   */
   bool StructPhraseHit(const char16_t* aInString, int32_t aInStringLength,
                        bool col0, const char16_t* tagTXT, int32_t aTagTxtLen,
                        const char* tagHTML, const char* attributeHTML,
-                       nsString& aOutputString, uint32_t& openTags);
+                       nsAString& aOutputString, uint32_t& openTags);
 
   /**
     @param text (in), col0 (in): see GlyphHit
     @param tagTXT (in): Smily, see also StructPhraseHit
     @param imageName (in): the basename of the file that contains the image for
                            this smilie
     @param outputHTML (out): new string containing the html for the smily
     @param glyphTextLen (out): see GlyphHit
@@ -273,17 +262,17 @@ class mozTXTToHTMLConv : public mozITXTT
                 else
                   starting one char before Glyph
     @param col0 (in): text starts at the beginning of the line (or paragraph)
     @param aOutString (out): APPENDS html for the glyph to this string
     @param glyphTextLen (out): Length of original text to replace
     @return see StructPhraseHit
   */
   bool GlyphHit(const char16_t* aInString, int32_t aInLength, bool col0,
-                nsString& aOutString, int32_t& glyphTextLen);
+                nsAString& aOutString, int32_t& glyphTextLen);
 
   /**
     Check if a given url should be linkified.
     @param aURL (in): url to be checked on.
   */
   bool ShouldLinkify(const nsCString& aURL);
 };
 
--- a/netwerk/streamconv/mozITXTToHTMLConv.idl
+++ b/netwerk/streamconv/mozITXTToHTMLConv.idl
@@ -36,17 +36,17 @@ interface mozITXTToHTMLConv : nsIStreamC
                or just a substring.<p>
                Must be non-escaped, pure unicode.<p>
                <em>Note:</em> ScanTXT(a, o) + ScanTXT(b, o) may be !=
                Scan(a + b, o)
   @param whattodo: Bitfield describing the modes of operation
   @result      "<", ">" and "&" are escaped and HTML tags are inserted where
                appropriate.
  */
-  wstring   scanTXT(in wstring text, in unsigned long whattodo);
+  AString   scanTXT(in AString text, in unsigned long whattodo);
 
 /**
   Adds additional formatting to user edited text, that the user was too lazy
   or "unknowledged" (DELETEME: is that a word?) to make.
   <p>
   <em>Note:</em> Don't use kGlyphSubstitution with this function. This option
   generates tags, that are unuseable for UAs other than Mozilla. This would
   be a data loss bug.
@@ -55,17 +55,17 @@ interface mozITXTToHTMLConv : nsIStreamC
                or just a substring.<p>
                Must be correct HTML. "<", ">" and "&" must be escaped,
                other chars must be pure unicode.<p>
                <em>Note:</em> ScanTXT(a, o) + ScanTXT(b, o) may be !=
                Scan(a + b, o)
   @param whattodo: Bitfield describing the modes of operation
   @result      Additional HTML tags are inserted where appropriate.
  */
-  wstring   scanHTML(in wstring text, in unsigned long whattodo);
+  AString   scanHTML(in AString text, in unsigned long whattodo);
 
 /**
   @param line: line in original msg, possibly starting starting with
                txt quote tags like ">"
   @param logLineStart: pos in line, where the real content (logical line)
                begins, i.e. pos after all txt quote tags.
                E.g. position of "t" in "> > text".
                Initial value must be 0, unless line is not real line.
--- a/netwerk/test/unit/test_mozTXTToHTMLConv.js
+++ b/netwerk/test/unit/test_mozTXTToHTMLConv.js
@@ -122,16 +122,77 @@ function run_test() {
       url: "http://[2001:db8::1]:80/"
     },
     {
       input: "test http://www.map.com/map.php?t=Nova_Scotia&markers=//Not_a_survey||description=plm2 test",
       url: "http://www.map.com/map.php?t=Nova_Scotia&amp;markers=//Not_a_survey||description=plm2"
     }
   ];
 
+  const scanTXTglyph = [
+    // Some "glyph" testing (not exhaustive, the system supports 16 different
+    // smiley types).
+    {
+      input: "this is superscript: x^2",
+      results: ["<sup", "2", "</sup>"]
+    },
+    {
+      input: "this is plus-minus: +/-",
+      results: ["&plusmn;"]
+    },
+    {
+      input: "this is a smiley :)",
+      results: ["moz-smiley-s1"]
+    },
+    {
+      input: "this is a smiley :-)",
+      results: ["moz-smiley-s1"]
+    },
+    {
+      input: "this is a smiley :-(",
+      results: ["moz-smiley-s2"]
+    },
+  ];
+
+  const scanTXTstrings = [
+    "underline",                                  // ASCII
+    "äöüßáéíóúî",                                 // Latin-1
+    "\u016B\u00F1\u0257\u0119\u0211\u0142\u00ED\u00F1\u0119",
+                                                  // Pseudo-ese ūñɗęȑłíñę
+    "\u01DDu\u0131\u0283\u0279\u01DDpun",         // Upside down ǝuıʃɹǝpun
+    "\u03C5\u03C0\u03BF\u03B3\u03C1\u03AC\u03BC\u03BC\u03B9\u03C3\u03B7",
+                                                  // Greek υπογράμμιση
+    "\u0441\u0438\u043B\u044C\u043D\u0443\u044E", // Russian сильную
+    "\u0C2C\u0C32\u0C2E\u0C46\u0C56\u0C28",       // Telugu బలమైన
+    "\u508D\u7DDA\u3059\u308B"                    // Japanese 傍線する
+  ];
+
+  const scanTXTstructs = [
+      {
+        delimiter: "/",
+        tag: "i",
+        class: "moz-txt-slash"
+      },
+      {
+        delimiter: "*",
+        tag: "b",
+        class: "moz-txt-star"
+      },
+      {
+        delimiter: "_",
+        tag: "span",
+        class: "moz-txt-underscore"
+      },
+      {
+        delimiter: "|",
+        tag: "code",
+        class: "moz-txt-verticalline"
+      }
+    ];
+
   const scanHTMLtests = [
     {
       input: "http://foo.example.com",
       shouldChange: true
     },
     {
       input: " <a href='http://a.example.com/'>foo</a>",
       shouldChange: false
@@ -190,16 +251,43 @@ function run_test() {
     let t = scanTXTtests[i];
     let output = converter.scanTXT(t.input, Ci.mozITXTToHTMLConv.kURLs);
     let link = hrefLink(t.url);
     if (!output.includes(link))
       do_throw("Unexpected conversion by scanTXT: input=" + t.input +
                ", output=" + output + ", link=" + link);
   }
 
+  for (let i = 0; i < scanTXTglyph.length; i++) {
+    let t = scanTXTglyph[i];
+    let output = converter.scanTXT(t.input, Ci.mozITXTToHTMLConv.kGlyphSubstitution);
+    for (let j = 0; j < t.results.length; j++)
+      if (!output.includes(t.results[j]))
+        do_throw("Unexpected conversion by scanTXT: input=" + t.input +
+                 ", output=" + output + ", expected=" + t.results[j]);
+  }
+
+  for (let i = 0; i < scanTXTstrings.length; ++i) {
+    for (let j = 0; j < scanTXTstructs.length; ++j) {
+      let input = scanTXTstructs[j].delimiter + scanTXTstrings[i] + scanTXTstructs[j].delimiter;
+      let expected = "<" + scanTXTstructs[j].tag +
+                     " class=\"" + scanTXTstructs[j].class + "\">" +
+                     "<span class=\"moz-txt-tag\">" +
+                     scanTXTstructs[j].delimiter +
+                     "</span>" +
+                     scanTXTstrings[i] +
+                     "<span class=\"moz-txt-tag\">" +
+                     scanTXTstructs[j].delimiter +
+                     "</span>" +
+                     "</" + scanTXTstructs[j].tag + ">";
+      let actual = converter.scanTXT(input, Ci.mozITXTToHTMLConv.kStructPhrase);
+      Assert.equal(encodeURIComponent(actual), encodeURIComponent(expected));
+    }
+  }
+
   for (let i = 0; i < scanHTMLtests.length; i++) {
     let t = scanHTMLtests[i];
     let output = converter.scanHTML(t.input, Ci.mozITXTToHTMLConv.kURLs);
     let changed = (t.input != output);
     if (changed != t.shouldChange) {
       do_throw("Unexpected change by scanHTML: changed=" + changed +
                ", shouldChange=" + t.shouldChange +
                ", \ninput=" + t.input +