modules/libpref/src/prefapi.cpp

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

mercurial