diff --git a/modules/libpref/Preferences.cpp b/modules/libpref/Preferences.cpp index cb41867a2011..1fdd3874d9ce 100644 --- a/modules/libpref/Preferences.cpp +++ b/modules/libpref/Preferences.cpp @@ -219,6 +219,14 @@ AssertNotAlreadyCached(const char* aPrefType, } #endif +static void +ReportToConsole(const char* aMessage, int aLine, bool aError) +{ + nsPrintfCString message("** Preference parsing %s (line %d) = %s **\n", + (aError ? "error" : "warning"), aLine, aMessage); + nsPrefBranch::ReportToConsole(NS_ConvertUTF8toUTF16(message.get())); +} + // Although this is a member of Preferences, it measures sPreferences and // several other global structures. /* static */ int64_t @@ -683,7 +691,7 @@ ReadExtensionPrefs(nsIFile *aFile) uint32_t read; PrefParseState ps; - PREF_InitParseState(&ps, PREF_ReaderCallback, nullptr); + PREF_InitParseState(&ps, PREF_ReaderCallback, ReportToConsole, nullptr); while (NS_SUCCEEDED(rv = stream->Available(&avail)) && avail) { rv = stream->Read(buffer, 4096, &read); if (NS_FAILED(rv)) { @@ -997,7 +1005,7 @@ static nsresult openPrefFile(nsIFile* aFile) return NS_ERROR_OUT_OF_MEMORY; PrefParseState ps; - PREF_InitParseState(&ps, PREF_ReaderCallback, nullptr); + PREF_InitParseState(&ps, PREF_ReaderCallback, ReportToConsole, nullptr); // Read is not guaranteed to return a buf the size of fileSize, // but usually will. @@ -1179,7 +1187,7 @@ static nsresult pref_ReadPrefFromJar(nsZipArchive* jarReader, const char *name) NS_ENSURE_TRUE(manifest.Buffer(), NS_ERROR_NOT_AVAILABLE); PrefParseState ps; - PREF_InitParseState(&ps, PREF_ReaderCallback, nullptr); + PREF_InitParseState(&ps, PREF_ReaderCallback, ReportToConsole, nullptr); PREF_ParseBuf(&ps, manifest, manifest.Length()); PREF_FinalizeParseState(&ps); diff --git a/modules/libpref/nsPrefBranch.cpp b/modules/libpref/nsPrefBranch.cpp index fe57c241dfa8..004915e410db 100644 --- a/modules/libpref/nsPrefBranch.cpp +++ b/modules/libpref/nsPrefBranch.cpp @@ -390,6 +390,17 @@ nsresult nsPrefBranch::CheckSanityOfStringLength(const char* aPrefName, const ui return NS_OK; } +/*static*/ +void nsPrefBranch::ReportToConsole(const nsAString& aMessage) +{ + nsresult rv; + nsCOMPtr console = do_GetService("@mozilla.org/consoleservice;1", &rv); + if (NS_FAILED(rv)) { + return; + } + nsAutoString message(aMessage); + console->LogStringMessage(message.get()); +} NS_IMETHODIMP nsPrefBranch::SetComplexValue(const char *aPrefName, const nsIID & aType, nsISupports *aValue) { diff --git a/modules/libpref/nsPrefBranch.h b/modules/libpref/nsPrefBranch.h index e52f7649af02..37cf5c2c4f59 100644 --- a/modules/libpref/nsPrefBranch.h +++ b/modules/libpref/nsPrefBranch.h @@ -196,6 +196,8 @@ public: size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf); + static void ReportToConsole(const nsAString& aMessage); + protected: virtual ~nsPrefBranch(); diff --git a/modules/libpref/prefread.cpp b/modules/libpref/prefread.cpp index 7e2583699928..605dcaac6308 100644 --- a/modules/libpref/prefread.cpp +++ b/modules/libpref/prefread.cpp @@ -97,6 +97,20 @@ pref_GrowBuf(PrefParseState *ps) return true; } +/** + * Report an error or a warning. If not specified, just dump to stderr. + */ +static void +pref_ReportParseProblem(PrefParseState& ps, const char* aMessage, int aLine, bool aError) +{ + if (ps.reporter) { + ps.reporter(aMessage, aLine, aError); + } else { + printf_stderr("**** Preference parsing %s (line %d) = %s **\n", + (aError ? "error" : "warning"), aLine, aMessage); + } +} + /** * pref_DoCallback * @@ -119,6 +133,7 @@ pref_DoCallback(PrefParseState *ps) break; case PrefType::Int: if ((ps->vb[0] == '-' || ps->vb[0] == '+') && ps->vb[1] == '\0') { + pref_ReportParseProblem(*ps, "invalid integer value", 0, true); NS_WARNING("malformed integer value"); return false; } @@ -136,11 +151,13 @@ pref_DoCallback(PrefParseState *ps) } void -PREF_InitParseState(PrefParseState *ps, PrefReader reader, void *closure) +PREF_InitParseState(PrefParseState *ps, PrefReader reader, + PrefParseErrorReporter reporter, void *closure) { memset(ps, 0, sizeof(*ps)); ps->reader = reader; ps->closure = closure; + ps->reporter = reporter; } void @@ -179,9 +196,16 @@ PREF_ParseBuf(PrefParseState *ps, const char *buf, int bufLen) char udigit; int state; + // The line number is currently only used for the error/warning reporting. + int lineNum = 0; + state = ps->state; for (end = buf + bufLen; buf != end; ++buf) { c = *buf; + if (c == '\r' || c == '\n' || c == 0x1A) { + lineNum ++; + } + switch (state) { /* initial state */ case PREF_PARSE_INIT: @@ -228,6 +252,7 @@ PREF_ParseBuf(PrefParseState *ps, const char *buf, int bufLen) /* else wait for next char */ } else { + pref_ReportParseProblem(*ps, "non-matching string", lineNum, true); NS_WARNING("malformed pref file"); return false; } @@ -263,6 +288,7 @@ PREF_ParseBuf(PrefParseState *ps, const char *buf, int bufLen) state = PREF_PARSE_COMMENT_MAYBE_START; } else if (!isspace(c)) { + pref_ReportParseProblem(*ps, "need space, comment or quote", lineNum, true); NS_WARNING("malformed pref file"); return false; } @@ -279,6 +305,7 @@ PREF_ParseBuf(PrefParseState *ps, const char *buf, int bufLen) state = PREF_PARSE_COMMENT_MAYBE_START; } else if (!isspace(c)) { + pref_ReportParseProblem(*ps, "need space, comment or comma", lineNum, true); NS_WARNING("malformed pref file"); return false; } @@ -315,6 +342,7 @@ PREF_ParseBuf(PrefParseState *ps, const char *buf, int bufLen) state = PREF_PARSE_COMMENT_MAYBE_START; } else if (!isspace(c)) { + pref_ReportParseProblem(*ps, "need value, comment or space", lineNum, true); NS_WARNING("malformed pref file"); return false; } @@ -336,6 +364,7 @@ PREF_ParseBuf(PrefParseState *ps, const char *buf, int bufLen) else if (isspace(c)) state = PREF_PARSE_UNTIL_CLOSE_PAREN; else { + pref_ReportParseProblem(*ps, "while parsing integer", lineNum, true); NS_WARNING("malformed pref file"); return false; } @@ -353,12 +382,13 @@ PREF_ParseBuf(PrefParseState *ps, const char *buf, int bufLen) break; default: /* pref file is malformed */ + pref_ReportParseProblem(*ps, "while parsing comment", lineNum, true); NS_WARNING("malformed pref file"); return false; } break; case PREF_PARSE_COMMENT_BLOCK: - if (c == '*') + if (c == '*') state = PREF_PARSE_COMMENT_BLOCK_MAYBE_END; break; case PREF_PARSE_COMMENT_BLOCK_MAYBE_END: @@ -401,6 +431,8 @@ PREF_ParseBuf(PrefParseState *ps, const char *buf, int bufLen) state = PREF_PARSE_HEX_ESCAPE; continue; default: + pref_ReportParseProblem(*ps, "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... */ @@ -423,6 +455,8 @@ PREF_ParseBuf(PrefParseState *ps, const char *buf, int bufLen) udigit = (c - 'a') + 10; else { /* bad escape sequence found, write out broken escape as-is */ + pref_ReportParseProblem(*ps, "preserving invalid or incomplete hex escape", + lineNum, false); NS_WARNING("preserving invalid or incomplete hex escape"); *ps->lbcur++ = '\\'; /* original escape slash */ if ((ps->lbcur + ps->esclen) >= ps->lbend && !pref_GrowBuf(ps)) @@ -510,6 +544,8 @@ PREF_ParseBuf(PrefParseState *ps, const char *buf, int bufLen) state = PREF_PARSE_COMMENT_MAYBE_START; } else if (!isspace(c)) { + pref_ReportParseProblem(*ps, "need space, comment or open parentheses", + lineNum, true); NS_WARNING("malformed pref file"); return false; } @@ -522,6 +558,8 @@ PREF_ParseBuf(PrefParseState *ps, const char *buf, int bufLen) ps->nextstate = state; /* return here when done with comment */ state = PREF_PARSE_COMMENT_MAYBE_START; } else if (!isspace(c)) { + pref_ReportParseProblem(*ps, "need space, comment or closing parentheses", + lineNum, true); NS_WARNING("malformed pref file"); return false; } @@ -540,6 +578,8 @@ PREF_ParseBuf(PrefParseState *ps, const char *buf, int bufLen) state = PREF_PARSE_COMMENT_MAYBE_START; } else if (!isspace(c)) { + pref_ReportParseProblem(*ps, "need space, comment or semicolon", + lineNum, true); NS_WARNING("malformed pref file"); return false; } @@ -603,7 +643,7 @@ main(int argc, char **argv) return -1; } - PREF_InitParseState(&ps, pref_reader, nullptr); + PREF_InitParseState(&ps, pref_reader, nullptr, nullptr); while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) PREF_ParseBuf(&ps, buf, n); diff --git a/modules/libpref/prefread.h b/modules/libpref/prefread.h index 5e5cd0989cd1..2a09b30b6e31 100644 --- a/modules/libpref/prefread.h +++ b/modules/libpref/prefread.h @@ -36,9 +36,15 @@ typedef void (*PrefReader)(void *closure, bool defPref, bool stickyPref); +/** + * Report any errors or warnings we encounter during parsing. + */ +typedef void (*PrefParseErrorReporter)(const char* message, int line, bool error); + /* structure fields are private */ typedef struct PrefParseState { PrefReader reader; + PrefParseErrorReporter reporter; void *closure; int state; /* PREF_PARSE_... */ int nextstate; /* sometimes used... */ @@ -62,16 +68,20 @@ typedef struct PrefParseState { * PREF_InitParseState * * Called to initialize a PrefParseState instance. - * + * * @param ps * PrefParseState instance. * @param reader * PrefReader callback function, which will be called once for each * preference name value pair extracted. + * @param reporter + * PrefParseErrorReporter callback function, which will be called if we + * encounter any errors (stop) or warnings (continue) during parsing. * @param closure * PrefReader closure. */ -void PREF_InitParseState(PrefParseState *ps, PrefReader reader, void *closure); +void PREF_InitParseState(PrefParseState *ps, PrefReader reader, + PrefParseErrorReporter reporter, void *closure); /** * PREF_FinalizeParseState