1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/modules/libpref/src/prefapi.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1011 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "base/basictypes.h" 1.10 + 1.11 +#include "prefapi.h" 1.12 +#include "prefapi_private_data.h" 1.13 +#include "prefread.h" 1.14 +#include "MainThreadUtils.h" 1.15 +#include "nsReadableUtils.h" 1.16 +#include "nsCRT.h" 1.17 + 1.18 +#define PL_ARENA_CONST_ALIGN_MASK 3 1.19 +#include "plarena.h" 1.20 + 1.21 +#ifdef _WIN32 1.22 + #include "windows.h" 1.23 +#endif /* _WIN32 */ 1.24 + 1.25 +#include "plstr.h" 1.26 +#include "pldhash.h" 1.27 +#include "plbase64.h" 1.28 +#include "prlog.h" 1.29 +#include "prprf.h" 1.30 +#include "mozilla/MemoryReporting.h" 1.31 +#include "mozilla/dom/PContent.h" 1.32 +#include "nsQuickSort.h" 1.33 +#include "nsString.h" 1.34 +#include "nsPrintfCString.h" 1.35 +#include "prlink.h" 1.36 + 1.37 +using namespace mozilla; 1.38 + 1.39 +static void 1.40 +clearPrefEntry(PLDHashTable *table, PLDHashEntryHdr *entry) 1.41 +{ 1.42 + PrefHashEntry *pref = static_cast<PrefHashEntry *>(entry); 1.43 + if (pref->flags & PREF_STRING) 1.44 + { 1.45 + if (pref->defaultPref.stringVal) 1.46 + PL_strfree(pref->defaultPref.stringVal); 1.47 + if (pref->userPref.stringVal) 1.48 + PL_strfree(pref->userPref.stringVal); 1.49 + } 1.50 + // don't need to free this as it's allocated in memory owned by 1.51 + // gPrefNameArena 1.52 + pref->key = nullptr; 1.53 + memset(entry, 0, table->entrySize); 1.54 +} 1.55 + 1.56 +static bool 1.57 +matchPrefEntry(PLDHashTable*, const PLDHashEntryHdr* entry, 1.58 + const void* key) 1.59 +{ 1.60 + const PrefHashEntry *prefEntry = 1.61 + static_cast<const PrefHashEntry*>(entry); 1.62 + 1.63 + if (prefEntry->key == key) return true; 1.64 + 1.65 + if (!prefEntry->key || !key) return false; 1.66 + 1.67 + const char *otherKey = reinterpret_cast<const char*>(key); 1.68 + return (strcmp(prefEntry->key, otherKey) == 0); 1.69 +} 1.70 + 1.71 +PLDHashTable gHashTable = { nullptr }; 1.72 +static PLArenaPool gPrefNameArena; 1.73 +bool gDirty = false; 1.74 + 1.75 +static struct CallbackNode* gCallbacks = nullptr; 1.76 +static bool gIsAnyPrefLocked = false; 1.77 +// These are only used during the call to pref_DoCallback 1.78 +static bool gCallbacksInProgress = false; 1.79 +static bool gShouldCleanupDeadNodes = false; 1.80 + 1.81 + 1.82 +static PLDHashTableOps pref_HashTableOps = { 1.83 + PL_DHashAllocTable, 1.84 + PL_DHashFreeTable, 1.85 + PL_DHashStringKey, 1.86 + matchPrefEntry, 1.87 + PL_DHashMoveEntryStub, 1.88 + clearPrefEntry, 1.89 + PL_DHashFinalizeStub, 1.90 + nullptr, 1.91 +}; 1.92 + 1.93 +// PR_ALIGN_OF_WORD is only defined on some platforms. ALIGN_OF_WORD has 1.94 +// already been defined to PR_ALIGN_OF_WORD everywhere 1.95 +#ifndef PR_ALIGN_OF_WORD 1.96 +#define PR_ALIGN_OF_WORD PR_ALIGN_OF_POINTER 1.97 +#endif 1.98 + 1.99 +// making PrefName arena 8k for nice allocation 1.100 +#define PREFNAME_ARENA_SIZE 8192 1.101 + 1.102 +#define WORD_ALIGN_MASK (PR_ALIGN_OF_WORD - 1) 1.103 + 1.104 +// sanity checking 1.105 +#if (PR_ALIGN_OF_WORD & WORD_ALIGN_MASK) != 0 1.106 +#error "PR_ALIGN_OF_WORD must be a power of 2!" 1.107 +#endif 1.108 + 1.109 +// equivalent to strdup() - does no error checking, 1.110 +// we're assuming we're only called with a valid pointer 1.111 +static char *ArenaStrDup(const char* str, PLArenaPool* aArena) 1.112 +{ 1.113 + void* mem; 1.114 + uint32_t len = strlen(str); 1.115 + PL_ARENA_ALLOCATE(mem, aArena, len+1); 1.116 + if (mem) 1.117 + memcpy(mem, str, len+1); 1.118 + return static_cast<char*>(mem); 1.119 +} 1.120 + 1.121 +/*---------------------------------------------------------------------------*/ 1.122 + 1.123 +#define PREF_IS_LOCKED(pref) ((pref)->flags & PREF_LOCKED) 1.124 +#define PREF_HAS_DEFAULT_VALUE(pref) ((pref)->flags & PREF_HAS_DEFAULT) 1.125 +#define PREF_HAS_USER_VALUE(pref) ((pref)->flags & PREF_USERSET) 1.126 +#define PREF_TYPE(pref) (PrefType)((pref)->flags & PREF_VALUETYPE_MASK) 1.127 + 1.128 +static bool pref_ValueChanged(PrefValue oldValue, PrefValue newValue, PrefType type); 1.129 + 1.130 +/* -- Privates */ 1.131 +struct CallbackNode { 1.132 + char* domain; 1.133 + // If someone attempts to remove the node from the callback list while 1.134 + // pref_DoCallback is running, |func| is set to nullptr. Such nodes will 1.135 + // be removed at the end of pref_DoCallback. 1.136 + PrefChangedFunc func; 1.137 + void* data; 1.138 + struct CallbackNode* next; 1.139 +}; 1.140 + 1.141 +/* -- Prototypes */ 1.142 +static nsresult pref_DoCallback(const char* changed_pref); 1.143 + 1.144 +enum { 1.145 + kPrefSetDefault = 1, 1.146 + kPrefForceSet = 2 1.147 +}; 1.148 +static nsresult pref_HashPref(const char *key, PrefValue value, PrefType type, uint32_t flags); 1.149 + 1.150 +#define PREF_HASHTABLE_INITIAL_SIZE 2048 1.151 + 1.152 +nsresult PREF_Init() 1.153 +{ 1.154 + if (!gHashTable.ops) { 1.155 + if (!PL_DHashTableInit(&gHashTable, &pref_HashTableOps, nullptr, 1.156 + sizeof(PrefHashEntry), PREF_HASHTABLE_INITIAL_SIZE, 1.157 + fallible_t())) { 1.158 + gHashTable.ops = nullptr; 1.159 + return NS_ERROR_OUT_OF_MEMORY; 1.160 + } 1.161 + 1.162 + PL_INIT_ARENA_POOL(&gPrefNameArena, "PrefNameArena", 1.163 + PREFNAME_ARENA_SIZE); 1.164 + } 1.165 + return NS_OK; 1.166 +} 1.167 + 1.168 +/* Frees the callback list. */ 1.169 +void PREF_Cleanup() 1.170 +{ 1.171 + NS_ASSERTION(!gCallbacksInProgress, 1.172 + "PREF_Cleanup was called while gCallbacksInProgress is true!"); 1.173 + struct CallbackNode* node = gCallbacks; 1.174 + struct CallbackNode* next_node; 1.175 + 1.176 + while (node) 1.177 + { 1.178 + next_node = node->next; 1.179 + PL_strfree(node->domain); 1.180 + free(node); 1.181 + node = next_node; 1.182 + } 1.183 + gCallbacks = nullptr; 1.184 + 1.185 + PREF_CleanupPrefs(); 1.186 +} 1.187 + 1.188 +/* Frees up all the objects except the callback list. */ 1.189 +void PREF_CleanupPrefs() 1.190 +{ 1.191 + if (gHashTable.ops) { 1.192 + PL_DHashTableFinish(&gHashTable); 1.193 + gHashTable.ops = nullptr; 1.194 + PL_FinishArenaPool(&gPrefNameArena); 1.195 + } 1.196 +} 1.197 + 1.198 +// note that this appends to aResult, and does not assign! 1.199 +static void str_escape(const char * original, nsAFlatCString& aResult) 1.200 +{ 1.201 + /* JavaScript does not allow quotes, slashes, or line terminators inside 1.202 + * strings so we must escape them. ECMAScript defines four line 1.203 + * terminators, but we're only worrying about \r and \n here. We currently 1.204 + * feed our pref script to the JS interpreter as Latin-1 so we won't 1.205 + * encounter \u2028 (line separator) or \u2029 (paragraph separator). 1.206 + * 1.207 + * WARNING: There are hints that we may be moving to storing prefs 1.208 + * as utf8. If we ever feed them to the JS compiler as UTF8 then 1.209 + * we'll have to worry about the multibyte sequences that would be 1.210 + * interpreted as \u2028 and \u2029 1.211 + */ 1.212 + const char *p; 1.213 + 1.214 + if (original == nullptr) 1.215 + return; 1.216 + 1.217 + /* Paranoid worst case all slashes will free quickly */ 1.218 + for (p=original; *p; ++p) 1.219 + { 1.220 + switch (*p) 1.221 + { 1.222 + case '\n': 1.223 + aResult.Append("\\n"); 1.224 + break; 1.225 + 1.226 + case '\r': 1.227 + aResult.Append("\\r"); 1.228 + break; 1.229 + 1.230 + case '\\': 1.231 + aResult.Append("\\\\"); 1.232 + break; 1.233 + 1.234 + case '\"': 1.235 + aResult.Append("\\\""); 1.236 + break; 1.237 + 1.238 + default: 1.239 + aResult.Append(*p); 1.240 + break; 1.241 + } 1.242 + } 1.243 +} 1.244 + 1.245 +/* 1.246 +** External calls 1.247 +*/ 1.248 +nsresult 1.249 +PREF_SetCharPref(const char *pref_name, const char *value, bool set_default) 1.250 +{ 1.251 + if ((uint32_t)strlen(value) > MAX_PREF_LENGTH) { 1.252 + return NS_ERROR_ILLEGAL_VALUE; 1.253 + } 1.254 + 1.255 + PrefValue pref; 1.256 + pref.stringVal = (char*)value; 1.257 + 1.258 + return pref_HashPref(pref_name, pref, PREF_STRING, set_default ? kPrefSetDefault : 0); 1.259 +} 1.260 + 1.261 +nsresult 1.262 +PREF_SetIntPref(const char *pref_name, int32_t value, bool set_default) 1.263 +{ 1.264 + PrefValue pref; 1.265 + pref.intVal = value; 1.266 + 1.267 + return pref_HashPref(pref_name, pref, PREF_INT, set_default ? kPrefSetDefault : 0); 1.268 +} 1.269 + 1.270 +nsresult 1.271 +PREF_SetBoolPref(const char *pref_name, bool value, bool set_default) 1.272 +{ 1.273 + PrefValue pref; 1.274 + pref.boolVal = value; 1.275 + 1.276 + return pref_HashPref(pref_name, pref, PREF_BOOL, set_default ? kPrefSetDefault : 0); 1.277 +} 1.278 + 1.279 +enum WhichValue { DEFAULT_VALUE, USER_VALUE }; 1.280 +static nsresult 1.281 +SetPrefValue(const char* aPrefName, const dom::PrefValue& aValue, 1.282 + WhichValue aWhich) 1.283 +{ 1.284 + bool setDefault = (aWhich == DEFAULT_VALUE); 1.285 + switch (aValue.type()) { 1.286 + case dom::PrefValue::TnsCString: 1.287 + return PREF_SetCharPref(aPrefName, aValue.get_nsCString().get(), 1.288 + setDefault); 1.289 + case dom::PrefValue::Tint32_t: 1.290 + return PREF_SetIntPref(aPrefName, aValue.get_int32_t(), 1.291 + setDefault); 1.292 + case dom::PrefValue::Tbool: 1.293 + return PREF_SetBoolPref(aPrefName, aValue.get_bool(), 1.294 + setDefault); 1.295 + default: 1.296 + MOZ_CRASH(); 1.297 + } 1.298 +} 1.299 + 1.300 +nsresult 1.301 +pref_SetPref(const dom::PrefSetting& aPref) 1.302 +{ 1.303 + const char* prefName = aPref.name().get(); 1.304 + const dom::MaybePrefValue& defaultValue = aPref.defaultValue(); 1.305 + const dom::MaybePrefValue& userValue = aPref.userValue(); 1.306 + 1.307 + nsresult rv; 1.308 + if (defaultValue.type() == dom::MaybePrefValue::TPrefValue) { 1.309 + rv = SetPrefValue(prefName, defaultValue.get_PrefValue(), DEFAULT_VALUE); 1.310 + if (NS_FAILED(rv)) { 1.311 + return rv; 1.312 + } 1.313 + } 1.314 + 1.315 + if (userValue.type() == dom::MaybePrefValue::TPrefValue) { 1.316 + rv = SetPrefValue(prefName, userValue.get_PrefValue(), USER_VALUE); 1.317 + } else { 1.318 + rv = PREF_ClearUserPref(prefName); 1.319 + } 1.320 + 1.321 + // NB: we should never try to clear a default value, that doesn't 1.322 + // make sense 1.323 + 1.324 + return rv; 1.325 +} 1.326 + 1.327 +PLDHashOperator 1.328 +pref_savePref(PLDHashTable *table, PLDHashEntryHdr *heh, uint32_t i, void *arg) 1.329 +{ 1.330 + pref_saveArgs *argData = static_cast<pref_saveArgs *>(arg); 1.331 + PrefHashEntry *pref = static_cast<PrefHashEntry *>(heh); 1.332 + 1.333 + PR_ASSERT(pref); 1.334 + if (!pref) 1.335 + return PL_DHASH_NEXT; 1.336 + 1.337 + nsAutoCString prefValue; 1.338 + nsAutoCString prefPrefix; 1.339 + prefPrefix.Assign(NS_LITERAL_CSTRING("user_pref(\"")); 1.340 + 1.341 + // where we're getting our pref from 1.342 + PrefValue* sourcePref; 1.343 + 1.344 + if (PREF_HAS_USER_VALUE(pref) && 1.345 + (pref_ValueChanged(pref->defaultPref, 1.346 + pref->userPref, 1.347 + (PrefType) PREF_TYPE(pref)) || 1.348 + !(pref->flags & PREF_HAS_DEFAULT))) { 1.349 + sourcePref = &pref->userPref; 1.350 + } else { 1.351 + if (argData->saveTypes == SAVE_ALL_AND_DEFAULTS) { 1.352 + prefPrefix.Assign(NS_LITERAL_CSTRING("pref(\"")); 1.353 + sourcePref = &pref->defaultPref; 1.354 + } 1.355 + else 1.356 + // do not save default prefs that haven't changed 1.357 + return PL_DHASH_NEXT; 1.358 + } 1.359 + 1.360 + // strings are in quotes! 1.361 + if (pref->flags & PREF_STRING) { 1.362 + prefValue = '\"'; 1.363 + str_escape(sourcePref->stringVal, prefValue); 1.364 + prefValue += '\"'; 1.365 + } 1.366 + 1.367 + else if (pref->flags & PREF_INT) 1.368 + prefValue.AppendInt(sourcePref->intVal); 1.369 + 1.370 + else if (pref->flags & PREF_BOOL) 1.371 + prefValue = (sourcePref->boolVal) ? "true" : "false"; 1.372 + 1.373 + nsAutoCString prefName; 1.374 + str_escape(pref->key, prefName); 1.375 + 1.376 + argData->prefArray[i] = ToNewCString(prefPrefix + 1.377 + prefName + 1.378 + NS_LITERAL_CSTRING("\", ") + 1.379 + prefValue + 1.380 + NS_LITERAL_CSTRING(");")); 1.381 + 1.382 + return PL_DHASH_NEXT; 1.383 +} 1.384 + 1.385 +PLDHashOperator 1.386 +pref_GetPrefs(PLDHashTable *table, 1.387 + PLDHashEntryHdr *heh, 1.388 + uint32_t i, 1.389 + void *arg) 1.390 +{ 1.391 + if (heh) { 1.392 + PrefHashEntry *entry = static_cast<PrefHashEntry *>(heh); 1.393 + dom::PrefSetting *pref = 1.394 + static_cast<InfallibleTArray<dom::PrefSetting>*>(arg)->AppendElement(); 1.395 + 1.396 + pref_GetPrefFromEntry(entry, pref); 1.397 + } 1.398 + return PL_DHASH_NEXT; 1.399 +} 1.400 + 1.401 +static void 1.402 +GetPrefValueFromEntry(PrefHashEntry *aHashEntry, dom::PrefSetting* aPref, 1.403 + WhichValue aWhich) 1.404 +{ 1.405 + PrefValue* value; 1.406 + dom::PrefValue* settingValue; 1.407 + if (aWhich == USER_VALUE) { 1.408 + value = &aHashEntry->userPref; 1.409 + aPref->userValue() = dom::PrefValue(); 1.410 + settingValue = &aPref->userValue().get_PrefValue(); 1.411 + } else { 1.412 + value = &aHashEntry->defaultPref; 1.413 + aPref->defaultValue() = dom::PrefValue(); 1.414 + settingValue = &aPref->defaultValue().get_PrefValue(); 1.415 + } 1.416 + 1.417 + switch (aHashEntry->flags & PREF_VALUETYPE_MASK) { 1.418 + case PREF_STRING: 1.419 + *settingValue = nsDependentCString(value->stringVal); 1.420 + return; 1.421 + case PREF_INT: 1.422 + *settingValue = value->intVal; 1.423 + return; 1.424 + case PREF_BOOL: 1.425 + *settingValue = !!value->boolVal; 1.426 + return; 1.427 + default: 1.428 + MOZ_CRASH(); 1.429 + } 1.430 +} 1.431 + 1.432 +void 1.433 +pref_GetPrefFromEntry(PrefHashEntry *aHashEntry, dom::PrefSetting* aPref) 1.434 +{ 1.435 + aPref->name() = aHashEntry->key; 1.436 + if (PREF_HAS_DEFAULT_VALUE(aHashEntry)) { 1.437 + GetPrefValueFromEntry(aHashEntry, aPref, DEFAULT_VALUE); 1.438 + } else { 1.439 + aPref->defaultValue() = null_t(); 1.440 + } 1.441 + if (PREF_HAS_USER_VALUE(aHashEntry)) { 1.442 + GetPrefValueFromEntry(aHashEntry, aPref, USER_VALUE); 1.443 + } else { 1.444 + aPref->userValue() = null_t(); 1.445 + } 1.446 + 1.447 + MOZ_ASSERT(aPref->defaultValue().type() == dom::MaybePrefValue::Tnull_t || 1.448 + aPref->userValue().type() == dom::MaybePrefValue::Tnull_t || 1.449 + (aPref->defaultValue().get_PrefValue().type() == 1.450 + aPref->userValue().get_PrefValue().type())); 1.451 +} 1.452 + 1.453 + 1.454 +int 1.455 +pref_CompareStrings(const void *v1, const void *v2, void *unused) 1.456 +{ 1.457 + char *s1 = *(char**) v1; 1.458 + char *s2 = *(char**) v2; 1.459 + 1.460 + if (!s1) 1.461 + { 1.462 + if (!s2) 1.463 + return 0; 1.464 + else 1.465 + return -1; 1.466 + } 1.467 + else if (!s2) 1.468 + return 1; 1.469 + else 1.470 + return strcmp(s1, s2); 1.471 +} 1.472 + 1.473 +bool PREF_HasUserPref(const char *pref_name) 1.474 +{ 1.475 + if (!gHashTable.ops) 1.476 + return false; 1.477 + 1.478 + PrefHashEntry *pref = pref_HashTableLookup(pref_name); 1.479 + if (!pref) return false; 1.480 + 1.481 + /* convert PREF_HAS_USER_VALUE to bool */ 1.482 + return (PREF_HAS_USER_VALUE(pref) != 0); 1.483 + 1.484 +} 1.485 + 1.486 +nsresult 1.487 +PREF_CopyCharPref(const char *pref_name, char ** return_buffer, bool get_default) 1.488 +{ 1.489 + if (!gHashTable.ops) 1.490 + return NS_ERROR_NOT_INITIALIZED; 1.491 + 1.492 + nsresult rv = NS_ERROR_UNEXPECTED; 1.493 + char* stringVal; 1.494 + PrefHashEntry* pref = pref_HashTableLookup(pref_name); 1.495 + 1.496 + if (pref && (pref->flags & PREF_STRING)) 1.497 + { 1.498 + if (get_default || PREF_IS_LOCKED(pref) || !PREF_HAS_USER_VALUE(pref)) 1.499 + stringVal = pref->defaultPref.stringVal; 1.500 + else 1.501 + stringVal = pref->userPref.stringVal; 1.502 + 1.503 + if (stringVal) { 1.504 + *return_buffer = NS_strdup(stringVal); 1.505 + rv = NS_OK; 1.506 + } 1.507 + } 1.508 + return rv; 1.509 +} 1.510 + 1.511 +nsresult PREF_GetIntPref(const char *pref_name,int32_t * return_int, bool get_default) 1.512 +{ 1.513 + if (!gHashTable.ops) 1.514 + return NS_ERROR_NOT_INITIALIZED; 1.515 + 1.516 + nsresult rv = NS_ERROR_UNEXPECTED; 1.517 + PrefHashEntry* pref = pref_HashTableLookup(pref_name); 1.518 + if (pref && (pref->flags & PREF_INT)) 1.519 + { 1.520 + if (get_default || PREF_IS_LOCKED(pref) || !PREF_HAS_USER_VALUE(pref)) 1.521 + { 1.522 + int32_t tempInt = pref->defaultPref.intVal; 1.523 + /* check to see if we even had a default */ 1.524 + if (!(pref->flags & PREF_HAS_DEFAULT)) 1.525 + return NS_ERROR_UNEXPECTED; 1.526 + *return_int = tempInt; 1.527 + } 1.528 + else 1.529 + *return_int = pref->userPref.intVal; 1.530 + rv = NS_OK; 1.531 + } 1.532 + return rv; 1.533 +} 1.534 + 1.535 +nsresult PREF_GetBoolPref(const char *pref_name, bool * return_value, bool get_default) 1.536 +{ 1.537 + if (!gHashTable.ops) 1.538 + return NS_ERROR_NOT_INITIALIZED; 1.539 + 1.540 + nsresult rv = NS_ERROR_UNEXPECTED; 1.541 + PrefHashEntry* pref = pref_HashTableLookup(pref_name); 1.542 + //NS_ASSERTION(pref, pref_name); 1.543 + if (pref && (pref->flags & PREF_BOOL)) 1.544 + { 1.545 + if (get_default || PREF_IS_LOCKED(pref) || !PREF_HAS_USER_VALUE(pref)) 1.546 + { 1.547 + bool tempBool = pref->defaultPref.boolVal; 1.548 + /* check to see if we even had a default */ 1.549 + if (pref->flags & PREF_HAS_DEFAULT) { 1.550 + *return_value = tempBool; 1.551 + rv = NS_OK; 1.552 + } 1.553 + } 1.554 + else { 1.555 + *return_value = pref->userPref.boolVal; 1.556 + rv = NS_OK; 1.557 + } 1.558 + } 1.559 + return rv; 1.560 +} 1.561 + 1.562 +/* Delete a branch. Used for deleting mime types */ 1.563 +static PLDHashOperator 1.564 +pref_DeleteItem(PLDHashTable *table, PLDHashEntryHdr *heh, uint32_t i, void *arg) 1.565 +{ 1.566 + PrefHashEntry* he = static_cast<PrefHashEntry*>(heh); 1.567 + const char *to_delete = (const char *) arg; 1.568 + int len = strlen(to_delete); 1.569 + 1.570 + /* note if we're deleting "ldap" then we want to delete "ldap.xxx" 1.571 + and "ldap" (if such a leaf node exists) but not "ldap_1.xxx" */ 1.572 + if (to_delete && (PL_strncmp(he->key, to_delete, (uint32_t) len) == 0 || 1.573 + (len-1 == (int)strlen(he->key) && PL_strncmp(he->key, to_delete, (uint32_t)(len-1)) == 0))) 1.574 + return PL_DHASH_REMOVE; 1.575 + 1.576 + return PL_DHASH_NEXT; 1.577 +} 1.578 + 1.579 +nsresult 1.580 +PREF_DeleteBranch(const char *branch_name) 1.581 +{ 1.582 +#ifndef MOZ_B2G 1.583 + MOZ_ASSERT(NS_IsMainThread()); 1.584 +#endif 1.585 + 1.586 + int len = (int)strlen(branch_name); 1.587 + 1.588 + if (!gHashTable.ops) 1.589 + return NS_ERROR_NOT_INITIALIZED; 1.590 + 1.591 + /* The following check insures that if the branch name already has a "." 1.592 + * at the end, we don't end up with a "..". This fixes an incompatibility 1.593 + * between nsIPref, which needs the period added, and nsIPrefBranch which 1.594 + * does not. When nsIPref goes away this function should be fixed to 1.595 + * never add the period at all. 1.596 + */ 1.597 + nsAutoCString branch_dot(branch_name); 1.598 + if ((len > 1) && branch_name[len - 1] != '.') 1.599 + branch_dot += '.'; 1.600 + 1.601 + PL_DHashTableEnumerate(&gHashTable, pref_DeleteItem, 1.602 + (void*) branch_dot.get()); 1.603 + gDirty = true; 1.604 + return NS_OK; 1.605 +} 1.606 + 1.607 + 1.608 +nsresult 1.609 +PREF_ClearUserPref(const char *pref_name) 1.610 +{ 1.611 + if (!gHashTable.ops) 1.612 + return NS_ERROR_NOT_INITIALIZED; 1.613 + 1.614 + PrefHashEntry* pref = pref_HashTableLookup(pref_name); 1.615 + if (pref && PREF_HAS_USER_VALUE(pref)) 1.616 + { 1.617 + pref->flags &= ~PREF_USERSET; 1.618 + 1.619 + if (!(pref->flags & PREF_HAS_DEFAULT)) { 1.620 + PL_DHashTableOperate(&gHashTable, pref_name, PL_DHASH_REMOVE); 1.621 + } 1.622 + 1.623 + pref_DoCallback(pref_name); 1.624 + gDirty = true; 1.625 + } 1.626 + return NS_OK; 1.627 +} 1.628 + 1.629 +static PLDHashOperator 1.630 +pref_ClearUserPref(PLDHashTable *table, PLDHashEntryHdr *he, uint32_t, 1.631 + void *arg) 1.632 +{ 1.633 + PrefHashEntry *pref = static_cast<PrefHashEntry*>(he); 1.634 + 1.635 + PLDHashOperator nextOp = PL_DHASH_NEXT; 1.636 + 1.637 + if (PREF_HAS_USER_VALUE(pref)) 1.638 + { 1.639 + pref->flags &= ~PREF_USERSET; 1.640 + 1.641 + if (!(pref->flags & PREF_HAS_DEFAULT)) { 1.642 + nextOp = PL_DHASH_REMOVE; 1.643 + } 1.644 + 1.645 + pref_DoCallback(pref->key); 1.646 + } 1.647 + return nextOp; 1.648 +} 1.649 + 1.650 +nsresult 1.651 +PREF_ClearAllUserPrefs() 1.652 +{ 1.653 +#ifndef MOZ_B2G 1.654 + MOZ_ASSERT(NS_IsMainThread()); 1.655 +#endif 1.656 + 1.657 + if (!gHashTable.ops) 1.658 + return NS_ERROR_NOT_INITIALIZED; 1.659 + 1.660 + PL_DHashTableEnumerate(&gHashTable, pref_ClearUserPref, nullptr); 1.661 + 1.662 + gDirty = true; 1.663 + return NS_OK; 1.664 +} 1.665 + 1.666 +nsresult PREF_LockPref(const char *key, bool lockit) 1.667 +{ 1.668 + if (!gHashTable.ops) 1.669 + return NS_ERROR_NOT_INITIALIZED; 1.670 + 1.671 + PrefHashEntry* pref = pref_HashTableLookup(key); 1.672 + if (!pref) 1.673 + return NS_ERROR_UNEXPECTED; 1.674 + 1.675 + if (lockit) { 1.676 + if (!PREF_IS_LOCKED(pref)) 1.677 + { 1.678 + pref->flags |= PREF_LOCKED; 1.679 + gIsAnyPrefLocked = true; 1.680 + pref_DoCallback(key); 1.681 + } 1.682 + } 1.683 + else 1.684 + { 1.685 + if (PREF_IS_LOCKED(pref)) 1.686 + { 1.687 + pref->flags &= ~PREF_LOCKED; 1.688 + pref_DoCallback(key); 1.689 + } 1.690 + } 1.691 + return NS_OK; 1.692 +} 1.693 + 1.694 +/* 1.695 + * Hash table functions 1.696 + */ 1.697 +static bool pref_ValueChanged(PrefValue oldValue, PrefValue newValue, PrefType type) 1.698 +{ 1.699 + bool changed = true; 1.700 + if (type & PREF_STRING) 1.701 + { 1.702 + if (oldValue.stringVal && newValue.stringVal) 1.703 + changed = (strcmp(oldValue.stringVal, newValue.stringVal) != 0); 1.704 + } 1.705 + else if (type & PREF_INT) 1.706 + changed = oldValue.intVal != newValue.intVal; 1.707 + else if (type & PREF_BOOL) 1.708 + changed = oldValue.boolVal != newValue.boolVal; 1.709 + return changed; 1.710 +} 1.711 + 1.712 +/* 1.713 + * Overwrite the type and value of an existing preference. Caller must 1.714 + * ensure that they are not changing the type of a preference that has 1.715 + * a default value. 1.716 + */ 1.717 +static void pref_SetValue(PrefValue* existingValue, uint16_t *existingFlags, 1.718 + PrefValue newValue, PrefType newType) 1.719 +{ 1.720 + if ((*existingFlags & PREF_STRING) && existingValue->stringVal) { 1.721 + PL_strfree(existingValue->stringVal); 1.722 + } 1.723 + *existingFlags = (*existingFlags & ~PREF_VALUETYPE_MASK) | newType; 1.724 + if (newType & PREF_STRING) { 1.725 + PR_ASSERT(newValue.stringVal); 1.726 + existingValue->stringVal = newValue.stringVal ? PL_strdup(newValue.stringVal) : nullptr; 1.727 + } 1.728 + else { 1.729 + *existingValue = newValue; 1.730 + } 1.731 + gDirty = true; 1.732 +} 1.733 + 1.734 +PrefHashEntry* pref_HashTableLookup(const void *key) 1.735 +{ 1.736 +#ifndef MOZ_B2G 1.737 + MOZ_ASSERT(NS_IsMainThread()); 1.738 +#endif 1.739 + 1.740 + PrefHashEntry* result = 1.741 + static_cast<PrefHashEntry*>(PL_DHashTableOperate(&gHashTable, key, PL_DHASH_LOOKUP)); 1.742 + 1.743 + if (PL_DHASH_ENTRY_IS_FREE(result)) 1.744 + return nullptr; 1.745 + 1.746 + return result; 1.747 +} 1.748 + 1.749 +nsresult pref_HashPref(const char *key, PrefValue value, PrefType type, uint32_t flags) 1.750 +{ 1.751 +#ifndef MOZ_B2G 1.752 + MOZ_ASSERT(NS_IsMainThread()); 1.753 +#endif 1.754 + 1.755 + if (!gHashTable.ops) 1.756 + return NS_ERROR_OUT_OF_MEMORY; 1.757 + 1.758 + PrefHashEntry* pref = static_cast<PrefHashEntry*>(PL_DHashTableOperate(&gHashTable, key, PL_DHASH_ADD)); 1.759 + 1.760 + if (!pref) 1.761 + return NS_ERROR_OUT_OF_MEMORY; 1.762 + 1.763 + // new entry, better initialize 1.764 + if (!pref->key) { 1.765 + 1.766 + // initialize the pref entry 1.767 + pref->flags = type; 1.768 + pref->key = ArenaStrDup(key, &gPrefNameArena); 1.769 + memset(&pref->defaultPref, 0, sizeof(pref->defaultPref)); 1.770 + memset(&pref->userPref, 0, sizeof(pref->userPref)); 1.771 + } 1.772 + else if ((pref->flags & PREF_HAS_DEFAULT) && PREF_TYPE(pref) != type) 1.773 + { 1.774 + NS_WARNING(nsPrintfCString("Trying to overwrite value of default pref %s with the wrong type!", key).get()); 1.775 + return NS_ERROR_UNEXPECTED; 1.776 + } 1.777 + 1.778 + bool valueChanged = false; 1.779 + if (flags & kPrefSetDefault) 1.780 + { 1.781 + if (!PREF_IS_LOCKED(pref)) 1.782 + { /* ?? change of semantics? */ 1.783 + if (pref_ValueChanged(pref->defaultPref, value, type) || 1.784 + !(pref->flags & PREF_HAS_DEFAULT)) 1.785 + { 1.786 + pref_SetValue(&pref->defaultPref, &pref->flags, value, type); 1.787 + pref->flags |= PREF_HAS_DEFAULT; 1.788 + if (!PREF_HAS_USER_VALUE(pref)) 1.789 + valueChanged = true; 1.790 + } 1.791 + } 1.792 + } 1.793 + else 1.794 + { 1.795 + /* If new value is same as the default value, then un-set the user value. 1.796 + Otherwise, set the user value only if it has changed */ 1.797 + if ((pref->flags & PREF_HAS_DEFAULT) && 1.798 + !pref_ValueChanged(pref->defaultPref, value, type) && 1.799 + !(flags & kPrefForceSet)) 1.800 + { 1.801 + if (PREF_HAS_USER_VALUE(pref)) 1.802 + { 1.803 + /* XXX should we free a user-set string value if there is one? */ 1.804 + pref->flags &= ~PREF_USERSET; 1.805 + if (!PREF_IS_LOCKED(pref)) 1.806 + valueChanged = true; 1.807 + } 1.808 + } 1.809 + else if (!PREF_HAS_USER_VALUE(pref) || 1.810 + PREF_TYPE(pref) != type || 1.811 + pref_ValueChanged(pref->userPref, value, type) ) 1.812 + { 1.813 + pref_SetValue(&pref->userPref, &pref->flags, value, type); 1.814 + pref->flags |= PREF_USERSET; 1.815 + if (!PREF_IS_LOCKED(pref)) 1.816 + valueChanged = true; 1.817 + } 1.818 + } 1.819 + 1.820 + nsresult rv = NS_OK; 1.821 + if (valueChanged) { 1.822 + gDirty = true; 1.823 + 1.824 + nsresult rv2 = pref_DoCallback(key); 1.825 + if (NS_FAILED(rv2)) 1.826 + rv = rv2; 1.827 + } 1.828 + return rv; 1.829 +} 1.830 + 1.831 +size_t 1.832 +pref_SizeOfPrivateData(MallocSizeOf aMallocSizeOf) 1.833 +{ 1.834 + size_t n = PL_SizeOfArenaPoolExcludingPool(&gPrefNameArena, aMallocSizeOf); 1.835 + for (struct CallbackNode* node = gCallbacks; node; node = node->next) { 1.836 + n += aMallocSizeOf(node); 1.837 + n += aMallocSizeOf(node->domain); 1.838 + } 1.839 + return n; 1.840 +} 1.841 + 1.842 +PrefType 1.843 +PREF_GetPrefType(const char *pref_name) 1.844 +{ 1.845 + if (gHashTable.ops) 1.846 + { 1.847 + PrefHashEntry* pref = pref_HashTableLookup(pref_name); 1.848 + if (pref) 1.849 + { 1.850 + if (pref->flags & PREF_STRING) 1.851 + return PREF_STRING; 1.852 + else if (pref->flags & PREF_INT) 1.853 + return PREF_INT; 1.854 + else if (pref->flags & PREF_BOOL) 1.855 + return PREF_BOOL; 1.856 + } 1.857 + } 1.858 + return PREF_INVALID; 1.859 +} 1.860 + 1.861 +/* -- */ 1.862 + 1.863 +bool 1.864 +PREF_PrefIsLocked(const char *pref_name) 1.865 +{ 1.866 + bool result = false; 1.867 + if (gIsAnyPrefLocked && gHashTable.ops) { 1.868 + PrefHashEntry* pref = pref_HashTableLookup(pref_name); 1.869 + if (pref && PREF_IS_LOCKED(pref)) 1.870 + result = true; 1.871 + } 1.872 + 1.873 + return result; 1.874 +} 1.875 + 1.876 +/* Adds a node to the beginning of the callback list. */ 1.877 +void 1.878 +PREF_RegisterCallback(const char *pref_node, 1.879 + PrefChangedFunc callback, 1.880 + void * instance_data) 1.881 +{ 1.882 + NS_PRECONDITION(pref_node, "pref_node must not be nullptr"); 1.883 + NS_PRECONDITION(callback, "callback must not be nullptr"); 1.884 + 1.885 + struct CallbackNode* node = (struct CallbackNode*) malloc(sizeof(struct CallbackNode)); 1.886 + if (node) 1.887 + { 1.888 + node->domain = PL_strdup(pref_node); 1.889 + node->func = callback; 1.890 + node->data = instance_data; 1.891 + node->next = gCallbacks; 1.892 + gCallbacks = node; 1.893 + } 1.894 + return; 1.895 +} 1.896 + 1.897 +/* Removes |node| from gCallbacks list. 1.898 + Returns the node after the deleted one. */ 1.899 +struct CallbackNode* 1.900 +pref_RemoveCallbackNode(struct CallbackNode* node, 1.901 + struct CallbackNode* prev_node) 1.902 +{ 1.903 + NS_PRECONDITION(!prev_node || prev_node->next == node, "invalid params"); 1.904 + NS_PRECONDITION(prev_node || gCallbacks == node, "invalid params"); 1.905 + 1.906 + NS_ASSERTION(!gCallbacksInProgress, 1.907 + "modifying the callback list while gCallbacksInProgress is true"); 1.908 + 1.909 + struct CallbackNode* next_node = node->next; 1.910 + if (prev_node) 1.911 + prev_node->next = next_node; 1.912 + else 1.913 + gCallbacks = next_node; 1.914 + PL_strfree(node->domain); 1.915 + free(node); 1.916 + return next_node; 1.917 +} 1.918 + 1.919 +/* Deletes a node from the callback list or marks it for deletion. */ 1.920 +nsresult 1.921 +PREF_UnregisterCallback(const char *pref_node, 1.922 + PrefChangedFunc callback, 1.923 + void * instance_data) 1.924 +{ 1.925 + nsresult rv = NS_ERROR_FAILURE; 1.926 + struct CallbackNode* node = gCallbacks; 1.927 + struct CallbackNode* prev_node = nullptr; 1.928 + 1.929 + while (node != nullptr) 1.930 + { 1.931 + if ( node->func == callback && 1.932 + node->data == instance_data && 1.933 + strcmp(node->domain, pref_node) == 0) 1.934 + { 1.935 + if (gCallbacksInProgress) 1.936 + { 1.937 + // postpone the node removal until after 1.938 + // gCallbacks enumeration is finished. 1.939 + node->func = nullptr; 1.940 + gShouldCleanupDeadNodes = true; 1.941 + prev_node = node; 1.942 + node = node->next; 1.943 + } 1.944 + else 1.945 + { 1.946 + node = pref_RemoveCallbackNode(node, prev_node); 1.947 + } 1.948 + rv = NS_OK; 1.949 + } 1.950 + else 1.951 + { 1.952 + prev_node = node; 1.953 + node = node->next; 1.954 + } 1.955 + } 1.956 + return rv; 1.957 +} 1.958 + 1.959 +static nsresult pref_DoCallback(const char* changed_pref) 1.960 +{ 1.961 + nsresult rv = NS_OK; 1.962 + struct CallbackNode* node; 1.963 + 1.964 + bool reentered = gCallbacksInProgress; 1.965 + gCallbacksInProgress = true; 1.966 + // Nodes must not be deleted while gCallbacksInProgress is true. 1.967 + // Nodes that need to be deleted are marked for deletion by nulling 1.968 + // out the |func| pointer. We release them at the end of this function 1.969 + // if we haven't reentered. 1.970 + 1.971 + for (node = gCallbacks; node != nullptr; node = node->next) 1.972 + { 1.973 + if ( node->func && 1.974 + PL_strncmp(changed_pref, 1.975 + node->domain, 1.976 + strlen(node->domain)) == 0 ) 1.977 + { 1.978 + (*node->func) (changed_pref, node->data); 1.979 + } 1.980 + } 1.981 + 1.982 + gCallbacksInProgress = reentered; 1.983 + 1.984 + if (gShouldCleanupDeadNodes && !gCallbacksInProgress) 1.985 + { 1.986 + struct CallbackNode* prev_node = nullptr; 1.987 + node = gCallbacks; 1.988 + 1.989 + while (node != nullptr) 1.990 + { 1.991 + if (!node->func) 1.992 + { 1.993 + node = pref_RemoveCallbackNode(node, prev_node); 1.994 + } 1.995 + else 1.996 + { 1.997 + prev_node = node; 1.998 + node = node->next; 1.999 + } 1.1000 + } 1.1001 + gShouldCleanupDeadNodes = false; 1.1002 + } 1.1003 + 1.1004 + return rv; 1.1005 +} 1.1006 + 1.1007 +void PREF_ReaderCallback(void *closure, 1.1008 + const char *pref, 1.1009 + PrefValue value, 1.1010 + PrefType type, 1.1011 + bool isDefault) 1.1012 +{ 1.1013 + pref_HashPref(pref, value, type, isDefault ? kPrefSetDefault : kPrefForceSet); 1.1014 +}