modules/libpref/src/prefapi.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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 }

mercurial