LCOV - code coverage report
Current view: top level - modules/libpref - prefapi.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 302 465 64.9 %
Date: 2017-07-14 16:53:18 Functions: 36 44 81.8 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13