Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #include "base/basictypes.h" |
michael@0 | 7 | |
michael@0 | 8 | #include "prefapi.h" |
michael@0 | 9 | #include "prefapi_private_data.h" |
michael@0 | 10 | #include "prefread.h" |
michael@0 | 11 | #include "MainThreadUtils.h" |
michael@0 | 12 | #include "nsReadableUtils.h" |
michael@0 | 13 | #include "nsCRT.h" |
michael@0 | 14 | |
michael@0 | 15 | #define PL_ARENA_CONST_ALIGN_MASK 3 |
michael@0 | 16 | #include "plarena.h" |
michael@0 | 17 | |
michael@0 | 18 | #ifdef _WIN32 |
michael@0 | 19 | #include "windows.h" |
michael@0 | 20 | #endif /* _WIN32 */ |
michael@0 | 21 | |
michael@0 | 22 | #include "plstr.h" |
michael@0 | 23 | #include "pldhash.h" |
michael@0 | 24 | #include "plbase64.h" |
michael@0 | 25 | #include "prlog.h" |
michael@0 | 26 | #include "prprf.h" |
michael@0 | 27 | #include "mozilla/MemoryReporting.h" |
michael@0 | 28 | #include "mozilla/dom/PContent.h" |
michael@0 | 29 | #include "nsQuickSort.h" |
michael@0 | 30 | #include "nsString.h" |
michael@0 | 31 | #include "nsPrintfCString.h" |
michael@0 | 32 | #include "prlink.h" |
michael@0 | 33 | |
michael@0 | 34 | using namespace mozilla; |
michael@0 | 35 | |
michael@0 | 36 | static void |
michael@0 | 37 | clearPrefEntry(PLDHashTable *table, PLDHashEntryHdr *entry) |
michael@0 | 38 | { |
michael@0 | 39 | PrefHashEntry *pref = static_cast<PrefHashEntry *>(entry); |
michael@0 | 40 | if (pref->flags & PREF_STRING) |
michael@0 | 41 | { |
michael@0 | 42 | if (pref->defaultPref.stringVal) |
michael@0 | 43 | PL_strfree(pref->defaultPref.stringVal); |
michael@0 | 44 | if (pref->userPref.stringVal) |
michael@0 | 45 | PL_strfree(pref->userPref.stringVal); |
michael@0 | 46 | } |
michael@0 | 47 | // don't need to free this as it's allocated in memory owned by |
michael@0 | 48 | // gPrefNameArena |
michael@0 | 49 | pref->key = nullptr; |
michael@0 | 50 | memset(entry, 0, table->entrySize); |
michael@0 | 51 | } |
michael@0 | 52 | |
michael@0 | 53 | static bool |
michael@0 | 54 | matchPrefEntry(PLDHashTable*, const PLDHashEntryHdr* entry, |
michael@0 | 55 | const void* key) |
michael@0 | 56 | { |
michael@0 | 57 | const PrefHashEntry *prefEntry = |
michael@0 | 58 | static_cast<const PrefHashEntry*>(entry); |
michael@0 | 59 | |
michael@0 | 60 | if (prefEntry->key == key) return true; |
michael@0 | 61 | |
michael@0 | 62 | if (!prefEntry->key || !key) return false; |
michael@0 | 63 | |
michael@0 | 64 | const char *otherKey = reinterpret_cast<const char*>(key); |
michael@0 | 65 | return (strcmp(prefEntry->key, otherKey) == 0); |
michael@0 | 66 | } |
michael@0 | 67 | |
michael@0 | 68 | PLDHashTable gHashTable = { nullptr }; |
michael@0 | 69 | static PLArenaPool gPrefNameArena; |
michael@0 | 70 | bool gDirty = false; |
michael@0 | 71 | |
michael@0 | 72 | static struct CallbackNode* gCallbacks = nullptr; |
michael@0 | 73 | static bool gIsAnyPrefLocked = false; |
michael@0 | 74 | // These are only used during the call to pref_DoCallback |
michael@0 | 75 | static bool gCallbacksInProgress = false; |
michael@0 | 76 | static bool gShouldCleanupDeadNodes = false; |
michael@0 | 77 | |
michael@0 | 78 | |
michael@0 | 79 | static PLDHashTableOps pref_HashTableOps = { |
michael@0 | 80 | PL_DHashAllocTable, |
michael@0 | 81 | PL_DHashFreeTable, |
michael@0 | 82 | PL_DHashStringKey, |
michael@0 | 83 | matchPrefEntry, |
michael@0 | 84 | PL_DHashMoveEntryStub, |
michael@0 | 85 | clearPrefEntry, |
michael@0 | 86 | PL_DHashFinalizeStub, |
michael@0 | 87 | nullptr, |
michael@0 | 88 | }; |
michael@0 | 89 | |
michael@0 | 90 | // PR_ALIGN_OF_WORD is only defined on some platforms. ALIGN_OF_WORD has |
michael@0 | 91 | // already been defined to PR_ALIGN_OF_WORD everywhere |
michael@0 | 92 | #ifndef PR_ALIGN_OF_WORD |
michael@0 | 93 | #define PR_ALIGN_OF_WORD PR_ALIGN_OF_POINTER |
michael@0 | 94 | #endif |
michael@0 | 95 | |
michael@0 | 96 | // making PrefName arena 8k for nice allocation |
michael@0 | 97 | #define PREFNAME_ARENA_SIZE 8192 |
michael@0 | 98 | |
michael@0 | 99 | #define WORD_ALIGN_MASK (PR_ALIGN_OF_WORD - 1) |
michael@0 | 100 | |
michael@0 | 101 | // sanity checking |
michael@0 | 102 | #if (PR_ALIGN_OF_WORD & WORD_ALIGN_MASK) != 0 |
michael@0 | 103 | #error "PR_ALIGN_OF_WORD must be a power of 2!" |
michael@0 | 104 | #endif |
michael@0 | 105 | |
michael@0 | 106 | // equivalent to strdup() - does no error checking, |
michael@0 | 107 | // we're assuming we're only called with a valid pointer |
michael@0 | 108 | static char *ArenaStrDup(const char* str, PLArenaPool* aArena) |
michael@0 | 109 | { |
michael@0 | 110 | void* mem; |
michael@0 | 111 | uint32_t len = strlen(str); |
michael@0 | 112 | PL_ARENA_ALLOCATE(mem, aArena, len+1); |
michael@0 | 113 | if (mem) |
michael@0 | 114 | memcpy(mem, str, len+1); |
michael@0 | 115 | return static_cast<char*>(mem); |
michael@0 | 116 | } |
michael@0 | 117 | |
michael@0 | 118 | /*---------------------------------------------------------------------------*/ |
michael@0 | 119 | |
michael@0 | 120 | #define PREF_IS_LOCKED(pref) ((pref)->flags & PREF_LOCKED) |
michael@0 | 121 | #define PREF_HAS_DEFAULT_VALUE(pref) ((pref)->flags & PREF_HAS_DEFAULT) |
michael@0 | 122 | #define PREF_HAS_USER_VALUE(pref) ((pref)->flags & PREF_USERSET) |
michael@0 | 123 | #define PREF_TYPE(pref) (PrefType)((pref)->flags & PREF_VALUETYPE_MASK) |
michael@0 | 124 | |
michael@0 | 125 | static bool pref_ValueChanged(PrefValue oldValue, PrefValue newValue, PrefType type); |
michael@0 | 126 | |
michael@0 | 127 | /* -- Privates */ |
michael@0 | 128 | struct CallbackNode { |
michael@0 | 129 | char* domain; |
michael@0 | 130 | // If someone attempts to remove the node from the callback list while |
michael@0 | 131 | // pref_DoCallback is running, |func| is set to nullptr. Such nodes will |
michael@0 | 132 | // be removed at the end of pref_DoCallback. |
michael@0 | 133 | PrefChangedFunc func; |
michael@0 | 134 | void* data; |
michael@0 | 135 | struct CallbackNode* next; |
michael@0 | 136 | }; |
michael@0 | 137 | |
michael@0 | 138 | /* -- Prototypes */ |
michael@0 | 139 | static nsresult pref_DoCallback(const char* changed_pref); |
michael@0 | 140 | |
michael@0 | 141 | enum { |
michael@0 | 142 | kPrefSetDefault = 1, |
michael@0 | 143 | kPrefForceSet = 2 |
michael@0 | 144 | }; |
michael@0 | 145 | static nsresult pref_HashPref(const char *key, PrefValue value, PrefType type, uint32_t flags); |
michael@0 | 146 | |
michael@0 | 147 | #define PREF_HASHTABLE_INITIAL_SIZE 2048 |
michael@0 | 148 | |
michael@0 | 149 | nsresult PREF_Init() |
michael@0 | 150 | { |
michael@0 | 151 | if (!gHashTable.ops) { |
michael@0 | 152 | if (!PL_DHashTableInit(&gHashTable, &pref_HashTableOps, nullptr, |
michael@0 | 153 | sizeof(PrefHashEntry), PREF_HASHTABLE_INITIAL_SIZE, |
michael@0 | 154 | fallible_t())) { |
michael@0 | 155 | gHashTable.ops = nullptr; |
michael@0 | 156 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 157 | } |
michael@0 | 158 | |
michael@0 | 159 | PL_INIT_ARENA_POOL(&gPrefNameArena, "PrefNameArena", |
michael@0 | 160 | PREFNAME_ARENA_SIZE); |
michael@0 | 161 | } |
michael@0 | 162 | return NS_OK; |
michael@0 | 163 | } |
michael@0 | 164 | |
michael@0 | 165 | /* Frees the callback list. */ |
michael@0 | 166 | void PREF_Cleanup() |
michael@0 | 167 | { |
michael@0 | 168 | NS_ASSERTION(!gCallbacksInProgress, |
michael@0 | 169 | "PREF_Cleanup was called while gCallbacksInProgress is true!"); |
michael@0 | 170 | struct CallbackNode* node = gCallbacks; |
michael@0 | 171 | struct CallbackNode* next_node; |
michael@0 | 172 | |
michael@0 | 173 | while (node) |
michael@0 | 174 | { |
michael@0 | 175 | next_node = node->next; |
michael@0 | 176 | PL_strfree(node->domain); |
michael@0 | 177 | free(node); |
michael@0 | 178 | node = next_node; |
michael@0 | 179 | } |
michael@0 | 180 | gCallbacks = nullptr; |
michael@0 | 181 | |
michael@0 | 182 | PREF_CleanupPrefs(); |
michael@0 | 183 | } |
michael@0 | 184 | |
michael@0 | 185 | /* Frees up all the objects except the callback list. */ |
michael@0 | 186 | void PREF_CleanupPrefs() |
michael@0 | 187 | { |
michael@0 | 188 | if (gHashTable.ops) { |
michael@0 | 189 | PL_DHashTableFinish(&gHashTable); |
michael@0 | 190 | gHashTable.ops = nullptr; |
michael@0 | 191 | PL_FinishArenaPool(&gPrefNameArena); |
michael@0 | 192 | } |
michael@0 | 193 | } |
michael@0 | 194 | |
michael@0 | 195 | // note that this appends to aResult, and does not assign! |
michael@0 | 196 | static void str_escape(const char * original, nsAFlatCString& aResult) |
michael@0 | 197 | { |
michael@0 | 198 | /* JavaScript does not allow quotes, slashes, or line terminators inside |
michael@0 | 199 | * strings so we must escape them. ECMAScript defines four line |
michael@0 | 200 | * terminators, but we're only worrying about \r and \n here. We currently |
michael@0 | 201 | * feed our pref script to the JS interpreter as Latin-1 so we won't |
michael@0 | 202 | * encounter \u2028 (line separator) or \u2029 (paragraph separator). |
michael@0 | 203 | * |
michael@0 | 204 | * WARNING: There are hints that we may be moving to storing prefs |
michael@0 | 205 | * as utf8. If we ever feed them to the JS compiler as UTF8 then |
michael@0 | 206 | * we'll have to worry about the multibyte sequences that would be |
michael@0 | 207 | * interpreted as \u2028 and \u2029 |
michael@0 | 208 | */ |
michael@0 | 209 | const char *p; |
michael@0 | 210 | |
michael@0 | 211 | if (original == nullptr) |
michael@0 | 212 | return; |
michael@0 | 213 | |
michael@0 | 214 | /* Paranoid worst case all slashes will free quickly */ |
michael@0 | 215 | for (p=original; *p; ++p) |
michael@0 | 216 | { |
michael@0 | 217 | switch (*p) |
michael@0 | 218 | { |
michael@0 | 219 | case '\n': |
michael@0 | 220 | aResult.Append("\\n"); |
michael@0 | 221 | break; |
michael@0 | 222 | |
michael@0 | 223 | case '\r': |
michael@0 | 224 | aResult.Append("\\r"); |
michael@0 | 225 | break; |
michael@0 | 226 | |
michael@0 | 227 | case '\\': |
michael@0 | 228 | aResult.Append("\\\\"); |
michael@0 | 229 | break; |
michael@0 | 230 | |
michael@0 | 231 | case '\"': |
michael@0 | 232 | aResult.Append("\\\""); |
michael@0 | 233 | break; |
michael@0 | 234 | |
michael@0 | 235 | default: |
michael@0 | 236 | aResult.Append(*p); |
michael@0 | 237 | break; |
michael@0 | 238 | } |
michael@0 | 239 | } |
michael@0 | 240 | } |
michael@0 | 241 | |
michael@0 | 242 | /* |
michael@0 | 243 | ** External calls |
michael@0 | 244 | */ |
michael@0 | 245 | nsresult |
michael@0 | 246 | PREF_SetCharPref(const char *pref_name, const char *value, bool set_default) |
michael@0 | 247 | { |
michael@0 | 248 | if ((uint32_t)strlen(value) > MAX_PREF_LENGTH) { |
michael@0 | 249 | return NS_ERROR_ILLEGAL_VALUE; |
michael@0 | 250 | } |
michael@0 | 251 | |
michael@0 | 252 | PrefValue pref; |
michael@0 | 253 | pref.stringVal = (char*)value; |
michael@0 | 254 | |
michael@0 | 255 | return pref_HashPref(pref_name, pref, PREF_STRING, set_default ? kPrefSetDefault : 0); |
michael@0 | 256 | } |
michael@0 | 257 | |
michael@0 | 258 | nsresult |
michael@0 | 259 | PREF_SetIntPref(const char *pref_name, int32_t value, bool set_default) |
michael@0 | 260 | { |
michael@0 | 261 | PrefValue pref; |
michael@0 | 262 | pref.intVal = value; |
michael@0 | 263 | |
michael@0 | 264 | return pref_HashPref(pref_name, pref, PREF_INT, set_default ? kPrefSetDefault : 0); |
michael@0 | 265 | } |
michael@0 | 266 | |
michael@0 | 267 | nsresult |
michael@0 | 268 | PREF_SetBoolPref(const char *pref_name, bool value, bool set_default) |
michael@0 | 269 | { |
michael@0 | 270 | PrefValue pref; |
michael@0 | 271 | pref.boolVal = value; |
michael@0 | 272 | |
michael@0 | 273 | return pref_HashPref(pref_name, pref, PREF_BOOL, set_default ? kPrefSetDefault : 0); |
michael@0 | 274 | } |
michael@0 | 275 | |
michael@0 | 276 | enum WhichValue { DEFAULT_VALUE, USER_VALUE }; |
michael@0 | 277 | static nsresult |
michael@0 | 278 | SetPrefValue(const char* aPrefName, const dom::PrefValue& aValue, |
michael@0 | 279 | WhichValue aWhich) |
michael@0 | 280 | { |
michael@0 | 281 | bool setDefault = (aWhich == DEFAULT_VALUE); |
michael@0 | 282 | switch (aValue.type()) { |
michael@0 | 283 | case dom::PrefValue::TnsCString: |
michael@0 | 284 | return PREF_SetCharPref(aPrefName, aValue.get_nsCString().get(), |
michael@0 | 285 | setDefault); |
michael@0 | 286 | case dom::PrefValue::Tint32_t: |
michael@0 | 287 | return PREF_SetIntPref(aPrefName, aValue.get_int32_t(), |
michael@0 | 288 | setDefault); |
michael@0 | 289 | case dom::PrefValue::Tbool: |
michael@0 | 290 | return PREF_SetBoolPref(aPrefName, aValue.get_bool(), |
michael@0 | 291 | setDefault); |
michael@0 | 292 | default: |
michael@0 | 293 | MOZ_CRASH(); |
michael@0 | 294 | } |
michael@0 | 295 | } |
michael@0 | 296 | |
michael@0 | 297 | nsresult |
michael@0 | 298 | pref_SetPref(const dom::PrefSetting& aPref) |
michael@0 | 299 | { |
michael@0 | 300 | const char* prefName = aPref.name().get(); |
michael@0 | 301 | const dom::MaybePrefValue& defaultValue = aPref.defaultValue(); |
michael@0 | 302 | const dom::MaybePrefValue& userValue = aPref.userValue(); |
michael@0 | 303 | |
michael@0 | 304 | nsresult rv; |
michael@0 | 305 | if (defaultValue.type() == dom::MaybePrefValue::TPrefValue) { |
michael@0 | 306 | rv = SetPrefValue(prefName, defaultValue.get_PrefValue(), DEFAULT_VALUE); |
michael@0 | 307 | if (NS_FAILED(rv)) { |
michael@0 | 308 | return rv; |
michael@0 | 309 | } |
michael@0 | 310 | } |
michael@0 | 311 | |
michael@0 | 312 | if (userValue.type() == dom::MaybePrefValue::TPrefValue) { |
michael@0 | 313 | rv = SetPrefValue(prefName, userValue.get_PrefValue(), USER_VALUE); |
michael@0 | 314 | } else { |
michael@0 | 315 | rv = PREF_ClearUserPref(prefName); |
michael@0 | 316 | } |
michael@0 | 317 | |
michael@0 | 318 | // NB: we should never try to clear a default value, that doesn't |
michael@0 | 319 | // make sense |
michael@0 | 320 | |
michael@0 | 321 | return rv; |
michael@0 | 322 | } |
michael@0 | 323 | |
michael@0 | 324 | PLDHashOperator |
michael@0 | 325 | pref_savePref(PLDHashTable *table, PLDHashEntryHdr *heh, uint32_t i, void *arg) |
michael@0 | 326 | { |
michael@0 | 327 | pref_saveArgs *argData = static_cast<pref_saveArgs *>(arg); |
michael@0 | 328 | PrefHashEntry *pref = static_cast<PrefHashEntry *>(heh); |
michael@0 | 329 | |
michael@0 | 330 | PR_ASSERT(pref); |
michael@0 | 331 | if (!pref) |
michael@0 | 332 | return PL_DHASH_NEXT; |
michael@0 | 333 | |
michael@0 | 334 | nsAutoCString prefValue; |
michael@0 | 335 | nsAutoCString prefPrefix; |
michael@0 | 336 | prefPrefix.Assign(NS_LITERAL_CSTRING("user_pref(\"")); |
michael@0 | 337 | |
michael@0 | 338 | // where we're getting our pref from |
michael@0 | 339 | PrefValue* sourcePref; |
michael@0 | 340 | |
michael@0 | 341 | if (PREF_HAS_USER_VALUE(pref) && |
michael@0 | 342 | (pref_ValueChanged(pref->defaultPref, |
michael@0 | 343 | pref->userPref, |
michael@0 | 344 | (PrefType) PREF_TYPE(pref)) || |
michael@0 | 345 | !(pref->flags & PREF_HAS_DEFAULT))) { |
michael@0 | 346 | sourcePref = &pref->userPref; |
michael@0 | 347 | } else { |
michael@0 | 348 | if (argData->saveTypes == SAVE_ALL_AND_DEFAULTS) { |
michael@0 | 349 | prefPrefix.Assign(NS_LITERAL_CSTRING("pref(\"")); |
michael@0 | 350 | sourcePref = &pref->defaultPref; |
michael@0 | 351 | } |
michael@0 | 352 | else |
michael@0 | 353 | // do not save default prefs that haven't changed |
michael@0 | 354 | return PL_DHASH_NEXT; |
michael@0 | 355 | } |
michael@0 | 356 | |
michael@0 | 357 | // strings are in quotes! |
michael@0 | 358 | if (pref->flags & PREF_STRING) { |
michael@0 | 359 | prefValue = '\"'; |
michael@0 | 360 | str_escape(sourcePref->stringVal, prefValue); |
michael@0 | 361 | prefValue += '\"'; |
michael@0 | 362 | } |
michael@0 | 363 | |
michael@0 | 364 | else if (pref->flags & PREF_INT) |
michael@0 | 365 | prefValue.AppendInt(sourcePref->intVal); |
michael@0 | 366 | |
michael@0 | 367 | else if (pref->flags & PREF_BOOL) |
michael@0 | 368 | prefValue = (sourcePref->boolVal) ? "true" : "false"; |
michael@0 | 369 | |
michael@0 | 370 | nsAutoCString prefName; |
michael@0 | 371 | str_escape(pref->key, prefName); |
michael@0 | 372 | |
michael@0 | 373 | argData->prefArray[i] = ToNewCString(prefPrefix + |
michael@0 | 374 | prefName + |
michael@0 | 375 | NS_LITERAL_CSTRING("\", ") + |
michael@0 | 376 | prefValue + |
michael@0 | 377 | NS_LITERAL_CSTRING(");")); |
michael@0 | 378 | |
michael@0 | 379 | return PL_DHASH_NEXT; |
michael@0 | 380 | } |
michael@0 | 381 | |
michael@0 | 382 | PLDHashOperator |
michael@0 | 383 | pref_GetPrefs(PLDHashTable *table, |
michael@0 | 384 | PLDHashEntryHdr *heh, |
michael@0 | 385 | uint32_t i, |
michael@0 | 386 | void *arg) |
michael@0 | 387 | { |
michael@0 | 388 | if (heh) { |
michael@0 | 389 | PrefHashEntry *entry = static_cast<PrefHashEntry *>(heh); |
michael@0 | 390 | dom::PrefSetting *pref = |
michael@0 | 391 | static_cast<InfallibleTArray<dom::PrefSetting>*>(arg)->AppendElement(); |
michael@0 | 392 | |
michael@0 | 393 | pref_GetPrefFromEntry(entry, pref); |
michael@0 | 394 | } |
michael@0 | 395 | return PL_DHASH_NEXT; |
michael@0 | 396 | } |
michael@0 | 397 | |
michael@0 | 398 | static void |
michael@0 | 399 | GetPrefValueFromEntry(PrefHashEntry *aHashEntry, dom::PrefSetting* aPref, |
michael@0 | 400 | WhichValue aWhich) |
michael@0 | 401 | { |
michael@0 | 402 | PrefValue* value; |
michael@0 | 403 | dom::PrefValue* settingValue; |
michael@0 | 404 | if (aWhich == USER_VALUE) { |
michael@0 | 405 | value = &aHashEntry->userPref; |
michael@0 | 406 | aPref->userValue() = dom::PrefValue(); |
michael@0 | 407 | settingValue = &aPref->userValue().get_PrefValue(); |
michael@0 | 408 | } else { |
michael@0 | 409 | value = &aHashEntry->defaultPref; |
michael@0 | 410 | aPref->defaultValue() = dom::PrefValue(); |
michael@0 | 411 | settingValue = &aPref->defaultValue().get_PrefValue(); |
michael@0 | 412 | } |
michael@0 | 413 | |
michael@0 | 414 | switch (aHashEntry->flags & PREF_VALUETYPE_MASK) { |
michael@0 | 415 | case PREF_STRING: |
michael@0 | 416 | *settingValue = nsDependentCString(value->stringVal); |
michael@0 | 417 | return; |
michael@0 | 418 | case PREF_INT: |
michael@0 | 419 | *settingValue = value->intVal; |
michael@0 | 420 | return; |
michael@0 | 421 | case PREF_BOOL: |
michael@0 | 422 | *settingValue = !!value->boolVal; |
michael@0 | 423 | return; |
michael@0 | 424 | default: |
michael@0 | 425 | MOZ_CRASH(); |
michael@0 | 426 | } |
michael@0 | 427 | } |
michael@0 | 428 | |
michael@0 | 429 | void |
michael@0 | 430 | pref_GetPrefFromEntry(PrefHashEntry *aHashEntry, dom::PrefSetting* aPref) |
michael@0 | 431 | { |
michael@0 | 432 | aPref->name() = aHashEntry->key; |
michael@0 | 433 | if (PREF_HAS_DEFAULT_VALUE(aHashEntry)) { |
michael@0 | 434 | GetPrefValueFromEntry(aHashEntry, aPref, DEFAULT_VALUE); |
michael@0 | 435 | } else { |
michael@0 | 436 | aPref->defaultValue() = null_t(); |
michael@0 | 437 | } |
michael@0 | 438 | if (PREF_HAS_USER_VALUE(aHashEntry)) { |
michael@0 | 439 | GetPrefValueFromEntry(aHashEntry, aPref, USER_VALUE); |
michael@0 | 440 | } else { |
michael@0 | 441 | aPref->userValue() = null_t(); |
michael@0 | 442 | } |
michael@0 | 443 | |
michael@0 | 444 | MOZ_ASSERT(aPref->defaultValue().type() == dom::MaybePrefValue::Tnull_t || |
michael@0 | 445 | aPref->userValue().type() == dom::MaybePrefValue::Tnull_t || |
michael@0 | 446 | (aPref->defaultValue().get_PrefValue().type() == |
michael@0 | 447 | aPref->userValue().get_PrefValue().type())); |
michael@0 | 448 | } |
michael@0 | 449 | |
michael@0 | 450 | |
michael@0 | 451 | int |
michael@0 | 452 | pref_CompareStrings(const void *v1, const void *v2, void *unused) |
michael@0 | 453 | { |
michael@0 | 454 | char *s1 = *(char**) v1; |
michael@0 | 455 | char *s2 = *(char**) v2; |
michael@0 | 456 | |
michael@0 | 457 | if (!s1) |
michael@0 | 458 | { |
michael@0 | 459 | if (!s2) |
michael@0 | 460 | return 0; |
michael@0 | 461 | else |
michael@0 | 462 | return -1; |
michael@0 | 463 | } |
michael@0 | 464 | else if (!s2) |
michael@0 | 465 | return 1; |
michael@0 | 466 | else |
michael@0 | 467 | return strcmp(s1, s2); |
michael@0 | 468 | } |
michael@0 | 469 | |
michael@0 | 470 | bool PREF_HasUserPref(const char *pref_name) |
michael@0 | 471 | { |
michael@0 | 472 | if (!gHashTable.ops) |
michael@0 | 473 | return false; |
michael@0 | 474 | |
michael@0 | 475 | PrefHashEntry *pref = pref_HashTableLookup(pref_name); |
michael@0 | 476 | if (!pref) return false; |
michael@0 | 477 | |
michael@0 | 478 | /* convert PREF_HAS_USER_VALUE to bool */ |
michael@0 | 479 | return (PREF_HAS_USER_VALUE(pref) != 0); |
michael@0 | 480 | |
michael@0 | 481 | } |
michael@0 | 482 | |
michael@0 | 483 | nsresult |
michael@0 | 484 | PREF_CopyCharPref(const char *pref_name, char ** return_buffer, bool get_default) |
michael@0 | 485 | { |
michael@0 | 486 | if (!gHashTable.ops) |
michael@0 | 487 | return NS_ERROR_NOT_INITIALIZED; |
michael@0 | 488 | |
michael@0 | 489 | nsresult rv = NS_ERROR_UNEXPECTED; |
michael@0 | 490 | char* stringVal; |
michael@0 | 491 | PrefHashEntry* pref = pref_HashTableLookup(pref_name); |
michael@0 | 492 | |
michael@0 | 493 | if (pref && (pref->flags & PREF_STRING)) |
michael@0 | 494 | { |
michael@0 | 495 | if (get_default || PREF_IS_LOCKED(pref) || !PREF_HAS_USER_VALUE(pref)) |
michael@0 | 496 | stringVal = pref->defaultPref.stringVal; |
michael@0 | 497 | else |
michael@0 | 498 | stringVal = pref->userPref.stringVal; |
michael@0 | 499 | |
michael@0 | 500 | if (stringVal) { |
michael@0 | 501 | *return_buffer = NS_strdup(stringVal); |
michael@0 | 502 | rv = NS_OK; |
michael@0 | 503 | } |
michael@0 | 504 | } |
michael@0 | 505 | return rv; |
michael@0 | 506 | } |
michael@0 | 507 | |
michael@0 | 508 | nsresult PREF_GetIntPref(const char *pref_name,int32_t * return_int, bool get_default) |
michael@0 | 509 | { |
michael@0 | 510 | if (!gHashTable.ops) |
michael@0 | 511 | return NS_ERROR_NOT_INITIALIZED; |
michael@0 | 512 | |
michael@0 | 513 | nsresult rv = NS_ERROR_UNEXPECTED; |
michael@0 | 514 | PrefHashEntry* pref = pref_HashTableLookup(pref_name); |
michael@0 | 515 | if (pref && (pref->flags & PREF_INT)) |
michael@0 | 516 | { |
michael@0 | 517 | if (get_default || PREF_IS_LOCKED(pref) || !PREF_HAS_USER_VALUE(pref)) |
michael@0 | 518 | { |
michael@0 | 519 | int32_t tempInt = pref->defaultPref.intVal; |
michael@0 | 520 | /* check to see if we even had a default */ |
michael@0 | 521 | if (!(pref->flags & PREF_HAS_DEFAULT)) |
michael@0 | 522 | return NS_ERROR_UNEXPECTED; |
michael@0 | 523 | *return_int = tempInt; |
michael@0 | 524 | } |
michael@0 | 525 | else |
michael@0 | 526 | *return_int = pref->userPref.intVal; |
michael@0 | 527 | rv = NS_OK; |
michael@0 | 528 | } |
michael@0 | 529 | return rv; |
michael@0 | 530 | } |
michael@0 | 531 | |
michael@0 | 532 | nsresult PREF_GetBoolPref(const char *pref_name, bool * return_value, bool get_default) |
michael@0 | 533 | { |
michael@0 | 534 | if (!gHashTable.ops) |
michael@0 | 535 | return NS_ERROR_NOT_INITIALIZED; |
michael@0 | 536 | |
michael@0 | 537 | nsresult rv = NS_ERROR_UNEXPECTED; |
michael@0 | 538 | PrefHashEntry* pref = pref_HashTableLookup(pref_name); |
michael@0 | 539 | //NS_ASSERTION(pref, pref_name); |
michael@0 | 540 | if (pref && (pref->flags & PREF_BOOL)) |
michael@0 | 541 | { |
michael@0 | 542 | if (get_default || PREF_IS_LOCKED(pref) || !PREF_HAS_USER_VALUE(pref)) |
michael@0 | 543 | { |
michael@0 | 544 | bool tempBool = pref->defaultPref.boolVal; |
michael@0 | 545 | /* check to see if we even had a default */ |
michael@0 | 546 | if (pref->flags & PREF_HAS_DEFAULT) { |
michael@0 | 547 | *return_value = tempBool; |
michael@0 | 548 | rv = NS_OK; |
michael@0 | 549 | } |
michael@0 | 550 | } |
michael@0 | 551 | else { |
michael@0 | 552 | *return_value = pref->userPref.boolVal; |
michael@0 | 553 | rv = NS_OK; |
michael@0 | 554 | } |
michael@0 | 555 | } |
michael@0 | 556 | return rv; |
michael@0 | 557 | } |
michael@0 | 558 | |
michael@0 | 559 | /* Delete a branch. Used for deleting mime types */ |
michael@0 | 560 | static PLDHashOperator |
michael@0 | 561 | pref_DeleteItem(PLDHashTable *table, PLDHashEntryHdr *heh, uint32_t i, void *arg) |
michael@0 | 562 | { |
michael@0 | 563 | PrefHashEntry* he = static_cast<PrefHashEntry*>(heh); |
michael@0 | 564 | const char *to_delete = (const char *) arg; |
michael@0 | 565 | int len = strlen(to_delete); |
michael@0 | 566 | |
michael@0 | 567 | /* note if we're deleting "ldap" then we want to delete "ldap.xxx" |
michael@0 | 568 | and "ldap" (if such a leaf node exists) but not "ldap_1.xxx" */ |
michael@0 | 569 | if (to_delete && (PL_strncmp(he->key, to_delete, (uint32_t) len) == 0 || |
michael@0 | 570 | (len-1 == (int)strlen(he->key) && PL_strncmp(he->key, to_delete, (uint32_t)(len-1)) == 0))) |
michael@0 | 571 | return PL_DHASH_REMOVE; |
michael@0 | 572 | |
michael@0 | 573 | return PL_DHASH_NEXT; |
michael@0 | 574 | } |
michael@0 | 575 | |
michael@0 | 576 | nsresult |
michael@0 | 577 | PREF_DeleteBranch(const char *branch_name) |
michael@0 | 578 | { |
michael@0 | 579 | #ifndef MOZ_B2G |
michael@0 | 580 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 581 | #endif |
michael@0 | 582 | |
michael@0 | 583 | int len = (int)strlen(branch_name); |
michael@0 | 584 | |
michael@0 | 585 | if (!gHashTable.ops) |
michael@0 | 586 | return NS_ERROR_NOT_INITIALIZED; |
michael@0 | 587 | |
michael@0 | 588 | /* The following check insures that if the branch name already has a "." |
michael@0 | 589 | * at the end, we don't end up with a "..". This fixes an incompatibility |
michael@0 | 590 | * between nsIPref, which needs the period added, and nsIPrefBranch which |
michael@0 | 591 | * does not. When nsIPref goes away this function should be fixed to |
michael@0 | 592 | * never add the period at all. |
michael@0 | 593 | */ |
michael@0 | 594 | nsAutoCString branch_dot(branch_name); |
michael@0 | 595 | if ((len > 1) && branch_name[len - 1] != '.') |
michael@0 | 596 | branch_dot += '.'; |
michael@0 | 597 | |
michael@0 | 598 | PL_DHashTableEnumerate(&gHashTable, pref_DeleteItem, |
michael@0 | 599 | (void*) branch_dot.get()); |
michael@0 | 600 | gDirty = true; |
michael@0 | 601 | return NS_OK; |
michael@0 | 602 | } |
michael@0 | 603 | |
michael@0 | 604 | |
michael@0 | 605 | nsresult |
michael@0 | 606 | PREF_ClearUserPref(const char *pref_name) |
michael@0 | 607 | { |
michael@0 | 608 | if (!gHashTable.ops) |
michael@0 | 609 | return NS_ERROR_NOT_INITIALIZED; |
michael@0 | 610 | |
michael@0 | 611 | PrefHashEntry* pref = pref_HashTableLookup(pref_name); |
michael@0 | 612 | if (pref && PREF_HAS_USER_VALUE(pref)) |
michael@0 | 613 | { |
michael@0 | 614 | pref->flags &= ~PREF_USERSET; |
michael@0 | 615 | |
michael@0 | 616 | if (!(pref->flags & PREF_HAS_DEFAULT)) { |
michael@0 | 617 | PL_DHashTableOperate(&gHashTable, pref_name, PL_DHASH_REMOVE); |
michael@0 | 618 | } |
michael@0 | 619 | |
michael@0 | 620 | pref_DoCallback(pref_name); |
michael@0 | 621 | gDirty = true; |
michael@0 | 622 | } |
michael@0 | 623 | return NS_OK; |
michael@0 | 624 | } |
michael@0 | 625 | |
michael@0 | 626 | static PLDHashOperator |
michael@0 | 627 | pref_ClearUserPref(PLDHashTable *table, PLDHashEntryHdr *he, uint32_t, |
michael@0 | 628 | void *arg) |
michael@0 | 629 | { |
michael@0 | 630 | PrefHashEntry *pref = static_cast<PrefHashEntry*>(he); |
michael@0 | 631 | |
michael@0 | 632 | PLDHashOperator nextOp = PL_DHASH_NEXT; |
michael@0 | 633 | |
michael@0 | 634 | if (PREF_HAS_USER_VALUE(pref)) |
michael@0 | 635 | { |
michael@0 | 636 | pref->flags &= ~PREF_USERSET; |
michael@0 | 637 | |
michael@0 | 638 | if (!(pref->flags & PREF_HAS_DEFAULT)) { |
michael@0 | 639 | nextOp = PL_DHASH_REMOVE; |
michael@0 | 640 | } |
michael@0 | 641 | |
michael@0 | 642 | pref_DoCallback(pref->key); |
michael@0 | 643 | } |
michael@0 | 644 | return nextOp; |
michael@0 | 645 | } |
michael@0 | 646 | |
michael@0 | 647 | nsresult |
michael@0 | 648 | PREF_ClearAllUserPrefs() |
michael@0 | 649 | { |
michael@0 | 650 | #ifndef MOZ_B2G |
michael@0 | 651 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 652 | #endif |
michael@0 | 653 | |
michael@0 | 654 | if (!gHashTable.ops) |
michael@0 | 655 | return NS_ERROR_NOT_INITIALIZED; |
michael@0 | 656 | |
michael@0 | 657 | PL_DHashTableEnumerate(&gHashTable, pref_ClearUserPref, nullptr); |
michael@0 | 658 | |
michael@0 | 659 | gDirty = true; |
michael@0 | 660 | return NS_OK; |
michael@0 | 661 | } |
michael@0 | 662 | |
michael@0 | 663 | nsresult PREF_LockPref(const char *key, bool lockit) |
michael@0 | 664 | { |
michael@0 | 665 | if (!gHashTable.ops) |
michael@0 | 666 | return NS_ERROR_NOT_INITIALIZED; |
michael@0 | 667 | |
michael@0 | 668 | PrefHashEntry* pref = pref_HashTableLookup(key); |
michael@0 | 669 | if (!pref) |
michael@0 | 670 | return NS_ERROR_UNEXPECTED; |
michael@0 | 671 | |
michael@0 | 672 | if (lockit) { |
michael@0 | 673 | if (!PREF_IS_LOCKED(pref)) |
michael@0 | 674 | { |
michael@0 | 675 | pref->flags |= PREF_LOCKED; |
michael@0 | 676 | gIsAnyPrefLocked = true; |
michael@0 | 677 | pref_DoCallback(key); |
michael@0 | 678 | } |
michael@0 | 679 | } |
michael@0 | 680 | else |
michael@0 | 681 | { |
michael@0 | 682 | if (PREF_IS_LOCKED(pref)) |
michael@0 | 683 | { |
michael@0 | 684 | pref->flags &= ~PREF_LOCKED; |
michael@0 | 685 | pref_DoCallback(key); |
michael@0 | 686 | } |
michael@0 | 687 | } |
michael@0 | 688 | return NS_OK; |
michael@0 | 689 | } |
michael@0 | 690 | |
michael@0 | 691 | /* |
michael@0 | 692 | * Hash table functions |
michael@0 | 693 | */ |
michael@0 | 694 | static bool pref_ValueChanged(PrefValue oldValue, PrefValue newValue, PrefType type) |
michael@0 | 695 | { |
michael@0 | 696 | bool changed = true; |
michael@0 | 697 | if (type & PREF_STRING) |
michael@0 | 698 | { |
michael@0 | 699 | if (oldValue.stringVal && newValue.stringVal) |
michael@0 | 700 | changed = (strcmp(oldValue.stringVal, newValue.stringVal) != 0); |
michael@0 | 701 | } |
michael@0 | 702 | else if (type & PREF_INT) |
michael@0 | 703 | changed = oldValue.intVal != newValue.intVal; |
michael@0 | 704 | else if (type & PREF_BOOL) |
michael@0 | 705 | changed = oldValue.boolVal != newValue.boolVal; |
michael@0 | 706 | return changed; |
michael@0 | 707 | } |
michael@0 | 708 | |
michael@0 | 709 | /* |
michael@0 | 710 | * Overwrite the type and value of an existing preference. Caller must |
michael@0 | 711 | * ensure that they are not changing the type of a preference that has |
michael@0 | 712 | * a default value. |
michael@0 | 713 | */ |
michael@0 | 714 | static void pref_SetValue(PrefValue* existingValue, uint16_t *existingFlags, |
michael@0 | 715 | PrefValue newValue, PrefType newType) |
michael@0 | 716 | { |
michael@0 | 717 | if ((*existingFlags & PREF_STRING) && existingValue->stringVal) { |
michael@0 | 718 | PL_strfree(existingValue->stringVal); |
michael@0 | 719 | } |
michael@0 | 720 | *existingFlags = (*existingFlags & ~PREF_VALUETYPE_MASK) | newType; |
michael@0 | 721 | if (newType & PREF_STRING) { |
michael@0 | 722 | PR_ASSERT(newValue.stringVal); |
michael@0 | 723 | existingValue->stringVal = newValue.stringVal ? PL_strdup(newValue.stringVal) : nullptr; |
michael@0 | 724 | } |
michael@0 | 725 | else { |
michael@0 | 726 | *existingValue = newValue; |
michael@0 | 727 | } |
michael@0 | 728 | gDirty = true; |
michael@0 | 729 | } |
michael@0 | 730 | |
michael@0 | 731 | PrefHashEntry* pref_HashTableLookup(const void *key) |
michael@0 | 732 | { |
michael@0 | 733 | #ifndef MOZ_B2G |
michael@0 | 734 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 735 | #endif |
michael@0 | 736 | |
michael@0 | 737 | PrefHashEntry* result = |
michael@0 | 738 | static_cast<PrefHashEntry*>(PL_DHashTableOperate(&gHashTable, key, PL_DHASH_LOOKUP)); |
michael@0 | 739 | |
michael@0 | 740 | if (PL_DHASH_ENTRY_IS_FREE(result)) |
michael@0 | 741 | return nullptr; |
michael@0 | 742 | |
michael@0 | 743 | return result; |
michael@0 | 744 | } |
michael@0 | 745 | |
michael@0 | 746 | nsresult pref_HashPref(const char *key, PrefValue value, PrefType type, uint32_t flags) |
michael@0 | 747 | { |
michael@0 | 748 | #ifndef MOZ_B2G |
michael@0 | 749 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 750 | #endif |
michael@0 | 751 | |
michael@0 | 752 | if (!gHashTable.ops) |
michael@0 | 753 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 754 | |
michael@0 | 755 | PrefHashEntry* pref = static_cast<PrefHashEntry*>(PL_DHashTableOperate(&gHashTable, key, PL_DHASH_ADD)); |
michael@0 | 756 | |
michael@0 | 757 | if (!pref) |
michael@0 | 758 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 759 | |
michael@0 | 760 | // new entry, better initialize |
michael@0 | 761 | if (!pref->key) { |
michael@0 | 762 | |
michael@0 | 763 | // initialize the pref entry |
michael@0 | 764 | pref->flags = type; |
michael@0 | 765 | pref->key = ArenaStrDup(key, &gPrefNameArena); |
michael@0 | 766 | memset(&pref->defaultPref, 0, sizeof(pref->defaultPref)); |
michael@0 | 767 | memset(&pref->userPref, 0, sizeof(pref->userPref)); |
michael@0 | 768 | } |
michael@0 | 769 | else if ((pref->flags & PREF_HAS_DEFAULT) && PREF_TYPE(pref) != type) |
michael@0 | 770 | { |
michael@0 | 771 | NS_WARNING(nsPrintfCString("Trying to overwrite value of default pref %s with the wrong type!", key).get()); |
michael@0 | 772 | return NS_ERROR_UNEXPECTED; |
michael@0 | 773 | } |
michael@0 | 774 | |
michael@0 | 775 | bool valueChanged = false; |
michael@0 | 776 | if (flags & kPrefSetDefault) |
michael@0 | 777 | { |
michael@0 | 778 | if (!PREF_IS_LOCKED(pref)) |
michael@0 | 779 | { /* ?? change of semantics? */ |
michael@0 | 780 | if (pref_ValueChanged(pref->defaultPref, value, type) || |
michael@0 | 781 | !(pref->flags & PREF_HAS_DEFAULT)) |
michael@0 | 782 | { |
michael@0 | 783 | pref_SetValue(&pref->defaultPref, &pref->flags, value, type); |
michael@0 | 784 | pref->flags |= PREF_HAS_DEFAULT; |
michael@0 | 785 | if (!PREF_HAS_USER_VALUE(pref)) |
michael@0 | 786 | valueChanged = true; |
michael@0 | 787 | } |
michael@0 | 788 | } |
michael@0 | 789 | } |
michael@0 | 790 | else |
michael@0 | 791 | { |
michael@0 | 792 | /* If new value is same as the default value, then un-set the user value. |
michael@0 | 793 | Otherwise, set the user value only if it has changed */ |
michael@0 | 794 | if ((pref->flags & PREF_HAS_DEFAULT) && |
michael@0 | 795 | !pref_ValueChanged(pref->defaultPref, value, type) && |
michael@0 | 796 | !(flags & kPrefForceSet)) |
michael@0 | 797 | { |
michael@0 | 798 | if (PREF_HAS_USER_VALUE(pref)) |
michael@0 | 799 | { |
michael@0 | 800 | /* XXX should we free a user-set string value if there is one? */ |
michael@0 | 801 | pref->flags &= ~PREF_USERSET; |
michael@0 | 802 | if (!PREF_IS_LOCKED(pref)) |
michael@0 | 803 | valueChanged = true; |
michael@0 | 804 | } |
michael@0 | 805 | } |
michael@0 | 806 | else if (!PREF_HAS_USER_VALUE(pref) || |
michael@0 | 807 | PREF_TYPE(pref) != type || |
michael@0 | 808 | pref_ValueChanged(pref->userPref, value, type) ) |
michael@0 | 809 | { |
michael@0 | 810 | pref_SetValue(&pref->userPref, &pref->flags, value, type); |
michael@0 | 811 | pref->flags |= PREF_USERSET; |
michael@0 | 812 | if (!PREF_IS_LOCKED(pref)) |
michael@0 | 813 | valueChanged = true; |
michael@0 | 814 | } |
michael@0 | 815 | } |
michael@0 | 816 | |
michael@0 | 817 | nsresult rv = NS_OK; |
michael@0 | 818 | if (valueChanged) { |
michael@0 | 819 | gDirty = true; |
michael@0 | 820 | |
michael@0 | 821 | nsresult rv2 = pref_DoCallback(key); |
michael@0 | 822 | if (NS_FAILED(rv2)) |
michael@0 | 823 | rv = rv2; |
michael@0 | 824 | } |
michael@0 | 825 | return rv; |
michael@0 | 826 | } |
michael@0 | 827 | |
michael@0 | 828 | size_t |
michael@0 | 829 | pref_SizeOfPrivateData(MallocSizeOf aMallocSizeOf) |
michael@0 | 830 | { |
michael@0 | 831 | size_t n = PL_SizeOfArenaPoolExcludingPool(&gPrefNameArena, aMallocSizeOf); |
michael@0 | 832 | for (struct CallbackNode* node = gCallbacks; node; node = node->next) { |
michael@0 | 833 | n += aMallocSizeOf(node); |
michael@0 | 834 | n += aMallocSizeOf(node->domain); |
michael@0 | 835 | } |
michael@0 | 836 | return n; |
michael@0 | 837 | } |
michael@0 | 838 | |
michael@0 | 839 | PrefType |
michael@0 | 840 | PREF_GetPrefType(const char *pref_name) |
michael@0 | 841 | { |
michael@0 | 842 | if (gHashTable.ops) |
michael@0 | 843 | { |
michael@0 | 844 | PrefHashEntry* pref = pref_HashTableLookup(pref_name); |
michael@0 | 845 | if (pref) |
michael@0 | 846 | { |
michael@0 | 847 | if (pref->flags & PREF_STRING) |
michael@0 | 848 | return PREF_STRING; |
michael@0 | 849 | else if (pref->flags & PREF_INT) |
michael@0 | 850 | return PREF_INT; |
michael@0 | 851 | else if (pref->flags & PREF_BOOL) |
michael@0 | 852 | return PREF_BOOL; |
michael@0 | 853 | } |
michael@0 | 854 | } |
michael@0 | 855 | return PREF_INVALID; |
michael@0 | 856 | } |
michael@0 | 857 | |
michael@0 | 858 | /* -- */ |
michael@0 | 859 | |
michael@0 | 860 | bool |
michael@0 | 861 | PREF_PrefIsLocked(const char *pref_name) |
michael@0 | 862 | { |
michael@0 | 863 | bool result = false; |
michael@0 | 864 | if (gIsAnyPrefLocked && gHashTable.ops) { |
michael@0 | 865 | PrefHashEntry* pref = pref_HashTableLookup(pref_name); |
michael@0 | 866 | if (pref && PREF_IS_LOCKED(pref)) |
michael@0 | 867 | result = true; |
michael@0 | 868 | } |
michael@0 | 869 | |
michael@0 | 870 | return result; |
michael@0 | 871 | } |
michael@0 | 872 | |
michael@0 | 873 | /* Adds a node to the beginning of the callback list. */ |
michael@0 | 874 | void |
michael@0 | 875 | PREF_RegisterCallback(const char *pref_node, |
michael@0 | 876 | PrefChangedFunc callback, |
michael@0 | 877 | void * instance_data) |
michael@0 | 878 | { |
michael@0 | 879 | NS_PRECONDITION(pref_node, "pref_node must not be nullptr"); |
michael@0 | 880 | NS_PRECONDITION(callback, "callback must not be nullptr"); |
michael@0 | 881 | |
michael@0 | 882 | struct CallbackNode* node = (struct CallbackNode*) malloc(sizeof(struct CallbackNode)); |
michael@0 | 883 | if (node) |
michael@0 | 884 | { |
michael@0 | 885 | node->domain = PL_strdup(pref_node); |
michael@0 | 886 | node->func = callback; |
michael@0 | 887 | node->data = instance_data; |
michael@0 | 888 | node->next = gCallbacks; |
michael@0 | 889 | gCallbacks = node; |
michael@0 | 890 | } |
michael@0 | 891 | return; |
michael@0 | 892 | } |
michael@0 | 893 | |
michael@0 | 894 | /* Removes |node| from gCallbacks list. |
michael@0 | 895 | Returns the node after the deleted one. */ |
michael@0 | 896 | struct CallbackNode* |
michael@0 | 897 | pref_RemoveCallbackNode(struct CallbackNode* node, |
michael@0 | 898 | struct CallbackNode* prev_node) |
michael@0 | 899 | { |
michael@0 | 900 | NS_PRECONDITION(!prev_node || prev_node->next == node, "invalid params"); |
michael@0 | 901 | NS_PRECONDITION(prev_node || gCallbacks == node, "invalid params"); |
michael@0 | 902 | |
michael@0 | 903 | NS_ASSERTION(!gCallbacksInProgress, |
michael@0 | 904 | "modifying the callback list while gCallbacksInProgress is true"); |
michael@0 | 905 | |
michael@0 | 906 | struct CallbackNode* next_node = node->next; |
michael@0 | 907 | if (prev_node) |
michael@0 | 908 | prev_node->next = next_node; |
michael@0 | 909 | else |
michael@0 | 910 | gCallbacks = next_node; |
michael@0 | 911 | PL_strfree(node->domain); |
michael@0 | 912 | free(node); |
michael@0 | 913 | return next_node; |
michael@0 | 914 | } |
michael@0 | 915 | |
michael@0 | 916 | /* Deletes a node from the callback list or marks it for deletion. */ |
michael@0 | 917 | nsresult |
michael@0 | 918 | PREF_UnregisterCallback(const char *pref_node, |
michael@0 | 919 | PrefChangedFunc callback, |
michael@0 | 920 | void * instance_data) |
michael@0 | 921 | { |
michael@0 | 922 | nsresult rv = NS_ERROR_FAILURE; |
michael@0 | 923 | struct CallbackNode* node = gCallbacks; |
michael@0 | 924 | struct CallbackNode* prev_node = nullptr; |
michael@0 | 925 | |
michael@0 | 926 | while (node != nullptr) |
michael@0 | 927 | { |
michael@0 | 928 | if ( node->func == callback && |
michael@0 | 929 | node->data == instance_data && |
michael@0 | 930 | strcmp(node->domain, pref_node) == 0) |
michael@0 | 931 | { |
michael@0 | 932 | if (gCallbacksInProgress) |
michael@0 | 933 | { |
michael@0 | 934 | // postpone the node removal until after |
michael@0 | 935 | // gCallbacks enumeration is finished. |
michael@0 | 936 | node->func = nullptr; |
michael@0 | 937 | gShouldCleanupDeadNodes = true; |
michael@0 | 938 | prev_node = node; |
michael@0 | 939 | node = node->next; |
michael@0 | 940 | } |
michael@0 | 941 | else |
michael@0 | 942 | { |
michael@0 | 943 | node = pref_RemoveCallbackNode(node, prev_node); |
michael@0 | 944 | } |
michael@0 | 945 | rv = NS_OK; |
michael@0 | 946 | } |
michael@0 | 947 | else |
michael@0 | 948 | { |
michael@0 | 949 | prev_node = node; |
michael@0 | 950 | node = node->next; |
michael@0 | 951 | } |
michael@0 | 952 | } |
michael@0 | 953 | return rv; |
michael@0 | 954 | } |
michael@0 | 955 | |
michael@0 | 956 | static nsresult pref_DoCallback(const char* changed_pref) |
michael@0 | 957 | { |
michael@0 | 958 | nsresult rv = NS_OK; |
michael@0 | 959 | struct CallbackNode* node; |
michael@0 | 960 | |
michael@0 | 961 | bool reentered = gCallbacksInProgress; |
michael@0 | 962 | gCallbacksInProgress = true; |
michael@0 | 963 | // Nodes must not be deleted while gCallbacksInProgress is true. |
michael@0 | 964 | // Nodes that need to be deleted are marked for deletion by nulling |
michael@0 | 965 | // out the |func| pointer. We release them at the end of this function |
michael@0 | 966 | // if we haven't reentered. |
michael@0 | 967 | |
michael@0 | 968 | for (node = gCallbacks; node != nullptr; node = node->next) |
michael@0 | 969 | { |
michael@0 | 970 | if ( node->func && |
michael@0 | 971 | PL_strncmp(changed_pref, |
michael@0 | 972 | node->domain, |
michael@0 | 973 | strlen(node->domain)) == 0 ) |
michael@0 | 974 | { |
michael@0 | 975 | (*node->func) (changed_pref, node->data); |
michael@0 | 976 | } |
michael@0 | 977 | } |
michael@0 | 978 | |
michael@0 | 979 | gCallbacksInProgress = reentered; |
michael@0 | 980 | |
michael@0 | 981 | if (gShouldCleanupDeadNodes && !gCallbacksInProgress) |
michael@0 | 982 | { |
michael@0 | 983 | struct CallbackNode* prev_node = nullptr; |
michael@0 | 984 | node = gCallbacks; |
michael@0 | 985 | |
michael@0 | 986 | while (node != nullptr) |
michael@0 | 987 | { |
michael@0 | 988 | if (!node->func) |
michael@0 | 989 | { |
michael@0 | 990 | node = pref_RemoveCallbackNode(node, prev_node); |
michael@0 | 991 | } |
michael@0 | 992 | else |
michael@0 | 993 | { |
michael@0 | 994 | prev_node = node; |
michael@0 | 995 | node = node->next; |
michael@0 | 996 | } |
michael@0 | 997 | } |
michael@0 | 998 | gShouldCleanupDeadNodes = false; |
michael@0 | 999 | } |
michael@0 | 1000 | |
michael@0 | 1001 | return rv; |
michael@0 | 1002 | } |
michael@0 | 1003 | |
michael@0 | 1004 | void PREF_ReaderCallback(void *closure, |
michael@0 | 1005 | const char *pref, |
michael@0 | 1006 | PrefValue value, |
michael@0 | 1007 | PrefType type, |
michael@0 | 1008 | bool isDefault) |
michael@0 | 1009 | { |
michael@0 | 1010 | pref_HashPref(pref, value, type, isDefault ? kPrefSetDefault : kPrefForceSet); |
michael@0 | 1011 | } |