Merge mozilla-central to autoland. on a CLOSED TREE

This commit is contained in:
Andreea Pavel 2018-03-07 17:19:04 +02:00
Родитель 5966e7cd7b 33cc9e0331
Коммит 6043805daf
24 изменённых файлов: 472 добавлений и 253 удалений

Просмотреть файл

@ -1241,9 +1241,10 @@ pref("services.sync.syncedTabs.showRemoteIcons", true);
// Developer edition preferences // Developer edition preferences
#ifdef MOZ_DEV_EDITION #ifdef MOZ_DEV_EDITION
sticky_pref("lightweightThemes.selectedThemeID", "firefox-compact-dark@mozilla.org"); pref("lightweightThemes.selectedThemeID", "firefox-compact-dark@mozilla.org",
sticky);
#else #else
sticky_pref("lightweightThemes.selectedThemeID", ""); pref("lightweightThemes.selectedThemeID", "", sticky);
#endif #endif
// Whether the character encoding menu is under the main Firefox button. This // Whether the character encoding menu is under the main Firefox button. This

Просмотреть файл

@ -13,9 +13,9 @@ pref("devtools.jsonview.enabled", true);
// Default theme ("dark" or "light") // Default theme ("dark" or "light")
#ifdef MOZ_DEV_EDITION #ifdef MOZ_DEV_EDITION
sticky_pref("devtools.theme", "dark"); pref("devtools.theme", "dark", sticky);
#else #else
sticky_pref("devtools.theme", "light"); pref("devtools.theme", "light", sticky);
#endif #endif
// Should the devtools toolbar be opened on startup // Should the devtools toolbar be opened on startup
@ -38,4 +38,4 @@ pref("devtools.onboarding.telemetry.logged", false);
// Completely disable DevTools entry points, as well as all DevTools command line // Completely disable DevTools entry points, as well as all DevTools command line
// arguments This should be merged with devtools.enabled, see Bug 1440675. // arguments This should be merged with devtools.enabled, see Bug 1440675.
pref("devtools.policy.disabled", false); pref("devtools.policy.disabled", false);

Просмотреть файл

@ -76,7 +76,6 @@ static HWND sWinlessPopupSurrogateHWND = nullptr;
static User32TrackPopupMenu sUser32TrackPopupMenuStub = nullptr; static User32TrackPopupMenu sUser32TrackPopupMenuStub = nullptr;
typedef HIMC (WINAPI *Imm32ImmGetContext)(HWND hWND); typedef HIMC (WINAPI *Imm32ImmGetContext)(HWND hWND);
typedef BOOL (WINAPI *Imm32ImmReleaseContext)(HWND hWND, HIMC hIMC);
typedef LONG (WINAPI *Imm32ImmGetCompositionString)(HIMC hIMC, typedef LONG (WINAPI *Imm32ImmGetCompositionString)(HIMC hIMC,
DWORD dwIndex, DWORD dwIndex,
LPVOID lpBuf, LPVOID lpBuf,
@ -87,7 +86,6 @@ typedef BOOL (WINAPI *Imm32ImmNotifyIME)(HIMC hIMC, DWORD dwAction,
DWORD dwIndex, DWORD dwValue); DWORD dwIndex, DWORD dwValue);
static WindowsDllInterceptor sImm32Intercept; static WindowsDllInterceptor sImm32Intercept;
static Imm32ImmGetContext sImm32ImmGetContextStub = nullptr; static Imm32ImmGetContext sImm32ImmGetContextStub = nullptr;
static Imm32ImmReleaseContext sImm32ImmReleaseContextStub = nullptr;
static Imm32ImmGetCompositionString sImm32ImmGetCompositionStringStub = nullptr; static Imm32ImmGetCompositionString sImm32ImmGetCompositionStringStub = nullptr;
static Imm32ImmSetCandidateWindow sImm32ImmSetCandidateWindowStub = nullptr; static Imm32ImmSetCandidateWindow sImm32ImmSetCandidateWindowStub = nullptr;
static Imm32ImmNotifyIME sImm32ImmNotifyIME = nullptr; static Imm32ImmNotifyIME sImm32ImmNotifyIME = nullptr;
@ -2035,17 +2033,6 @@ PluginInstanceChild::ImmGetContextProc(HWND aWND)
return sHookIMC; return sHookIMC;
} }
// static
BOOL
PluginInstanceChild::ImmReleaseContextProc(HWND aWND, HIMC aIMC)
{
if (aIMC == sHookIMC) {
return TRUE;
}
return sImm32ImmReleaseContextStub(aWND, aIMC);
}
// static // static
LONG LONG
PluginInstanceChild::ImmGetCompositionStringProc(HIMC aIMC, DWORD aIndex, PluginInstanceChild::ImmGetCompositionStringProc(HIMC aIMC, DWORD aIndex,
@ -2130,16 +2117,15 @@ PluginInstanceChild::InitImm32Hook()
} }
// When using windowless plugin, IMM API won't work due ot OOP. // When using windowless plugin, IMM API won't work due ot OOP.
//
// ImmReleaseContext on Windows 7+ just returns TRUE only, so we don't
// need to hook this.
sImm32Intercept.Init("imm32.dll"); sImm32Intercept.Init("imm32.dll");
sImm32Intercept.AddHook( sImm32Intercept.AddHook(
"ImmGetContext", "ImmGetContext",
reinterpret_cast<intptr_t>(ImmGetContextProc), reinterpret_cast<intptr_t>(ImmGetContextProc),
(void**)&sImm32ImmGetContextStub); (void**)&sImm32ImmGetContextStub);
sImm32Intercept.AddHook(
"ImmReleaseContext",
reinterpret_cast<intptr_t>(ImmReleaseContextProc),
(void**)&sImm32ImmReleaseContextStub);
sImm32Intercept.AddHook( sImm32Intercept.AddHook(
"ImmGetCompositionStringW", "ImmGetCompositionStringW",
reinterpret_cast<intptr_t>(ImmGetCompositionStringProc), reinterpret_cast<intptr_t>(ImmGetCompositionStringProc),

Просмотреть файл

@ -335,7 +335,6 @@ private:
#endif #endif
static HIMC WINAPI ImmGetContextProc(HWND aWND); static HIMC WINAPI ImmGetContextProc(HWND aWND);
static BOOL WINAPI ImmReleaseContextProc(HWND aWND, HIMC aIMC);
static LONG WINAPI ImmGetCompositionStringProc(HIMC aIMC, DWORD aIndex, static LONG WINAPI ImmGetCompositionStringProc(HIMC aIMC, DWORD aIndex,
LPVOID aBuf, DWORD aLen); LPVOID aBuf, DWORD aLen);
static BOOL WINAPI ImmSetCandidateWindowProc(HIMC hIMC, static BOOL WINAPI ImmSetCandidateWindowProc(HIMC hIMC,

Просмотреть файл

@ -136,12 +136,25 @@ using mozilla::TimeStamp;
// Avoid an unnecessary NSPR dependency on Linux and OS X just for the shell. // Avoid an unnecessary NSPR dependency on Linux and OS X just for the shell.
#ifdef JS_POSIX_NSPR #ifdef JS_POSIX_NSPR
enum PRLibSpecType { PR_LibSpec_Pathname };
struct PRLibSpec {
PRLibSpecType type;
union {
const char *pathname;
} value;
};
typedef void PRLibrary; typedef void PRLibrary;
static PRLibrary* #define PR_LD_NOW RTLD_NOW
PR_LoadLibrary(const char* path) #define PR_LD_GLOBAL RTLD_GLOBAL
static PRLibrary *
PR_LoadLibraryWithFlags(PRLibSpec libSpec, int flags)
{ {
return dlopen(path, RTLD_LAZY | RTLD_GLOBAL); return dlopen(libSpec.value.pathname, flags);
} }
static void static void

Просмотреть файл

@ -596,8 +596,9 @@ public:
nsresult SetDefaultValue(PrefType aType, nsresult SetDefaultValue(PrefType aType,
PrefValue aValue, PrefValue aValue,
bool aFromFile,
bool aIsSticky, bool aIsSticky,
bool aIsLocked,
bool aFromFile,
bool* aValueChanged) bool* aValueChanged)
{ {
// Types must always match when setting the default value. // Types must always match when setting the default value.
@ -607,20 +608,25 @@ public:
// Should we set the default value? Only if the pref is not locked, and // Should we set the default value? Only if the pref is not locked, and
// doing so would change the default value. // doing so would change the default value.
if (!IsLocked() && !ValueMatches(PrefValueKind::Default, aType, aValue)) { if (!IsLocked()) {
mDefaultValue.Replace(Type(), aType, aValue); if (aIsLocked) {
mHasDefaultValue = true; SetIsLocked(true);
if (!aFromFile) {
mHasChangedSinceInit = true;
} }
if (aIsSticky) { if (!ValueMatches(PrefValueKind::Default, aType, aValue)) {
mIsSticky = true; mDefaultValue.Replace(Type(), aType, aValue);
mHasDefaultValue = true;
if (!aFromFile) {
mHasChangedSinceInit = true;
}
if (aIsSticky) {
mIsSticky = true;
}
if (!mHasUserValue) {
*aValueChanged = true;
}
// What if we change the default to be the same as the user value?
// Should we clear the user value? Currently we don't.
} }
if (!mHasUserValue) {
*aValueChanged = true;
}
// What if we change the default to be the same as the user value?
// Should we clear the user value? Currently we don't.
} }
return NS_OK; return NS_OK;
} }
@ -820,8 +826,6 @@ static PLDHashTable* gHashTable;
static CallbackNode* gFirstCallback = nullptr; static CallbackNode* gFirstCallback = nullptr;
static CallbackNode* gLastPriorityNode = nullptr; static CallbackNode* gLastPriorityNode = nullptr;
static bool gIsAnyPrefLocked = false;
// These are only used during the call to NotifyCallbacks(). // These are only used during the call to NotifyCallbacks().
static bool gCallbacksInProgress = false; static bool gCallbacksInProgress = false;
static bool gShouldCleanupDeadNodes = false; static bool gShouldCleanupDeadNodes = false;
@ -946,6 +950,7 @@ pref_SetPref(const char* aPrefName,
PrefValueKind aKind, PrefValueKind aKind,
PrefValue aValue, PrefValue aValue,
bool aIsSticky, bool aIsSticky,
bool aIsLocked,
bool aFromFile) bool aFromFile)
{ {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
@ -968,9 +973,10 @@ pref_SetPref(const char* aPrefName,
bool valueChanged = false; bool valueChanged = false;
nsresult rv; nsresult rv;
if (aKind == PrefValueKind::Default) { if (aKind == PrefValueKind::Default) {
rv = rv = pref->SetDefaultValue(
pref->SetDefaultValue(aType, aValue, aFromFile, aIsSticky, &valueChanged); aType, aValue, aIsSticky, aIsLocked, aFromFile, &valueChanged);
} else { } else {
MOZ_ASSERT(!aIsLocked); // `locked` is disallowed in user pref files
rv = pref->SetUserValue(aType, aValue, aFromFile, &valueChanged); rv = pref->SetUserValue(aType, aValue, aFromFile, &valueChanged);
} }
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
@ -1078,7 +1084,8 @@ typedef void (*PrefsParserPrefFn)(const char* aPrefName,
PrefType aType, PrefType aType,
PrefValueKind aKind, PrefValueKind aKind,
PrefValue aValue, PrefValue aValue,
bool aIsSticky); bool aIsSticky,
bool aIsLocked);
// Keep this in sync with ErrorFn in prefs_parser/src/lib.rs. // Keep this in sync with ErrorFn in prefs_parser/src/lib.rs.
// //
@ -1089,6 +1096,7 @@ typedef void (*PrefsParserErrorFn)(const char* aMsg);
// Keep this in sync with prefs_parser_parse() in prefs_parser/src/lib.rs. // Keep this in sync with prefs_parser_parse() in prefs_parser/src/lib.rs.
bool bool
prefs_parser_parse(const char* aPath, prefs_parser_parse(const char* aPath,
PrefValueKind aKind,
const char* aBuf, const char* aBuf,
size_t aLen, size_t aLen,
PrefsParserPrefFn aPrefFn, PrefsParserPrefFn aPrefFn,
@ -1102,13 +1110,14 @@ public:
~Parser() = default; ~Parser() = default;
bool Parse(const nsCString& aName, bool Parse(const nsCString& aName,
PrefValueKind aKind,
const char* aPath, const char* aPath,
const TimeStamp& aStartTime, const TimeStamp& aStartTime,
const nsCString& aBuf) const nsCString& aBuf)
{ {
sNumPrefs = 0; sNumPrefs = 0;
bool ok = prefs_parser_parse( bool ok = prefs_parser_parse(
aPath, aBuf.get(), aBuf.Length(), HandlePref, HandleError); aPath, aKind, aBuf.get(), aBuf.Length(), HandlePref, HandleError);
if (!ok) { if (!ok) {
return false; return false;
} }
@ -1130,11 +1139,17 @@ private:
PrefType aType, PrefType aType,
PrefValueKind aKind, PrefValueKind aKind,
PrefValue aValue, PrefValue aValue,
bool aIsSticky) bool aIsSticky,
bool aIsLocked)
{ {
sNumPrefs++; sNumPrefs++;
pref_SetPref( pref_SetPref(aPrefName,
aPrefName, aType, aKind, aValue, aIsSticky, /* fromFile */ true); aType,
aKind,
aValue,
aIsSticky,
aIsLocked,
/* fromFile */ true);
} }
static void HandleError(const char* aMsg) static void HandleError(const char* aMsg)
@ -1166,7 +1181,8 @@ TestParseErrorHandlePref(const char* aPrefName,
PrefType aType, PrefType aType,
PrefValueKind aKind, PrefValueKind aKind,
PrefValue aValue, PrefValue aValue,
bool aIsSticky) bool aIsSticky,
bool aIsLocked)
{ {
} }
@ -1181,9 +1197,10 @@ TestParseErrorHandleError(const char* aMsg)
// Keep this in sync with the declaration in test/gtest/Parser.cpp. // Keep this in sync with the declaration in test/gtest/Parser.cpp.
void void
TestParseError(const char* aText, nsCString& aErrorMsg) TestParseError(PrefValueKind aKind, const char* aText, nsCString& aErrorMsg)
{ {
prefs_parser_parse("test", prefs_parser_parse("test",
aKind,
aText, aText,
strlen(aText), strlen(aText),
TestParseErrorHandlePref, TestParseErrorHandlePref,
@ -2451,7 +2468,7 @@ Preferences::HandleDirty()
} }
static nsresult static nsresult
openPrefFile(nsIFile* aFile); openPrefFile(nsIFile* aFile, PrefValueKind aKind);
static const char kTelemetryPref[] = "toolkit.telemetry.enabled"; static const char kTelemetryPref[] = "toolkit.telemetry.enabled";
static const char kChannelPref[] = "app.update.channel"; static const char kChannelPref[] = "app.update.channel";
@ -3158,6 +3175,19 @@ Preferences::Observe(nsISupports* aSubject,
return rv; return rv;
} }
NS_IMETHODIMP
Preferences::ReadDefaultPrefsFromFile(nsIFile* aFile)
{
ENSURE_PARENT_PROCESS("Preferences::ReadDefaultPrefsFromFile", "all prefs");
if (!aFile) {
NS_ERROR("ReadDefaultPrefsFromFile requires a parameter");
return NS_ERROR_INVALID_ARG;
}
return openPrefFile(aFile, PrefValueKind::Default);
}
NS_IMETHODIMP NS_IMETHODIMP
Preferences::ReadUserPrefsFromFile(nsIFile* aFile) Preferences::ReadUserPrefsFromFile(nsIFile* aFile)
{ {
@ -3168,7 +3198,7 @@ Preferences::ReadUserPrefsFromFile(nsIFile* aFile)
return NS_ERROR_INVALID_ARG; return NS_ERROR_INVALID_ARG;
} }
return openPrefFile(aFile); return openPrefFile(aFile, PrefValueKind::User);
} }
NS_IMETHODIMP NS_IMETHODIMP
@ -3415,7 +3445,7 @@ Preferences::ReadSavedPrefs()
return nullptr; return nullptr;
} }
rv = openPrefFile(file); rv = openPrefFile(file, PrefValueKind::User);
if (rv == NS_ERROR_FILE_NOT_FOUND) { if (rv == NS_ERROR_FILE_NOT_FOUND) {
// This is a normal case for new users. // This is a normal case for new users.
Telemetry::ScalarSet( Telemetry::ScalarSet(
@ -3444,7 +3474,7 @@ Preferences::ReadUserOverridePrefs()
} }
aFile->AppendNative(NS_LITERAL_CSTRING("user.js")); aFile->AppendNative(NS_LITERAL_CSTRING("user.js"));
rv = openPrefFile(aFile); rv = openPrefFile(aFile, PrefValueKind::User);
if (rv != NS_ERROR_FILE_NOT_FOUND) { if (rv != NS_ERROR_FILE_NOT_FOUND) {
// If the file exists and was at least partially read, record that in // If the file exists and was at least partially read, record that in
// telemetry as it may be a sign of pref injection. // telemetry as it may be a sign of pref injection.
@ -3586,7 +3616,7 @@ Preferences::WritePrefFile(nsIFile* aFile, SaveMethod aSaveMethod)
} }
static nsresult static nsresult
openPrefFile(nsIFile* aFile) openPrefFile(nsIFile* aFile, PrefValueKind aKind)
{ {
TimeStamp startTime = TimeStamp::Now(); TimeStamp startTime = TimeStamp::Now();
@ -3602,7 +3632,7 @@ openPrefFile(nsIFile* aFile)
Parser parser; Parser parser;
if (!parser.Parse( if (!parser.Parse(
filename, NS_ConvertUTF16toUTF8(path).get(), startTime, data)) { filename, aKind, NS_ConvertUTF16toUTF8(path).get(), startTime, data)) {
return NS_ERROR_FILE_CORRUPTED; return NS_ERROR_FILE_CORRUPTED;
} }
@ -3703,7 +3733,7 @@ pref_LoadPrefsInDir(nsIFile* aDir,
uint32_t arrayCount = prefFiles.Count(); uint32_t arrayCount = prefFiles.Count();
uint32_t i; uint32_t i;
for (i = 0; i < arrayCount; ++i) { for (i = 0; i < arrayCount; ++i) {
rv2 = openPrefFile(prefFiles[i]); rv2 = openPrefFile(prefFiles[i], PrefValueKind::Default);
if (NS_FAILED(rv2)) { if (NS_FAILED(rv2)) {
NS_ERROR("Default pref file not parsed successfully."); NS_ERROR("Default pref file not parsed successfully.");
rv = rv2; rv = rv2;
@ -3715,7 +3745,7 @@ pref_LoadPrefsInDir(nsIFile* aDir,
// This may be a sparse array; test before parsing. // This may be a sparse array; test before parsing.
nsIFile* file = specialFiles[i]; nsIFile* file = specialFiles[i];
if (file) { if (file) {
rv2 = openPrefFile(file); rv2 = openPrefFile(file, PrefValueKind::Default);
if (NS_FAILED(rv2)) { if (NS_FAILED(rv2)) {
NS_ERROR("Special default pref file not parsed successfully."); NS_ERROR("Special default pref file not parsed successfully.");
rv = rv2; rv = rv2;
@ -3736,7 +3766,11 @@ pref_ReadPrefFromJar(nsZipArchive* aJarReader, const char* aName)
URLPreloader::ReadZip(aJarReader, nsDependentCString(aName))); URLPreloader::ReadZip(aJarReader, nsDependentCString(aName)));
Parser parser; Parser parser;
if (!parser.Parse(nsDependentCString(aName), aName, startTime, manifest)) { if (!parser.Parse(nsDependentCString(aName),
PrefValueKind::Default,
aName,
startTime,
manifest)) {
return NS_ERROR_FILE_CORRUPTED; return NS_ERROR_FILE_CORRUPTED;
} }
@ -3816,7 +3850,7 @@ Preferences::InitInitialObjects()
rv = greprefsFile->AppendNative(NS_LITERAL_CSTRING("greprefs.js")); rv = greprefsFile->AppendNative(NS_LITERAL_CSTRING("greprefs.js"));
NS_ENSURE_SUCCESS(rv, Err("greprefsFile->AppendNative() failed")); NS_ENSURE_SUCCESS(rv, Err("greprefsFile->AppendNative() failed"));
rv = openPrefFile(greprefsFile); rv = openPrefFile(greprefsFile, PrefValueKind::Default);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
NS_WARNING("Error parsing GRE default preferences. Is this an old-style " NS_WARNING("Error parsing GRE default preferences. Is this an old-style "
"embedding app?"); "embedding app?");
@ -3945,15 +3979,14 @@ Preferences::InitInitialObjects()
bool releaseCandidateOnBeta = false; bool releaseCandidateOnBeta = false;
if (!strcmp(NS_STRINGIFY(MOZ_UPDATE_CHANNEL), "release")) { if (!strcmp(NS_STRINGIFY(MOZ_UPDATE_CHANNEL), "release")) {
nsAutoCString updateChannelPrefValue; nsAutoCString updateChannelPrefValue;
Preferences::GetCString(kChannelPref, updateChannelPrefValue, Preferences::GetCString(
PrefValueKind::Default); kChannelPref, updateChannelPrefValue, PrefValueKind::Default);
releaseCandidateOnBeta = updateChannelPrefValue.EqualsLiteral("beta"); releaseCandidateOnBeta = updateChannelPrefValue.EqualsLiteral("beta");
} }
if (!strcmp(NS_STRINGIFY(MOZ_UPDATE_CHANNEL), "nightly") || if (!strcmp(NS_STRINGIFY(MOZ_UPDATE_CHANNEL), "nightly") ||
!strcmp(NS_STRINGIFY(MOZ_UPDATE_CHANNEL), "aurora") || !strcmp(NS_STRINGIFY(MOZ_UPDATE_CHANNEL), "aurora") ||
!strcmp(NS_STRINGIFY(MOZ_UPDATE_CHANNEL), "beta") || !strcmp(NS_STRINGIFY(MOZ_UPDATE_CHANNEL), "beta") || developerBuild ||
developerBuild ||
releaseCandidateOnBeta) { releaseCandidateOnBeta) {
Preferences::SetBoolInAnyProcess( Preferences::SetBoolInAnyProcess(
kTelemetryPref, true, PrefValueKind::Default); kTelemetryPref, true, PrefValueKind::Default);
@ -4103,6 +4136,7 @@ Preferences::SetCStringInAnyProcess(const char* aPrefName,
aKind, aKind,
prefValue, prefValue,
/* isSticky */ false, /* isSticky */ false,
/* isLocked */ false,
/* fromFile */ false); /* fromFile */ false);
} }
@ -4129,6 +4163,7 @@ Preferences::SetBoolInAnyProcess(const char* aPrefName,
aKind, aKind,
prefValue, prefValue,
/* isSticky */ false, /* isSticky */ false,
/* isLocked */ false,
/* fromFile */ false); /* fromFile */ false);
} }
@ -4153,6 +4188,7 @@ Preferences::SetIntInAnyProcess(const char* aPrefName,
aKind, aKind,
prefValue, prefValue,
/* isSticky */ false, /* isSticky */ false,
/* isLocked */ false,
/* fromFile */ false); /* fromFile */ false);
} }
@ -4185,7 +4221,6 @@ Preferences::LockInAnyProcess(const char* aPrefName)
if (!pref->IsLocked()) { if (!pref->IsLocked()) {
pref->SetIsLocked(true); pref->SetIsLocked(true);
gIsAnyPrefLocked = true;
NotifyCallbacks(aPrefName); NotifyCallbacks(aPrefName);
} }
@ -4223,14 +4258,8 @@ Preferences::IsLocked(const char* aPrefName)
{ {
NS_ENSURE_TRUE(InitStaticMembers(), false); NS_ENSURE_TRUE(InitStaticMembers(), false);
if (gIsAnyPrefLocked) { Pref* pref = pref_HashTableLookup(aPrefName);
Pref* pref = pref_HashTableLookup(aPrefName); return pref && pref->IsLocked();
if (pref && pref->IsLocked()) {
return true;
}
}
return false;
} }
/* static */ nsresult /* static */ nsresult

Просмотреть файл

@ -1089,23 +1089,23 @@ pref("devtools.errorconsole.deprecation_warnings", true);
#ifdef NIGHTLY_BUILD #ifdef NIGHTLY_BUILD
// Don't show the Browser Toolbox prompt on local builds / nightly // Don't show the Browser Toolbox prompt on local builds / nightly
sticky_pref("devtools.debugger.prompt-connection", false); pref("devtools.debugger.prompt-connection", false, sticky);
#else #else
sticky_pref("devtools.debugger.prompt-connection", true); pref("devtools.debugger.prompt-connection", true, sticky);
#endif #endif
#ifdef MOZILLA_OFFICIAL #ifdef MOZILLA_OFFICIAL
// Disable debugging chrome // Disable debugging chrome
sticky_pref("devtools.chrome.enabled", false); pref("devtools.chrome.enabled", false, sticky);
// Disable remote debugging connections // Disable remote debugging connections
sticky_pref("devtools.debugger.remote-enabled", false); pref("devtools.debugger.remote-enabled", false, sticky);
// enable JS dump() function. // enable JS dump() function.
sticky_pref("browser.dom.window.dump.enabled", false); pref("browser.dom.window.dump.enabled", false, sticky);
#else #else
// In local builds, enable the browser toolbox by default // In local builds, enable the browser toolbox by default
sticky_pref("devtools.chrome.enabled", true); pref("devtools.chrome.enabled", true, sticky);
sticky_pref("devtools.debugger.remote-enabled", true); pref("devtools.debugger.remote-enabled", true, sticky);
sticky_pref("browser.dom.window.dump.enabled", true); pref("browser.dom.window.dump.enabled", true, sticky);
#endif #endif

Просмотреть файл

@ -45,7 +45,7 @@ interface nsIPrefService : nsISupports
* *
* @throws Error File failed to write. * @throws Error File failed to write.
* *
* @see readUserPrefs * @see readUserPrefsFromFile
* @see nsIFile * @see nsIFile
*/ */
void savePrefFile(in nsIFile aFile); void savePrefFile(in nsIFile aFile);
@ -102,14 +102,20 @@ interface nsIPrefService : nsISupports
readonly attribute boolean dirty; readonly attribute boolean dirty;
/** /**
* Read in the preferences specified in a user preference file. This method * Read in the preferences specified in a default preference file. This
* does not clear user preferences that were already set. * method does not clear preferences that were already set, but it may
* overwrite existing preferences.
* *
* @param aFile The file to be read. * @param aFile The file to be read.
* *
* @throws Error File failed to read or contained invalid data. * @throws Error File failed to read or contained invalid data.
* @note This method is intended for internal unit testing only! * @note This method is intended for internal unit testing only!
*/ */
void readDefaultPrefsFromFile(in nsIFile aFile);
/**
* Like readDefaultPrefsFromFile, but for a user prefs file.
*/
void readUserPrefsFromFile(in nsIFile aFile); void readUserPrefsFromFile(in nsIFile aFile);
}; };

Просмотреть файл

@ -4,10 +4,12 @@
//! This crate implements a prefs file parser. //! This crate implements a prefs file parser.
//! //!
//! Pref files have the following grammar. //! Pref files have the following grammar. Note that there are slight
//! differences between the grammar for a default prefs files and a user prefs
//! file.
//! //!
//! <pref-file> = <pref>* //! <pref-file> = <pref>*
//! <pref> = <pref-spec> "(" <pref-name> "," <pref-value> ")" ";" //! <pref> = <pref-spec> "(" <pref-name> "," <pref-value> <pref-attrs> ")" ";"
//! <pref-spec> = "user_pref" | "pref" | "sticky_pref" //! <pref-spec> = "user_pref" | "pref" | "sticky_pref"
//! <pref-name> = <string-literal> //! <pref-name> = <string-literal>
//! <pref-value> = <string-literal> | "true" | "false" | <int-value> //! <pref-value> = <string-literal> | "true" | "false" | <int-value>
@ -21,6 +23,9 @@
//! gives a UTF-16 code unit that is converted to UTF-8 before being copied //! gives a UTF-16 code unit that is converted to UTF-8 before being copied
//! into an 8-bit string value. \x00 and \u0000 are disallowed because they //! into an 8-bit string value. \x00 and \u0000 are disallowed because they
//! would cause C++ code handling such strings to misbehave. //! would cause C++ code handling such strings to misbehave.
//! <pref-attrs> = ("," <pref-attr>)* // in default pref files
//! = <empty> // in user pref files
//! <pref-attr> = "sticky" | "locked" // default pref files only
//! //!
//! Comments can take three forms: //! Comments can take three forms:
//! - # Python-style comments //! - # Python-style comments
@ -94,7 +99,7 @@ pub enum PrefType {
} }
/// Keep this in sync with PrefValueKind in Preferences.h. /// Keep this in sync with PrefValueKind in Preferences.h.
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug, PartialEq)]
#[repr(u8)] #[repr(u8)]
pub enum PrefValueKind { pub enum PrefValueKind {
Default, Default,
@ -112,7 +117,7 @@ pub union PrefValue {
/// Keep this in sync with PrefsParserPrefFn in Preferences.cpp. /// Keep this in sync with PrefsParserPrefFn in Preferences.cpp.
type PrefFn = unsafe extern "C" fn(pref_name: *const c_char, pref_type: PrefType, type PrefFn = unsafe extern "C" fn(pref_name: *const c_char, pref_type: PrefType,
pref_value_kind: PrefValueKind, pref_value: PrefValue, pref_value_kind: PrefValueKind, pref_value: PrefValue,
is_sticky: bool); is_sticky: bool, is_locked: bool);
/// Keep this in sync with PrefsParserErrorFn in Preferences.cpp. /// Keep this in sync with PrefsParserErrorFn in Preferences.cpp.
type ErrorFn = unsafe extern "C" fn(msg: *const c_char); type ErrorFn = unsafe extern "C" fn(msg: *const c_char);
@ -129,8 +134,8 @@ type ErrorFn = unsafe extern "C" fn(msg: *const c_char);
/// Keep this in sync with the prefs_parser_parse() declaration in /// Keep this in sync with the prefs_parser_parse() declaration in
/// Preferences.cpp. /// Preferences.cpp.
#[no_mangle] #[no_mangle]
pub extern "C" fn prefs_parser_parse(path: *const c_char, buf: *const c_char, len: usize, pub extern "C" fn prefs_parser_parse(path: *const c_char, kind: PrefValueKind, buf: *const c_char,
pref_fn: PrefFn, error_fn: ErrorFn) -> bool { len: usize, pref_fn: PrefFn, error_fn: ErrorFn) -> bool {
let path = unsafe { std::ffi::CStr::from_ptr(path).to_string_lossy().into_owned() }; let path = unsafe { std::ffi::CStr::from_ptr(path).to_string_lossy().into_owned() };
// Make sure `buf` ends in a '\0', and include that in the length, because // Make sure `buf` ends in a '\0', and include that in the length, because
@ -138,7 +143,7 @@ pub extern "C" fn prefs_parser_parse(path: *const c_char, buf: *const c_char, le
let buf = unsafe { std::slice::from_raw_parts(buf as *const c_uchar, len + 1) }; let buf = unsafe { std::slice::from_raw_parts(buf as *const c_uchar, len + 1) };
assert!(buf.last() == Some(&EOF)); assert!(buf.last() == Some(&EOF));
let mut parser = Parser::new(&path, &buf, pref_fn, error_fn); let mut parser = Parser::new(&path, kind, &buf, pref_fn, error_fn);
parser.parse() parser.parse()
} }
@ -157,6 +162,8 @@ enum Token {
UserPref, // user_pref UserPref, // user_pref
True, // true True, // true
False, // false False, // false
Sticky, // sticky
Locked, // locked
// String literal, e.g. '"string"'. The value is stored elsewhere. // String literal, e.g. '"string"'. The value is stored elsewhere.
String, String,
@ -272,36 +279,41 @@ struct KeywordInfo {
token: Token, token: Token,
} }
const KEYWORD_INFOS: &[KeywordInfo; 5] = &[ const KEYWORD_INFOS: [KeywordInfo; 7] = [
// These are ordered by frequency. // These are ordered by frequency.
KeywordInfo { string: b"pref", token: Token::Pref }, KeywordInfo { string: b"pref", token: Token::Pref },
KeywordInfo { string: b"true", token: Token::True }, KeywordInfo { string: b"true", token: Token::True },
KeywordInfo { string: b"false", token: Token::False }, KeywordInfo { string: b"false", token: Token::False },
KeywordInfo { string: b"user_pref", token: Token::UserPref }, KeywordInfo { string: b"user_pref", token: Token::UserPref },
KeywordInfo { string: b"sticky", token: Token::Sticky },
KeywordInfo { string: b"locked", token: Token::Locked },
KeywordInfo { string: b"sticky_pref", token: Token::StickyPref }, KeywordInfo { string: b"sticky_pref", token: Token::StickyPref },
]; ];
struct Parser<'t> { struct Parser<'t> {
path: &'t str, // Path to the file being parsed. Used in error messages. path: &'t str, // Path to the file being parsed. Used in error messages.
buf: &'t [u8], // Text being parsed. kind: PrefValueKind, // Default prefs file or user prefs file?
i: usize, // Index of next char to be read. buf: &'t [u8], // Text being parsed.
line_num: u32, // Current line number within the text. i: usize, // Index of next char to be read.
pref_fn: PrefFn, // Callback for processing each pref. line_num: u32, // Current line number within the text.
error_fn: ErrorFn, // Callback for parse errors. pref_fn: PrefFn, // Callback for processing each pref.
has_errors: bool, // Have we encountered errors? error_fn: ErrorFn, // Callback for parse errors.
has_errors: bool, // Have we encountered errors?
} }
// As described above, we use 0 to represent EOF. // As described above, we use 0 to represent EOF.
const EOF: u8 = b'\0'; const EOF: u8 = b'\0';
impl<'t> Parser<'t> { impl<'t> Parser<'t> {
fn new(path: &'t str, buf: &'t [u8], pref_fn: PrefFn, error_fn: ErrorFn) -> Parser<'t> { fn new(path: &'t str, kind: PrefValueKind, buf: &'t [u8], pref_fn: PrefFn, error_fn: ErrorFn)
-> Parser<'t> {
// Make sure these tables take up 1 byte per entry. // Make sure these tables take up 1 byte per entry.
assert!(std::mem::size_of_val(&CHAR_KINDS) == 256); assert!(std::mem::size_of_val(&CHAR_KINDS) == 256);
assert!(std::mem::size_of_val(&SPECIAL_STRING_CHARS) == 256); assert!(std::mem::size_of_val(&SPECIAL_STRING_CHARS) == 256);
Parser { Parser {
path: path, path: path,
kind: kind,
buf: buf, buf: buf,
i: 0, i: 0,
line_num: 1, line_num: 1,
@ -323,7 +335,7 @@ impl<'t> Parser<'t> {
// this will be either the first token of a new pref, or EOF. // this will be either the first token of a new pref, or EOF.
loop { loop {
// <pref-spec> // <pref-spec>
let (pref_value_kind, is_sticky) = match token { let (pref_value_kind, mut is_sticky) = match token {
Token::Pref => (PrefValueKind::Default, false), Token::Pref => (PrefValueKind::Default, false),
Token::StickyPref => (PrefValueKind::Default, true), Token::StickyPref => (PrefValueKind::Default, true),
Token::UserPref => (PrefValueKind::User, false), Token::UserPref => (PrefValueKind::User, false),
@ -370,7 +382,6 @@ impl<'t> Parser<'t> {
Token::String => { Token::String => {
(PrefType::String, (PrefType::String,
PrefValue { string_val: value_str.as_ptr() as *const c_char }) PrefValue { string_val: value_str.as_ptr() as *const c_char })
} }
Token::Int(u) => { Token::Int(u) => {
// Accept u <= 2147483647; anything larger will overflow i32. // Accept u <= 2147483647; anything larger will overflow i32.
@ -425,10 +436,49 @@ impl<'t> Parser<'t> {
} }
}; };
// ("," <pref-attr>)* // default pref files only
let mut is_locked = false;
let mut has_attrs = false;
if self.kind == PrefValueKind::Default {
let ok = loop {
// ","
token = self.get_token(&mut none_str);
if token != Token::SingleChar(b',') {
break true;
}
// <pref-attr>
token = self.get_token(&mut none_str);
match token {
Token::Sticky => is_sticky = true,
Token::Locked => is_locked = true,
_ => {
token =
self.error_and_recover(token, "expected pref attribute after ','");
break false;
}
}
has_attrs = true;
};
if !ok {
continue;
}
} else {
token = self.get_token(&mut none_str);
}
// ")" // ")"
token = self.get_token(&mut none_str);
if token != Token::SingleChar(b')') { if token != Token::SingleChar(b')') {
token = self.error_and_recover(token, "expected ')' after pref value"); let expected_msg = if self.kind == PrefValueKind::Default {
if has_attrs {
"expected ',' or ')' after pref attribute"
} else {
"expected ',' or ')' after pref value"
}
} else {
"expected ')' after pref value"
};
token = self.error_and_recover(token, expected_msg);
continue; continue;
} }
@ -440,7 +490,7 @@ impl<'t> Parser<'t> {
} }
unsafe { (self.pref_fn)(pref_name.as_ptr() as *const c_char, pref_type, pref_value_kind, unsafe { (self.pref_fn)(pref_name.as_ptr() as *const c_char, pref_type, pref_value_kind,
pref_value, is_sticky) }; pref_value, is_sticky, is_locked) };
token = self.get_token(&mut none_str); token = self.get_token(&mut none_str);
} }

Просмотреть файл

@ -6,12 +6,15 @@
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "mozilla/ArrayUtils.h" #include "mozilla/ArrayUtils.h"
#include "Preferences.h"
using namespace mozilla;
// Keep this in sync with the declaration in Preferences.cpp. // Keep this in sync with the declaration in Preferences.cpp.
// //
// It's declared here to avoid polluting Preferences.h with test-only stuff. // It's declared here to avoid polluting Preferences.h with test-only stuff.
void void
TestParseError(const char* aText, nsCString& aErrorMsg); TestParseError(PrefValueKind aKind, const char* aText, nsCString& aErrorMsg);
TEST(PrefsParser, Errors) TEST(PrefsParser, Errors)
{ {
@ -19,12 +22,18 @@ TEST(PrefsParser, Errors)
// Use a macro rather than a function so that the line number reported by // Use a macro rather than a function so that the line number reported by
// gtest on failure is useful. // gtest on failure is useful.
#define P(text_, expectedErrorMsg_) \ #define P(kind_, text_, expectedErrorMsg_) \
do { \ do { \
TestParseError(text_, actualErrorMsg); \ TestParseError(kind_, text_, actualErrorMsg); \
ASSERT_STREQ(expectedErrorMsg_, actualErrorMsg.get()); \ ASSERT_STREQ(expectedErrorMsg_, actualErrorMsg.get()); \
} while (0) } while (0)
#define DEFAULT(text_, expectedErrorMsg_) \
P(PrefValueKind::Default, text_, expectedErrorMsg_)
#define USER(text_, expectedErrorMsg_) \
P(PrefValueKind::User, text_, expectedErrorMsg_)
// clang-format off // clang-format off
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
@ -33,7 +42,7 @@ TEST(PrefsParser, Errors)
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// Normal prefs. // Normal prefs.
P(R"( DEFAULT(R"(
pref("bool", true); pref("bool", true);
sticky_pref("int", 123); sticky_pref("int", 123);
user_pref("string", "value"); user_pref("string", "value");
@ -42,12 +51,12 @@ user_pref("string", "value");
); );
// Totally empty input. // Totally empty input.
P("", DEFAULT("",
"" ""
); );
// Whitespace-only input. // Whitespace-only input.
P(R"( DEFAULT(R"(
)" "\v \t \v \f", )" "\v \t \v \f",
"" ""
@ -60,7 +69,7 @@ user_pref("string", "value");
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
// Integer overflow errors. // Integer overflow errors.
P(R"( DEFAULT(R"(
pref("int.ok", 2147483647); pref("int.ok", 2147483647);
pref("int.overflow", 2147483648); pref("int.overflow", 2147483648);
pref("int.ok", +2147483647); pref("int.ok", +2147483647);
@ -84,7 +93,7 @@ pref("int.overflow", 1234567890987654321);
); );
// Other integer errors. // Other integer errors.
P(R"( DEFAULT(R"(
pref("int.unexpected", 100foo); pref("int.unexpected", 100foo);
pref("int.ok", 0); pref("int.ok", 0);
)", )",
@ -92,7 +101,7 @@ pref("int.ok", 0);
); );
// \x00 is not allowed. // \x00 is not allowed.
P(R"( DEFAULT(R"(
pref("string.bad-x-escape", "foo\x00bar"); pref("string.bad-x-escape", "foo\x00bar");
pref("int.ok", 0); pref("int.ok", 0);
)", )",
@ -101,7 +110,7 @@ pref("int.ok", 0);
// Various bad things after \x: end of string, punctuation, space, newline, // Various bad things after \x: end of string, punctuation, space, newline,
// EOF. // EOF.
P(R"( DEFAULT(R"(
pref("string.bad-x-escape", "foo\x"); pref("string.bad-x-escape", "foo\x");
pref("string.bad-x-escape", "foo\x,bar"); pref("string.bad-x-escape", "foo\x,bar");
pref("string.bad-x-escape", "foo\x 12"); pref("string.bad-x-escape", "foo\x 12");
@ -116,7 +125,7 @@ pref("string.bad-x-escape", "foo\x)",
); );
// Not enough hex digits. // Not enough hex digits.
P(R"( DEFAULT(R"(
pref("string.bad-x-escape", "foo\x1"); pref("string.bad-x-escape", "foo\x1");
pref("int.ok", 0); pref("int.ok", 0);
)", )",
@ -124,7 +133,7 @@ pref("int.ok", 0);
); );
// Invalid hex digit. // Invalid hex digit.
P(R"( DEFAULT(R"(
pref("string.bad-x-escape", "foo\x1G"); pref("string.bad-x-escape", "foo\x1G");
pref("int.ok", 0); pref("int.ok", 0);
)", )",
@ -134,7 +143,7 @@ pref("int.ok", 0);
// \u0000 is not allowed. // \u0000 is not allowed.
// (The string literal is broken in two so that MSVC doesn't complain about // (The string literal is broken in two so that MSVC doesn't complain about
// an invalid universal-character-name.) // an invalid universal-character-name.)
P(R"( DEFAULT(R"(
pref("string.bad-u-escape", "foo\)" R"(u0000 bar"); pref("string.bad-u-escape", "foo\)" R"(u0000 bar");
pref("int.ok", 0); pref("int.ok", 0);
)", )",
@ -143,7 +152,7 @@ pref("int.ok", 0);
// Various bad things after \u: end of string, punctuation, space, newline, // Various bad things after \u: end of string, punctuation, space, newline,
// EOF. // EOF.
P(R"( DEFAULT(R"(
pref("string.bad-u-escape", "foo\u"); pref("string.bad-u-escape", "foo\u");
pref("string.bad-u-escape", "foo\u,bar"); pref("string.bad-u-escape", "foo\u,bar");
pref("string.bad-u-escape", "foo\u 1234"); pref("string.bad-u-escape", "foo\u 1234");
@ -158,7 +167,7 @@ pref("string.bad-u-escape", "foo\u)",
); );
// Not enough hex digits. // Not enough hex digits.
P(R"( DEFAULT(R"(
pref("string.bad-u-escape", "foo\u1"); pref("string.bad-u-escape", "foo\u1");
pref("string.bad-u-escape", "foo\u12"); pref("string.bad-u-escape", "foo\u12");
pref("string.bad-u-escape", "foo\u123"); pref("string.bad-u-escape", "foo\u123");
@ -170,7 +179,7 @@ pref("int.ok", 0);
); );
// Invalid hex digit. // Invalid hex digit.
P(R"( DEFAULT(R"(
pref("string.bad-u-escape", "foo\u1G34"); pref("string.bad-u-escape", "foo\u1G34");
pref("int.ok", 0); pref("int.ok", 0);
)", )",
@ -180,7 +189,7 @@ pref("int.ok", 0);
// High surrogate not followed by low surrogate. // High surrogate not followed by low surrogate.
// (The string literal is broken in two so that MSVC doesn't complain about // (The string literal is broken in two so that MSVC doesn't complain about
// an invalid universal-character-name.) // an invalid universal-character-name.)
P(R"( DEFAULT(R"(
pref("string.bad-u-surrogate", "foo\)" R"(ud83c,blah"); pref("string.bad-u-surrogate", "foo\)" R"(ud83c,blah");
pref("int.ok", 0); pref("int.ok", 0);
)", )",
@ -190,7 +199,7 @@ pref("int.ok", 0);
// High surrogate followed by invalid low surrogate value. // High surrogate followed by invalid low surrogate value.
// (The string literal is broken in two so that MSVC doesn't complain about // (The string literal is broken in two so that MSVC doesn't complain about
// an invalid universal-character-name.) // an invalid universal-character-name.)
P(R"( DEFAULT(R"(
pref("string.bad-u-surrogate", "foo\)" R"(ud83c\u1234"); pref("string.bad-u-surrogate", "foo\)" R"(ud83c\u1234");
pref("int.ok", 0); pref("int.ok", 0);
)", )",
@ -198,7 +207,7 @@ pref("int.ok", 0);
); );
// Unlike in JavaScript, \b, \f, \t, \v aren't allowed. // Unlike in JavaScript, \b, \f, \t, \v aren't allowed.
P(R"( DEFAULT(R"(
pref("string.bad-escape", "foo\b"); pref("string.bad-escape", "foo\b");
pref("string.bad-escape", "foo\f"); pref("string.bad-escape", "foo\f");
pref("string.bad-escape", "foo\t"); pref("string.bad-escape", "foo\t");
@ -213,7 +222,7 @@ pref("int.ok", 0);
// Various bad things after \: non-special letter, number, punctuation, // Various bad things after \: non-special letter, number, punctuation,
// space, newline, EOF. // space, newline, EOF.
P(R"( DEFAULT(R"(
pref("string.bad-escape", "foo\Q"); pref("string.bad-escape", "foo\Q");
pref("string.bad-escape", "foo\1"); pref("string.bad-escape", "foo\1");
pref("string.bad-escape", "foo\,"); pref("string.bad-escape", "foo\,");
@ -232,7 +241,7 @@ pref("string.bad-escape", "foo\)",
// Unterminated string literals. // Unterminated string literals.
// Simple case. // Simple case.
P(R"( DEFAULT(R"(
pref("string.unterminated-string", "foo pref("string.unterminated-string", "foo
)", )",
"test:3: prefs parse error: unterminated string literal\n" "test:3: prefs parse error: unterminated string literal\n"
@ -241,7 +250,7 @@ pref("string.unterminated-string", "foo
// Alternative case; `int` comes after the string and is seen as a keyword. // Alternative case; `int` comes after the string and is seen as a keyword.
// The parser then skips to the ';', so no error about the unterminated // The parser then skips to the ';', so no error about the unterminated
// string is issued. // string is issued.
P(R"( DEFAULT(R"(
pref("string.unterminated-string", "foo); pref("string.unterminated-string", "foo);
pref("int.ok", 0); pref("int.ok", 0);
)", )",
@ -249,21 +258,21 @@ pref("int.ok", 0);
); );
// Mismatched quotes (1). // Mismatched quotes (1).
P(R"( DEFAULT(R"(
pref("string.unterminated-string", "foo'); pref("string.unterminated-string", "foo');
)", )",
"test:3: prefs parse error: unterminated string literal\n" "test:3: prefs parse error: unterminated string literal\n"
); );
// Mismatched quotes (2). // Mismatched quotes (2).
P(R"( DEFAULT(R"(
pref("string.unterminated-string", 'foo"); pref("string.unterminated-string", 'foo");
)", )",
"test:3: prefs parse error: unterminated string literal\n" "test:3: prefs parse error: unterminated string literal\n"
); );
// Unknown keywords. // Unknown keywords.
P(R"( DEFAULT(R"(
foo; foo;
preff("string.bad-keyword", true); preff("string.bad-keyword", true);
ticky_pref("string.bad-keyword", true); ticky_pref("string.bad-keyword", true);
@ -278,33 +287,33 @@ pref("string.bad-keyword", TRUE);
); );
// Unterminated C-style comment. // Unterminated C-style comment.
P(R"( DEFAULT(R"(
/* comment /* comment
)", )",
"test:3: prefs parse error: unterminated /* comment\n" "test:3: prefs parse error: unterminated /* comment\n"
); );
// Malformed comment. // Malformed comment.
P(R"( DEFAULT(R"(
/ comment / comment
)", )",
"test:2: prefs parse error: expected '/' or '*' after '/'\n" "test:2: prefs parse error: expected '/' or '*' after '/'\n"
); );
// C++-style comment ending in EOF (1). // C++-style comment ending in EOF (1).
P(R"( DEFAULT(R"(
// comment)", // comment)",
"" ""
); );
// C++-style comment ending in EOF (2). // C++-style comment ending in EOF (2).
P(R"( DEFAULT(R"(
//)", //)",
"" ""
); );
// Various unexpected characters. // Various unexpected characters.
P(R"( DEFAULT(R"(
pref("unexpected.chars", &true); pref("unexpected.chars", &true);
pref("unexpected.chars" : true); pref("unexpected.chars" : true);
@pref("unexpected.chars", true); @pref("unexpected.chars", true);
@ -320,7 +329,7 @@ pref["unexpected.chars": true];
// All the parsing errors. // All the parsing errors.
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
P(R"( DEFAULT(R"(
"pref"("parse.error": true); "pref"("parse.error": true);
pref1("parse.error": true); pref1("parse.error": true);
pref(123: true); pref(123: true);
@ -328,7 +337,9 @@ pref("parse.error" true);
pref("parse.error", pref); pref("parse.error", pref);
pref("parse.error", -true); pref("parse.error", -true);
pref("parse.error", +"value"); pref("parse.error", +"value");
pref("parse.error", true,);
pref("parse.error", true; pref("parse.error", true;
pref("parse.error", true, sticky, locked;
pref("parse.error", true) pref("parse.error", true)
pref("int.ok", 1); pref("int.ok", 1);
pref("parse.error", true))", pref("parse.error", true))",
@ -339,59 +350,83 @@ pref("parse.error", true))",
"test:6: prefs parse error: expected pref value after ','\n" "test:6: prefs parse error: expected pref value after ','\n"
"test:7: prefs parse error: expected integer literal after '-'\n" "test:7: prefs parse error: expected integer literal after '-'\n"
"test:8: prefs parse error: expected integer literal after '+'\n" "test:8: prefs parse error: expected integer literal after '+'\n"
"test:9: prefs parse error: expected ')' after pref value\n" "test:9: prefs parse error: expected pref attribute after ','\n"
"test:11: prefs parse error: expected ';' after ')'\n" "test:10: prefs parse error: expected ',' or ')' after pref value\n"
"test:12: prefs parse error: expected ';' after ')'\n" "test:11: prefs parse error: expected ',' or ')' after pref attribute\n"
"test:13: prefs parse error: expected ';' after ')'\n"
"test:14: prefs parse error: expected ';' after ')'\n"
);
USER(R"(
pref("parse.error", true;
pref("int.ok", 1);
)",
"test:2: prefs parse error: expected ')' after pref value\n"
); );
// Parse errors involving unexpected EOF. // Parse errors involving unexpected EOF.
P(R"( DEFAULT(R"(
pref)", pref)",
"test:2: prefs parse error: expected '(' after pref specifier\n" "test:2: prefs parse error: expected '(' after pref specifier\n"
); );
P(R"( DEFAULT(R"(
pref()", pref()",
"test:2: prefs parse error: expected pref name after '('\n" "test:2: prefs parse error: expected pref name after '('\n"
); );
P(R"( DEFAULT(R"(
pref("parse.error")", pref("parse.error")",
"test:2: prefs parse error: expected ',' after pref name\n" "test:2: prefs parse error: expected ',' after pref name\n"
); );
P(R"( DEFAULT(R"(
pref("parse.error",)", pref("parse.error",)",
"test:2: prefs parse error: expected pref value after ','\n" "test:2: prefs parse error: expected pref value after ','\n"
); );
P(R"( DEFAULT(R"(
pref("parse.error", -)", pref("parse.error", -)",
"test:2: prefs parse error: expected integer literal after '-'\n" "test:2: prefs parse error: expected integer literal after '-'\n"
); );
P(R"( DEFAULT(R"(
pref("parse.error", +)", pref("parse.error", +)",
"test:2: prefs parse error: expected integer literal after '+'\n" "test:2: prefs parse error: expected integer literal after '+'\n"
); );
P(R"( DEFAULT(R"(
pref("parse.error", true)",
"test:2: prefs parse error: expected ',' or ')' after pref value\n"
);
USER(R"(
pref("parse.error", true)", pref("parse.error", true)",
"test:2: prefs parse error: expected ')' after pref value\n" "test:2: prefs parse error: expected ')' after pref value\n"
); );
P(R"( DEFAULT(R"(
pref("parse.error", true,)",
"test:2: prefs parse error: expected pref attribute after ','\n"
);
DEFAULT(R"(
pref("parse.error", true, sticky)",
"test:2: prefs parse error: expected ',' or ')' after pref attribute\n"
);
DEFAULT(R"(
pref("parse.error", true))", pref("parse.error", true))",
"test:2: prefs parse error: expected ';' after ')'\n" "test:2: prefs parse error: expected ';' after ')'\n"
); );
// This is something we saw in practice with the old parser, which allowed // This is something we saw in practice with the old parser, which allowed
// repeated semicolons. // repeated semicolons.
P(R"( DEFAULT(R"(
pref("parse.error", true);; pref("parse.error", true);;
pref("parse.error", true);;; pref("parse.error", true, locked);;;
pref("parse.error", true);;;; pref("parse.error", true, sticky, locked);;;;
pref("int.ok", 0); pref("int.ok", 0);
)", )",
"test:2: prefs parse error: expected pref specifier at start of pref definition\n" "test:2: prefs parse error: expected pref specifier at start of pref definition\n"
@ -411,24 +446,24 @@ pref("int.ok", 0);
// the error is on line 4. (Note: these ones don't use raw string literals // the error is on line 4. (Note: these ones don't use raw string literals
// because MSVC somehow swallows any \r that appears in them.) // because MSVC somehow swallows any \r that appears in them.)
P("\n \r \r\n bad", DEFAULT("\n \r \r\n bad",
"test:4: prefs parse error: unknown keyword\n" "test:4: prefs parse error: unknown keyword\n"
); );
P("#\n#\r#\r\n bad", DEFAULT("#\n#\r#\r\n bad",
"test:4: prefs parse error: unknown keyword\n" "test:4: prefs parse error: unknown keyword\n"
); );
P("//\n//\r//\r\n bad", DEFAULT("//\n//\r//\r\n bad",
"test:4: prefs parse error: unknown keyword\n" "test:4: prefs parse error: unknown keyword\n"
); );
P("/*\n \r \r\n*/ bad", DEFAULT("/*\n \r \r\n*/ bad",
"test:4: prefs parse error: unknown keyword\n" "test:4: prefs parse error: unknown keyword\n"
); );
// Note: the escape sequences do *not* affect the line number. // Note: the escape sequences do *not* affect the line number.
P("pref(\"foo\\n\n foo\\r\r foo\\r\\n\r\n foo\", bad);", DEFAULT("pref(\"foo\\n\n foo\\r\r foo\\r\\n\r\n foo\", bad);",
"test:4: prefs parse error: unknown keyword\n" "test:4: prefs parse error: unknown keyword\n"
); );

Просмотреть файл

@ -1,5 +1,6 @@
// Note: this file tests only valid syntax. See // Note: this file tests only valid syntax (of user pref files, not default
// modules/libpref/test/gtest/Parser.cpp for tests if invalid syntax. // pref files). See modules/libpref/test/gtest/Parser.cpp for tests if invalid
// syntax.
# #
# comment # comment
@ -50,6 +51,10 @@ pref
pref("pref", true); pref("pref", true);
sticky_pref("sticky_pref", true); sticky_pref("sticky_pref", true);
user_pref("user_pref", true); user_pref("user_pref", true);
pref("sticky_pref2", true, sticky);
pref("locked_pref", true, locked);
pref("locked_sticky_pref", true, locked, sticky,sticky,
locked, locked, locked);
pref("bool.true", true); pref("bool.true", true);
pref("bool.false", false); pref("bool.false", false);

Просмотреть файл

@ -0,0 +1,2 @@
pref("testPref.unlocked.int", 333);
pref("testPref.locked.int", 444, locked);

Просмотреть файл

@ -0,0 +1,3 @@
// testPrefLocked.js defined this pref as a locked pref.
// Changing a locked pref has no effect.
user_pref("testPref.locked.int", 555);

Просмотреть файл

@ -1,2 +1,2 @@
pref("testPref.unsticky.bool", true); pref("testPref.unsticky.bool", true);
sticky_pref("testPref.sticky.bool", false); pref("testPref.sticky.bool", false, sticky);

Просмотреть файл

@ -1,4 +1,4 @@
// testPrefSticky.js defined this pref as a sticky_pref(). Once a sticky // testPrefSticky.js defined this pref as sticky. Once a sticky
// pref has been changed, it's written as a user_pref(). // pref has been changed, it's written as a user_pref().
// So this test file reflects that scenario. // So this test file reflects that scenario.
// Note the default in testPrefSticky.js is also false. // Note the default in testPrefSticky.js is also false.

Просмотреть файл

@ -0,0 +1,45 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/ */
// This file tests the `locked` attribute in default pref files.
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
const ps = Services.prefs;
add_test(function notChangedFromAPI() {
ps.resetPrefs();
ps.readDefaultPrefsFromFile(do_get_file("data/testPrefLocked.js"));
Assert.strictEqual(ps.getIntPref("testPref.unlocked.int"), 333);
Assert.strictEqual(ps.getIntPref("testPref.locked.int"), 444);
// Unlocked pref: can set the user value, which is used upon reading.
ps.setIntPref("testPref.unlocked.int", 334);
Assert.ok(ps.prefHasUserValue("testPref.unlocked.int"), "has a user value");
Assert.strictEqual(ps.getIntPref("testPref.unlocked.int"), 334);
// Locked pref: can set the user value, but the default value is used upon
// reading.
ps.setIntPref("testPref.locked.int", 445);
Assert.ok(ps.prefHasUserValue("testPref.locked.int"), "has a user value");
Assert.strictEqual(ps.getIntPref("testPref.locked.int"), 444);
// After unlocking, the user value is used.
ps.unlockPref("testPref.locked.int");
Assert.ok(ps.prefHasUserValue("testPref.locked.int"), "has a user value");
Assert.strictEqual(ps.getIntPref("testPref.locked.int"), 445);
run_next_test();
});
add_test(function notChangedFromUserPrefs() {
ps.resetPrefs();
ps.readDefaultPrefsFromFile(do_get_file("data/testPrefLocked.js"));
ps.readUserPrefsFromFile(do_get_file("data/testPrefLockedUser.js"));
Assert.strictEqual(ps.getIntPref("testPref.unlocked.int"), 333);
Assert.strictEqual(ps.getIntPref("testPref.locked.int"), 444);
run_next_test();
});

Просмотреть файл

@ -10,7 +10,7 @@ function run_test() {
var prefs = ps.getBranch(null); var prefs = ps.getBranch(null);
ps.resetPrefs(); ps.resetPrefs();
ps.readUserPrefsFromFile(do_get_file('data/testParser.js')); ps.readDefaultPrefsFromFile(do_get_file('data/testParser.js'));
Assert.equal(ps.getBoolPref("comment1"), true); Assert.equal(ps.getBoolPref("comment1"), true);
Assert.equal(ps.getBoolPref("comment2"), true); Assert.equal(ps.getBoolPref("comment2"), true);
@ -19,6 +19,11 @@ function run_test() {
Assert.equal(ps.getBoolPref("pref"), true); Assert.equal(ps.getBoolPref("pref"), true);
Assert.equal(ps.getBoolPref("sticky_pref"), true); Assert.equal(ps.getBoolPref("sticky_pref"), true);
Assert.equal(ps.getBoolPref("user_pref"), true); Assert.equal(ps.getBoolPref("user_pref"), true);
Assert.equal(ps.getBoolPref("sticky_pref2"), true);
Assert.equal(ps.getBoolPref("locked_pref"), true);
Assert.equal(ps.getBoolPref("locked_sticky_pref"), true);
Assert.equal(ps.prefIsLocked("locked_pref"), true);
Assert.equal(ps.prefIsLocked("locked_sticky_pref"), true);
Assert.equal(ps.getBoolPref("bool.true"), true); Assert.equal(ps.getBoolPref("bool.true"), true);
Assert.equal(ps.getBoolPref("bool.false"), false); Assert.equal(ps.getBoolPref("bool.false"), false);

Просмотреть файл

@ -6,12 +6,17 @@ ChromeUtils.import("resource://gre/modules/Services.jsm");
const ps = Services.prefs; const ps = Services.prefs;
// A little helper to reset the service and load some pref files // A little helper to reset the service and load one pref file.
function resetAndLoad(filenames) { function resetAndLoadDefaults() {
ps.resetPrefs(); ps.resetPrefs();
for (let filename of filenames) { ps.readDefaultPrefsFromFile(do_get_file("data/testPrefSticky.js"));
ps.readUserPrefsFromFile(do_get_file(filename)); }
}
// A little helper to reset the service and load two pref files.
function resetAndLoadAll() {
ps.resetPrefs();
ps.readDefaultPrefsFromFile(do_get_file("data/testPrefSticky.js"));
ps.readUserPrefsFromFile(do_get_file("data/testPrefStickyUser.js"));
} }
// A little helper that saves the current state to a file in the profile // A little helper that saves the current state to a file in the profile
@ -39,7 +44,7 @@ function run_test() {
// A sticky pref should not be written if the value is unchanged. // A sticky pref should not be written if the value is unchanged.
add_test(function notWrittenWhenUnchanged() { add_test(function notWrittenWhenUnchanged() {
resetAndLoad(["data/testPrefSticky.js"]); resetAndLoadDefaults();
Assert.strictEqual(ps.getBoolPref("testPref.unsticky.bool"), true); Assert.strictEqual(ps.getBoolPref("testPref.unsticky.bool"), true);
Assert.strictEqual(ps.getBoolPref("testPref.sticky.bool"), false); Assert.strictEqual(ps.getBoolPref("testPref.sticky.bool"), false);
@ -55,13 +60,13 @@ add_test(function notWrittenWhenUnchanged() {
run_next_test(); run_next_test();
}); });
// Loading a sticky_pref then a user_pref for the same pref means it should // Loading a sticky `pref` then a `user_pref` for the same pref means it should
// always be written. // always be written.
add_test(function writtenOnceLoadedWithoutChange() { add_test(function writtenOnceLoadedWithoutChange() {
// Load the same pref file *as well as* a pref file that has a user_pref for // Load the same pref file *as well as* a pref file that has a user_pref for
// our sticky with the default value. It should be re-written without us // our sticky with the default value. It should be re-written without us
// touching it. // touching it.
resetAndLoad(["data/testPrefSticky.js", "data/testPrefStickyUser.js"]); resetAndLoadAll();
// reset and re-read what we just wrote - it should be written. // reset and re-read what we just wrote - it should be written.
saveAndReload(); saveAndReload();
Assert.strictEqual(ps.getBoolPref("testPref.sticky.bool"), false, Assert.strictEqual(ps.getBoolPref("testPref.sticky.bool"), false,
@ -73,7 +78,7 @@ add_test(function writtenOnceLoadedWithoutChange() {
add_test(function writtenOnceLoadedWithChangeNonDefault() { add_test(function writtenOnceLoadedWithChangeNonDefault() {
// Load the same pref file *as well as* a pref file that has a user_pref for // Load the same pref file *as well as* a pref file that has a user_pref for
// our sticky - then change the pref. It should be written. // our sticky - then change the pref. It should be written.
resetAndLoad(["data/testPrefSticky.js", "data/testPrefStickyUser.js"]); resetAndLoadAll();
// Set a new val and check we wrote it. // Set a new val and check we wrote it.
ps.setBoolPref("testPref.sticky.bool", false); ps.setBoolPref("testPref.sticky.bool", false);
saveAndReload(); saveAndReload();
@ -86,7 +91,7 @@ add_test(function writtenOnceLoadedWithChangeNonDefault() {
add_test(function writtenOnceLoadedWithChangeNonDefault() { add_test(function writtenOnceLoadedWithChangeNonDefault() {
// Load the same pref file *as well as* a pref file that has a user_pref for // Load the same pref file *as well as* a pref file that has a user_pref for
// our sticky - then change the pref. It should be written. // our sticky - then change the pref. It should be written.
resetAndLoad(["data/testPrefSticky.js", "data/testPrefStickyUser.js"]); resetAndLoadAll();
// Set a new val and check we wrote it. // Set a new val and check we wrote it.
ps.setBoolPref("testPref.sticky.bool", true); ps.setBoolPref("testPref.sticky.bool", true);
saveAndReload(); saveAndReload();
@ -102,7 +107,7 @@ add_test(function writtenOnceLoadedWithChangeNonDefault() {
// the pref had never changed.) // the pref had never changed.)
add_test(function hasUserValue() { add_test(function hasUserValue() {
// sticky pref without user value. // sticky pref without user value.
resetAndLoad(["data/testPrefSticky.js"]); resetAndLoadDefaults();
Assert.strictEqual(ps.getBoolPref("testPref.sticky.bool"), false); Assert.strictEqual(ps.getBoolPref("testPref.sticky.bool"), false);
Assert.ok(!ps.prefHasUserValue("testPref.sticky.bool"), Assert.ok(!ps.prefHasUserValue("testPref.sticky.bool"),
"should not initially reflect a user value"); "should not initially reflect a user value");
@ -121,7 +126,7 @@ add_test(function hasUserValue() {
ps.setBoolPref("testPref.sticky.bool", false, "expected default"); ps.setBoolPref("testPref.sticky.bool", false, "expected default");
// And make sure the pref immediately reflects a user value after load. // And make sure the pref immediately reflects a user value after load.
resetAndLoad(["data/testPrefSticky.js", "data/testPrefStickyUser.js"]); resetAndLoadAll();
Assert.strictEqual(ps.getBoolPref("testPref.sticky.bool"), false); Assert.strictEqual(ps.getBoolPref("testPref.sticky.bool"), false);
Assert.ok(ps.prefHasUserValue("testPref.sticky.bool"), Assert.ok(ps.prefHasUserValue("testPref.sticky.bool"),
"should have a user value when loaded value is the default"); "should have a user value when loaded value is the default");
@ -132,7 +137,7 @@ add_test(function hasUserValue() {
add_test(function clearUserPref() { add_test(function clearUserPref() {
// load things such that we have a sticky value which is the same as the // load things such that we have a sticky value which is the same as the
// default. // default.
resetAndLoad(["data/testPrefSticky.js", "data/testPrefStickyUser.js"]); resetAndLoadAll();
ps.clearUserPref("testPref.sticky.bool"); ps.clearUserPref("testPref.sticky.bool");
// Once we save prefs the sticky pref should no longer be written. // Once we save prefs the sticky pref should no longer be written.
@ -153,7 +158,7 @@ add_test(function clearUserPref() {
// even if the value has not) // even if the value has not)
add_test(function observerFires() { add_test(function observerFires() {
// load things so there's no sticky value. // load things so there's no sticky value.
resetAndLoad(["data/testPrefSticky.js"]); resetAndLoadDefaults();
function observe(subject, topic, data) { function observe(subject, topic, data) {
Assert.equal(data, "testPref.sticky.bool"); Assert.equal(data, "testPref.sticky.bool");

Просмотреть файл

@ -11,6 +11,8 @@ support-files =
[test_bug790374.js] [test_bug790374.js]
[test_stickyprefs.js] [test_stickyprefs.js]
support-files = data/testPrefSticky.js data/testPrefStickyUser.js support-files = data/testPrefSticky.js data/testPrefStickyUser.js
[test_locked_file_prefs.js]
support-files = data/testPrefLocked.js data/testPrefLockedUser.js
[test_changeType.js] [test_changeType.js]
[test_defaultValues.js] [test_defaultValues.js]
[test_dirtyPrefs.js] [test_dirtyPrefs.js]

Просмотреть файл

@ -25,7 +25,7 @@ function makePersona(id) {
add_task(async function run_test() { add_task(async function run_test() {
_("Test fixtures."); _("Test fixtures.");
// read our custom prefs file before doing anything. // read our custom prefs file before doing anything.
Services.prefs.readUserPrefsFromFile(do_get_file("prefs_test_prefs_store.js")); Services.prefs.readDefaultPrefsFromFile(do_get_file("prefs_test_prefs_store.js"));
let engine = Service.engineManager.get("prefs"); let engine = Service.engineManager.get("prefs");
let store = engine._store; let store = engine._store;

Просмотреть файл

@ -5099,12 +5099,14 @@
${ElevateUAC} ${ElevateUAC}
${EndIf} ${EndIf}
ReadINIStr $R8 $R7 "Install" "OptionalExtensions" !ifdef MOZ_OPTIONAL_EXTENSIONS
${If} $R8 == "false" ReadINIStr $R8 $R7 "Install" "OptionalExtensions"
StrCpy $InstallOptionalExtensions "0" ${If} $R8 == "false"
${Else} StrCpy $InstallOptionalExtensions "0"
StrCpy $InstallOptionalExtensions "1" ${Else}
${EndIf} StrCpy $InstallOptionalExtensions "1"
${EndIf}
!endif
!ifndef NO_STARTMENU_DIR !ifndef NO_STARTMENU_DIR
ReadINIStr $R8 $R7 "Install" "StartMenuDirectoryName" ReadINIStr $R8 $R7 "Install" "StartMenuDirectoryName"

Просмотреть файл

@ -675,7 +675,6 @@ int main()
TestDetour("user32.dll", "CreateWindowExW") && TestDetour("user32.dll", "CreateWindowExW") &&
TestHook(TestInSendMessageEx, "user32.dll", "InSendMessageEx") && TestHook(TestInSendMessageEx, "user32.dll", "InSendMessageEx") &&
TestHook(TestImmGetContext, "imm32.dll", "ImmGetContext") && TestHook(TestImmGetContext, "imm32.dll", "ImmGetContext") &&
// TestHook("imm32.dll", "ImmReleaseContext") && // see Bug 1316415
TestHook(TestImmGetCompositionStringW, "imm32.dll", "ImmGetCompositionStringW") && TestHook(TestImmGetCompositionStringW, "imm32.dll", "ImmGetCompositionStringW") &&
TestHook(TestImmSetCandidateWindow, "imm32.dll", "ImmSetCandidateWindow") && TestHook(TestImmSetCandidateWindow, "imm32.dll", "ImmSetCandidateWindow") &&
TestHook(TestImmNotifyIME, "imm32.dll", "ImmNotifyIME") && TestHook(TestImmNotifyIME, "imm32.dll", "ImmNotifyIME") &&

Просмотреть файл

@ -38,12 +38,6 @@ public:
return Equals(aString.BeginReading(), aString.Length()); return Equals(aString.BeginReading(), aString.Length());
} }
void SetKind(AtomKind aKind)
{
mKind = static_cast<uint32_t>(aKind);
MOZ_ASSERT(Kind() == aKind);
}
AtomKind Kind() const { return static_cast<AtomKind>(mKind); } AtomKind Kind() const { return static_cast<AtomKind>(mKind); }
bool IsDynamicAtom() const { return Kind() == AtomKind::DynamicAtom; } bool IsDynamicAtom() const { return Kind() == AtomKind::DynamicAtom; }
@ -57,12 +51,13 @@ public:
void ToString(nsAString& aString) const; void ToString(nsAString& aString) const;
void ToUTF8String(nsACString& aString) const; void ToUTF8String(nsACString& aString) const;
// This is only valid for dynamic atoms. // This is not valid for static atoms. The caller must *not* mutate the
// string buffer, otherwise all hell will break loose.
nsStringBuffer* GetStringBuffer() const nsStringBuffer* GetStringBuffer() const
{ {
// See the comment on |mString|'s declaration. // See the comment on |mString|'s declaration.
MOZ_ASSERT(IsDynamicAtom()); MOZ_ASSERT(IsDynamicAtom() || IsHTML5Atom());
return nsStringBuffer::FromData(mString); return nsStringBuffer::FromData(const_cast<char16_t*>(mString));
} }
// A hashcode that is better distributed than the actual atom pointer, for // A hashcode that is better distributed than the actual atom pointer, for
@ -88,23 +83,23 @@ private:
friend class nsAtomSubTable; friend class nsAtomSubTable;
friend class nsHtml5AtomEntry; friend class nsHtml5AtomEntry;
// Dynamic atom construction is done by |friend|s. protected:
// Used by nsDynamicAtom and directly (by nsHtml5AtomEntry) for HTML5 atoms.
nsAtom(AtomKind aKind, const nsAString& aString, uint32_t aHash); nsAtom(AtomKind aKind, const nsAString& aString, uint32_t aHash);
protected: // Used by nsStaticAtom.
nsAtom(const char16_t* aString, uint32_t aLength, uint32_t aHash); nsAtom(const char16_t* aString, uint32_t aLength, uint32_t aHash);
~nsAtom(); ~nsAtom();
mozilla::ThreadSafeAutoRefCnt mRefCnt; const uint32_t mLength:30;
uint32_t mLength: 30; const uint32_t mKind:2; // nsAtom::AtomKind
uint32_t mKind: 2; // nsAtom::AtomKind const uint32_t mHash;
uint32_t mHash;
// WARNING! For static atoms, this is a pointer to a static char buffer. For // WARNING! For static atoms, this is a pointer to a static char buffer. For
// non-static atoms it points to the chars in an nsStringBuffer. This means // non-static atoms it points to the chars in an nsStringBuffer. This means
// that nsStringBuffer::FromData(mString) calls are only valid for non-static // that nsStringBuffer::FromData(mString) calls are only valid for non-static
// atoms. // atoms.
char16_t* mString; const char16_t* const mString;
}; };
// A trivial subclass of nsAtom that can be used for known static atoms. The // A trivial subclass of nsAtom that can be used for known static atoms. The
@ -118,7 +113,7 @@ class nsStaticAtom : public nsAtom
{ {
public: public:
// These are deleted so it's impossible to RefPtr<nsStaticAtom>. Raw // These are deleted so it's impossible to RefPtr<nsStaticAtom>. Raw
// nsStaticAtom atoms should be used instead. // nsStaticAtom pointers should be used instead.
MozExternalRefCountType AddRef() = delete; MozExternalRefCountType AddRef() = delete;
MozExternalRefCountType Release() = delete; MozExternalRefCountType Release() = delete;

Просмотреть файл

@ -64,39 +64,75 @@ enum class GCKind {
// See nsAtom::AddRef() and nsAtom::Release(). // See nsAtom::AddRef() and nsAtom::Release().
static Atomic<int32_t, ReleaseAcquire> gUnusedAtomCount(0); static Atomic<int32_t, ReleaseAcquire> gUnusedAtomCount(0);
// This constructor is for dynamic atoms and HTML5 atoms. // Dynamic atoms need a ref count; this class adds that to nsAtom.
nsAtom::nsAtom(AtomKind aKind, const nsAString& aString, uint32_t aHash) class nsDynamicAtom : public nsAtom
: mRefCnt(1)
, mLength(aString.Length())
, mKind(static_cast<uint32_t>(aKind))
, mHash(aHash)
{ {
MOZ_ASSERT(aKind == AtomKind::DynamicAtom || aKind == AtomKind::HTML5Atom); public:
RefPtr<nsStringBuffer> buf = nsStringBuffer::FromString(aString); // We can't use NS_INLINE_DECL_THREADSAFE_REFCOUNTING because the refcounting
if (buf) { // of this type is special.
mString = static_cast<char16_t*>(buf->Data()); MozExternalRefCountType AddRef();
} else { MozExternalRefCountType Release();
const size_t size = (mLength + 1) * sizeof(char16_t);
buf = nsStringBuffer::Alloc(size); static nsDynamicAtom* As(nsAtom* aAtom)
if (MOZ_UNLIKELY(!buf)) { {
// We OOM because atom allocations should be small and it's hard to MOZ_ASSERT(aAtom->IsDynamicAtom());
// handle them more gracefully in a constructor. return static_cast<nsDynamicAtom*>(aAtom);
NS_ABORT_OOM(size);
}
mString = static_cast<char16_t*>(buf->Data());
CopyUnicodeTo(aString, 0, mString, mLength);
mString[mLength] = char16_t(0);
} }
MOZ_ASSERT_IF(IsDynamicAtom(), mHash == HashString(mString, mLength)); private:
friend class nsAtomTable;
friend class nsAtomSubTable;
// Construction is done by |friend|s.
nsDynamicAtom(const nsAString& aString, uint32_t aHash)
: nsAtom(AtomKind::DynamicAtom, aString, aHash)
, mRefCnt(1)
{}
mozilla::ThreadSafeAutoRefCnt mRefCnt;
};
static char16_t*
FromStringBuffer(const nsAString& aString)
{
char16_t* str;
size_t length = aString.Length();
RefPtr<nsStringBuffer> buf = nsStringBuffer::FromString(aString);
if (buf) {
str = static_cast<char16_t*>(buf->Data());
} else {
const size_t size = (length + 1) * sizeof(char16_t);
buf = nsStringBuffer::Alloc(size);
if (MOZ_UNLIKELY(!buf)) {
NS_ABORT_OOM(size); // OOM because atom allocations should be small.
}
str = static_cast<char16_t*>(buf->Data());
CopyUnicodeTo(aString, 0, str, length);
str[length] = char16_t(0);
}
MOZ_ASSERT(buf && buf->StorageSize() >= (length + 1) * sizeof(char16_t),
"enough storage");
// Take ownership of the string buffer.
mozilla::Unused << buf.forget();
return str;
}
// This constructor is for dynamic atoms and HTML5 atoms.
nsAtom::nsAtom(AtomKind aKind, const nsAString& aString, uint32_t aHash)
: mLength(aString.Length())
, mKind(static_cast<uint32_t>(aKind))
, mHash(aHash)
, mString(FromStringBuffer(aString))
{
MOZ_ASSERT(aKind == AtomKind::DynamicAtom || aKind == AtomKind::HTML5Atom);
MOZ_ASSERT_IF(!IsHTML5Atom(), mHash == HashString(mString, mLength));
MOZ_ASSERT(mString[mLength] == char16_t(0), "null terminated"); MOZ_ASSERT(mString[mLength] == char16_t(0), "null terminated");
MOZ_ASSERT(buf && buf->StorageSize() >= (mLength + 1) * sizeof(char16_t),
"enough storage");
MOZ_ASSERT(Equals(aString), "correct data"); MOZ_ASSERT(Equals(aString), "correct data");
// Take ownership of buffer
mozilla::Unused << buf.forget();
} }
// This constructor is for static atoms. // This constructor is for static atoms.
@ -116,7 +152,7 @@ nsAtom::~nsAtom()
{ {
if (!IsStaticAtom()) { if (!IsStaticAtom()) {
MOZ_ASSERT(IsDynamicAtom() || IsHTML5Atom()); MOZ_ASSERT(IsDynamicAtom() || IsHTML5Atom());
nsStringBuffer::FromData(mString)->Release(); GetStringBuffer()->Release();
} }
} }
@ -130,7 +166,7 @@ nsAtom::ToString(nsAString& aString) const
// which is what's important. // which is what's important.
aString.AssignLiteral(mString, mLength); aString.AssignLiteral(mString, mLength);
} else { } else {
nsStringBuffer::FromData(mString)->ToString(mLength, aString); GetStringBuffer()->ToString(mLength, aString);
} }
} }
@ -155,8 +191,7 @@ nsAtom::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, AtomsSizes& aSizes)
} else { } else {
aSizes.mDynamicAtomObjects += thisSize; aSizes.mDynamicAtomObjects += thisSize;
aSizes.mDynamicUnsharedBuffers += aSizes.mDynamicUnsharedBuffers +=
nsStringBuffer::FromData(mString)->SizeOfIncludingThisIfUnshared( GetStringBuffer()->SizeOfIncludingThisIfUnshared(aMallocSizeOf);
aMallocSizeOf);
} }
} }
@ -474,7 +509,8 @@ nsAtomSubTable::GCLocked(GCKind aKind)
} }
nsAtom* atom = entry->mAtom; nsAtom* atom = entry->mAtom;
if (atom->mRefCnt == 0) { MOZ_ASSERT(!atom->IsHTML5Atom());
if (atom->IsDynamicAtom() && nsDynamicAtom::As(atom)->mRefCnt == 0) {
i.Remove(); i.Remove();
delete atom; delete atom;
++removedCount; ++removedCount;
@ -516,15 +552,9 @@ GCAtomTable()
} }
} }
MozExternalRefCountType MOZ_ALWAYS_INLINE MozExternalRefCountType
nsAtom::AddRef() nsDynamicAtom::AddRef()
{ {
MOZ_ASSERT(!IsHTML5Atom(), "Attempt to AddRef an HTML5 atom");
if (!IsDynamicAtom()) {
MOZ_ASSERT(IsStaticAtom());
return 2;
}
MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt");
nsrefcnt count = ++mRefCnt; nsrefcnt count = ++mRefCnt;
if (count == 1) { if (count == 1) {
@ -533,15 +563,9 @@ nsAtom::AddRef()
return count; return count;
} }
MozExternalRefCountType MOZ_ALWAYS_INLINE MozExternalRefCountType
nsAtom::Release() nsDynamicAtom::Release()
{ {
MOZ_ASSERT(!IsHTML5Atom(), "Attempt to Release an HTML5 atom");
if (!IsDynamicAtom()) {
MOZ_ASSERT(IsStaticAtom());
return 1;
}
#ifdef DEBUG #ifdef DEBUG
// We set a lower GC threshold for atoms in debug builds so that we exercise // We set a lower GC threshold for atoms in debug builds so that we exercise
// the GC machinery more often. // the GC machinery more often.
@ -561,6 +585,22 @@ nsAtom::Release()
return count; return count;
} }
MozExternalRefCountType
nsAtom::AddRef()
{
MOZ_ASSERT(!IsHTML5Atom(), "Attempt to AddRef an HTML5 atom");
return IsStaticAtom() ? 2 : nsDynamicAtom::As(this)->AddRef();
}
MozExternalRefCountType
nsAtom::Release()
{
MOZ_ASSERT(!IsHTML5Atom(), "Attempt to Release an HTML5 atom");
return IsStaticAtom() ? 1 : nsDynamicAtom::As(this)->Release();
}
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// Have the static atoms been inserted into the table? // Have the static atoms been inserted into the table?
@ -706,8 +746,7 @@ nsAtomTable::Atomize(const nsACString& aUTF8String)
// Actually, now there is, sort of: ForgetSharedBuffer. // Actually, now there is, sort of: ForgetSharedBuffer.
nsString str; nsString str;
CopyUTF8toUTF16(aUTF8String, str); CopyUTF8toUTF16(aUTF8String, str);
RefPtr<nsAtom> atom = RefPtr<nsAtom> atom = dont_AddRef(new nsDynamicAtom(str, hash));
dont_AddRef(new nsAtom(nsAtom::AtomKind::DynamicAtom, str, hash));
he->mAtom = atom; he->mAtom = atom;
@ -743,8 +782,7 @@ nsAtomTable::Atomize(const nsAString& aUTF16String)
return atom.forget(); return atom.forget();
} }
RefPtr<nsAtom> atom = RefPtr<nsAtom> atom = dont_AddRef(new nsDynamicAtom(aUTF16String, hash));
dont_AddRef(new nsAtom(nsAtom::AtomKind::DynamicAtom, aUTF16String, hash));
he->mAtom = atom; he->mAtom = atom;
return atom.forget(); return atom.forget();
@ -783,8 +821,7 @@ nsAtomTable::AtomizeMainThread(const nsAString& aUTF16String)
if (he->mAtom) { if (he->mAtom) {
retVal = he->mAtom; retVal = he->mAtom;
} else { } else {
RefPtr<nsAtom> newAtom = dont_AddRef( RefPtr<nsAtom> newAtom = dont_AddRef(new nsDynamicAtom(aUTF16String, hash));
new nsAtom(nsAtom::AtomKind::DynamicAtom, aUTF16String, hash));
he->mAtom = newAtom; he->mAtom = newAtom;
retVal = newAtom.forget(); retVal = newAtom.forget();
} }