Bug 1407112 (part 3) - Merge prefread.{cpp,h} into Preferences.cpp. r=glandium. draft
authorNicholas Nethercote <nnethercote@mozilla.com>
Tue, 10 Oct 2017 16:16:47 +1100
changeset 677126 5d81b1179f29cbec1b745b5706c97c6eb62e5adb
parent 677125 8f60740b1bb7ec2ef3da464fcdc21805b8744f9b
child 677127 97f0b592f37b292e22cd187ccb1cd20c9c216fc0
push id83701
push usernnethercote@mozilla.com
push dateTue, 10 Oct 2017 05:22:10 +0000
reviewersglandium
bugs1407112
milestone58.0a1
Bug 1407112 (part 3) - Merge prefread.{cpp,h} into Preferences.cpp. r=glandium. Note: the #include of prefread.h from prefapi.cpp was unnecessary. MozReview-Commit-ID: E0JefORRtgs
modules/libpref/Preferences.cpp
modules/libpref/moz.build
modules/libpref/prefapi.cpp
modules/libpref/prefread.cpp
modules/libpref/prefread.h
--- a/modules/libpref/Preferences.cpp
+++ b/modules/libpref/Preferences.cpp
@@ -1,14 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
 #include "GeckoProfiler.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/PContent.h"
 #include "mozilla/HashFunctions.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/ModuleUtils.h"
@@ -54,25 +58,25 @@
 #include "nsNetUtil.h"
 #include "nsPrintfCString.h"
 #include "nsQuickSort.h"
 #include "nsReadableUtils.h"
 #include "nsRefPtrHashtable.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsThreadUtils.h"
+#include "nsUTF8Utils.h"
 #include "nsWeakReference.h"
 #include "nsXPCOMCID.h"
 #include "nsXPCOM.h"
 #include "nsXULAppAPI.h"
 #include "nsZipArchive.h"
 #include "PLDHashTable.h"
 #include "prefapi.h"
 #include "prefapi_private_data.h"
-#include "prefread.h"
 
 #ifdef MOZ_CRASHREPORTER
 #include "nsICrashReporter.h"
 #endif
 
 using namespace mozilla;
 
 #ifdef DEBUG
@@ -102,16 +106,760 @@ public:
     return NS_ERROR_NOT_AVAILABLE;                                             \
   }
 
 #define WATCHING_PREF_RAII()
 
 #endif // DEBUG
 
 //===========================================================================
+// Prefs parsing
+//===========================================================================
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Callback function used to notify consumer of preference name value pairs.
+// The pref name and value must be copied by the implementor of the callback
+// if they are needed beyond the scope of the callback function.
+//
+// |aClosure| is user data passed to PREF_InitParseState.
+// |aPref| is the preference name.
+// |aValue| is the preference value.
+// |aType| is the preference type (PREF_STRING, PREF_INT, or PREF_BOOL).
+// |aIsDefault| indicates if it's a default preference.
+// |aIsStickyDefault| indicates if it's a sticky default preference.
+typedef void (*PrefReader)(void* aClosure,
+                           const char* aPref,
+                           PrefValue aValue,
+                           PrefType aType,
+                           bool aIsDefault,
+                           bool aIsStickyDefault);
+
+// Report any errors or warnings we encounter during parsing.
+typedef void (*PrefParseErrorReporter)(const char* aMessage,
+                                       int aLine,
+                                       bool aError);
+
+typedef struct PrefParseState
+{
+  PrefReader mReader;
+  PrefParseErrorReporter mReporter;
+  void* mClosure;
+  int mState;            // PREF_PARSE_...
+  int mNextState;        // sometimes used...
+  const char* mStrMatch; // string to match
+  int mStrIndex;         // next char of smatch to check;
+                         // also, counter in \u parsing
+  char16_t mUtf16[2];    // parsing UTF16 (\u) escape
+  int mEscLen;           // length in mEscTmp
+  char mEscTmp[6];       // raw escape to put back if err
+  char mQuoteChar;       // char delimiter for quotations
+  char* mLb;             // line buffer (only allocation)
+  char* mLbCur;          // line buffer cursor
+  char* mLbEnd;          // line buffer end
+  char* mVb;             // value buffer (ptr into mLb)
+  PrefType mVtype;       // PREF_{STRING,INT,BOOL}
+  bool mIsDefault;       // true if (default) pref
+  bool mIsStickyDefault; // true if (sticky) pref
+} PrefParseState;
+
+// Initialize a PrefParseState instance.
+//
+// |aPS| is the PrefParseState instance.
+// |aReader| is the PrefReader callback function, which will be called once for
+// each preference name value pair extracted.
+// |aReporter| is the PrefParseErrorReporter callback function, which will be
+// called if we encounter any errors (stop) or warnings (continue) during
+// parsing.
+// |aClosure| is extra data passed to |aReader|.
+void
+PREF_InitParseState(PrefParseState* aPS,
+                    PrefReader aReader,
+                    PrefParseErrorReporter aReporter,
+                    void* aClosure);
+
+// Release any memory in use by the PrefParseState instance.
+void
+PREF_FinalizeParseState(PrefParseState* aPS);
+
+// Parse a buffer containing some portion of a preference file.  This function
+// may be called repeatedly as new data is made available. The PrefReader
+// callback function passed PREF_InitParseState will be called as preference
+// name value pairs are extracted from the data. Returns false if buffer
+// contains malformed content.
+bool
+PREF_ParseBuf(PrefParseState* aPS, const char* aBuf, int aBufLen);
+
+#ifdef __cplusplus
+}
+#endif
+
+#ifdef TEST_PREFREAD
+#include <stdio.h>
+#define NS_WARNING(_s) printf(">>> " _s "!\n")
+#define NS_NOTREACHED(_s) NS_WARNING(_s)
+#else
+#include "nsDebug.h" // for NS_WARNING
+#endif
+
+// Pref parser states.
+enum
+{
+  PREF_PARSE_INIT,
+  PREF_PARSE_MATCH_STRING,
+  PREF_PARSE_UNTIL_NAME,
+  PREF_PARSE_QUOTED_STRING,
+  PREF_PARSE_UNTIL_COMMA,
+  PREF_PARSE_UNTIL_VALUE,
+  PREF_PARSE_INT_VALUE,
+  PREF_PARSE_COMMENT_MAYBE_START,
+  PREF_PARSE_COMMENT_BLOCK,
+  PREF_PARSE_COMMENT_BLOCK_MAYBE_END,
+  PREF_PARSE_ESC_SEQUENCE,
+  PREF_PARSE_HEX_ESCAPE,
+  PREF_PARSE_UTF16_LOW_SURROGATE,
+  PREF_PARSE_UNTIL_OPEN_PAREN,
+  PREF_PARSE_UNTIL_CLOSE_PAREN,
+  PREF_PARSE_UNTIL_SEMICOLON,
+  PREF_PARSE_UNTIL_EOL
+};
+
+#define UTF16_ESC_NUM_DIGITS 4
+#define HEX_ESC_NUM_DIGITS 2
+#define BITS_PER_HEX_DIGIT 4
+
+static const char kUserPref[] = "user_pref";
+static const char kPref[] = "pref";
+static const char kPrefSticky[] = "sticky_pref";
+static const char kTrue[] = "true";
+static const char kFalse[] = "false";
+
+// This function will increase the size of the buffer owned by the given pref
+// parse state. We currently use a simple doubling algorithm, but the only hard
+// requirement is that it increase the buffer by at least the size of the
+// aPS->mEscTmp buffer used for escape processing (currently 6 bytes).
+//
+// The buffer is used to store partial pref lines. It is freed when the parse
+// state is destroyed.
+//
+// @param aPS
+//        parse state instance
+//
+// This function updates all pointers that reference an address within mLb
+// since realloc may relocate the buffer.
+//
+// @return false if insufficient memory.
+static bool
+pref_GrowBuf(PrefParseState* aPS)
+{
+  int bufLen, curPos, valPos;
+
+  bufLen = aPS->mLbEnd - aPS->mLb;
+  curPos = aPS->mLbCur - aPS->mLb;
+  valPos = aPS->mVb - aPS->mLb;
+
+  if (bufLen == 0) {
+    bufLen = 128; // default buffer size
+  } else {
+    bufLen <<= 1; // double buffer size
+  }
+
+#ifdef TEST_PREFREAD
+  fprintf(stderr, ">>> realloc(%d)\n", bufLen);
+#endif
+
+  aPS->mLb = (char*)realloc(aPS->mLb, bufLen);
+  if (!aPS->mLb) {
+    return false;
+  }
+
+  aPS->mLbCur = aPS->mLb + curPos;
+  aPS->mLbEnd = aPS->mLb + bufLen;
+  aPS->mVb = aPS->mLb + valPos;
+
+  return true;
+}
+
+// Report an error or a warning. If not specified, just dump to stderr.
+static void
+pref_ReportParseProblem(PrefParseState& aPS,
+                        const char* aMessage,
+                        int aLine,
+                        bool aError)
+{
+  if (aPS.mReporter) {
+    aPS.mReporter(aMessage, aLine, aError);
+  } else {
+    printf_stderr("**** Preference parsing %s (line %d) = %s **\n",
+                  (aError ? "error" : "warning"),
+                  aLine,
+                  aMessage);
+  }
+}
+
+// This function is called when a complete pref name-value pair has been
+// extracted from the input data.
+//
+// @param aPS
+//        parse state instance
+//
+// @return false to indicate a fatal error.
+static bool
+pref_DoCallback(PrefParseState* aPS)
+{
+  PrefValue value;
+
+  switch (aPS->mVtype) {
+    case PrefType::String:
+      value.mStringVal = aPS->mVb;
+      break;
+
+    case PrefType::Int:
+      if ((aPS->mVb[0] == '-' || aPS->mVb[0] == '+') && aPS->mVb[1] == '\0') {
+        pref_ReportParseProblem(*aPS, "invalid integer value", 0, true);
+        NS_WARNING("malformed integer value");
+        return false;
+      }
+      value.mIntVal = atoi(aPS->mVb);
+      break;
+
+    case PrefType::Bool:
+      value.mBoolVal = (aPS->mVb == kTrue);
+      break;
+
+    default:
+      break;
+  }
+
+  (*aPS->mReader)(aPS->mClosure,
+                  aPS->mLb,
+                  value,
+                  aPS->mVtype,
+                  aPS->mIsDefault,
+                  aPS->mIsStickyDefault);
+  return true;
+}
+
+void
+PREF_InitParseState(PrefParseState* aPS,
+                    PrefReader aReader,
+                    PrefParseErrorReporter aReporter,
+                    void* aClosure)
+{
+  memset(aPS, 0, sizeof(*aPS));
+  aPS->mReader = aReader;
+  aPS->mClosure = aClosure;
+  aPS->mReporter = aReporter;
+}
+
+void
+PREF_FinalizeParseState(PrefParseState* aPS)
+{
+  if (aPS->mLb) {
+    free(aPS->mLb);
+  }
+}
+
+// Pseudo-BNF
+// ----------
+// function      = LJUNK function-name JUNK function-args
+// function-name = "user_pref" | "pref" | "sticky_pref"
+// function-args = "(" JUNK pref-name JUNK "," JUNK pref-value JUNK ")" JUNK ";"
+// pref-name     = quoted-string
+// pref-value    = quoted-string | "true" | "false" | integer-value
+// JUNK          = *(WS | comment-block | comment-line)
+// LJUNK         = *(WS | comment-block | comment-line | bcomment-line)
+// WS            = SP | HT | LF | VT | FF | CR
+// SP            = <US-ASCII SP, space (32)>
+// HT            = <US-ASCII HT, horizontal-tab (9)>
+// LF            = <US-ASCII LF, linefeed (10)>
+// VT            = <US-ASCII HT, vertical-tab (11)>
+// FF            = <US-ASCII FF, form-feed (12)>
+// CR            = <US-ASCII CR, carriage return (13)>
+// comment-block = <C/C++ style comment block>
+// comment-line  = <C++ style comment line>
+// bcomment-line = <bourne-shell style comment line>
+//
+bool
+PREF_ParseBuf(PrefParseState* aPS, const char* aBuf, int aBufLen)
+{
+  const char* end;
+  char c;
+  char udigit;
+  int state;
+
+  // The line number is currently only used for the error/warning reporting.
+  int lineNum = 0;
+
+  state = aPS->mState;
+  for (end = aBuf + aBufLen; aBuf != end; ++aBuf) {
+    c = *aBuf;
+    if (c == '\r' || c == '\n' || c == 0x1A) {
+      lineNum++;
+    }
+
+    switch (state) {
+      // initial state
+      case PREF_PARSE_INIT:
+        if (aPS->mLbCur != aPS->mLb) { // reset state
+          aPS->mLbCur = aPS->mLb;
+          aPS->mVb = nullptr;
+          aPS->mVtype = PrefType::Invalid;
+          aPS->mIsDefault = false;
+          aPS->mIsStickyDefault = false;
+        }
+        switch (c) {
+          case '/': // begin comment block or line?
+            state = PREF_PARSE_COMMENT_MAYBE_START;
+            break;
+          case '#': // accept shell style comments
+            state = PREF_PARSE_UNTIL_EOL;
+            break;
+          case 'u': // indicating user_pref
+          case 's': // indicating sticky_pref
+          case 'p': // indicating pref
+            if (c == 'u') {
+              aPS->mStrMatch = kUserPref;
+            } else if (c == 's') {
+              aPS->mStrMatch = kPrefSticky;
+            } else {
+              aPS->mStrMatch = kPref;
+            }
+            aPS->mStrIndex = 1;
+            aPS->mNextState = PREF_PARSE_UNTIL_OPEN_PAREN;
+            state = PREF_PARSE_MATCH_STRING;
+            break;
+            // else skip char
+        }
+        break;
+
+      // string matching
+      case PREF_PARSE_MATCH_STRING:
+        if (c == aPS->mStrMatch[aPS->mStrIndex++]) {
+          // If we've matched all characters, then move to next state.
+          if (aPS->mStrMatch[aPS->mStrIndex] == '\0') {
+            state = aPS->mNextState;
+            aPS->mNextState = PREF_PARSE_INIT; // reset next state
+          }
+          // else wait for next char
+        } else {
+          pref_ReportParseProblem(*aPS, "non-matching string", lineNum, true);
+          NS_WARNING("malformed pref file");
+          return false;
+        }
+        break;
+
+      // quoted string parsing
+      case PREF_PARSE_QUOTED_STRING:
+        // we assume that the initial quote has already been consumed
+        if (aPS->mLbCur == aPS->mLbEnd && !pref_GrowBuf(aPS)) {
+          return false; // out of memory
+        }
+        if (c == '\\') {
+          state = PREF_PARSE_ESC_SEQUENCE;
+        } else if (c == aPS->mQuoteChar) {
+          *aPS->mLbCur++ = '\0';
+          state = aPS->mNextState;
+          aPS->mNextState = PREF_PARSE_INIT; // reset next state
+        } else {
+          *aPS->mLbCur++ = c;
+        }
+        break;
+
+      // name parsing
+      case PREF_PARSE_UNTIL_NAME:
+        if (c == '\"' || c == '\'') {
+          aPS->mIsDefault =
+            (aPS->mStrMatch == kPref || aPS->mStrMatch == kPrefSticky);
+          aPS->mIsStickyDefault = (aPS->mStrMatch == kPrefSticky);
+          aPS->mQuoteChar = c;
+          aPS->mNextState = PREF_PARSE_UNTIL_COMMA; // return here when done
+          state = PREF_PARSE_QUOTED_STRING;
+        } else if (c == '/') {     // allow embedded comment
+          aPS->mNextState = state; // return here when done with comment
+          state = PREF_PARSE_COMMENT_MAYBE_START;
+        } else if (!isspace(c)) {
+          pref_ReportParseProblem(
+            *aPS, "need space, comment or quote", lineNum, true);
+          NS_WARNING("malformed pref file");
+          return false;
+        }
+        break;
+
+      // parse until we find a comma separating name and value
+      case PREF_PARSE_UNTIL_COMMA:
+        if (c == ',') {
+          aPS->mVb = aPS->mLbCur;
+          state = PREF_PARSE_UNTIL_VALUE;
+        } else if (c == '/') {     // allow embedded comment
+          aPS->mNextState = state; // return here when done with comment
+          state = PREF_PARSE_COMMENT_MAYBE_START;
+        } else if (!isspace(c)) {
+          pref_ReportParseProblem(
+            *aPS, "need space, comment or comma", lineNum, true);
+          NS_WARNING("malformed pref file");
+          return false;
+        }
+        break;
+
+      // value parsing
+      case PREF_PARSE_UNTIL_VALUE:
+        // The pref value type is unknown. So, we scan for the first character
+        // of the value, and determine the type from that.
+        if (c == '\"' || c == '\'') {
+          aPS->mVtype = PrefType::String;
+          aPS->mQuoteChar = c;
+          aPS->mNextState = PREF_PARSE_UNTIL_CLOSE_PAREN;
+          state = PREF_PARSE_QUOTED_STRING;
+        } else if (c == 't' || c == 'f') {
+          aPS->mVb = (char*)(c == 't' ? kTrue : kFalse);
+          aPS->mVtype = PrefType::Bool;
+          aPS->mStrMatch = aPS->mVb;
+          aPS->mStrIndex = 1;
+          aPS->mNextState = PREF_PARSE_UNTIL_CLOSE_PAREN;
+          state = PREF_PARSE_MATCH_STRING;
+        } else if (isdigit(c) || (c == '-') || (c == '+')) {
+          aPS->mVtype = PrefType::Int;
+          // write c to line buffer...
+          if (aPS->mLbCur == aPS->mLbEnd && !pref_GrowBuf(aPS)) {
+            return false; // out of memory
+          }
+          *aPS->mLbCur++ = c;
+          state = PREF_PARSE_INT_VALUE;
+        } else if (c == '/') {     // allow embedded comment
+          aPS->mNextState = state; // return here when done with comment
+          state = PREF_PARSE_COMMENT_MAYBE_START;
+        } else if (!isspace(c)) {
+          pref_ReportParseProblem(
+            *aPS, "need value, comment or space", lineNum, true);
+          NS_WARNING("malformed pref file");
+          return false;
+        }
+        break;
+
+      case PREF_PARSE_INT_VALUE:
+        // grow line buffer if necessary...
+        if (aPS->mLbCur == aPS->mLbEnd && !pref_GrowBuf(aPS)) {
+          return false; // out of memory
+        }
+        if (isdigit(c)) {
+          *aPS->mLbCur++ = c;
+        } else {
+          *aPS->mLbCur++ = '\0'; // stomp null terminator; we are done.
+          if (c == ')') {
+            state = PREF_PARSE_UNTIL_SEMICOLON;
+          } else if (c == '/') { // allow embedded comment
+            aPS->mNextState = PREF_PARSE_UNTIL_CLOSE_PAREN;
+            state = PREF_PARSE_COMMENT_MAYBE_START;
+          } else if (isspace(c)) {
+            state = PREF_PARSE_UNTIL_CLOSE_PAREN;
+          } else {
+            pref_ReportParseProblem(
+              *aPS, "while parsing integer", lineNum, true);
+            NS_WARNING("malformed pref file");
+            return false;
+          }
+        }
+        break;
+
+      // comment parsing
+      case PREF_PARSE_COMMENT_MAYBE_START:
+        switch (c) {
+          case '*': // comment block
+            state = PREF_PARSE_COMMENT_BLOCK;
+            break;
+          case '/': // comment line
+            state = PREF_PARSE_UNTIL_EOL;
+            break;
+          default:
+            // pref file is malformed
+            pref_ReportParseProblem(
+              *aPS, "while parsing comment", lineNum, true);
+            NS_WARNING("malformed pref file");
+            return false;
+        }
+        break;
+
+      case PREF_PARSE_COMMENT_BLOCK:
+        if (c == '*') {
+          state = PREF_PARSE_COMMENT_BLOCK_MAYBE_END;
+        }
+        break;
+
+      case PREF_PARSE_COMMENT_BLOCK_MAYBE_END:
+        switch (c) {
+          case '/':
+            state = aPS->mNextState;
+            aPS->mNextState = PREF_PARSE_INIT;
+            break;
+          case '*': // stay in this state
+            break;
+          default:
+            state = PREF_PARSE_COMMENT_BLOCK;
+            break;
+        }
+        break;
+
+      // string escape sequence parsing
+      case PREF_PARSE_ESC_SEQUENCE:
+        // It's not necessary to resize the buffer here since we should be
+        // writing only one character and the resize check would have been done
+        // for us in the previous state.
+        switch (c) {
+          case '\"':
+          case '\'':
+          case '\\':
+            break;
+          case 'r':
+            c = '\r';
+            break;
+          case 'n':
+            c = '\n';
+            break;
+          case 'x': // hex escape -- always interpreted as Latin-1
+          case 'u': // UTF16 escape
+            aPS->mEscTmp[0] = c;
+            aPS->mEscLen = 1;
+            aPS->mUtf16[0] = aPS->mUtf16[1] = 0;
+            aPS->mStrIndex =
+              (c == 'x') ? HEX_ESC_NUM_DIGITS : UTF16_ESC_NUM_DIGITS;
+            state = PREF_PARSE_HEX_ESCAPE;
+            continue;
+          default:
+            pref_ReportParseProblem(
+              *aPS, "preserving unexpected JS escape sequence", lineNum, false);
+            NS_WARNING("preserving unexpected JS escape sequence");
+            // Invalid escape sequence so we do have to write more than one
+            // character. Grow line buffer if necessary...
+            if ((aPS->mLbCur + 1) == aPS->mLbEnd && !pref_GrowBuf(aPS)) {
+              return false; // out of memory
+            }
+            *aPS->mLbCur++ = '\\'; // preserve the escape sequence
+            break;
+        }
+        *aPS->mLbCur++ = c;
+        state = PREF_PARSE_QUOTED_STRING;
+        break;
+
+      // parsing a hex (\xHH) or mUtf16 escape (\uHHHH)
+      case PREF_PARSE_HEX_ESCAPE:
+        if (c >= '0' && c <= '9') {
+          udigit = (c - '0');
+        } else if (c >= 'A' && c <= 'F') {
+          udigit = (c - 'A') + 10;
+        } else if (c >= 'a' && c <= 'f') {
+          udigit = (c - 'a') + 10;
+        } else {
+          // bad escape sequence found, write out broken escape as-is
+          pref_ReportParseProblem(*aPS,
+                                  "preserving invalid or incomplete hex escape",
+                                  lineNum,
+                                  false);
+          NS_WARNING("preserving invalid or incomplete hex escape");
+          *aPS->mLbCur++ = '\\'; // original escape slash
+          if ((aPS->mLbCur + aPS->mEscLen) >= aPS->mLbEnd &&
+              !pref_GrowBuf(aPS)) {
+            return false;
+          }
+          for (int i = 0; i < aPS->mEscLen; ++i) {
+            *aPS->mLbCur++ = aPS->mEscTmp[i];
+          }
+
+          // Push the non-hex character back for re-parsing. (++aBuf at the top
+          // of the loop keeps this safe.)
+          --aBuf;
+          state = PREF_PARSE_QUOTED_STRING;
+          continue;
+        }
+
+        // have a digit
+        aPS->mEscTmp[aPS->mEscLen++] = c; // preserve it
+        aPS->mUtf16[1] <<= BITS_PER_HEX_DIGIT;
+        aPS->mUtf16[1] |= udigit;
+        aPS->mStrIndex--;
+        if (aPS->mStrIndex == 0) {
+          // we have the full escape, convert to UTF8
+          int utf16len = 0;
+          if (aPS->mUtf16[0]) {
+            // already have a high surrogate, this is a two char seq
+            utf16len = 2;
+          } else if (0xD800 == (0xFC00 & aPS->mUtf16[1])) {
+            // a high surrogate, can't convert until we have the low
+            aPS->mUtf16[0] = aPS->mUtf16[1];
+            aPS->mUtf16[1] = 0;
+            state = PREF_PARSE_UTF16_LOW_SURROGATE;
+            break;
+          } else {
+            // a single mUtf16 character
+            aPS->mUtf16[0] = aPS->mUtf16[1];
+            utf16len = 1;
+          }
+
+          // The actual conversion.
+          // Make sure there's room, 6 bytes is max utf8 len (in theory; 4
+          // bytes covers the actual mUtf16 range).
+          if (aPS->mLbCur + 6 >= aPS->mLbEnd && !pref_GrowBuf(aPS)) {
+            return false;
+          }
+
+          ConvertUTF16toUTF8 converter(aPS->mLbCur);
+          converter.write(aPS->mUtf16, utf16len);
+          aPS->mLbCur += converter.Size();
+          state = PREF_PARSE_QUOTED_STRING;
+        }
+        break;
+
+      // looking for beginning of mUtf16 low surrogate
+      case PREF_PARSE_UTF16_LOW_SURROGATE:
+        if (aPS->mStrIndex == 0 && c == '\\') {
+          ++aPS->mStrIndex;
+        } else if (aPS->mStrIndex == 1 && c == 'u') {
+          // escape sequence is correct, now parse hex
+          aPS->mStrIndex = UTF16_ESC_NUM_DIGITS;
+          aPS->mEscTmp[0] = 'u';
+          aPS->mEscLen = 1;
+          state = PREF_PARSE_HEX_ESCAPE;
+        } else {
+          // Didn't find expected low surrogate. Ignore high surrogate (it
+          // would just get converted to nothing anyway) and start over with
+          // this character.
+          --aBuf;
+          if (aPS->mStrIndex == 1) {
+            state = PREF_PARSE_ESC_SEQUENCE;
+          } else {
+            state = PREF_PARSE_QUOTED_STRING;
+          }
+          continue;
+        }
+        break;
+
+      // function open and close parsing
+      case PREF_PARSE_UNTIL_OPEN_PAREN:
+        // tolerate only whitespace and embedded comments
+        if (c == '(') {
+          state = PREF_PARSE_UNTIL_NAME;
+        } else if (c == '/') {
+          aPS->mNextState = state; // return here when done with comment
+          state = PREF_PARSE_COMMENT_MAYBE_START;
+        } else if (!isspace(c)) {
+          pref_ReportParseProblem(
+            *aPS, "need space, comment or open parentheses", lineNum, true);
+          NS_WARNING("malformed pref file");
+          return false;
+        }
+        break;
+
+      case PREF_PARSE_UNTIL_CLOSE_PAREN:
+        // tolerate only whitespace and embedded comments
+        if (c == ')') {
+          state = PREF_PARSE_UNTIL_SEMICOLON;
+        } else if (c == '/') {
+          aPS->mNextState = state; // return here when done with comment
+          state = PREF_PARSE_COMMENT_MAYBE_START;
+        } else if (!isspace(c)) {
+          pref_ReportParseProblem(
+            *aPS, "need space, comment or closing parentheses", lineNum, true);
+          NS_WARNING("malformed pref file");
+          return false;
+        }
+        break;
+
+      // function terminator ';' parsing
+      case PREF_PARSE_UNTIL_SEMICOLON:
+        // tolerate only whitespace and embedded comments
+        if (c == ';') {
+          if (!pref_DoCallback(aPS)) {
+            return false;
+          }
+          state = PREF_PARSE_INIT;
+        } else if (c == '/') {
+          aPS->mNextState = state; // return here when done with comment
+          state = PREF_PARSE_COMMENT_MAYBE_START;
+        } else if (!isspace(c)) {
+          pref_ReportParseProblem(
+            *aPS, "need space, comment or semicolon", lineNum, true);
+          NS_WARNING("malformed pref file");
+          return false;
+        }
+        break;
+
+      // eol parsing
+      case PREF_PARSE_UNTIL_EOL:
+        // Need to handle mac, unix, or dos line endings. PREF_PARSE_INIT will
+        // eat the next \n in case we have \r\n.
+        if (c == '\r' || c == '\n' || c == 0x1A) {
+          state = aPS->mNextState;
+          aPS->mNextState = PREF_PARSE_INIT; // reset next state
+        }
+        break;
+    }
+  }
+  aPS->mState = state;
+  return true;
+}
+
+#ifdef TEST_PREFREAD
+
+static void
+pref_reader(void* aClosure,
+            const char* aPref,
+            PrefValue aVal,
+            PrefType aType,
+            bool aDefPref)
+{
+  printf("%spref(\"%s\", ", aDefPref ? "" : "user_", aPref);
+  switch (aType) {
+    case PREF_STRING:
+      printf("\"%s\");\n", aVal.mStringVal);
+      break;
+    case PREF_INT:
+      printf("%i);\n", aVal.mIntVal);
+      break;
+    case PREF_BOOL:
+      printf("%s);\n", aVal.mBoolVal == false ? "false" : "true");
+      break;
+  }
+}
+
+int
+main(int aArgc, char** aArgv)
+{
+  PrefParseState aPS;
+  char buf[4096]; // i/o buffer
+  FILE* fp;
+  int n;
+
+  if (aArgc == 1) {
+    printf("usage: prefread file.js\n");
+    return -1;
+  }
+
+  fp = fopen(aArgv[1], "r");
+  if (!fp) {
+    printf("failed to open file\n");
+    return -1;
+  }
+
+  PREF_InitParseState(&aPS, pref_reader, nullptr, nullptr);
+
+  while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
+    PREF_ParseBuf(&aPS, buf, n);
+  }
+
+  PREF_FinalizeParseState(&aPS);
+
+  fclose(fp);
+  return 0;
+}
+
+#endif // TEST_PREFREAD
+
+//===========================================================================
 // nsPrefBranch et al.
 //===========================================================================
 
 using mozilla::dom::ContentChild;
 
 namespace mozilla {
 class PreferenceServiceReporter;
 } // namespace mozilla
--- a/modules/libpref/moz.build
+++ b/modules/libpref/moz.build
@@ -26,17 +26,16 @@ XPIDL_MODULE = 'pref'
 
 EXPORTS.mozilla += [
     'Preferences.h',
 ]
 
 UNIFIED_SOURCES += [
     'prefapi.cpp',
     'Preferences.cpp',
-    'prefread.cpp',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 DEFINES['OS_ARCH'] = CONFIG['OS_ARCH']
 DEFINES['MOZ_WIDGET_TOOLKIT'] = CONFIG['MOZ_WIDGET_TOOLKIT']
--- a/modules/libpref/prefapi.cpp
+++ b/modules/libpref/prefapi.cpp
@@ -20,17 +20,16 @@
 #include "nsQuickSort.h"
 #include "nsReadableUtils.h"
 #include "nsString.h"
 #include "plbase64.h"
 #include "PLDHashTable.h"
 #include "plstr.h"
 #include "prefapi.h"
 #include "prefapi_private_data.h"
-#include "prefread.h"
 #include "prlink.h"
 
 #ifdef _WIN32
 #include "windows.h"
 #endif
 
 using namespace mozilla;
 
deleted file mode 100644
--- a/modules/libpref/prefread.cpp
+++ /dev/null
@@ -1,673 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* 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 <ctype.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "nsString.h"
-#include "nsUTF8Utils.h"
-#include "prefread.h"
-
-#ifdef TEST_PREFREAD
-#include <stdio.h>
-#define NS_WARNING(_s) printf(">>> " _s "!\n")
-#define NS_NOTREACHED(_s) NS_WARNING(_s)
-#else
-#include "nsDebug.h" // for NS_WARNING
-#endif
-
-// Pref parser states.
-enum
-{
-  PREF_PARSE_INIT,
-  PREF_PARSE_MATCH_STRING,
-  PREF_PARSE_UNTIL_NAME,
-  PREF_PARSE_QUOTED_STRING,
-  PREF_PARSE_UNTIL_COMMA,
-  PREF_PARSE_UNTIL_VALUE,
-  PREF_PARSE_INT_VALUE,
-  PREF_PARSE_COMMENT_MAYBE_START,
-  PREF_PARSE_COMMENT_BLOCK,
-  PREF_PARSE_COMMENT_BLOCK_MAYBE_END,
-  PREF_PARSE_ESC_SEQUENCE,
-  PREF_PARSE_HEX_ESCAPE,
-  PREF_PARSE_UTF16_LOW_SURROGATE,
-  PREF_PARSE_UNTIL_OPEN_PAREN,
-  PREF_PARSE_UNTIL_CLOSE_PAREN,
-  PREF_PARSE_UNTIL_SEMICOLON,
-  PREF_PARSE_UNTIL_EOL
-};
-
-#define UTF16_ESC_NUM_DIGITS 4
-#define HEX_ESC_NUM_DIGITS 2
-#define BITS_PER_HEX_DIGIT 4
-
-static const char kUserPref[] = "user_pref";
-static const char kPref[] = "pref";
-static const char kPrefSticky[] = "sticky_pref";
-static const char kTrue[] = "true";
-static const char kFalse[] = "false";
-
-// This function will increase the size of the buffer owned by the given pref
-// parse state. We currently use a simple doubling algorithm, but the only hard
-// requirement is that it increase the buffer by at least the size of the
-// aPS->mEscTmp buffer used for escape processing (currently 6 bytes).
-//
-// The buffer is used to store partial pref lines. It is freed when the parse
-// state is destroyed.
-//
-// @param aPS
-//        parse state instance
-//
-// This function updates all pointers that reference an address within mLb
-// since realloc may relocate the buffer.
-//
-// @return false if insufficient memory.
-static bool
-pref_GrowBuf(PrefParseState* aPS)
-{
-  int bufLen, curPos, valPos;
-
-  bufLen = aPS->mLbEnd - aPS->mLb;
-  curPos = aPS->mLbCur - aPS->mLb;
-  valPos = aPS->mVb - aPS->mLb;
-
-  if (bufLen == 0) {
-    bufLen = 128; // default buffer size
-  } else {
-    bufLen <<= 1; // double buffer size
-  }
-
-#ifdef TEST_PREFREAD
-  fprintf(stderr, ">>> realloc(%d)\n", bufLen);
-#endif
-
-  aPS->mLb = (char*)realloc(aPS->mLb, bufLen);
-  if (!aPS->mLb) {
-    return false;
-  }
-
-  aPS->mLbCur = aPS->mLb + curPos;
-  aPS->mLbEnd = aPS->mLb + bufLen;
-  aPS->mVb = aPS->mLb + valPos;
-
-  return true;
-}
-
-// Report an error or a warning. If not specified, just dump to stderr.
-static void
-pref_ReportParseProblem(PrefParseState& aPS,
-                        const char* aMessage,
-                        int aLine,
-                        bool aError)
-{
-  if (aPS.mReporter) {
-    aPS.mReporter(aMessage, aLine, aError);
-  } else {
-    printf_stderr("**** Preference parsing %s (line %d) = %s **\n",
-                  (aError ? "error" : "warning"),
-                  aLine,
-                  aMessage);
-  }
-}
-
-// This function is called when a complete pref name-value pair has been
-// extracted from the input data.
-//
-// @param aPS
-//        parse state instance
-//
-// @return false to indicate a fatal error.
-static bool
-pref_DoCallback(PrefParseState* aPS)
-{
-  PrefValue value;
-
-  switch (aPS->mVtype) {
-    case PrefType::String:
-      value.mStringVal = aPS->mVb;
-      break;
-
-    case PrefType::Int:
-      if ((aPS->mVb[0] == '-' || aPS->mVb[0] == '+') && aPS->mVb[1] == '\0') {
-        pref_ReportParseProblem(*aPS, "invalid integer value", 0, true);
-        NS_WARNING("malformed integer value");
-        return false;
-      }
-      value.mIntVal = atoi(aPS->mVb);
-      break;
-
-    case PrefType::Bool:
-      value.mBoolVal = (aPS->mVb == kTrue);
-      break;
-
-    default:
-      break;
-  }
-
-  (*aPS->mReader)(aPS->mClosure,
-                  aPS->mLb,
-                  value,
-                  aPS->mVtype,
-                  aPS->mIsDefault,
-                  aPS->mIsStickyDefault);
-  return true;
-}
-
-void
-PREF_InitParseState(PrefParseState* aPS,
-                    PrefReader aReader,
-                    PrefParseErrorReporter aReporter,
-                    void* aClosure)
-{
-  memset(aPS, 0, sizeof(*aPS));
-  aPS->mReader = aReader;
-  aPS->mClosure = aClosure;
-  aPS->mReporter = aReporter;
-}
-
-void
-PREF_FinalizeParseState(PrefParseState* aPS)
-{
-  if (aPS->mLb) {
-    free(aPS->mLb);
-  }
-}
-
-// Pseudo-BNF
-// ----------
-// function      = LJUNK function-name JUNK function-args
-// function-name = "user_pref" | "pref" | "sticky_pref"
-// function-args = "(" JUNK pref-name JUNK "," JUNK pref-value JUNK ")" JUNK ";"
-// pref-name     = quoted-string
-// pref-value    = quoted-string | "true" | "false" | integer-value
-// JUNK          = *(WS | comment-block | comment-line)
-// LJUNK         = *(WS | comment-block | comment-line | bcomment-line)
-// WS            = SP | HT | LF | VT | FF | CR
-// SP            = <US-ASCII SP, space (32)>
-// HT            = <US-ASCII HT, horizontal-tab (9)>
-// LF            = <US-ASCII LF, linefeed (10)>
-// VT            = <US-ASCII HT, vertical-tab (11)>
-// FF            = <US-ASCII FF, form-feed (12)>
-// CR            = <US-ASCII CR, carriage return (13)>
-// comment-block = <C/C++ style comment block>
-// comment-line  = <C++ style comment line>
-// bcomment-line = <bourne-shell style comment line>
-//
-bool
-PREF_ParseBuf(PrefParseState* aPS, const char* aBuf, int aBufLen)
-{
-  const char* end;
-  char c;
-  char udigit;
-  int state;
-
-  // The line number is currently only used for the error/warning reporting.
-  int lineNum = 0;
-
-  state = aPS->mState;
-  for (end = aBuf + aBufLen; aBuf != end; ++aBuf) {
-    c = *aBuf;
-    if (c == '\r' || c == '\n' || c == 0x1A) {
-      lineNum++;
-    }
-
-    switch (state) {
-      // initial state
-      case PREF_PARSE_INIT:
-        if (aPS->mLbCur != aPS->mLb) { // reset state
-          aPS->mLbCur = aPS->mLb;
-          aPS->mVb = nullptr;
-          aPS->mVtype = PrefType::Invalid;
-          aPS->mIsDefault = false;
-          aPS->mIsStickyDefault = false;
-        }
-        switch (c) {
-          case '/': // begin comment block or line?
-            state = PREF_PARSE_COMMENT_MAYBE_START;
-            break;
-          case '#': // accept shell style comments
-            state = PREF_PARSE_UNTIL_EOL;
-            break;
-          case 'u': // indicating user_pref
-          case 's': // indicating sticky_pref
-          case 'p': // indicating pref
-            if (c == 'u') {
-              aPS->mStrMatch = kUserPref;
-            } else if (c == 's') {
-              aPS->mStrMatch = kPrefSticky;
-            } else {
-              aPS->mStrMatch = kPref;
-            }
-            aPS->mStrIndex = 1;
-            aPS->mNextState = PREF_PARSE_UNTIL_OPEN_PAREN;
-            state = PREF_PARSE_MATCH_STRING;
-            break;
-            // else skip char
-        }
-        break;
-
-      // string matching
-      case PREF_PARSE_MATCH_STRING:
-        if (c == aPS->mStrMatch[aPS->mStrIndex++]) {
-          // If we've matched all characters, then move to next state.
-          if (aPS->mStrMatch[aPS->mStrIndex] == '\0') {
-            state = aPS->mNextState;
-            aPS->mNextState = PREF_PARSE_INIT; // reset next state
-          }
-          // else wait for next char
-        } else {
-          pref_ReportParseProblem(*aPS, "non-matching string", lineNum, true);
-          NS_WARNING("malformed pref file");
-          return false;
-        }
-        break;
-
-      // quoted string parsing
-      case PREF_PARSE_QUOTED_STRING:
-        // we assume that the initial quote has already been consumed
-        if (aPS->mLbCur == aPS->mLbEnd && !pref_GrowBuf(aPS)) {
-          return false; // out of memory
-        }
-        if (c == '\\') {
-          state = PREF_PARSE_ESC_SEQUENCE;
-        } else if (c == aPS->mQuoteChar) {
-          *aPS->mLbCur++ = '\0';
-          state = aPS->mNextState;
-          aPS->mNextState = PREF_PARSE_INIT; // reset next state
-        } else {
-          *aPS->mLbCur++ = c;
-        }
-        break;
-
-      // name parsing
-      case PREF_PARSE_UNTIL_NAME:
-        if (c == '\"' || c == '\'') {
-          aPS->mIsDefault =
-            (aPS->mStrMatch == kPref || aPS->mStrMatch == kPrefSticky);
-          aPS->mIsStickyDefault = (aPS->mStrMatch == kPrefSticky);
-          aPS->mQuoteChar = c;
-          aPS->mNextState = PREF_PARSE_UNTIL_COMMA; // return here when done
-          state = PREF_PARSE_QUOTED_STRING;
-        } else if (c == '/') {     // allow embedded comment
-          aPS->mNextState = state; // return here when done with comment
-          state = PREF_PARSE_COMMENT_MAYBE_START;
-        } else if (!isspace(c)) {
-          pref_ReportParseProblem(
-            *aPS, "need space, comment or quote", lineNum, true);
-          NS_WARNING("malformed pref file");
-          return false;
-        }
-        break;
-
-      // parse until we find a comma separating name and value
-      case PREF_PARSE_UNTIL_COMMA:
-        if (c == ',') {
-          aPS->mVb = aPS->mLbCur;
-          state = PREF_PARSE_UNTIL_VALUE;
-        } else if (c == '/') {     // allow embedded comment
-          aPS->mNextState = state; // return here when done with comment
-          state = PREF_PARSE_COMMENT_MAYBE_START;
-        } else if (!isspace(c)) {
-          pref_ReportParseProblem(
-            *aPS, "need space, comment or comma", lineNum, true);
-          NS_WARNING("malformed pref file");
-          return false;
-        }
-        break;
-
-      // value parsing
-      case PREF_PARSE_UNTIL_VALUE:
-        // The pref value type is unknown. So, we scan for the first character
-        // of the value, and determine the type from that.
-        if (c == '\"' || c == '\'') {
-          aPS->mVtype = PrefType::String;
-          aPS->mQuoteChar = c;
-          aPS->mNextState = PREF_PARSE_UNTIL_CLOSE_PAREN;
-          state = PREF_PARSE_QUOTED_STRING;
-        } else if (c == 't' || c == 'f') {
-          aPS->mVb = (char*)(c == 't' ? kTrue : kFalse);
-          aPS->mVtype = PrefType::Bool;
-          aPS->mStrMatch = aPS->mVb;
-          aPS->mStrIndex = 1;
-          aPS->mNextState = PREF_PARSE_UNTIL_CLOSE_PAREN;
-          state = PREF_PARSE_MATCH_STRING;
-        } else if (isdigit(c) || (c == '-') || (c == '+')) {
-          aPS->mVtype = PrefType::Int;
-          // write c to line buffer...
-          if (aPS->mLbCur == aPS->mLbEnd && !pref_GrowBuf(aPS)) {
-            return false; // out of memory
-          }
-          *aPS->mLbCur++ = c;
-          state = PREF_PARSE_INT_VALUE;
-        } else if (c == '/') {     // allow embedded comment
-          aPS->mNextState = state; // return here when done with comment
-          state = PREF_PARSE_COMMENT_MAYBE_START;
-        } else if (!isspace(c)) {
-          pref_ReportParseProblem(
-            *aPS, "need value, comment or space", lineNum, true);
-          NS_WARNING("malformed pref file");
-          return false;
-        }
-        break;
-
-      case PREF_PARSE_INT_VALUE:
-        // grow line buffer if necessary...
-        if (aPS->mLbCur == aPS->mLbEnd && !pref_GrowBuf(aPS)) {
-          return false; // out of memory
-        }
-        if (isdigit(c)) {
-          *aPS->mLbCur++ = c;
-        } else {
-          *aPS->mLbCur++ = '\0'; // stomp null terminator; we are done.
-          if (c == ')') {
-            state = PREF_PARSE_UNTIL_SEMICOLON;
-          } else if (c == '/') { // allow embedded comment
-            aPS->mNextState = PREF_PARSE_UNTIL_CLOSE_PAREN;
-            state = PREF_PARSE_COMMENT_MAYBE_START;
-          } else if (isspace(c)) {
-            state = PREF_PARSE_UNTIL_CLOSE_PAREN;
-          } else {
-            pref_ReportParseProblem(
-              *aPS, "while parsing integer", lineNum, true);
-            NS_WARNING("malformed pref file");
-            return false;
-          }
-        }
-        break;
-
-      // comment parsing
-      case PREF_PARSE_COMMENT_MAYBE_START:
-        switch (c) {
-          case '*': // comment block
-            state = PREF_PARSE_COMMENT_BLOCK;
-            break;
-          case '/': // comment line
-            state = PREF_PARSE_UNTIL_EOL;
-            break;
-          default:
-            // pref file is malformed
-            pref_ReportParseProblem(
-              *aPS, "while parsing comment", lineNum, true);
-            NS_WARNING("malformed pref file");
-            return false;
-        }
-        break;
-
-      case PREF_PARSE_COMMENT_BLOCK:
-        if (c == '*') {
-          state = PREF_PARSE_COMMENT_BLOCK_MAYBE_END;
-        }
-        break;
-
-      case PREF_PARSE_COMMENT_BLOCK_MAYBE_END:
-        switch (c) {
-          case '/':
-            state = aPS->mNextState;
-            aPS->mNextState = PREF_PARSE_INIT;
-            break;
-          case '*': // stay in this state
-            break;
-          default:
-            state = PREF_PARSE_COMMENT_BLOCK;
-            break;
-        }
-        break;
-
-      // string escape sequence parsing
-      case PREF_PARSE_ESC_SEQUENCE:
-        // It's not necessary to resize the buffer here since we should be
-        // writing only one character and the resize check would have been done
-        // for us in the previous state.
-        switch (c) {
-          case '\"':
-          case '\'':
-          case '\\':
-            break;
-          case 'r':
-            c = '\r';
-            break;
-          case 'n':
-            c = '\n';
-            break;
-          case 'x': // hex escape -- always interpreted as Latin-1
-          case 'u': // UTF16 escape
-            aPS->mEscTmp[0] = c;
-            aPS->mEscLen = 1;
-            aPS->mUtf16[0] = aPS->mUtf16[1] = 0;
-            aPS->mStrIndex =
-              (c == 'x') ? HEX_ESC_NUM_DIGITS : UTF16_ESC_NUM_DIGITS;
-            state = PREF_PARSE_HEX_ESCAPE;
-            continue;
-          default:
-            pref_ReportParseProblem(
-              *aPS, "preserving unexpected JS escape sequence", lineNum, false);
-            NS_WARNING("preserving unexpected JS escape sequence");
-            // Invalid escape sequence so we do have to write more than one
-            // character. Grow line buffer if necessary...
-            if ((aPS->mLbCur + 1) == aPS->mLbEnd && !pref_GrowBuf(aPS)) {
-              return false; // out of memory
-            }
-            *aPS->mLbCur++ = '\\'; // preserve the escape sequence
-            break;
-        }
-        *aPS->mLbCur++ = c;
-        state = PREF_PARSE_QUOTED_STRING;
-        break;
-
-      // parsing a hex (\xHH) or mUtf16 escape (\uHHHH)
-      case PREF_PARSE_HEX_ESCAPE:
-        if (c >= '0' && c <= '9') {
-          udigit = (c - '0');
-        } else if (c >= 'A' && c <= 'F') {
-          udigit = (c - 'A') + 10;
-        } else if (c >= 'a' && c <= 'f') {
-          udigit = (c - 'a') + 10;
-        } else {
-          // bad escape sequence found, write out broken escape as-is
-          pref_ReportParseProblem(*aPS,
-                                  "preserving invalid or incomplete hex escape",
-                                  lineNum,
-                                  false);
-          NS_WARNING("preserving invalid or incomplete hex escape");
-          *aPS->mLbCur++ = '\\'; // original escape slash
-          if ((aPS->mLbCur + aPS->mEscLen) >= aPS->mLbEnd &&
-              !pref_GrowBuf(aPS)) {
-            return false;
-          }
-          for (int i = 0; i < aPS->mEscLen; ++i) {
-            *aPS->mLbCur++ = aPS->mEscTmp[i];
-          }
-
-          // Push the non-hex character back for re-parsing. (++aBuf at the top
-          // of the loop keeps this safe.)
-          --aBuf;
-          state = PREF_PARSE_QUOTED_STRING;
-          continue;
-        }
-
-        // have a digit
-        aPS->mEscTmp[aPS->mEscLen++] = c; // preserve it
-        aPS->mUtf16[1] <<= BITS_PER_HEX_DIGIT;
-        aPS->mUtf16[1] |= udigit;
-        aPS->mStrIndex--;
-        if (aPS->mStrIndex == 0) {
-          // we have the full escape, convert to UTF8
-          int utf16len = 0;
-          if (aPS->mUtf16[0]) {
-            // already have a high surrogate, this is a two char seq
-            utf16len = 2;
-          } else if (0xD800 == (0xFC00 & aPS->mUtf16[1])) {
-            // a high surrogate, can't convert until we have the low
-            aPS->mUtf16[0] = aPS->mUtf16[1];
-            aPS->mUtf16[1] = 0;
-            state = PREF_PARSE_UTF16_LOW_SURROGATE;
-            break;
-          } else {
-            // a single mUtf16 character
-            aPS->mUtf16[0] = aPS->mUtf16[1];
-            utf16len = 1;
-          }
-
-          // The actual conversion.
-          // Make sure there's room, 6 bytes is max utf8 len (in theory; 4
-          // bytes covers the actual mUtf16 range).
-          if (aPS->mLbCur + 6 >= aPS->mLbEnd && !pref_GrowBuf(aPS)) {
-            return false;
-          }
-
-          ConvertUTF16toUTF8 converter(aPS->mLbCur);
-          converter.write(aPS->mUtf16, utf16len);
-          aPS->mLbCur += converter.Size();
-          state = PREF_PARSE_QUOTED_STRING;
-        }
-        break;
-
-      // looking for beginning of mUtf16 low surrogate
-      case PREF_PARSE_UTF16_LOW_SURROGATE:
-        if (aPS->mStrIndex == 0 && c == '\\') {
-          ++aPS->mStrIndex;
-        } else if (aPS->mStrIndex == 1 && c == 'u') {
-          // escape sequence is correct, now parse hex
-          aPS->mStrIndex = UTF16_ESC_NUM_DIGITS;
-          aPS->mEscTmp[0] = 'u';
-          aPS->mEscLen = 1;
-          state = PREF_PARSE_HEX_ESCAPE;
-        } else {
-          // Didn't find expected low surrogate. Ignore high surrogate (it
-          // would just get converted to nothing anyway) and start over with
-          // this character.
-          --aBuf;
-          if (aPS->mStrIndex == 1) {
-            state = PREF_PARSE_ESC_SEQUENCE;
-          } else {
-            state = PREF_PARSE_QUOTED_STRING;
-          }
-          continue;
-        }
-        break;
-
-      // function open and close parsing
-      case PREF_PARSE_UNTIL_OPEN_PAREN:
-        // tolerate only whitespace and embedded comments
-        if (c == '(') {
-          state = PREF_PARSE_UNTIL_NAME;
-        } else if (c == '/') {
-          aPS->mNextState = state; // return here when done with comment
-          state = PREF_PARSE_COMMENT_MAYBE_START;
-        } else if (!isspace(c)) {
-          pref_ReportParseProblem(
-            *aPS, "need space, comment or open parentheses", lineNum, true);
-          NS_WARNING("malformed pref file");
-          return false;
-        }
-        break;
-
-      case PREF_PARSE_UNTIL_CLOSE_PAREN:
-        // tolerate only whitespace and embedded comments
-        if (c == ')') {
-          state = PREF_PARSE_UNTIL_SEMICOLON;
-        } else if (c == '/') {
-          aPS->mNextState = state; // return here when done with comment
-          state = PREF_PARSE_COMMENT_MAYBE_START;
-        } else if (!isspace(c)) {
-          pref_ReportParseProblem(
-            *aPS, "need space, comment or closing parentheses", lineNum, true);
-          NS_WARNING("malformed pref file");
-          return false;
-        }
-        break;
-
-      // function terminator ';' parsing
-      case PREF_PARSE_UNTIL_SEMICOLON:
-        // tolerate only whitespace and embedded comments
-        if (c == ';') {
-          if (!pref_DoCallback(aPS)) {
-            return false;
-          }
-          state = PREF_PARSE_INIT;
-        } else if (c == '/') {
-          aPS->mNextState = state; // return here when done with comment
-          state = PREF_PARSE_COMMENT_MAYBE_START;
-        } else if (!isspace(c)) {
-          pref_ReportParseProblem(
-            *aPS, "need space, comment or semicolon", lineNum, true);
-          NS_WARNING("malformed pref file");
-          return false;
-        }
-        break;
-
-      // eol parsing
-      case PREF_PARSE_UNTIL_EOL:
-        // Need to handle mac, unix, or dos line endings. PREF_PARSE_INIT will
-        // eat the next \n in case we have \r\n.
-        if (c == '\r' || c == '\n' || c == 0x1A) {
-          state = aPS->mNextState;
-          aPS->mNextState = PREF_PARSE_INIT; // reset next state
-        }
-        break;
-    }
-  }
-  aPS->mState = state;
-  return true;
-}
-
-#ifdef TEST_PREFREAD
-
-static void
-pref_reader(void* aClosure,
-            const char* aPref,
-            PrefValue aVal,
-            PrefType aType,
-            bool aDefPref)
-{
-  printf("%spref(\"%s\", ", aDefPref ? "" : "user_", aPref);
-  switch (aType) {
-    case PREF_STRING:
-      printf("\"%s\");\n", aVal.mStringVal);
-      break;
-    case PREF_INT:
-      printf("%i);\n", aVal.mIntVal);
-      break;
-    case PREF_BOOL:
-      printf("%s);\n", aVal.mBoolVal == false ? "false" : "true");
-      break;
-  }
-}
-
-int
-main(int aArgc, char** aArgv)
-{
-  PrefParseState aPS;
-  char buf[4096]; // i/o buffer
-  FILE* fp;
-  int n;
-
-  if (aArgc == 1) {
-    printf("usage: prefread file.js\n");
-    return -1;
-  }
-
-  fp = fopen(aArgv[1], "r");
-  if (!fp) {
-    printf("failed to open file\n");
-    return -1;
-  }
-
-  PREF_InitParseState(&aPS, pref_reader, nullptr, nullptr);
-
-  while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
-    PREF_ParseBuf(&aPS, buf, n);
-  }
-
-  PREF_FinalizeParseState(&aPS);
-
-  fclose(fp);
-  return 0;
-}
-
-#endif // TEST_PREFREAD
deleted file mode 100644
--- a/modules/libpref/prefread.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* 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/. */
-
-#ifndef prefread_h__
-#define prefread_h__
-
-#include "prefapi.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// Callback function used to notify consumer of preference name value pairs.
-// The pref name and value must be copied by the implementor of the callback
-// if they are needed beyond the scope of the callback function.
-//
-// |aClosure| is user data passed to PREF_InitParseState.
-// |aPref| is the preference name.
-// |aValue| is the preference value.
-// |aType| is the preference type (PREF_STRING, PREF_INT, or PREF_BOOL).
-// |aIsDefault| indicates if it's a default preference.
-// |aIsStickyDefault| indicates if it's a sticky default preference.
-typedef void (*PrefReader)(void* aClosure,
-                           const char* aPref,
-                           PrefValue aValue,
-                           PrefType aType,
-                           bool aIsDefault,
-                           bool aIsStickyDefault);
-
-// Report any errors or warnings we encounter during parsing.
-typedef void (*PrefParseErrorReporter)(const char* aMessage,
-                                       int aLine,
-                                       bool aError);
-
-typedef struct PrefParseState
-{
-  PrefReader mReader;
-  PrefParseErrorReporter mReporter;
-  void* mClosure;
-  int mState;            // PREF_PARSE_...
-  int mNextState;        // sometimes used...
-  const char* mStrMatch; // string to match
-  int mStrIndex;         // next char of smatch to check;
-                         // also, counter in \u parsing
-  char16_t mUtf16[2];    // parsing UTF16 (\u) escape
-  int mEscLen;           // length in mEscTmp
-  char mEscTmp[6];       // raw escape to put back if err
-  char mQuoteChar;       // char delimiter for quotations
-  char* mLb;             // line buffer (only allocation)
-  char* mLbCur;          // line buffer cursor
-  char* mLbEnd;          // line buffer end
-  char* mVb;             // value buffer (ptr into mLb)
-  PrefType mVtype;       // PREF_{STRING,INT,BOOL}
-  bool mIsDefault;       // true if (default) pref
-  bool mIsStickyDefault; // true if (sticky) pref
-} PrefParseState;
-
-// Initialize a PrefParseState instance.
-//
-// |aPS| is the PrefParseState instance.
-// |aReader| is the PrefReader callback function, which will be called once for
-// each preference name value pair extracted.
-// |aReporter| is the PrefParseErrorReporter callback function, which will be
-// called if we encounter any errors (stop) or warnings (continue) during
-// parsing.
-// |aClosure| is extra data passed to |aReader|.
-void
-PREF_InitParseState(PrefParseState* aPS,
-                    PrefReader aReader,
-                    PrefParseErrorReporter aReporter,
-                    void* aClosure);
-
-// Release any memory in use by the PrefParseState instance.
-void
-PREF_FinalizeParseState(PrefParseState* aPS);
-
-// Parse a buffer containing some portion of a preference file.  This function
-// may be called repeatedly as new data is made available. The PrefReader
-// callback function passed PREF_InitParseState will be called as preference
-// name value pairs are extracted from the data. Returns false if buffer
-// contains malformed content.
-bool
-PREF_ParseBuf(PrefParseState* aPS, const char* aBuf, int aBufLen);
-
-#ifdef __cplusplus
-}
-#endif
-#endif /* prefread_h__ */