Back to index

lightning-sunbird  0.9+nobinonly
prefapi.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "prefapi.h"
00039 #include "prefapi_private_data.h"
00040 #include "prefread.h"
00041 #include "nsReadableUtils.h"
00042 #include "nsCRT.h"
00043 
00044 #define PL_ARENA_CONST_ALIGN_MASK 3
00045 #include "plarena.h"
00046 
00047 #if defined(XP_MAC)
00048   #include <stat.h>
00049 #else
00050   #ifdef XP_OS2_EMX
00051     #include <sys/types.h>
00052   #endif
00053 #endif
00054 #ifdef _WIN32
00055   #include "windows.h"
00056 #endif /* _WIN32 */
00057 
00058 #ifdef MOZ_SECURITY
00059 #include "sechash.h"
00060 #endif
00061 #include "plstr.h"
00062 #include "pldhash.h"
00063 #include "plbase64.h"
00064 #include "prlog.h"
00065 #include "prmem.h"
00066 #include "prprf.h"
00067 #include "nsQuickSort.h"
00068 #include "nsString.h"
00069 #include "nsPrintfCString.h"
00070 #include "prlink.h"
00071 
00072 #ifdef MOZ_PROFILESHARING
00073 #include "nsSharedPrefHandler.h"
00074 #endif
00075 
00076 #ifdef XP_OS2
00077 #define INCL_DOS
00078 #include <os2.h>
00079 #endif
00080 
00081 #ifdef XP_BEOS
00082 #include "Alert.h"
00083 #endif
00084 
00085 #define BOGUS_DEFAULT_INT_PREF_VALUE (-5632)
00086 #define BOGUS_DEFAULT_BOOL_PREF_VALUE (-2)
00087 
00088 PR_STATIC_CALLBACK(void)
00089 clearPrefEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
00090 {
00091     PrefHashEntry *pref = NS_STATIC_CAST(PrefHashEntry *, entry);
00092     if (pref->flags & PREF_STRING)
00093     {
00094         PR_FREEIF(pref->defaultPref.stringVal);
00095         PR_FREEIF(pref->userPref.stringVal);
00096     }
00097     // don't need to free this as it's allocated in memory owned by
00098     // gPrefNameArena
00099     pref->key = nsnull;
00100     memset(entry, 0, table->entrySize);
00101 }
00102 
00103 PR_STATIC_CALLBACK(PRBool)
00104 matchPrefEntry(PLDHashTable*, const PLDHashEntryHdr* entry,
00105                const void* key)
00106 {
00107     const PrefHashEntry *prefEntry =
00108         NS_STATIC_CAST(const PrefHashEntry*,entry);
00109 
00110     if (prefEntry->key == key) return PR_TRUE;
00111 
00112     if (!prefEntry->key || !key) return PR_FALSE;
00113 
00114     const char *otherKey = NS_REINTERPRET_CAST(const char*, key);
00115     return (strcmp(prefEntry->key, otherKey) == 0);
00116 }
00117 
00118 PLDHashTable        gHashTable = { nsnull };
00119 static PLArenaPool  gPrefNameArena;
00120 PRBool              gDirty = PR_FALSE;
00121 
00122 static struct CallbackNode* gCallbacks = NULL;
00123 static PRBool       gCallbacksEnabled = PR_TRUE;
00124 static PRBool       gIsAnyPrefLocked = PR_FALSE;
00125 // These are only used during the call to pref_DoCallback
00126 static PRBool       gCallbacksInProgress = PR_FALSE;
00127 static PRBool       gShouldCleanupDeadNodes = PR_FALSE;
00128 
00129 
00130 static PLDHashTableOps     pref_HashTableOps = {
00131     PL_DHashAllocTable,
00132     PL_DHashFreeTable,
00133     PL_DHashGetKeyStub,
00134     PL_DHashStringKey,
00135     matchPrefEntry,
00136     PL_DHashMoveEntryStub,
00137     clearPrefEntry,
00138     PL_DHashFinalizeStub,
00139     nsnull,
00140 };
00141 
00142 // PR_ALIGN_OF_WORD is only defined on some platforms.  ALIGN_OF_WORD has
00143 // already been defined to PR_ALIGN_OF_WORD everywhere
00144 #ifndef PR_ALIGN_OF_WORD
00145 #define PR_ALIGN_OF_WORD PR_ALIGN_OF_POINTER
00146 #endif
00147 
00148 // making PrefName arena 8k for nice allocation
00149 #define PREFNAME_ARENA_SIZE 8192
00150 
00151 #define WORD_ALIGN_MASK (PR_ALIGN_OF_WORD - 1)
00152 
00153 // sanity checking
00154 #if (PR_ALIGN_OF_WORD & WORD_ALIGN_MASK) != 0
00155 #error "PR_ALIGN_OF_WORD must be a power of 2!"
00156 #endif
00157 
00158 // equivalent to strdup() - does no error checking,
00159 // we're assuming we're only called with a valid pointer
00160 static char *ArenaStrDup(const char* str, PLArenaPool* aArena)
00161 {
00162     void* mem;
00163     PRUint32 len = strlen(str);
00164     PL_ARENA_ALLOCATE(mem, aArena, len+1);
00165     if (mem)
00166         memcpy(mem, str, len+1);
00167     return NS_STATIC_CAST(char*, mem);
00168 }
00169 
00170 /*---------------------------------------------------------------------------*/
00171 
00172 #define PREF_IS_LOCKED(pref)            ((pref)->flags & PREF_LOCKED)
00173 #define PREF_HAS_USER_VALUE(pref)       ((pref)->flags & PREF_USERSET)
00174 #define PREF_TYPE(pref)                 (PrefType)((pref)->flags & PREF_VALUETYPE_MASK)
00175 
00176 static PRBool pref_ValueChanged(PrefValue oldValue, PrefValue newValue, PrefType type);
00177 
00178 /* -- Privates */
00179 struct CallbackNode {
00180     char*                   domain;
00181     // If someone attempts to remove the node from the callback list while
00182     // pref_DoCallback is running, |func| is set to nsnull. Such nodes will
00183     // be removed at the end of pref_DoCallback.
00184     PrefChangedFunc         func;
00185     void*                   data;
00186     struct CallbackNode*    next;
00187 };
00188 
00189 /* -- Prototypes */
00190 static nsresult pref_DoCallback(const char* changed_pref);
00191 
00192 
00193 static nsresult pref_HashPref(const char *key, PrefValue value, PrefType type, PRBool defaultPref);
00194 static inline PrefHashEntry* pref_HashTableLookup(const void *key);
00195 
00196 
00197 nsresult PREF_Init()
00198 {
00199     if (!gHashTable.ops) {
00200         if (!PL_DHashTableInit(&gHashTable, &pref_HashTableOps, nsnull,
00201                                sizeof(PrefHashEntry), 1024)) {
00202             gHashTable.ops = nsnull;
00203             return NS_ERROR_OUT_OF_MEMORY;
00204         }
00205 
00206         PL_INIT_ARENA_POOL(&gPrefNameArena, "PrefNameArena",
00207                            PREFNAME_ARENA_SIZE);
00208     }
00209     return NS_OK;
00210 }
00211 
00212 /* Frees the callback list. */
00213 void PREF_Cleanup()
00214 {
00215     NS_ASSERTION(!gCallbacksInProgress,
00216         "PREF_Cleanup was called while gCallbacksInProgress is PR_TRUE!");
00217     struct CallbackNode* node = gCallbacks;
00218     struct CallbackNode* next_node;
00219 
00220     while (node)
00221     {
00222         next_node = node->next;
00223         PR_Free(node->domain);
00224         PR_Free(node);
00225         node = next_node;
00226     }
00227     gCallbacks = NULL;
00228 
00229     PREF_CleanupPrefs();
00230 }
00231 
00232 /* Frees up all the objects except the callback list. */
00233 void PREF_CleanupPrefs()
00234 {
00235     if (gHashTable.ops) {
00236         PL_DHashTableFinish(&gHashTable);
00237         gHashTable.ops = nsnull;
00238         PL_FinishArenaPool(&gPrefNameArena);
00239     }
00240 }
00241 
00242 // note that this appends to aResult, and does not assign!
00243 static void str_escape(const char * original, nsAFlatCString& aResult)
00244 {
00245     /* JavaScript does not allow quotes, slashes, or line terminators inside
00246      * strings so we must escape them. ECMAScript defines four line
00247      * terminators, but we're only worrying about \r and \n here.  We currently
00248      * feed our pref script to the JS interpreter as Latin-1 so  we won't
00249      * encounter \u2028 (line separator) or \u2029 (paragraph separator).
00250      *
00251      * WARNING: There are hints that we may be moving to storing prefs
00252      * as utf8. If we ever feed them to the JS compiler as UTF8 then
00253      * we'll have to worry about the multibyte sequences that would be
00254      * interpreted as \u2028 and \u2029
00255      */
00256     const char *p;
00257 
00258     if (original == NULL)
00259         return;
00260 
00261     /* Paranoid worst case all slashes will free quickly */
00262     for  (p=original; *p; ++p)
00263     {
00264         switch (*p)
00265         {
00266             case '\n':
00267                 aResult.Append("\\n");
00268                 break;
00269 
00270             case '\r':
00271                 aResult.Append("\\r");
00272                 break;
00273 
00274             case '\\':
00275                 aResult.Append("\\\\");
00276                 break;
00277 
00278             case '\"':
00279                 aResult.Append("\\\"");
00280                 break;
00281 
00282             default:
00283                 aResult.Append(*p);
00284                 break;
00285         }
00286     }
00287 }
00288 
00289 /*
00290 ** External calls
00291 */
00292 nsresult
00293 PREF_SetCharPref(const char *pref_name, const char *value, PRBool set_default)
00294 {
00295     PrefValue pref;
00296     pref.stringVal = (char*) value;
00297 
00298     return pref_HashPref(pref_name, pref, PREF_STRING, set_default);
00299 }
00300 
00301 nsresult
00302 PREF_SetIntPref(const char *pref_name, PRInt32 value, PRBool set_default)
00303 {
00304     PrefValue pref;
00305     pref.intVal = value;
00306 
00307     return pref_HashPref(pref_name, pref, PREF_INT, set_default);
00308 }
00309 
00310 nsresult
00311 PREF_SetBoolPref(const char *pref_name, PRBool value, PRBool set_default)
00312 {
00313     PrefValue pref;
00314     pref.boolVal = value ? PR_TRUE : PR_FALSE;
00315 
00316     return pref_HashPref(pref_name, pref, PREF_BOOL, set_default);
00317 }
00318 
00319 
00320 PLDHashOperator
00321 pref_savePref(PLDHashTable *table, PLDHashEntryHdr *heh, PRUint32 i, void *arg)
00322 {
00323     pref_saveArgs *argData = NS_STATIC_CAST(pref_saveArgs *, arg);
00324     PrefHashEntry *pref = NS_STATIC_CAST(PrefHashEntry *, heh);
00325 
00326     PR_ASSERT(pref);
00327     if (!pref)
00328         return PL_DHASH_NEXT;
00329 
00330     nsCAutoString prefValue;
00331 
00332     // where we're getting our pref from
00333     PrefValue* sourcePref;
00334 
00335     if (PREF_HAS_USER_VALUE(pref) &&
00336         pref_ValueChanged(pref->defaultPref,
00337                           pref->userPref,
00338                           (PrefType) PREF_TYPE(pref)))
00339         sourcePref = &pref->userPref;
00340     else if (PREF_IS_LOCKED(pref))
00341         sourcePref = &pref->defaultPref;
00342     else
00343         // do not save default prefs that haven't changed
00344         return PL_DHASH_NEXT;
00345 
00346 #if MOZ_PROFILESHARING
00347   if ((argData->saveTypes == SAVE_SHARED &&
00348       !gSharedPrefHandler->IsPrefShared(pref->key)) ||
00349       (argData->saveTypes == SAVE_NONSHARED &&
00350       gSharedPrefHandler->IsPrefShared(pref->key)))
00351     return PL_DHASH_NEXT;
00352 #endif
00353 
00354     // strings are in quotes!
00355     if (pref->flags & PREF_STRING) {
00356         prefValue = '\"';
00357         str_escape(sourcePref->stringVal, prefValue);
00358         prefValue += '\"';
00359     }
00360 
00361     else if (pref->flags & PREF_INT)
00362         prefValue.AppendInt(sourcePref->intVal);
00363 
00364     else if (pref->flags & PREF_BOOL)
00365         prefValue = (sourcePref->boolVal) ? "true" : "false";
00366 
00367     nsCAutoString prefName;
00368     str_escape(pref->key, prefName);
00369 
00370     argData->prefArray[i] = ToNewCString(NS_LITERAL_CSTRING("user_pref(\"") +
00371                                 prefName +
00372                                 NS_LITERAL_CSTRING("\", ") +
00373                                 prefValue +
00374                                 NS_LITERAL_CSTRING(");"));
00375     return PL_DHASH_NEXT;
00376 }
00377 
00378 int PR_CALLBACK
00379 pref_CompareStrings(const void *v1, const void *v2, void *unused)
00380 {
00381     char *s1 = *(char**) v1;
00382     char *s2 = *(char**) v2;
00383 
00384     if (!s1)
00385     {
00386         if (!s2)
00387             return 0;
00388         else
00389             return -1;
00390     }
00391     else if (!s2)
00392         return 1;
00393     else
00394         return strcmp(s1, s2);
00395 }
00396 
00397 
00398 PRBool PREF_HasUserPref(const char *pref_name)
00399 {
00400     if (!gHashTable.ops)
00401         return PR_FALSE;
00402 
00403     PrefHashEntry *pref = pref_HashTableLookup(pref_name);
00404     if (!pref) return PR_FALSE;
00405 
00406     /* convert PREF_HAS_USER_VALUE to bool */
00407     return (PREF_HAS_USER_VALUE(pref) != 0);
00408 
00409 }
00410 nsresult PREF_GetCharPref(const char *pref_name, char * return_buffer, int * length, PRBool get_default)
00411 {
00412     if (!gHashTable.ops)
00413         return NS_ERROR_NOT_INITIALIZED;
00414 
00415     nsresult rv = NS_ERROR_UNEXPECTED;
00416     char* stringVal;
00417 
00418     PrefHashEntry* pref = pref_HashTableLookup(pref_name);
00419 
00420     if (pref)
00421     {
00422         if (get_default || PREF_IS_LOCKED(pref) || !PREF_HAS_USER_VALUE(pref))
00423             stringVal = pref->defaultPref.stringVal;
00424         else
00425             stringVal = pref->userPref.stringVal;
00426 
00427         if (stringVal)
00428         {
00429             if (*length <= 0)
00430                 *length = PL_strlen(stringVal) + 1;
00431             else
00432             {
00433                 PL_strncpy(return_buffer, stringVal, PR_MIN((size_t)*length - 1, PL_strlen(stringVal) + 1));
00434                 return_buffer[*length - 1] = '\0';
00435             }
00436             rv = NS_OK;
00437         }
00438     }
00439 
00440     return rv;
00441 }
00442 
00443 nsresult
00444 PREF_CopyCharPref(const char *pref_name, char ** return_buffer, PRBool get_default)
00445 {
00446     if (!gHashTable.ops)
00447         return NS_ERROR_NOT_INITIALIZED;
00448 
00449     nsresult rv = NS_ERROR_UNEXPECTED;
00450     char* stringVal;
00451     PrefHashEntry* pref = pref_HashTableLookup(pref_name);
00452 
00453     if (pref && (pref->flags & PREF_STRING))
00454     {
00455         if (get_default || PREF_IS_LOCKED(pref) || !PREF_HAS_USER_VALUE(pref))
00456             stringVal = pref->defaultPref.stringVal;
00457         else
00458             stringVal = pref->userPref.stringVal;
00459 
00460         if (stringVal) {
00461             *return_buffer = PL_strdup(stringVal);
00462             rv = NS_OK;
00463         }
00464     }
00465     return rv;
00466 }
00467 
00468 nsresult PREF_GetIntPref(const char *pref_name,PRInt32 * return_int, PRBool get_default)
00469 {
00470     if (!gHashTable.ops)
00471         return NS_ERROR_NOT_INITIALIZED;
00472 
00473     nsresult rv = NS_ERROR_UNEXPECTED;
00474     PrefHashEntry* pref = pref_HashTableLookup(pref_name);
00475     if (pref && (pref->flags & PREF_INT))
00476     {
00477         if (get_default || PREF_IS_LOCKED(pref) || !PREF_HAS_USER_VALUE(pref))
00478         {
00479             PRInt32 tempInt = pref->defaultPref.intVal;
00480             /* check to see if we even had a default */
00481             if (tempInt == ((PRInt32) BOGUS_DEFAULT_INT_PREF_VALUE))
00482                 return NS_ERROR_UNEXPECTED;
00483             *return_int = tempInt;
00484         }
00485         else
00486             *return_int = pref->userPref.intVal;
00487         rv = NS_OK;
00488     }
00489     return rv;
00490 }
00491 
00492 nsresult PREF_GetBoolPref(const char *pref_name, PRBool * return_value, PRBool get_default)
00493 {
00494     if (!gHashTable.ops)
00495         return NS_ERROR_NOT_INITIALIZED;
00496 
00497     nsresult rv = NS_ERROR_UNEXPECTED;
00498     PrefHashEntry* pref = pref_HashTableLookup(pref_name);
00499     //NS_ASSERTION(pref, pref_name);
00500     if (pref && (pref->flags & PREF_BOOL))
00501     {
00502         if (get_default || PREF_IS_LOCKED(pref) || !PREF_HAS_USER_VALUE(pref))
00503         {
00504             PRBool tempBool = pref->defaultPref.boolVal;
00505             /* check to see if we even had a default */
00506             if (tempBool != ((PRBool) BOGUS_DEFAULT_BOOL_PREF_VALUE)) {
00507                 *return_value = tempBool;
00508                 rv = NS_OK;
00509             }
00510         }
00511         else {
00512             *return_value = pref->userPref.boolVal;
00513             rv = NS_OK;
00514         }
00515     }
00516     return rv;
00517 }
00518 
00519 /* Delete a branch. Used for deleting mime types */
00520 PR_STATIC_CALLBACK(PLDHashOperator)
00521 pref_DeleteItem(PLDHashTable *table, PLDHashEntryHdr *heh, PRUint32 i, void *arg)
00522 {
00523     PrefHashEntry* he = NS_STATIC_CAST(PrefHashEntry*,heh);
00524     const char *to_delete = (const char *) arg;
00525     int len = PL_strlen(to_delete);
00526 
00527     /* note if we're deleting "ldap" then we want to delete "ldap.xxx"
00528         and "ldap" (if such a leaf node exists) but not "ldap_1.xxx" */
00529     if (to_delete && (PL_strncmp(he->key, to_delete, (PRUint32) len) == 0 ||
00530         (len-1 == (int)PL_strlen(he->key) && PL_strncmp(he->key, to_delete, (PRUint32)(len-1)) == 0)))
00531         return PL_DHASH_REMOVE;
00532 
00533     return PL_DHASH_NEXT;
00534 }
00535 
00536 nsresult
00537 PREF_DeleteBranch(const char *branch_name)
00538 {
00539     int len = (int)PL_strlen(branch_name);
00540 
00541     if (!gHashTable.ops)
00542         return NS_ERROR_NOT_INITIALIZED;
00543 
00544     /* The following check insures that if the branch name already has a "."
00545      * at the end, we don't end up with a "..". This fixes an incompatibility
00546      * between nsIPref, which needs the period added, and nsIPrefBranch which
00547      * does not. When nsIPref goes away this function should be fixed to
00548      * never add the period at all.
00549      */
00550     nsCAutoString branch_dot(branch_name);
00551     if ((len > 1) && branch_name[len - 1] != '.')
00552         branch_dot += '.';
00553 
00554     PL_DHashTableEnumerate(&gHashTable, pref_DeleteItem,
00555                            (void*) branch_dot.get());
00556     gDirty = PR_TRUE;
00557     return NS_OK;
00558 }
00559 
00560 
00561 nsresult
00562 PREF_ClearUserPref(const char *pref_name)
00563 {
00564     if (!gHashTable.ops)
00565         return NS_ERROR_NOT_INITIALIZED;
00566 
00567     nsresult rv = NS_ERROR_UNEXPECTED;
00568     PrefHashEntry* pref = pref_HashTableLookup(pref_name);
00569     if (pref && PREF_HAS_USER_VALUE(pref))
00570     {
00571         pref->flags &= ~PREF_USERSET;
00572         if (gCallbacksEnabled)
00573             pref_DoCallback(pref_name);
00574         gDirty = PR_TRUE;
00575         rv = NS_OK;
00576     }
00577     return rv;
00578 }
00579 
00580 PR_STATIC_CALLBACK(PLDHashOperator)
00581 pref_ClearUserPref(PLDHashTable *table, PLDHashEntryHdr *he, PRUint32,
00582                    void *arg)
00583 {
00584     PrefHashEntry *pref = NS_STATIC_CAST(PrefHashEntry*,  he);
00585 
00586     if (PREF_HAS_USER_VALUE(pref))
00587     {
00588         // Note that we're not unhashing the pref. A pref which has both
00589         // a user value and a default value needs to remain. Currently,
00590         // there isn't a way to determine that a pref has a default value,
00591         // other than comparing its value to BOGUS_DEFAULT_XXX_PREF_VALUE.
00592         // This needs to be fixed. If we could positively identify a pref
00593         // as not having a set default value here, we could unhash it.
00594 
00595         pref->flags &= ~PREF_USERSET;
00596         if (gCallbacksEnabled)
00597             pref_DoCallback(pref->key);
00598     }
00599     return PL_DHASH_NEXT;
00600 }
00601 
00602 nsresult
00603 PREF_ClearAllUserPrefs()
00604 {
00605     if (!gHashTable.ops)
00606         return NS_ERROR_NOT_INITIALIZED;
00607 
00608     PL_DHashTableEnumerate(&gHashTable, pref_ClearUserPref, nsnull);
00609 
00610     gDirty = PR_TRUE;
00611     return NS_OK;
00612 }
00613 
00614 nsresult PREF_LockPref(const char *key, PRBool lockit)
00615 {
00616     if (!gHashTable.ops)
00617         return NS_ERROR_NOT_INITIALIZED;
00618 
00619     PrefHashEntry* pref = pref_HashTableLookup(key);
00620     if (!pref)
00621         return NS_ERROR_UNEXPECTED;
00622 
00623     if (lockit) {
00624         if (!PREF_IS_LOCKED(pref))
00625         {
00626             pref->flags |= PREF_LOCKED;
00627             gIsAnyPrefLocked = PR_TRUE;
00628             if (gCallbacksEnabled)
00629                 pref_DoCallback(key);
00630         }
00631     }
00632     else
00633     {
00634         if (PREF_IS_LOCKED(pref))
00635         {
00636             pref->flags &= ~PREF_LOCKED;
00637             if (gCallbacksEnabled)
00638                 pref_DoCallback(key);
00639         }
00640     }
00641     return NS_OK;
00642 }
00643 
00644 /*
00645  * Hash table functions
00646  */
00647 static PRBool pref_ValueChanged(PrefValue oldValue, PrefValue newValue, PrefType type)
00648 {
00649     PRBool changed = PR_TRUE;
00650     if (type & PREF_STRING)
00651     {
00652         if (oldValue.stringVal && newValue.stringVal)
00653             changed = (strcmp(oldValue.stringVal, newValue.stringVal) != 0);
00654     }
00655     else if (type & PREF_INT)
00656         changed = oldValue.intVal != newValue.intVal;
00657     else if (type & PREF_BOOL)
00658         changed = oldValue.boolVal != newValue.boolVal;
00659     return changed;
00660 }
00661 
00662 static void pref_SetValue(PrefValue* oldValue, PrefValue newValue, PrefType type)
00663 {
00664     switch (type & PREF_VALUETYPE_MASK)
00665     {
00666         case PREF_STRING:
00667             PR_ASSERT(newValue.stringVal);
00668             PR_FREEIF(oldValue->stringVal);
00669             oldValue->stringVal = newValue.stringVal ? PL_strdup(newValue.stringVal) : NULL;
00670             break;
00671 
00672         default:
00673             *oldValue = newValue;
00674     }
00675     gDirty = PR_TRUE;
00676 }
00677 
00678 static inline PrefHashEntry* pref_HashTableLookup(const void *key)
00679 {
00680     PrefHashEntry* result =
00681         NS_STATIC_CAST(PrefHashEntry*, PL_DHashTableOperate(&gHashTable, key, PL_DHASH_LOOKUP));
00682 
00683     if (PL_DHASH_ENTRY_IS_FREE(result))
00684         return nsnull;
00685 
00686     return result;
00687 }
00688 
00689 nsresult pref_HashPref(const char *key, PrefValue value, PrefType type, PRBool set_default)
00690 {
00691     if (!gHashTable.ops)
00692         return NS_ERROR_OUT_OF_MEMORY;
00693 
00694     PrefHashEntry* pref = NS_STATIC_CAST(PrefHashEntry*, PL_DHashTableOperate(&gHashTable, key, PL_DHASH_ADD));
00695 
00696     if (!pref)
00697         return NS_ERROR_OUT_OF_MEMORY;
00698 
00699     // new entry, better intialize
00700     if (!pref->key) {
00701 
00702         // initialize the pref entry
00703         pref->flags = type;
00704         pref->key = ArenaStrDup(key, &gPrefNameArena);
00705         pref->defaultPref.intVal = 0;
00706         pref->userPref.intVal = 0;
00707 
00708         /* ugly hack -- define it to a default that no pref will ever
00709            default to this should really get fixed right by some out
00710            of band data
00711         */
00712         if (pref->flags & PREF_BOOL)
00713             pref->defaultPref.boolVal = (PRBool) BOGUS_DEFAULT_BOOL_PREF_VALUE;
00714         if (pref->flags & PREF_INT)
00715             pref->defaultPref.intVal = (PRInt32) BOGUS_DEFAULT_INT_PREF_VALUE;
00716     }
00717     else if ((((PrefType)(pref->flags)) & PREF_VALUETYPE_MASK) !=
00718                  (type & PREF_VALUETYPE_MASK))
00719     {
00720         NS_WARNING(nsPrintfCString(192, "Trying to set pref %s to with the wrong type!", key).get());
00721         return NS_ERROR_UNEXPECTED;
00722     }
00723 
00724     PRBool valueChanged = PR_FALSE;
00725     if (set_default)
00726     {
00727         if (!PREF_IS_LOCKED(pref))
00728         {       /* ?? change of semantics? */
00729             if (pref_ValueChanged(pref->defaultPref, value, type))
00730             {
00731                 pref_SetValue(&pref->defaultPref, value, type);
00732                 if (!PREF_HAS_USER_VALUE(pref))
00733                     valueChanged = PR_TRUE;
00734             }
00735         }
00736     }
00737     else
00738     {
00739         /* If new value is same as the default value, then un-set the user value.
00740            Otherwise, set the user value only if it has changed */
00741         if ( !pref_ValueChanged(pref->defaultPref, value, type) )
00742         {
00743             if (PREF_HAS_USER_VALUE(pref))
00744             {
00745                 pref->flags &= ~PREF_USERSET;
00746                 if (!PREF_IS_LOCKED(pref))
00747                     valueChanged = PR_TRUE;
00748             }
00749         }
00750         else if ( !PREF_HAS_USER_VALUE(pref) ||
00751                    pref_ValueChanged(pref->userPref, value, type) )
00752         {
00753             pref_SetValue(&pref->userPref, value, type);
00754             pref->flags |= PREF_USERSET;
00755             if (!PREF_IS_LOCKED(pref))
00756                 valueChanged = PR_TRUE;
00757         }
00758     }
00759 
00760     nsresult rv = NS_OK;
00761     if (valueChanged) {
00762         gDirty = PR_TRUE;
00763 
00764         if (gCallbacksEnabled) {
00765             nsresult rv2 = pref_DoCallback(key);
00766             if (NS_FAILED(rv2))
00767                 rv = rv2;
00768         }
00769 #ifdef MOZ_PROFILESHARING
00770         if (gSharedPrefHandler)
00771             gSharedPrefHandler->OnPrefChanged(set_default, pref, value);
00772 #endif
00773     }
00774     return rv;
00775 }
00776 
00777 PrefType
00778 PREF_GetPrefType(const char *pref_name)
00779 {
00780     if (gHashTable.ops)
00781     {
00782         PrefHashEntry* pref = pref_HashTableLookup(pref_name);
00783         if (pref)
00784         {
00785             if (pref->flags & PREF_STRING)
00786                 return PREF_STRING;
00787             else if (pref->flags & PREF_INT)
00788                 return PREF_INT;
00789             else if (pref->flags & PREF_BOOL)
00790                 return PREF_BOOL;
00791         }
00792     }
00793     return PREF_INVALID;
00794 }
00795 
00796 /* -- */
00797 
00798 PRBool
00799 PREF_PrefIsLocked(const char *pref_name)
00800 {
00801     PRBool result = PR_FALSE;
00802     if (gIsAnyPrefLocked) {
00803         PrefHashEntry* pref = pref_HashTableLookup(pref_name);
00804         if (pref && PREF_IS_LOCKED(pref))
00805             result = PR_TRUE;
00806     }
00807 
00808     return result;
00809 }
00810 
00811 /* Adds a node to the beginning of the callback list. */
00812 void
00813 PREF_RegisterCallback(const char *pref_node,
00814                        PrefChangedFunc callback,
00815                        void * instance_data)
00816 {
00817     NS_PRECONDITION(pref_node, "pref_node must not be nsnull");
00818     NS_PRECONDITION(callback, "callback must not be nsnull");
00819 
00820     struct CallbackNode* node = (struct CallbackNode*) malloc(sizeof(struct CallbackNode));
00821     if (node)
00822     {
00823         node->domain = PL_strdup(pref_node);
00824         node->func = callback;
00825         node->data = instance_data;
00826         node->next = gCallbacks;
00827         gCallbacks = node;
00828     }
00829     return;
00830 }
00831 
00832 /* Removes |node| from gCallbacks list.
00833    Returns the node after the deleted one. */
00834 struct CallbackNode*
00835 pref_RemoveCallbackNode(struct CallbackNode* node,
00836                         struct CallbackNode* prev_node)
00837 {
00838     NS_PRECONDITION(!prev_node || prev_node->next == node, "invalid params");
00839     NS_PRECONDITION(prev_node || gCallbacks == node, "invalid params");
00840 
00841     NS_ASSERTION(!gCallbacksInProgress,
00842         "modifying the callback list while gCallbacksInProgress is PR_TRUE");
00843 
00844     struct CallbackNode* next_node = node->next;
00845     if (prev_node)
00846         prev_node->next = next_node;
00847     else
00848         gCallbacks = next_node;
00849     PR_Free(node->domain);
00850     PR_Free(node);
00851     return next_node;
00852 }
00853 
00854 /* Deletes a node from the callback list or marks it for deletion. */
00855 nsresult
00856 PREF_UnregisterCallback(const char *pref_node,
00857                          PrefChangedFunc callback,
00858                          void * instance_data)
00859 {
00860     nsresult rv = NS_ERROR_FAILURE;
00861     struct CallbackNode* node = gCallbacks;
00862     struct CallbackNode* prev_node = NULL;
00863 
00864     while (node != NULL)
00865     {
00866         if ( strcmp(node->domain, pref_node) == 0 &&
00867              node->func == callback &&
00868              node->data == instance_data)
00869         {
00870             if (gCallbacksInProgress)
00871             {
00872                 // postpone the node removal until after
00873                 // gCallbacks enumeration is finished.
00874                 node->func = nsnull;
00875                 gShouldCleanupDeadNodes = PR_TRUE;
00876                 prev_node = node;
00877                 node = node->next;
00878             }
00879             else
00880             {
00881                 node = pref_RemoveCallbackNode(node, prev_node);
00882             }
00883             rv = NS_OK;
00884         }
00885         else
00886         {
00887             prev_node = node;
00888             node = node->next;
00889         }
00890     }
00891     return rv;
00892 }
00893 
00894 static nsresult pref_DoCallback(const char* changed_pref)
00895 {
00896     nsresult rv = NS_OK;
00897     struct CallbackNode* node;
00898 
00899     PRBool reentered = gCallbacksInProgress;
00900     gCallbacksInProgress = PR_TRUE;
00901     // Nodes must not be deleted while gCallbacksInProgress is PR_TRUE.
00902     // Nodes that need to be deleted are marked for deletion by nulling
00903     // out the |func| pointer. We release them at the end of this function
00904     // if we haven't reentered.
00905 
00906     for (node = gCallbacks; node != NULL; node = node->next)
00907     {
00908         if ( node->func &&
00909              PL_strncmp(changed_pref,
00910                         node->domain,
00911                         PL_strlen(node->domain)) == 0 )
00912         {
00913             nsresult rv2 = (*node->func) (changed_pref, node->data);
00914             if (NS_FAILED(rv2))
00915                 rv = rv2;
00916         }
00917     }
00918 
00919     gCallbacksInProgress = reentered;
00920 
00921     if (gShouldCleanupDeadNodes && !gCallbacksInProgress)
00922     {
00923         struct CallbackNode* prev_node = NULL;
00924         node = gCallbacks;
00925 
00926         while (node != NULL)
00927         {
00928             if (!node->func)
00929             {
00930                 node = pref_RemoveCallbackNode(node, prev_node);
00931             }
00932             else
00933             {
00934                 prev_node = node;
00935                 node = node->next;
00936             }
00937         }
00938         gShouldCleanupDeadNodes = PR_FALSE;
00939     }
00940 
00941     return rv;
00942 }
00943 
00944 void PREF_ReaderCallback(void       *closure,
00945                          const char *pref,
00946                          PrefValue   value,
00947                          PrefType    type,
00948                          PRBool      isDefault)
00949 {
00950     pref_HashPref(pref, value, type, isDefault);
00951 }