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