prefapi.cpp 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006
  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. #include <string>
  6. #include <vector>
  7. #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"
  14. #define PL_ARENA_CONST_ALIGN_MASK 3
  15. #include "plarena.h"
  16. #ifdef _WIN32
  17. #include "windows.h"
  18. #endif /* _WIN32 */
  19. #include "plstr.h"
  20. #include "PLDHashTable.h"
  21. #include "plbase64.h"
  22. #include "mozilla/Logging.h"
  23. #include "prprf.h"
  24. #include "mozilla/MemoryReporting.h"
  25. #include "mozilla/dom/PContent.h"
  26. #include "nsQuickSort.h"
  27. #include "nsString.h"
  28. #include "nsPrintfCString.h"
  29. #include "prlink.h"
  30. using namespace mozilla;
  31. static void
  32. clearPrefEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
  33. {
  34. PrefHashEntry *pref = static_cast<PrefHashEntry *>(entry);
  35. if (pref->prefFlags.IsTypeString())
  36. {
  37. if (pref->defaultPref.stringVal)
  38. PL_strfree(pref->defaultPref.stringVal);
  39. if (pref->userPref.stringVal)
  40. PL_strfree(pref->userPref.stringVal);
  41. }
  42. // don't need to free this as it's allocated in memory owned by
  43. // gPrefNameArena
  44. pref->key = nullptr;
  45. memset(entry, 0, table->EntrySize());
  46. }
  47. static bool
  48. matchPrefEntry(const PLDHashEntryHdr* entry, const void* key)
  49. {
  50. const PrefHashEntry *prefEntry =
  51. static_cast<const PrefHashEntry*>(entry);
  52. if (prefEntry->key == key) return true;
  53. if (!prefEntry->key || !key) return false;
  54. const char *otherKey = reinterpret_cast<const char*>(key);
  55. return (strcmp(prefEntry->key, otherKey) == 0);
  56. }
  57. PLDHashTable* gHashTable;
  58. static PLArenaPool gPrefNameArena;
  59. static struct CallbackNode* gCallbacks = nullptr;
  60. static bool gIsAnyPrefLocked = false;
  61. // These are only used during the call to pref_DoCallback
  62. static bool gCallbacksInProgress = false;
  63. static bool gShouldCleanupDeadNodes = false;
  64. static PLDHashTableOps pref_HashTableOps = {
  65. PLDHashTable::HashStringKey,
  66. matchPrefEntry,
  67. PLDHashTable::MoveEntryStub,
  68. clearPrefEntry,
  69. nullptr,
  70. };
  71. // PR_ALIGN_OF_WORD is only defined on some platforms. ALIGN_OF_WORD has
  72. // already been defined to PR_ALIGN_OF_WORD everywhere
  73. #ifndef PR_ALIGN_OF_WORD
  74. #define PR_ALIGN_OF_WORD PR_ALIGN_OF_POINTER
  75. #endif
  76. // making PrefName arena 8k for nice allocation
  77. #define PREFNAME_ARENA_SIZE 8192
  78. #define WORD_ALIGN_MASK (PR_ALIGN_OF_WORD - 1)
  79. // sanity checking
  80. #if (PR_ALIGN_OF_WORD & WORD_ALIGN_MASK) != 0
  81. #error "PR_ALIGN_OF_WORD must be a power of 2!"
  82. #endif
  83. // equivalent to strdup() - does no error checking,
  84. // we're assuming we're only called with a valid pointer
  85. static char *ArenaStrDup(const char* str, PLArenaPool* aArena)
  86. {
  87. void* mem;
  88. uint32_t len = strlen(str);
  89. PL_ARENA_ALLOCATE(mem, aArena, len+1);
  90. if (mem)
  91. memcpy(mem, str, len+1);
  92. return static_cast<char*>(mem);
  93. }
  94. static PrefsDirtyFunc gDirtyCallback = nullptr;
  95. inline void MakeDirtyCallback()
  96. {
  97. // Right now the callback function is always set, so we don't need
  98. // to complicate the code to cover the scenario where we set the callback
  99. // after we've already tried to make it dirty. If this assert triggers
  100. // we will add that code.
  101. MOZ_ASSERT(gDirtyCallback);
  102. if (gDirtyCallback) {
  103. gDirtyCallback();
  104. }
  105. }
  106. void PREF_SetDirtyCallback(PrefsDirtyFunc aFunc)
  107. {
  108. gDirtyCallback = aFunc;
  109. }
  110. /*---------------------------------------------------------------------------*/
  111. static bool pref_ValueChanged(PrefValue oldValue, PrefValue newValue, PrefType type);
  112. /* -- Privates */
  113. struct CallbackNode {
  114. char* domain;
  115. // If someone attempts to remove the node from the callback list while
  116. // pref_DoCallback is running, |func| is set to nullptr. Such nodes will
  117. // be removed at the end of pref_DoCallback.
  118. PrefChangedFunc func;
  119. void* data;
  120. struct CallbackNode* next;
  121. };
  122. /* -- Prototypes */
  123. static nsresult pref_DoCallback(const char* changed_pref);
  124. enum {
  125. kPrefSetDefault = 1,
  126. kPrefForceSet = 2,
  127. kPrefStickyDefault = 4,
  128. };
  129. static nsresult pref_HashPref(const char *key, PrefValue value, PrefType type, uint32_t flags);
  130. #define PREF_HASHTABLE_INITIAL_LENGTH 1024
  131. void PREF_Init()
  132. {
  133. if (!gHashTable) {
  134. gHashTable = new PLDHashTable(&pref_HashTableOps,
  135. sizeof(PrefHashEntry),
  136. PREF_HASHTABLE_INITIAL_LENGTH);
  137. PL_INIT_ARENA_POOL(&gPrefNameArena, "PrefNameArena",
  138. PREFNAME_ARENA_SIZE);
  139. }
  140. }
  141. /* Frees the callback list. */
  142. void PREF_Cleanup()
  143. {
  144. NS_ASSERTION(!gCallbacksInProgress,
  145. "PREF_Cleanup was called while gCallbacksInProgress is true!");
  146. struct CallbackNode* node = gCallbacks;
  147. struct CallbackNode* next_node;
  148. while (node)
  149. {
  150. next_node = node->next;
  151. PL_strfree(node->domain);
  152. free(node);
  153. node = next_node;
  154. }
  155. gCallbacks = nullptr;
  156. PREF_CleanupPrefs();
  157. }
  158. /* Frees up all the objects except the callback list. */
  159. void PREF_CleanupPrefs()
  160. {
  161. if (gHashTable) {
  162. delete gHashTable;
  163. gHashTable = nullptr;
  164. PL_FinishArenaPool(&gPrefNameArena);
  165. }
  166. }
  167. // note that this appends to aResult, and does not assign!
  168. static void str_escape(const char * original, nsAFlatCString& aResult)
  169. {
  170. /* JavaScript does not allow quotes, slashes, or line terminators inside
  171. * strings so we must escape them. ECMAScript defines four line
  172. * terminators, but we're only worrying about \r and \n here. We currently
  173. * feed our pref script to the JS interpreter as Latin-1 so we won't
  174. * encounter \u2028 (line separator) or \u2029 (paragraph separator).
  175. *
  176. * WARNING: There are hints that we may be moving to storing prefs
  177. * as utf8. If we ever feed them to the JS compiler as UTF8 then
  178. * we'll have to worry about the multibyte sequences that would be
  179. * interpreted as \u2028 and \u2029
  180. */
  181. const char *p;
  182. if (original == nullptr)
  183. return;
  184. /* Paranoid worst case all slashes will free quickly */
  185. for (p=original; *p; ++p)
  186. {
  187. switch (*p)
  188. {
  189. case '\n':
  190. aResult.AppendLiteral("\\n");
  191. break;
  192. case '\r':
  193. aResult.AppendLiteral("\\r");
  194. break;
  195. case '\\':
  196. aResult.AppendLiteral("\\\\");
  197. break;
  198. case '\"':
  199. aResult.AppendLiteral("\\\"");
  200. break;
  201. default:
  202. aResult.Append(*p);
  203. break;
  204. }
  205. }
  206. }
  207. /*
  208. ** External calls
  209. */
  210. nsresult
  211. PREF_SetCharPref(const char *pref_name, const char *value, bool set_default)
  212. {
  213. if ((uint32_t)strlen(value) > MAX_PREF_LENGTH) {
  214. return NS_ERROR_ILLEGAL_VALUE;
  215. }
  216. PrefValue pref;
  217. pref.stringVal = (char*)value;
  218. return pref_HashPref(pref_name, pref, PrefType::String, set_default ? kPrefSetDefault : 0);
  219. }
  220. nsresult
  221. PREF_SetIntPref(const char *pref_name, int32_t value, bool set_default)
  222. {
  223. PrefValue pref;
  224. pref.intVal = value;
  225. return pref_HashPref(pref_name, pref, PrefType::Int, set_default ? kPrefSetDefault : 0);
  226. }
  227. nsresult
  228. PREF_SetBoolPref(const char *pref_name, bool value, bool set_default)
  229. {
  230. PrefValue pref;
  231. pref.boolVal = value;
  232. return pref_HashPref(pref_name, pref, PrefType::Bool, set_default ? kPrefSetDefault : 0);
  233. }
  234. enum WhichValue { DEFAULT_VALUE, USER_VALUE };
  235. static nsresult
  236. SetPrefValue(const char* aPrefName, const dom::PrefValue& aValue,
  237. WhichValue aWhich)
  238. {
  239. bool setDefault = (aWhich == DEFAULT_VALUE);
  240. switch (aValue.type()) {
  241. case dom::PrefValue::TnsCString:
  242. return PREF_SetCharPref(aPrefName, aValue.get_nsCString().get(),
  243. setDefault);
  244. case dom::PrefValue::Tint32_t:
  245. return PREF_SetIntPref(aPrefName, aValue.get_int32_t(),
  246. setDefault);
  247. case dom::PrefValue::Tbool:
  248. return PREF_SetBoolPref(aPrefName, aValue.get_bool(),
  249. setDefault);
  250. default:
  251. MOZ_CRASH();
  252. }
  253. }
  254. nsresult
  255. pref_SetPref(const dom::PrefSetting& aPref)
  256. {
  257. const char* prefName = aPref.name().get();
  258. const dom::MaybePrefValue& defaultValue = aPref.defaultValue();
  259. const dom::MaybePrefValue& userValue = aPref.userValue();
  260. nsresult rv;
  261. if (defaultValue.type() == dom::MaybePrefValue::TPrefValue) {
  262. rv = SetPrefValue(prefName, defaultValue.get_PrefValue(), DEFAULT_VALUE);
  263. if (NS_FAILED(rv)) {
  264. return rv;
  265. }
  266. }
  267. if (userValue.type() == dom::MaybePrefValue::TPrefValue) {
  268. rv = SetPrefValue(prefName, userValue.get_PrefValue(), USER_VALUE);
  269. } else {
  270. rv = PREF_ClearUserPref(prefName);
  271. }
  272. // NB: we should never try to clear a default value, that doesn't
  273. // make sense
  274. return rv;
  275. }
  276. UniquePtr<char*[]>
  277. pref_savePrefs(PLDHashTable* aTable, uint32_t* aPrefCount)
  278. {
  279. // This function allocates the entries in the savedPrefs array it returns.
  280. // It is the callers responsibility to go through the array and free
  281. // all of them. The aPrefCount entries will be non-null. Any end padding
  282. // is an implementation detail and may change.
  283. MOZ_ASSERT(aPrefCount);
  284. auto savedPrefs = MakeUnique<char*[]>(aTable->EntryCount());
  285. // This is not necessary, but leaving it in for now
  286. memset(savedPrefs.get(), 0, aTable->EntryCount() * sizeof(char*));
  287. int32_t j = 0;
  288. for (auto iter = aTable->Iter(); !iter.Done(); iter.Next()) {
  289. auto pref = static_cast<PrefHashEntry*>(iter.Get());
  290. nsAutoCString prefValue;
  291. nsAutoCString prefPrefix;
  292. prefPrefix.AssignLiteral("user_pref(\"");
  293. // where we're getting our pref from
  294. PrefValue* sourcePref;
  295. if (pref->prefFlags.HasUserValue() &&
  296. (pref_ValueChanged(pref->defaultPref,
  297. pref->userPref,
  298. pref->prefFlags.GetPrefType()) ||
  299. !(pref->prefFlags.HasDefault()) ||
  300. pref->prefFlags.HasStickyDefault())) {
  301. sourcePref = &pref->userPref;
  302. } else {
  303. // do not save default prefs that haven't changed
  304. continue;
  305. }
  306. // strings are in quotes!
  307. if (pref->prefFlags.IsTypeString()) {
  308. prefValue = '\"';
  309. str_escape(sourcePref->stringVal, prefValue);
  310. prefValue += '\"';
  311. } else if (pref->prefFlags.IsTypeInt()) {
  312. prefValue.AppendInt(sourcePref->intVal);
  313. } else if (pref->prefFlags.IsTypeBool()) {
  314. prefValue = (sourcePref->boolVal) ? "true" : "false";
  315. }
  316. nsAutoCString prefName;
  317. str_escape(pref->key, prefName);
  318. savedPrefs[j++] = ToNewCString(prefPrefix +
  319. prefName +
  320. NS_LITERAL_CSTRING("\", ") +
  321. prefValue +
  322. NS_LITERAL_CSTRING(");"));
  323. }
  324. *aPrefCount = j;
  325. return savedPrefs;
  326. }
  327. bool
  328. pref_EntryHasAdvisablySizedValues(PrefHashEntry* aHashEntry)
  329. {
  330. if (aHashEntry->prefFlags.GetPrefType() != PrefType::String) {
  331. return true;
  332. }
  333. char* stringVal;
  334. if (aHashEntry->prefFlags.HasDefault()) {
  335. stringVal = aHashEntry->defaultPref.stringVal;
  336. if (strlen(stringVal) > MAX_ADVISABLE_PREF_LENGTH) {
  337. return false;
  338. }
  339. }
  340. if (aHashEntry->prefFlags.HasUserValue()) {
  341. stringVal = aHashEntry->userPref.stringVal;
  342. if (strlen(stringVal) > MAX_ADVISABLE_PREF_LENGTH) {
  343. return false;
  344. }
  345. }
  346. return true;
  347. }
  348. static void
  349. GetPrefValueFromEntry(PrefHashEntry *aHashEntry, dom::PrefSetting* aPref,
  350. WhichValue aWhich)
  351. {
  352. PrefValue* value;
  353. dom::PrefValue* settingValue;
  354. if (aWhich == USER_VALUE) {
  355. value = &aHashEntry->userPref;
  356. aPref->userValue() = dom::PrefValue();
  357. settingValue = &aPref->userValue().get_PrefValue();
  358. } else {
  359. value = &aHashEntry->defaultPref;
  360. aPref->defaultValue() = dom::PrefValue();
  361. settingValue = &aPref->defaultValue().get_PrefValue();
  362. }
  363. switch (aHashEntry->prefFlags.GetPrefType()) {
  364. case PrefType::String:
  365. *settingValue = nsDependentCString(value->stringVal);
  366. return;
  367. case PrefType::Int:
  368. *settingValue = value->intVal;
  369. return;
  370. case PrefType::Bool:
  371. *settingValue = !!value->boolVal;
  372. return;
  373. default:
  374. MOZ_CRASH();
  375. }
  376. }
  377. void
  378. pref_GetPrefFromEntry(PrefHashEntry *aHashEntry, dom::PrefSetting* aPref)
  379. {
  380. aPref->name() = aHashEntry->key;
  381. if (aHashEntry->prefFlags.HasDefault()) {
  382. GetPrefValueFromEntry(aHashEntry, aPref, DEFAULT_VALUE);
  383. } else {
  384. aPref->defaultValue() = null_t();
  385. }
  386. if (aHashEntry->prefFlags.HasUserValue()) {
  387. GetPrefValueFromEntry(aHashEntry, aPref, USER_VALUE);
  388. } else {
  389. aPref->userValue() = null_t();
  390. }
  391. MOZ_ASSERT(aPref->defaultValue().type() == dom::MaybePrefValue::Tnull_t ||
  392. aPref->userValue().type() == dom::MaybePrefValue::Tnull_t ||
  393. (aPref->defaultValue().get_PrefValue().type() ==
  394. aPref->userValue().get_PrefValue().type()));
  395. }
  396. int
  397. pref_CompareStrings(const void *v1, const void *v2, void *unused)
  398. {
  399. char *s1 = *(char**) v1;
  400. char *s2 = *(char**) v2;
  401. if (!s1)
  402. {
  403. if (!s2)
  404. return 0;
  405. else
  406. return -1;
  407. }
  408. else if (!s2)
  409. return 1;
  410. else
  411. return strcmp(s1, s2);
  412. }
  413. bool PREF_HasUserPref(const char *pref_name)
  414. {
  415. if (!gHashTable)
  416. return false;
  417. PrefHashEntry *pref = pref_HashTableLookup(pref_name);
  418. return pref && pref->prefFlags.HasUserValue();
  419. }
  420. nsresult
  421. PREF_CopyCharPref(const char *pref_name, char ** return_buffer, bool get_default)
  422. {
  423. if (!gHashTable)
  424. return NS_ERROR_NOT_INITIALIZED;
  425. nsresult rv = NS_ERROR_UNEXPECTED;
  426. char* stringVal;
  427. PrefHashEntry* pref = pref_HashTableLookup(pref_name);
  428. if (pref && (pref->prefFlags.IsTypeString())) {
  429. if (get_default || pref->prefFlags.IsLocked() || !pref->prefFlags.HasUserValue()) {
  430. stringVal = pref->defaultPref.stringVal;
  431. } else {
  432. stringVal = pref->userPref.stringVal;
  433. }
  434. if (stringVal) {
  435. *return_buffer = NS_strdup(stringVal);
  436. rv = NS_OK;
  437. }
  438. }
  439. return rv;
  440. }
  441. nsresult PREF_GetIntPref(const char *pref_name,int32_t * return_int, bool get_default)
  442. {
  443. if (!gHashTable)
  444. return NS_ERROR_NOT_INITIALIZED;
  445. nsresult rv = NS_ERROR_UNEXPECTED;
  446. PrefHashEntry* pref = pref_HashTableLookup(pref_name);
  447. if (pref && (pref->prefFlags.IsTypeInt())) {
  448. if (get_default || pref->prefFlags.IsLocked() || !pref->prefFlags.HasUserValue()) {
  449. int32_t tempInt = pref->defaultPref.intVal;
  450. /* check to see if we even had a default */
  451. if (!pref->prefFlags.HasDefault()) {
  452. return NS_ERROR_UNEXPECTED;
  453. }
  454. *return_int = tempInt;
  455. } else {
  456. *return_int = pref->userPref.intVal;
  457. }
  458. rv = NS_OK;
  459. }
  460. return rv;
  461. }
  462. nsresult PREF_GetBoolPref(const char *pref_name, bool * return_value, bool get_default)
  463. {
  464. if (!gHashTable)
  465. return NS_ERROR_NOT_INITIALIZED;
  466. nsresult rv = NS_ERROR_UNEXPECTED;
  467. PrefHashEntry* pref = pref_HashTableLookup(pref_name);
  468. //NS_ASSERTION(pref, pref_name);
  469. if (pref && (pref->prefFlags.IsTypeBool())) {
  470. if (get_default || pref->prefFlags.IsLocked() || !pref->prefFlags.HasUserValue()) {
  471. bool tempBool = pref->defaultPref.boolVal;
  472. /* check to see if we even had a default */
  473. if (pref->prefFlags.HasDefault()) {
  474. *return_value = tempBool;
  475. rv = NS_OK;
  476. }
  477. } else {
  478. *return_value = pref->userPref.boolVal;
  479. rv = NS_OK;
  480. }
  481. }
  482. return rv;
  483. }
  484. nsresult
  485. PREF_DeleteBranch(const char *branch_name)
  486. {
  487. MOZ_ASSERT(NS_IsMainThread());
  488. int len = (int)strlen(branch_name);
  489. if (!gHashTable)
  490. return NS_ERROR_NOT_INITIALIZED;
  491. /* The following check insures that if the branch name already has a "."
  492. * at the end, we don't end up with a "..". This fixes an incompatibility
  493. * between nsIPref, which needs the period added, and nsIPrefBranch which
  494. * does not. When nsIPref goes away this function should be fixed to
  495. * never add the period at all.
  496. */
  497. nsAutoCString branch_dot(branch_name);
  498. if ((len > 1) && branch_name[len - 1] != '.')
  499. branch_dot += '.';
  500. /* Delete a branch. Used for deleting mime types */
  501. const char *to_delete = branch_dot.get();
  502. MOZ_ASSERT(to_delete);
  503. len = strlen(to_delete);
  504. for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
  505. auto entry = static_cast<PrefHashEntry*>(iter.Get());
  506. /* note if we're deleting "ldap" then we want to delete "ldap.xxx"
  507. and "ldap" (if such a leaf node exists) but not "ldap_1.xxx" */
  508. if (PL_strncmp(entry->key, to_delete, (uint32_t) len) == 0 ||
  509. (len-1 == (int)strlen(entry->key) &&
  510. PL_strncmp(entry->key, to_delete, (uint32_t)(len-1)) == 0)) {
  511. iter.Remove();
  512. }
  513. }
  514. MakeDirtyCallback();
  515. return NS_OK;
  516. }
  517. nsresult
  518. PREF_ClearUserPref(const char *pref_name)
  519. {
  520. if (!gHashTable)
  521. return NS_ERROR_NOT_INITIALIZED;
  522. PrefHashEntry* pref = pref_HashTableLookup(pref_name);
  523. if (pref && pref->prefFlags.HasUserValue()) {
  524. pref->prefFlags.SetHasUserValue(false);
  525. if (!pref->prefFlags.HasDefault()) {
  526. gHashTable->RemoveEntry(pref);
  527. }
  528. pref_DoCallback(pref_name);
  529. MakeDirtyCallback();
  530. }
  531. return NS_OK;
  532. }
  533. nsresult
  534. PREF_ClearAllUserPrefs()
  535. {
  536. MOZ_ASSERT(NS_IsMainThread());
  537. if (!gHashTable)
  538. return NS_ERROR_NOT_INITIALIZED;
  539. std::vector<std::string> prefStrings;
  540. for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
  541. auto pref = static_cast<PrefHashEntry*>(iter.Get());
  542. if (pref->prefFlags.HasUserValue()) {
  543. prefStrings.push_back(std::string(pref->key));
  544. pref->prefFlags.SetHasUserValue(false);
  545. if (!pref->prefFlags.HasDefault()) {
  546. iter.Remove();
  547. }
  548. }
  549. }
  550. for (std::string& prefString : prefStrings) {
  551. pref_DoCallback(prefString.c_str());
  552. }
  553. MakeDirtyCallback();
  554. return NS_OK;
  555. }
  556. nsresult PREF_LockPref(const char *key, bool lockit)
  557. {
  558. if (!gHashTable)
  559. return NS_ERROR_NOT_INITIALIZED;
  560. PrefHashEntry* pref = pref_HashTableLookup(key);
  561. if (!pref)
  562. return NS_ERROR_UNEXPECTED;
  563. if (lockit) {
  564. if (!pref->prefFlags.IsLocked()) {
  565. pref->prefFlags.SetLocked(true);
  566. gIsAnyPrefLocked = true;
  567. pref_DoCallback(key);
  568. }
  569. } else {
  570. if (pref->prefFlags.IsLocked()) {
  571. pref->prefFlags.SetLocked(false);
  572. pref_DoCallback(key);
  573. }
  574. }
  575. return NS_OK;
  576. }
  577. /*
  578. * Hash table functions
  579. */
  580. static bool pref_ValueChanged(PrefValue oldValue, PrefValue newValue, PrefType type)
  581. {
  582. bool changed = true;
  583. switch(type) {
  584. case PrefType::String:
  585. if (oldValue.stringVal && newValue.stringVal) {
  586. changed = (strcmp(oldValue.stringVal, newValue.stringVal) != 0);
  587. }
  588. break;
  589. case PrefType::Int:
  590. changed = oldValue.intVal != newValue.intVal;
  591. break;
  592. case PrefType::Bool:
  593. changed = oldValue.boolVal != newValue.boolVal;
  594. break;
  595. case PrefType::Invalid:
  596. default:
  597. changed = false;
  598. break;
  599. }
  600. return changed;
  601. }
  602. /*
  603. * Overwrite the type and value of an existing preference. Caller must
  604. * ensure that they are not changing the type of a preference that has
  605. * a default value.
  606. */
  607. static PrefTypeFlags pref_SetValue(PrefValue* existingValue, PrefTypeFlags flags,
  608. PrefValue newValue, PrefType newType)
  609. {
  610. if (flags.IsTypeString() && existingValue->stringVal) {
  611. PL_strfree(existingValue->stringVal);
  612. }
  613. flags.SetPrefType(newType);
  614. if (flags.IsTypeString()) {
  615. MOZ_ASSERT(newValue.stringVal);
  616. existingValue->stringVal = newValue.stringVal ? PL_strdup(newValue.stringVal) : nullptr;
  617. }
  618. else {
  619. *existingValue = newValue;
  620. }
  621. return flags;
  622. }
  623. PrefHashEntry* pref_HashTableLookup(const char *key)
  624. {
  625. MOZ_ASSERT(NS_IsMainThread());
  626. return static_cast<PrefHashEntry*>(gHashTable->Search(key));
  627. }
  628. nsresult pref_HashPref(const char *key, PrefValue value, PrefType type, uint32_t flags)
  629. {
  630. MOZ_ASSERT(NS_IsMainThread());
  631. if (!gHashTable)
  632. return NS_ERROR_OUT_OF_MEMORY;
  633. auto pref = static_cast<PrefHashEntry*>(gHashTable->Add(key, fallible));
  634. if (!pref)
  635. return NS_ERROR_OUT_OF_MEMORY;
  636. // new entry, better initialize
  637. if (!pref->key) {
  638. // initialize the pref entry
  639. pref->prefFlags.Reset().SetPrefType(type);
  640. pref->key = ArenaStrDup(key, &gPrefNameArena);
  641. memset(&pref->defaultPref, 0, sizeof(pref->defaultPref));
  642. memset(&pref->userPref, 0, sizeof(pref->userPref));
  643. } else if (pref->prefFlags.HasDefault() && !pref->prefFlags.IsPrefType(type)) {
  644. NS_WARNING(nsPrintfCString("Trying to overwrite value of default pref %s with the wrong type!", key).get());
  645. return NS_ERROR_UNEXPECTED;
  646. }
  647. bool valueChanged = false;
  648. if (flags & kPrefSetDefault) {
  649. if (!pref->prefFlags.IsLocked()) {
  650. /* ?? change of semantics? */
  651. if (pref_ValueChanged(pref->defaultPref, value, type) ||
  652. !pref->prefFlags.HasDefault()) {
  653. pref->prefFlags = pref_SetValue(&pref->defaultPref, pref->prefFlags, value, type).SetHasDefault(true);
  654. if (flags & kPrefStickyDefault) {
  655. pref->prefFlags.SetHasStickyDefault(true);
  656. }
  657. if (!pref->prefFlags.HasUserValue()) {
  658. valueChanged = true;
  659. }
  660. }
  661. // What if we change the default to be the same as the user value?
  662. // Should we clear the user value?
  663. }
  664. } else {
  665. /* If new value is same as the default value and it's not a "sticky"
  666. pref, then un-set the user value.
  667. Otherwise, set the user value only if it has changed */
  668. if ((pref->prefFlags.HasDefault()) &&
  669. !(pref->prefFlags.HasStickyDefault()) &&
  670. !pref_ValueChanged(pref->defaultPref, value, type) &&
  671. !(flags & kPrefForceSet)) {
  672. if (pref->prefFlags.HasUserValue()) {
  673. /* XXX should we free a user-set string value if there is one? */
  674. pref->prefFlags.SetHasUserValue(false);
  675. if (!pref->prefFlags.IsLocked()) {
  676. MakeDirtyCallback();
  677. valueChanged = true;
  678. }
  679. }
  680. } else if (!pref->prefFlags.HasUserValue() ||
  681. !pref->prefFlags.IsPrefType(type) ||
  682. pref_ValueChanged(pref->userPref, value, type) ) {
  683. pref->prefFlags = pref_SetValue(&pref->userPref, pref->prefFlags, value, type).SetHasUserValue(true);
  684. if (!pref->prefFlags.IsLocked()) {
  685. MakeDirtyCallback();
  686. valueChanged = true;
  687. }
  688. }
  689. }
  690. if (valueChanged) {
  691. return pref_DoCallback(key);
  692. }
  693. return NS_OK;
  694. }
  695. size_t
  696. pref_SizeOfPrivateData(MallocSizeOf aMallocSizeOf)
  697. {
  698. size_t n = PL_SizeOfArenaPoolExcludingPool(&gPrefNameArena, aMallocSizeOf);
  699. for (struct CallbackNode* node = gCallbacks; node; node = node->next) {
  700. n += aMallocSizeOf(node);
  701. n += aMallocSizeOf(node->domain);
  702. }
  703. return n;
  704. }
  705. PrefType
  706. PREF_GetPrefType(const char *pref_name)
  707. {
  708. if (gHashTable) {
  709. PrefHashEntry* pref = pref_HashTableLookup(pref_name);
  710. if (pref) {
  711. return pref->prefFlags.GetPrefType();
  712. }
  713. }
  714. return PrefType::Invalid;
  715. }
  716. /* -- */
  717. bool
  718. PREF_PrefIsLocked(const char *pref_name)
  719. {
  720. bool result = false;
  721. if (gIsAnyPrefLocked && gHashTable) {
  722. PrefHashEntry* pref = pref_HashTableLookup(pref_name);
  723. if (pref && pref->prefFlags.IsLocked()) {
  724. result = true;
  725. }
  726. }
  727. return result;
  728. }
  729. /* Adds a node to the beginning of the callback list. */
  730. void
  731. PREF_RegisterCallback(const char *pref_node,
  732. PrefChangedFunc callback,
  733. void * instance_data)
  734. {
  735. NS_PRECONDITION(pref_node, "pref_node must not be nullptr");
  736. NS_PRECONDITION(callback, "callback must not be nullptr");
  737. struct CallbackNode* node = (struct CallbackNode*) malloc(sizeof(struct CallbackNode));
  738. if (node)
  739. {
  740. node->domain = PL_strdup(pref_node);
  741. node->func = callback;
  742. node->data = instance_data;
  743. node->next = gCallbacks;
  744. gCallbacks = node;
  745. }
  746. return;
  747. }
  748. /* Removes |node| from gCallbacks list.
  749. Returns the node after the deleted one. */
  750. struct CallbackNode*
  751. pref_RemoveCallbackNode(struct CallbackNode* node,
  752. struct CallbackNode* prev_node)
  753. {
  754. NS_PRECONDITION(!prev_node || prev_node->next == node, "invalid params");
  755. NS_PRECONDITION(prev_node || gCallbacks == node, "invalid params");
  756. NS_ASSERTION(!gCallbacksInProgress,
  757. "modifying the callback list while gCallbacksInProgress is true");
  758. struct CallbackNode* next_node = node->next;
  759. if (prev_node)
  760. prev_node->next = next_node;
  761. else
  762. gCallbacks = next_node;
  763. PL_strfree(node->domain);
  764. free(node);
  765. return next_node;
  766. }
  767. /* Deletes a node from the callback list or marks it for deletion. */
  768. nsresult
  769. PREF_UnregisterCallback(const char *pref_node,
  770. PrefChangedFunc callback,
  771. void * instance_data)
  772. {
  773. nsresult rv = NS_ERROR_FAILURE;
  774. struct CallbackNode* node = gCallbacks;
  775. struct CallbackNode* prev_node = nullptr;
  776. while (node != nullptr)
  777. {
  778. if ( node->func == callback &&
  779. node->data == instance_data &&
  780. strcmp(node->domain, pref_node) == 0)
  781. {
  782. if (gCallbacksInProgress)
  783. {
  784. // postpone the node removal until after
  785. // gCallbacks enumeration is finished.
  786. node->func = nullptr;
  787. gShouldCleanupDeadNodes = true;
  788. prev_node = node;
  789. node = node->next;
  790. }
  791. else
  792. {
  793. node = pref_RemoveCallbackNode(node, prev_node);
  794. }
  795. rv = NS_OK;
  796. }
  797. else
  798. {
  799. prev_node = node;
  800. node = node->next;
  801. }
  802. }
  803. return rv;
  804. }
  805. static nsresult pref_DoCallback(const char* changed_pref)
  806. {
  807. nsresult rv = NS_OK;
  808. struct CallbackNode* node;
  809. bool reentered = gCallbacksInProgress;
  810. gCallbacksInProgress = true;
  811. // Nodes must not be deleted while gCallbacksInProgress is true.
  812. // Nodes that need to be deleted are marked for deletion by nulling
  813. // out the |func| pointer. We release them at the end of this function
  814. // if we haven't reentered.
  815. for (node = gCallbacks; node != nullptr; node = node->next)
  816. {
  817. if ( node->func &&
  818. PL_strncmp(changed_pref,
  819. node->domain,
  820. strlen(node->domain)) == 0 )
  821. {
  822. (*node->func) (changed_pref, node->data);
  823. }
  824. }
  825. gCallbacksInProgress = reentered;
  826. if (gShouldCleanupDeadNodes && !gCallbacksInProgress)
  827. {
  828. struct CallbackNode* prev_node = nullptr;
  829. node = gCallbacks;
  830. while (node != nullptr)
  831. {
  832. if (!node->func)
  833. {
  834. node = pref_RemoveCallbackNode(node, prev_node);
  835. }
  836. else
  837. {
  838. prev_node = node;
  839. node = node->next;
  840. }
  841. }
  842. gShouldCleanupDeadNodes = false;
  843. }
  844. return rv;
  845. }
  846. void PREF_ReaderCallback(void *closure,
  847. const char *pref,
  848. PrefValue value,
  849. PrefType type,
  850. bool isDefault,
  851. bool isStickyDefault)
  852. {
  853. uint32_t flags = 0;
  854. if (isDefault) {
  855. flags |= kPrefSetDefault;
  856. if (isStickyDefault) {
  857. flags |= kPrefStickyDefault;
  858. }
  859. } else {
  860. flags |= kPrefForceSet;
  861. }
  862. pref_HashPref(pref, value, type, flags);
  863. }