modules/libpref/src/prefapi.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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/. */
     6 #include "base/basictypes.h"
     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"
    15 #define PL_ARENA_CONST_ALIGN_MASK 3
    16 #include "plarena.h"
    18 #ifdef _WIN32
    19   #include "windows.h"
    20 #endif /* _WIN32 */
    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"
    34 using namespace mozilla;
    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 }
    53 static bool
    54 matchPrefEntry(PLDHashTable*, const PLDHashEntryHdr* entry,
    55                const void* key)
    56 {
    57     const PrefHashEntry *prefEntry =
    58         static_cast<const PrefHashEntry*>(entry);
    60     if (prefEntry->key == key) return true;
    62     if (!prefEntry->key || !key) return false;
    64     const char *otherKey = reinterpret_cast<const char*>(key);
    65     return (strcmp(prefEntry->key, otherKey) == 0);
    66 }
    68 PLDHashTable        gHashTable = { nullptr };
    69 static PLArenaPool  gPrefNameArena;
    70 bool                gDirty = false;
    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;
    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 };
    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
    96 // making PrefName arena 8k for nice allocation
    97 #define PREFNAME_ARENA_SIZE 8192
    99 #define WORD_ALIGN_MASK (PR_ALIGN_OF_WORD - 1)
   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
   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 }
   118 /*---------------------------------------------------------------------------*/
   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)
   125 static bool pref_ValueChanged(PrefValue oldValue, PrefValue newValue, PrefType type);
   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 };
   138 /* -- Prototypes */
   139 static nsresult pref_DoCallback(const char* changed_pref);
   141 enum {
   142     kPrefSetDefault = 1,
   143     kPrefForceSet = 2
   144 };
   145 static nsresult pref_HashPref(const char *key, PrefValue value, PrefType type, uint32_t flags);
   147 #define PREF_HASHTABLE_INITIAL_SIZE	2048
   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         }
   159         PL_INIT_ARENA_POOL(&gPrefNameArena, "PrefNameArena",
   160                            PREFNAME_ARENA_SIZE);
   161     }
   162     return NS_OK;
   163 }
   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;
   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;
   182     PREF_CleanupPrefs();
   183 }
   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 }
   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;
   211     if (original == nullptr)
   212         return;
   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;
   223             case '\r':
   224                 aResult.Append("\\r");
   225                 break;
   227             case '\\':
   228                 aResult.Append("\\\\");
   229                 break;
   231             case '\"':
   232                 aResult.Append("\\\"");
   233                 break;
   235             default:
   236                 aResult.Append(*p);
   237                 break;
   238         }
   239     }
   240 }
   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     }
   252     PrefValue pref;
   253     pref.stringVal = (char*)value;
   255     return pref_HashPref(pref_name, pref, PREF_STRING, set_default ? kPrefSetDefault : 0);
   256 }
   258 nsresult
   259 PREF_SetIntPref(const char *pref_name, int32_t value, bool set_default)
   260 {
   261     PrefValue pref;
   262     pref.intVal = value;
   264     return pref_HashPref(pref_name, pref, PREF_INT, set_default ? kPrefSetDefault : 0);
   265 }
   267 nsresult
   268 PREF_SetBoolPref(const char *pref_name, bool value, bool set_default)
   269 {
   270     PrefValue pref;
   271     pref.boolVal = value;
   273     return pref_HashPref(pref_name, pref, PREF_BOOL, set_default ? kPrefSetDefault : 0);
   274 }
   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 }
   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();
   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     }
   312     if (userValue.type() == dom::MaybePrefValue::TPrefValue) {
   313         rv = SetPrefValue(prefName, userValue.get_PrefValue(), USER_VALUE);
   314     } else {
   315         rv = PREF_ClearUserPref(prefName);      
   316     }
   318     // NB: we should never try to clear a default value, that doesn't
   319     // make sense
   321     return rv;
   322 }
   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);
   330     PR_ASSERT(pref);
   331     if (!pref)
   332         return PL_DHASH_NEXT;
   334     nsAutoCString prefValue;
   335     nsAutoCString prefPrefix;
   336     prefPrefix.Assign(NS_LITERAL_CSTRING("user_pref(\""));
   338     // where we're getting our pref from
   339     PrefValue* sourcePref;
   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     }
   357     // strings are in quotes!
   358     if (pref->flags & PREF_STRING) {
   359         prefValue = '\"';
   360         str_escape(sourcePref->stringVal, prefValue);
   361         prefValue += '\"';
   362     }
   364     else if (pref->flags & PREF_INT)
   365         prefValue.AppendInt(sourcePref->intVal);
   367     else if (pref->flags & PREF_BOOL)
   368         prefValue = (sourcePref->boolVal) ? "true" : "false";
   370     nsAutoCString prefName;
   371     str_escape(pref->key, prefName);
   373     argData->prefArray[i] = ToNewCString(prefPrefix +
   374                                          prefName +
   375                                          NS_LITERAL_CSTRING("\", ") +
   376                                          prefValue +
   377                                          NS_LITERAL_CSTRING(");"));
   379     return PL_DHASH_NEXT;
   380 }
   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();
   393         pref_GetPrefFromEntry(entry, pref);
   394     }
   395     return PL_DHASH_NEXT;
   396 }
   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     }
   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 }
   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     }
   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 }
   451 int
   452 pref_CompareStrings(const void *v1, const void *v2, void *unused)
   453 {
   454     char *s1 = *(char**) v1;
   455     char *s2 = *(char**) v2;
   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 }
   470 bool PREF_HasUserPref(const char *pref_name)
   471 {
   472     if (!gHashTable.ops)
   473         return false;
   475     PrefHashEntry *pref = pref_HashTableLookup(pref_name);
   476     if (!pref) return false;
   478     /* convert PREF_HAS_USER_VALUE to bool */
   479     return (PREF_HAS_USER_VALUE(pref) != 0);
   481 }
   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;
   489     nsresult rv = NS_ERROR_UNEXPECTED;
   490     char* stringVal;
   491     PrefHashEntry* pref = pref_HashTableLookup(pref_name);
   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;
   500         if (stringVal) {
   501             *return_buffer = NS_strdup(stringVal);
   502             rv = NS_OK;
   503         }
   504     }
   505     return rv;
   506 }
   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;
   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 }
   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;
   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 }
   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);
   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;
   573     return PL_DHASH_NEXT;
   574 }
   576 nsresult
   577 PREF_DeleteBranch(const char *branch_name)
   578 {
   579 #ifndef MOZ_B2G
   580     MOZ_ASSERT(NS_IsMainThread());
   581 #endif
   583     int len = (int)strlen(branch_name);
   585     if (!gHashTable.ops)
   586         return NS_ERROR_NOT_INITIALIZED;
   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 += '.';
   598     PL_DHashTableEnumerate(&gHashTable, pref_DeleteItem,
   599                            (void*) branch_dot.get());
   600     gDirty = true;
   601     return NS_OK;
   602 }
   605 nsresult
   606 PREF_ClearUserPref(const char *pref_name)
   607 {
   608     if (!gHashTable.ops)
   609         return NS_ERROR_NOT_INITIALIZED;
   611     PrefHashEntry* pref = pref_HashTableLookup(pref_name);
   612     if (pref && PREF_HAS_USER_VALUE(pref))
   613     {
   614         pref->flags &= ~PREF_USERSET;
   616         if (!(pref->flags & PREF_HAS_DEFAULT)) {
   617             PL_DHashTableOperate(&gHashTable, pref_name, PL_DHASH_REMOVE);
   618         }
   620         pref_DoCallback(pref_name);
   621         gDirty = true;
   622     }
   623     return NS_OK;
   624 }
   626 static PLDHashOperator
   627 pref_ClearUserPref(PLDHashTable *table, PLDHashEntryHdr *he, uint32_t,
   628                    void *arg)
   629 {
   630     PrefHashEntry *pref = static_cast<PrefHashEntry*>(he);
   632     PLDHashOperator nextOp = PL_DHASH_NEXT;
   634     if (PREF_HAS_USER_VALUE(pref))
   635     {
   636         pref->flags &= ~PREF_USERSET;
   638         if (!(pref->flags & PREF_HAS_DEFAULT)) {
   639             nextOp = PL_DHASH_REMOVE;
   640         }
   642         pref_DoCallback(pref->key);
   643     }
   644     return nextOp;
   645 }
   647 nsresult
   648 PREF_ClearAllUserPrefs()
   649 {
   650 #ifndef MOZ_B2G
   651     MOZ_ASSERT(NS_IsMainThread());
   652 #endif
   654     if (!gHashTable.ops)
   655         return NS_ERROR_NOT_INITIALIZED;
   657     PL_DHashTableEnumerate(&gHashTable, pref_ClearUserPref, nullptr);
   659     gDirty = true;
   660     return NS_OK;
   661 }
   663 nsresult PREF_LockPref(const char *key, bool lockit)
   664 {
   665     if (!gHashTable.ops)
   666         return NS_ERROR_NOT_INITIALIZED;
   668     PrefHashEntry* pref = pref_HashTableLookup(key);
   669     if (!pref)
   670         return NS_ERROR_UNEXPECTED;
   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 }
   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 }
   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 }
   731 PrefHashEntry* pref_HashTableLookup(const void *key)
   732 {
   733 #ifndef MOZ_B2G
   734     MOZ_ASSERT(NS_IsMainThread());
   735 #endif
   737     PrefHashEntry* result =
   738         static_cast<PrefHashEntry*>(PL_DHashTableOperate(&gHashTable, key, PL_DHASH_LOOKUP));
   740     if (PL_DHASH_ENTRY_IS_FREE(result))
   741         return nullptr;
   743     return result;
   744 }
   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
   752     if (!gHashTable.ops)
   753         return NS_ERROR_OUT_OF_MEMORY;
   755     PrefHashEntry* pref = static_cast<PrefHashEntry*>(PL_DHashTableOperate(&gHashTable, key, PL_DHASH_ADD));
   757     if (!pref)
   758         return NS_ERROR_OUT_OF_MEMORY;
   760     // new entry, better initialize
   761     if (!pref->key) {
   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     }
   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     }
   817     nsresult rv = NS_OK;
   818     if (valueChanged) {
   819         gDirty = true;
   821         nsresult rv2 = pref_DoCallback(key);
   822         if (NS_FAILED(rv2))
   823             rv = rv2;
   824     }
   825     return rv;
   826 }
   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 }
   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 }
   858 /* -- */
   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     }
   870     return result;
   871 }
   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");
   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 }
   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");
   903     NS_ASSERTION(!gCallbacksInProgress,
   904         "modifying the callback list while gCallbacksInProgress is true");
   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 }
   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;
   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 }
   956 static nsresult pref_DoCallback(const char* changed_pref)
   957 {
   958     nsresult rv = NS_OK;
   959     struct CallbackNode* node;
   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.
   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     }
   979     gCallbacksInProgress = reentered;
   981     if (gShouldCleanupDeadNodes && !gCallbacksInProgress)
   982     {
   983         struct CallbackNode* prev_node = nullptr;
   984         node = gCallbacks;
   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     }
  1001     return rv;
  1004 void PREF_ReaderCallback(void       *closure,
  1005                          const char *pref,
  1006                          PrefValue   value,
  1007                          PrefType    type,
  1008                          bool        isDefault)
  1010     pref_HashPref(pref, value, type, isDefault ? kPrefSetDefault : kPrefForceSet);

mercurial