1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006 |
- /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
- /* 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 <string>
- #include <vector>
- #include "base/basictypes.h"
- #include "prefapi.h"
- #include "prefapi_private_data.h"
- #include "prefread.h"
- #include "MainThreadUtils.h"
- #include "nsReadableUtils.h"
- #include "nsCRT.h"
- #define PL_ARENA_CONST_ALIGN_MASK 3
- #include "plarena.h"
- #ifdef _WIN32
- #include "windows.h"
- #endif /* _WIN32 */
- #include "plstr.h"
- #include "PLDHashTable.h"
- #include "plbase64.h"
- #include "mozilla/Logging.h"
- #include "prprf.h"
- #include "mozilla/MemoryReporting.h"
- #include "mozilla/dom/PContent.h"
- #include "nsQuickSort.h"
- #include "nsString.h"
- #include "nsPrintfCString.h"
- #include "prlink.h"
- using namespace mozilla;
- static void
- clearPrefEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
- {
- PrefHashEntry *pref = static_cast<PrefHashEntry *>(entry);
- if (pref->prefFlags.IsTypeString())
- {
- if (pref->defaultPref.stringVal)
- PL_strfree(pref->defaultPref.stringVal);
- if (pref->userPref.stringVal)
- PL_strfree(pref->userPref.stringVal);
- }
- // don't need to free this as it's allocated in memory owned by
- // gPrefNameArena
- pref->key = nullptr;
- memset(entry, 0, table->EntrySize());
- }
- static bool
- matchPrefEntry(const PLDHashEntryHdr* entry, const void* key)
- {
- const PrefHashEntry *prefEntry =
- static_cast<const PrefHashEntry*>(entry);
- if (prefEntry->key == key) return true;
- if (!prefEntry->key || !key) return false;
- const char *otherKey = reinterpret_cast<const char*>(key);
- return (strcmp(prefEntry->key, otherKey) == 0);
- }
- PLDHashTable* gHashTable;
- static PLArenaPool gPrefNameArena;
- static struct CallbackNode* gCallbacks = nullptr;
- static bool gIsAnyPrefLocked = false;
- // These are only used during the call to pref_DoCallback
- static bool gCallbacksInProgress = false;
- static bool gShouldCleanupDeadNodes = false;
- static PLDHashTableOps pref_HashTableOps = {
- PLDHashTable::HashStringKey,
- matchPrefEntry,
- PLDHashTable::MoveEntryStub,
- clearPrefEntry,
- nullptr,
- };
- // PR_ALIGN_OF_WORD is only defined on some platforms. ALIGN_OF_WORD has
- // already been defined to PR_ALIGN_OF_WORD everywhere
- #ifndef PR_ALIGN_OF_WORD
- #define PR_ALIGN_OF_WORD PR_ALIGN_OF_POINTER
- #endif
- // making PrefName arena 8k for nice allocation
- #define PREFNAME_ARENA_SIZE 8192
- #define WORD_ALIGN_MASK (PR_ALIGN_OF_WORD - 1)
- // sanity checking
- #if (PR_ALIGN_OF_WORD & WORD_ALIGN_MASK) != 0
- #error "PR_ALIGN_OF_WORD must be a power of 2!"
- #endif
- // equivalent to strdup() - does no error checking,
- // we're assuming we're only called with a valid pointer
- static char *ArenaStrDup(const char* str, PLArenaPool* aArena)
- {
- void* mem;
- uint32_t len = strlen(str);
- PL_ARENA_ALLOCATE(mem, aArena, len+1);
- if (mem)
- memcpy(mem, str, len+1);
- return static_cast<char*>(mem);
- }
- static PrefsDirtyFunc gDirtyCallback = nullptr;
- inline void MakeDirtyCallback()
- {
- // Right now the callback function is always set, so we don't need
- // to complicate the code to cover the scenario where we set the callback
- // after we've already tried to make it dirty. If this assert triggers
- // we will add that code.
- MOZ_ASSERT(gDirtyCallback);
- if (gDirtyCallback) {
- gDirtyCallback();
- }
- }
- void PREF_SetDirtyCallback(PrefsDirtyFunc aFunc)
- {
- gDirtyCallback = aFunc;
- }
- /*---------------------------------------------------------------------------*/
- static bool pref_ValueChanged(PrefValue oldValue, PrefValue newValue, PrefType type);
- /* -- Privates */
- struct CallbackNode {
- char* domain;
- // If someone attempts to remove the node from the callback list while
- // pref_DoCallback is running, |func| is set to nullptr. Such nodes will
- // be removed at the end of pref_DoCallback.
- PrefChangedFunc func;
- void* data;
- struct CallbackNode* next;
- };
- /* -- Prototypes */
- static nsresult pref_DoCallback(const char* changed_pref);
- enum {
- kPrefSetDefault = 1,
- kPrefForceSet = 2,
- kPrefStickyDefault = 4,
- };
- static nsresult pref_HashPref(const char *key, PrefValue value, PrefType type, uint32_t flags);
- #define PREF_HASHTABLE_INITIAL_LENGTH 1024
- void PREF_Init()
- {
- if (!gHashTable) {
- gHashTable = new PLDHashTable(&pref_HashTableOps,
- sizeof(PrefHashEntry),
- PREF_HASHTABLE_INITIAL_LENGTH);
- PL_INIT_ARENA_POOL(&gPrefNameArena, "PrefNameArena",
- PREFNAME_ARENA_SIZE);
- }
- }
- /* Frees the callback list. */
- void PREF_Cleanup()
- {
- NS_ASSERTION(!gCallbacksInProgress,
- "PREF_Cleanup was called while gCallbacksInProgress is true!");
- struct CallbackNode* node = gCallbacks;
- struct CallbackNode* next_node;
- while (node)
- {
- next_node = node->next;
- PL_strfree(node->domain);
- free(node);
- node = next_node;
- }
- gCallbacks = nullptr;
- PREF_CleanupPrefs();
- }
- /* Frees up all the objects except the callback list. */
- void PREF_CleanupPrefs()
- {
- if (gHashTable) {
- delete gHashTable;
- gHashTable = nullptr;
- PL_FinishArenaPool(&gPrefNameArena);
- }
- }
- // note that this appends to aResult, and does not assign!
- static void str_escape(const char * original, nsAFlatCString& aResult)
- {
- /* JavaScript does not allow quotes, slashes, or line terminators inside
- * strings so we must escape them. ECMAScript defines four line
- * terminators, but we're only worrying about \r and \n here. We currently
- * feed our pref script to the JS interpreter as Latin-1 so we won't
- * encounter \u2028 (line separator) or \u2029 (paragraph separator).
- *
- * WARNING: There are hints that we may be moving to storing prefs
- * as utf8. If we ever feed them to the JS compiler as UTF8 then
- * we'll have to worry about the multibyte sequences that would be
- * interpreted as \u2028 and \u2029
- */
- const char *p;
- if (original == nullptr)
- return;
- /* Paranoid worst case all slashes will free quickly */
- for (p=original; *p; ++p)
- {
- switch (*p)
- {
- case '\n':
- aResult.AppendLiteral("\\n");
- break;
- case '\r':
- aResult.AppendLiteral("\\r");
- break;
- case '\\':
- aResult.AppendLiteral("\\\\");
- break;
- case '\"':
- aResult.AppendLiteral("\\\"");
- break;
- default:
- aResult.Append(*p);
- break;
- }
- }
- }
- /*
- ** External calls
- */
- nsresult
- PREF_SetCharPref(const char *pref_name, const char *value, bool set_default)
- {
- if ((uint32_t)strlen(value) > MAX_PREF_LENGTH) {
- return NS_ERROR_ILLEGAL_VALUE;
- }
- PrefValue pref;
- pref.stringVal = (char*)value;
- return pref_HashPref(pref_name, pref, PrefType::String, set_default ? kPrefSetDefault : 0);
- }
- nsresult
- PREF_SetIntPref(const char *pref_name, int32_t value, bool set_default)
- {
- PrefValue pref;
- pref.intVal = value;
- return pref_HashPref(pref_name, pref, PrefType::Int, set_default ? kPrefSetDefault : 0);
- }
- nsresult
- PREF_SetBoolPref(const char *pref_name, bool value, bool set_default)
- {
- PrefValue pref;
- pref.boolVal = value;
- return pref_HashPref(pref_name, pref, PrefType::Bool, set_default ? kPrefSetDefault : 0);
- }
- enum WhichValue { DEFAULT_VALUE, USER_VALUE };
- static nsresult
- SetPrefValue(const char* aPrefName, const dom::PrefValue& aValue,
- WhichValue aWhich)
- {
- bool setDefault = (aWhich == DEFAULT_VALUE);
- switch (aValue.type()) {
- case dom::PrefValue::TnsCString:
- return PREF_SetCharPref(aPrefName, aValue.get_nsCString().get(),
- setDefault);
- case dom::PrefValue::Tint32_t:
- return PREF_SetIntPref(aPrefName, aValue.get_int32_t(),
- setDefault);
- case dom::PrefValue::Tbool:
- return PREF_SetBoolPref(aPrefName, aValue.get_bool(),
- setDefault);
- default:
- MOZ_CRASH();
- }
- }
- nsresult
- pref_SetPref(const dom::PrefSetting& aPref)
- {
- const char* prefName = aPref.name().get();
- const dom::MaybePrefValue& defaultValue = aPref.defaultValue();
- const dom::MaybePrefValue& userValue = aPref.userValue();
- nsresult rv;
- if (defaultValue.type() == dom::MaybePrefValue::TPrefValue) {
- rv = SetPrefValue(prefName, defaultValue.get_PrefValue(), DEFAULT_VALUE);
- if (NS_FAILED(rv)) {
- return rv;
- }
- }
- if (userValue.type() == dom::MaybePrefValue::TPrefValue) {
- rv = SetPrefValue(prefName, userValue.get_PrefValue(), USER_VALUE);
- } else {
- rv = PREF_ClearUserPref(prefName);
- }
- // NB: we should never try to clear a default value, that doesn't
- // make sense
- return rv;
- }
- UniquePtr<char*[]>
- pref_savePrefs(PLDHashTable* aTable, uint32_t* aPrefCount)
- {
- // This function allocates the entries in the savedPrefs array it returns.
- // It is the callers responsibility to go through the array and free
- // all of them. The aPrefCount entries will be non-null. Any end padding
- // is an implementation detail and may change.
- MOZ_ASSERT(aPrefCount);
- auto savedPrefs = MakeUnique<char*[]>(aTable->EntryCount());
- // This is not necessary, but leaving it in for now
- memset(savedPrefs.get(), 0, aTable->EntryCount() * sizeof(char*));
- int32_t j = 0;
- for (auto iter = aTable->Iter(); !iter.Done(); iter.Next()) {
- auto pref = static_cast<PrefHashEntry*>(iter.Get());
- nsAutoCString prefValue;
- nsAutoCString prefPrefix;
- prefPrefix.AssignLiteral("user_pref(\"");
- // where we're getting our pref from
- PrefValue* sourcePref;
- if (pref->prefFlags.HasUserValue() &&
- (pref_ValueChanged(pref->defaultPref,
- pref->userPref,
- pref->prefFlags.GetPrefType()) ||
- !(pref->prefFlags.HasDefault()) ||
- pref->prefFlags.HasStickyDefault())) {
- sourcePref = &pref->userPref;
- } else {
- // do not save default prefs that haven't changed
- continue;
- }
- // strings are in quotes!
- if (pref->prefFlags.IsTypeString()) {
- prefValue = '\"';
- str_escape(sourcePref->stringVal, prefValue);
- prefValue += '\"';
- } else if (pref->prefFlags.IsTypeInt()) {
- prefValue.AppendInt(sourcePref->intVal);
- } else if (pref->prefFlags.IsTypeBool()) {
- prefValue = (sourcePref->boolVal) ? "true" : "false";
- }
- nsAutoCString prefName;
- str_escape(pref->key, prefName);
- savedPrefs[j++] = ToNewCString(prefPrefix +
- prefName +
- NS_LITERAL_CSTRING("\", ") +
- prefValue +
- NS_LITERAL_CSTRING(");"));
- }
- *aPrefCount = j;
- return savedPrefs;
- }
- bool
- pref_EntryHasAdvisablySizedValues(PrefHashEntry* aHashEntry)
- {
- if (aHashEntry->prefFlags.GetPrefType() != PrefType::String) {
- return true;
- }
- char* stringVal;
- if (aHashEntry->prefFlags.HasDefault()) {
- stringVal = aHashEntry->defaultPref.stringVal;
- if (strlen(stringVal) > MAX_ADVISABLE_PREF_LENGTH) {
- return false;
- }
- }
- if (aHashEntry->prefFlags.HasUserValue()) {
- stringVal = aHashEntry->userPref.stringVal;
- if (strlen(stringVal) > MAX_ADVISABLE_PREF_LENGTH) {
- return false;
- }
- }
- return true;
- }
- static void
- GetPrefValueFromEntry(PrefHashEntry *aHashEntry, dom::PrefSetting* aPref,
- WhichValue aWhich)
- {
- PrefValue* value;
- dom::PrefValue* settingValue;
- if (aWhich == USER_VALUE) {
- value = &aHashEntry->userPref;
- aPref->userValue() = dom::PrefValue();
- settingValue = &aPref->userValue().get_PrefValue();
- } else {
- value = &aHashEntry->defaultPref;
- aPref->defaultValue() = dom::PrefValue();
- settingValue = &aPref->defaultValue().get_PrefValue();
- }
- switch (aHashEntry->prefFlags.GetPrefType()) {
- case PrefType::String:
- *settingValue = nsDependentCString(value->stringVal);
- return;
- case PrefType::Int:
- *settingValue = value->intVal;
- return;
- case PrefType::Bool:
- *settingValue = !!value->boolVal;
- return;
- default:
- MOZ_CRASH();
- }
- }
- void
- pref_GetPrefFromEntry(PrefHashEntry *aHashEntry, dom::PrefSetting* aPref)
- {
- aPref->name() = aHashEntry->key;
- if (aHashEntry->prefFlags.HasDefault()) {
- GetPrefValueFromEntry(aHashEntry, aPref, DEFAULT_VALUE);
- } else {
- aPref->defaultValue() = null_t();
- }
- if (aHashEntry->prefFlags.HasUserValue()) {
- GetPrefValueFromEntry(aHashEntry, aPref, USER_VALUE);
- } else {
- aPref->userValue() = null_t();
- }
- MOZ_ASSERT(aPref->defaultValue().type() == dom::MaybePrefValue::Tnull_t ||
- aPref->userValue().type() == dom::MaybePrefValue::Tnull_t ||
- (aPref->defaultValue().get_PrefValue().type() ==
- aPref->userValue().get_PrefValue().type()));
- }
- int
- pref_CompareStrings(const void *v1, const void *v2, void *unused)
- {
- char *s1 = *(char**) v1;
- char *s2 = *(char**) v2;
- if (!s1)
- {
- if (!s2)
- return 0;
- else
- return -1;
- }
- else if (!s2)
- return 1;
- else
- return strcmp(s1, s2);
- }
- bool PREF_HasUserPref(const char *pref_name)
- {
- if (!gHashTable)
- return false;
- PrefHashEntry *pref = pref_HashTableLookup(pref_name);
- return pref && pref->prefFlags.HasUserValue();
- }
- nsresult
- PREF_CopyCharPref(const char *pref_name, char ** return_buffer, bool get_default)
- {
- if (!gHashTable)
- return NS_ERROR_NOT_INITIALIZED;
- nsresult rv = NS_ERROR_UNEXPECTED;
- char* stringVal;
- PrefHashEntry* pref = pref_HashTableLookup(pref_name);
- if (pref && (pref->prefFlags.IsTypeString())) {
- if (get_default || pref->prefFlags.IsLocked() || !pref->prefFlags.HasUserValue()) {
- stringVal = pref->defaultPref.stringVal;
- } else {
- stringVal = pref->userPref.stringVal;
- }
- if (stringVal) {
- *return_buffer = NS_strdup(stringVal);
- rv = NS_OK;
- }
- }
- return rv;
- }
- nsresult PREF_GetIntPref(const char *pref_name,int32_t * return_int, bool get_default)
- {
- if (!gHashTable)
- return NS_ERROR_NOT_INITIALIZED;
- nsresult rv = NS_ERROR_UNEXPECTED;
- PrefHashEntry* pref = pref_HashTableLookup(pref_name);
- if (pref && (pref->prefFlags.IsTypeInt())) {
- if (get_default || pref->prefFlags.IsLocked() || !pref->prefFlags.HasUserValue()) {
- int32_t tempInt = pref->defaultPref.intVal;
- /* check to see if we even had a default */
- if (!pref->prefFlags.HasDefault()) {
- return NS_ERROR_UNEXPECTED;
- }
- *return_int = tempInt;
- } else {
- *return_int = pref->userPref.intVal;
- }
- rv = NS_OK;
- }
- return rv;
- }
- nsresult PREF_GetBoolPref(const char *pref_name, bool * return_value, bool get_default)
- {
- if (!gHashTable)
- return NS_ERROR_NOT_INITIALIZED;
- nsresult rv = NS_ERROR_UNEXPECTED;
- PrefHashEntry* pref = pref_HashTableLookup(pref_name);
- //NS_ASSERTION(pref, pref_name);
- if (pref && (pref->prefFlags.IsTypeBool())) {
- if (get_default || pref->prefFlags.IsLocked() || !pref->prefFlags.HasUserValue()) {
- bool tempBool = pref->defaultPref.boolVal;
- /* check to see if we even had a default */
- if (pref->prefFlags.HasDefault()) {
- *return_value = tempBool;
- rv = NS_OK;
- }
- } else {
- *return_value = pref->userPref.boolVal;
- rv = NS_OK;
- }
- }
- return rv;
- }
- nsresult
- PREF_DeleteBranch(const char *branch_name)
- {
- MOZ_ASSERT(NS_IsMainThread());
- int len = (int)strlen(branch_name);
- if (!gHashTable)
- return NS_ERROR_NOT_INITIALIZED;
- /* The following check insures that if the branch name already has a "."
- * at the end, we don't end up with a "..". This fixes an incompatibility
- * between nsIPref, which needs the period added, and nsIPrefBranch which
- * does not. When nsIPref goes away this function should be fixed to
- * never add the period at all.
- */
- nsAutoCString branch_dot(branch_name);
- if ((len > 1) && branch_name[len - 1] != '.')
- branch_dot += '.';
- /* Delete a branch. Used for deleting mime types */
- const char *to_delete = branch_dot.get();
- MOZ_ASSERT(to_delete);
- len = strlen(to_delete);
- for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
- auto entry = static_cast<PrefHashEntry*>(iter.Get());
- /* note if we're deleting "ldap" then we want to delete "ldap.xxx"
- and "ldap" (if such a leaf node exists) but not "ldap_1.xxx" */
- if (PL_strncmp(entry->key, to_delete, (uint32_t) len) == 0 ||
- (len-1 == (int)strlen(entry->key) &&
- PL_strncmp(entry->key, to_delete, (uint32_t)(len-1)) == 0)) {
- iter.Remove();
- }
- }
- MakeDirtyCallback();
- return NS_OK;
- }
- nsresult
- PREF_ClearUserPref(const char *pref_name)
- {
- if (!gHashTable)
- return NS_ERROR_NOT_INITIALIZED;
- PrefHashEntry* pref = pref_HashTableLookup(pref_name);
- if (pref && pref->prefFlags.HasUserValue()) {
- pref->prefFlags.SetHasUserValue(false);
- if (!pref->prefFlags.HasDefault()) {
- gHashTable->RemoveEntry(pref);
- }
- pref_DoCallback(pref_name);
- MakeDirtyCallback();
- }
- return NS_OK;
- }
- nsresult
- PREF_ClearAllUserPrefs()
- {
- MOZ_ASSERT(NS_IsMainThread());
- if (!gHashTable)
- return NS_ERROR_NOT_INITIALIZED;
- std::vector<std::string> prefStrings;
- for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
- auto pref = static_cast<PrefHashEntry*>(iter.Get());
- if (pref->prefFlags.HasUserValue()) {
- prefStrings.push_back(std::string(pref->key));
- pref->prefFlags.SetHasUserValue(false);
- if (!pref->prefFlags.HasDefault()) {
- iter.Remove();
- }
- }
- }
- for (std::string& prefString : prefStrings) {
- pref_DoCallback(prefString.c_str());
- }
- MakeDirtyCallback();
- return NS_OK;
- }
- nsresult PREF_LockPref(const char *key, bool lockit)
- {
- if (!gHashTable)
- return NS_ERROR_NOT_INITIALIZED;
- PrefHashEntry* pref = pref_HashTableLookup(key);
- if (!pref)
- return NS_ERROR_UNEXPECTED;
- if (lockit) {
- if (!pref->prefFlags.IsLocked()) {
- pref->prefFlags.SetLocked(true);
- gIsAnyPrefLocked = true;
- pref_DoCallback(key);
- }
- } else {
- if (pref->prefFlags.IsLocked()) {
- pref->prefFlags.SetLocked(false);
- pref_DoCallback(key);
- }
- }
- return NS_OK;
- }
- /*
- * Hash table functions
- */
- static bool pref_ValueChanged(PrefValue oldValue, PrefValue newValue, PrefType type)
- {
- bool changed = true;
- switch(type) {
- case PrefType::String:
- if (oldValue.stringVal && newValue.stringVal) {
- changed = (strcmp(oldValue.stringVal, newValue.stringVal) != 0);
- }
- break;
- case PrefType::Int:
- changed = oldValue.intVal != newValue.intVal;
- break;
- case PrefType::Bool:
- changed = oldValue.boolVal != newValue.boolVal;
- break;
- case PrefType::Invalid:
- default:
- changed = false;
- break;
- }
- return changed;
- }
- /*
- * Overwrite the type and value of an existing preference. Caller must
- * ensure that they are not changing the type of a preference that has
- * a default value.
- */
- static PrefTypeFlags pref_SetValue(PrefValue* existingValue, PrefTypeFlags flags,
- PrefValue newValue, PrefType newType)
- {
- if (flags.IsTypeString() && existingValue->stringVal) {
- PL_strfree(existingValue->stringVal);
- }
- flags.SetPrefType(newType);
- if (flags.IsTypeString()) {
- MOZ_ASSERT(newValue.stringVal);
- existingValue->stringVal = newValue.stringVal ? PL_strdup(newValue.stringVal) : nullptr;
- }
- else {
- *existingValue = newValue;
- }
- return flags;
- }
- PrefHashEntry* pref_HashTableLookup(const char *key)
- {
- MOZ_ASSERT(NS_IsMainThread());
- return static_cast<PrefHashEntry*>(gHashTable->Search(key));
- }
- nsresult pref_HashPref(const char *key, PrefValue value, PrefType type, uint32_t flags)
- {
- MOZ_ASSERT(NS_IsMainThread());
- if (!gHashTable)
- return NS_ERROR_OUT_OF_MEMORY;
- auto pref = static_cast<PrefHashEntry*>(gHashTable->Add(key, fallible));
- if (!pref)
- return NS_ERROR_OUT_OF_MEMORY;
- // new entry, better initialize
- if (!pref->key) {
- // initialize the pref entry
- pref->prefFlags.Reset().SetPrefType(type);
- pref->key = ArenaStrDup(key, &gPrefNameArena);
- memset(&pref->defaultPref, 0, sizeof(pref->defaultPref));
- memset(&pref->userPref, 0, sizeof(pref->userPref));
- } else if (pref->prefFlags.HasDefault() && !pref->prefFlags.IsPrefType(type)) {
- NS_WARNING(nsPrintfCString("Trying to overwrite value of default pref %s with the wrong type!", key).get());
- return NS_ERROR_UNEXPECTED;
- }
- bool valueChanged = false;
- if (flags & kPrefSetDefault) {
- if (!pref->prefFlags.IsLocked()) {
- /* ?? change of semantics? */
- if (pref_ValueChanged(pref->defaultPref, value, type) ||
- !pref->prefFlags.HasDefault()) {
- pref->prefFlags = pref_SetValue(&pref->defaultPref, pref->prefFlags, value, type).SetHasDefault(true);
- if (flags & kPrefStickyDefault) {
- pref->prefFlags.SetHasStickyDefault(true);
- }
- if (!pref->prefFlags.HasUserValue()) {
- valueChanged = true;
- }
- }
- // What if we change the default to be the same as the user value?
- // Should we clear the user value?
- }
- } else {
- /* If new value is same as the default value and it's not a "sticky"
- pref, then un-set the user value.
- Otherwise, set the user value only if it has changed */
- if ((pref->prefFlags.HasDefault()) &&
- !(pref->prefFlags.HasStickyDefault()) &&
- !pref_ValueChanged(pref->defaultPref, value, type) &&
- !(flags & kPrefForceSet)) {
- if (pref->prefFlags.HasUserValue()) {
- /* XXX should we free a user-set string value if there is one? */
- pref->prefFlags.SetHasUserValue(false);
- if (!pref->prefFlags.IsLocked()) {
- MakeDirtyCallback();
- valueChanged = true;
- }
- }
- } else if (!pref->prefFlags.HasUserValue() ||
- !pref->prefFlags.IsPrefType(type) ||
- pref_ValueChanged(pref->userPref, value, type) ) {
- pref->prefFlags = pref_SetValue(&pref->userPref, pref->prefFlags, value, type).SetHasUserValue(true);
- if (!pref->prefFlags.IsLocked()) {
- MakeDirtyCallback();
- valueChanged = true;
- }
- }
- }
- if (valueChanged) {
- return pref_DoCallback(key);
- }
- return NS_OK;
- }
- size_t
- pref_SizeOfPrivateData(MallocSizeOf aMallocSizeOf)
- {
- size_t n = PL_SizeOfArenaPoolExcludingPool(&gPrefNameArena, aMallocSizeOf);
- for (struct CallbackNode* node = gCallbacks; node; node = node->next) {
- n += aMallocSizeOf(node);
- n += aMallocSizeOf(node->domain);
- }
- return n;
- }
- PrefType
- PREF_GetPrefType(const char *pref_name)
- {
- if (gHashTable) {
- PrefHashEntry* pref = pref_HashTableLookup(pref_name);
- if (pref) {
- return pref->prefFlags.GetPrefType();
- }
- }
- return PrefType::Invalid;
- }
- /* -- */
- bool
- PREF_PrefIsLocked(const char *pref_name)
- {
- bool result = false;
- if (gIsAnyPrefLocked && gHashTable) {
- PrefHashEntry* pref = pref_HashTableLookup(pref_name);
- if (pref && pref->prefFlags.IsLocked()) {
- result = true;
- }
- }
- return result;
- }
- /* Adds a node to the beginning of the callback list. */
- void
- PREF_RegisterCallback(const char *pref_node,
- PrefChangedFunc callback,
- void * instance_data)
- {
- NS_PRECONDITION(pref_node, "pref_node must not be nullptr");
- NS_PRECONDITION(callback, "callback must not be nullptr");
- struct CallbackNode* node = (struct CallbackNode*) malloc(sizeof(struct CallbackNode));
- if (node)
- {
- node->domain = PL_strdup(pref_node);
- node->func = callback;
- node->data = instance_data;
- node->next = gCallbacks;
- gCallbacks = node;
- }
- return;
- }
- /* Removes |node| from gCallbacks list.
- Returns the node after the deleted one. */
- struct CallbackNode*
- pref_RemoveCallbackNode(struct CallbackNode* node,
- struct CallbackNode* prev_node)
- {
- NS_PRECONDITION(!prev_node || prev_node->next == node, "invalid params");
- NS_PRECONDITION(prev_node || gCallbacks == node, "invalid params");
- NS_ASSERTION(!gCallbacksInProgress,
- "modifying the callback list while gCallbacksInProgress is true");
- struct CallbackNode* next_node = node->next;
- if (prev_node)
- prev_node->next = next_node;
- else
- gCallbacks = next_node;
- PL_strfree(node->domain);
- free(node);
- return next_node;
- }
- /* Deletes a node from the callback list or marks it for deletion. */
- nsresult
- PREF_UnregisterCallback(const char *pref_node,
- PrefChangedFunc callback,
- void * instance_data)
- {
- nsresult rv = NS_ERROR_FAILURE;
- struct CallbackNode* node = gCallbacks;
- struct CallbackNode* prev_node = nullptr;
- while (node != nullptr)
- {
- if ( node->func == callback &&
- node->data == instance_data &&
- strcmp(node->domain, pref_node) == 0)
- {
- if (gCallbacksInProgress)
- {
- // postpone the node removal until after
- // gCallbacks enumeration is finished.
- node->func = nullptr;
- gShouldCleanupDeadNodes = true;
- prev_node = node;
- node = node->next;
- }
- else
- {
- node = pref_RemoveCallbackNode(node, prev_node);
- }
- rv = NS_OK;
- }
- else
- {
- prev_node = node;
- node = node->next;
- }
- }
- return rv;
- }
- static nsresult pref_DoCallback(const char* changed_pref)
- {
- nsresult rv = NS_OK;
- struct CallbackNode* node;
- bool reentered = gCallbacksInProgress;
- gCallbacksInProgress = true;
- // Nodes must not be deleted while gCallbacksInProgress is true.
- // Nodes that need to be deleted are marked for deletion by nulling
- // out the |func| pointer. We release them at the end of this function
- // if we haven't reentered.
- for (node = gCallbacks; node != nullptr; node = node->next)
- {
- if ( node->func &&
- PL_strncmp(changed_pref,
- node->domain,
- strlen(node->domain)) == 0 )
- {
- (*node->func) (changed_pref, node->data);
- }
- }
- gCallbacksInProgress = reentered;
- if (gShouldCleanupDeadNodes && !gCallbacksInProgress)
- {
- struct CallbackNode* prev_node = nullptr;
- node = gCallbacks;
- while (node != nullptr)
- {
- if (!node->func)
- {
- node = pref_RemoveCallbackNode(node, prev_node);
- }
- else
- {
- prev_node = node;
- node = node->next;
- }
- }
- gShouldCleanupDeadNodes = false;
- }
- return rv;
- }
- void PREF_ReaderCallback(void *closure,
- const char *pref,
- PrefValue value,
- PrefType type,
- bool isDefault,
- bool isStickyDefault)
- {
- uint32_t flags = 0;
- if (isDefault) {
- flags |= kPrefSetDefault;
- if (isStickyDefault) {
- flags |= kPrefStickyDefault;
- }
- } else {
- flags |= kPrefForceSet;
- }
- pref_HashPref(pref, value, type, flags);
- }
|