Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include <ctype.h>
8 : #include <stdlib.h>
9 : #include <string.h>
10 :
11 : #include "base/basictypes.h"
12 : #include "GeckoProfiler.h"
13 : #include "MainThreadUtils.h"
14 : #include "mozilla/ArenaAllocatorExtensions.h"
15 : #include "mozilla/ArenaAllocator.h"
16 : #include "mozilla/ArrayUtils.h"
17 : #include "mozilla/Attributes.h"
18 : #include "mozilla/dom/PContent.h"
19 : #include "mozilla/HashFunctions.h"
20 : #include "mozilla/Logging.h"
21 : #include "mozilla/Maybe.h"
22 : #include "mozilla/MemoryReporting.h"
23 : #include "mozilla/ModuleUtils.h"
24 : #include "mozilla/Omnijar.h"
25 : #include "mozilla/Preferences.h"
26 : #include "mozilla/ResultExtensions.h"
27 : #include "mozilla/ScopeExit.h"
28 : #include "mozilla/Services.h"
29 : #include "mozilla/ServoStyleSet.h"
30 : #include "mozilla/StaticPrefs.h"
31 : #include "mozilla/SyncRunnable.h"
32 : #include "mozilla/SystemGroup.h"
33 : #include "mozilla/Telemetry.h"
34 : #include "mozilla/UniquePtrExtensions.h"
35 : #include "mozilla/URLPreloader.h"
36 : #include "mozilla/Variant.h"
37 : #include "mozilla/Vector.h"
38 : #include "nsAppDirectoryServiceDefs.h"
39 : #include "nsAutoPtr.h"
40 : #include "nsCategoryManagerUtils.h"
41 : #include "nsClassHashtable.h"
42 : #include "nsCOMArray.h"
43 : #include "nsCOMPtr.h"
44 : #include "nsCRT.h"
45 : #include "nsDataHashtable.h"
46 : #include "nsDirectoryServiceDefs.h"
47 : #include "nsHashKeys.h"
48 : #include "nsICategoryManager.h"
49 : #include "nsIConsoleService.h"
50 : #include "nsIDirectoryService.h"
51 : #include "nsIFile.h"
52 : #include "nsIInputStream.h"
53 : #include "nsIMemoryReporter.h"
54 : #include "nsIObserver.h"
55 : #include "nsIObserverService.h"
56 : #include "nsIOutputStream.h"
57 : #include "nsIPrefBranch.h"
58 : #include "nsIPrefLocalizedString.h"
59 : #include "nsIRelativeFilePref.h"
60 : #include "nsISafeOutputStream.h"
61 : #include "nsISimpleEnumerator.h"
62 : #include "nsIStringBundle.h"
63 : #include "nsIStringEnumerator.h"
64 : #include "nsISupportsImpl.h"
65 : #include "nsISupportsPrimitives.h"
66 : #include "nsIZipReader.h"
67 : #include "nsNetUtil.h"
68 : #include "nsPrintfCString.h"
69 : #include "nsQuickSort.h"
70 : #include "nsReadableUtils.h"
71 : #include "nsRefPtrHashtable.h"
72 : #include "nsString.h"
73 : #include "nsTArray.h"
74 : #include "nsThreadUtils.h"
75 : #include "nsUTF8Utils.h"
76 : #include "nsWeakReference.h"
77 : #include "nsXPCOMCID.h"
78 : #include "nsXPCOM.h"
79 : #include "nsXULAppAPI.h"
80 : #include "nsZipArchive.h"
81 : #include "plbase64.h"
82 : #include "PLDHashTable.h"
83 : #include "plstr.h"
84 : #include "prlink.h"
85 :
86 : #ifdef XP_WIN
87 : #include "windows.h"
88 : #endif
89 :
90 : using namespace mozilla;
91 :
92 : #ifdef DEBUG
93 :
94 : #define ENSURE_PARENT_PROCESS(func, pref) \
95 : do { \
96 : if (MOZ_UNLIKELY(!XRE_IsParentProcess())) { \
97 : nsPrintfCString msg( \
98 : "ENSURE_PARENT_PROCESS: called %s on %s in a non-parent process", \
99 : func, \
100 : pref); \
101 : NS_ERROR(msg.get()); \
102 : return NS_ERROR_NOT_AVAILABLE; \
103 : } \
104 : } while (0)
105 :
106 : #else // DEBUG
107 :
108 : #define ENSURE_PARENT_PROCESS(func, pref) \
109 : if (MOZ_UNLIKELY(!XRE_IsParentProcess())) { \
110 : return NS_ERROR_NOT_AVAILABLE; \
111 : }
112 :
113 : #endif // DEBUG
114 :
115 : //===========================================================================
116 : // Low-level types and operations
117 : //===========================================================================
118 :
119 : typedef nsTArray<nsCString> PrefSaveData;
120 :
121 : // 1 MB should be enough for everyone.
122 : static const uint32_t MAX_PREF_LENGTH = 1 * 1024 * 1024;
123 : // Actually, 4kb should be enough for everyone.
124 : static const uint32_t MAX_ADVISABLE_PREF_LENGTH = 4 * 1024;
125 :
126 : // Keep this in sync with PrefType in parser/src/lib.rs.
127 : enum class PrefType : uint8_t
128 : {
129 : None = 0, // only used when neither the default nor user value is set
130 : String = 1,
131 : Int = 2,
132 : Bool = 3,
133 : };
134 :
135 : // This is used for pref names and string pref values. We encode the string
136 : // length, then a '/', then the string chars. This encoding means there are no
137 : // special chars that are forbidden or require escaping.
138 : static void
139 0 : SerializeAndAppendString(const char* aChars, nsCString& aStr)
140 : {
141 0 : aStr.AppendInt(uint32_t(strlen(aChars)));
142 0 : aStr.Append('/');
143 0 : aStr.Append(aChars);
144 0 : }
145 :
146 : static char*
147 0 : DeserializeString(char* aChars, nsCString& aStr)
148 : {
149 0 : char* p = aChars;
150 0 : uint32_t length = strtol(p, &p, 10);
151 0 : MOZ_ASSERT(p[0] == '/');
152 0 : p++; // move past the '/'
153 0 : aStr.Assign(p, length);
154 0 : p += length; // move past the string itself
155 0 : return p;
156 : }
157 :
158 : // Keep this in sync with PrefValue in prefs_parser/src/lib.rs.
159 : union PrefValue {
160 : const char* mStringVal;
161 : int32_t mIntVal;
162 : bool mBoolVal;
163 :
164 0 : bool Equals(PrefType aType, PrefValue aValue)
165 : {
166 0 : switch (aType) {
167 : case PrefType::String: {
168 0 : if (mStringVal && aValue.mStringVal) {
169 0 : return strcmp(mStringVal, aValue.mStringVal) == 0;
170 : }
171 0 : if (!mStringVal && !aValue.mStringVal) {
172 : return true;
173 : }
174 0 : return false;
175 : }
176 :
177 : case PrefType::Int:
178 0 : return mIntVal == aValue.mIntVal;
179 :
180 : case PrefType::Bool:
181 0 : return mBoolVal == aValue.mBoolVal;
182 :
183 : default:
184 0 : MOZ_CRASH("Unhandled enum value");
185 : }
186 : }
187 :
188 0 : void Init(PrefType aNewType, PrefValue aNewValue)
189 : {
190 0 : if (aNewType == PrefType::String) {
191 0 : MOZ_ASSERT(aNewValue.mStringVal);
192 0 : aNewValue.mStringVal = moz_xstrdup(aNewValue.mStringVal);
193 : }
194 0 : *this = aNewValue;
195 0 : }
196 :
197 0 : void Clear(PrefType aType)
198 : {
199 0 : if (aType == PrefType::String) {
200 0 : free(const_cast<char*>(mStringVal));
201 : }
202 :
203 : // Zero the entire value (regardless of type) via mStringVal.
204 0 : mStringVal = nullptr;
205 0 : }
206 :
207 0 : void Replace(bool aHasValue, PrefType aOldType, PrefType aNewType, PrefValue aNewValue)
208 : {
209 0 : if (aHasValue) {
210 0 : Clear(aOldType);
211 : }
212 0 : Init(aNewType, aNewValue);
213 0 : }
214 :
215 0 : void ToDomPrefValue(PrefType aType, dom::PrefValue* aDomValue)
216 : {
217 0 : switch (aType) {
218 : case PrefType::String:
219 0 : *aDomValue = nsDependentCString(mStringVal);
220 0 : return;
221 :
222 : case PrefType::Int:
223 0 : *aDomValue = mIntVal;
224 0 : return;
225 :
226 : case PrefType::Bool:
227 0 : *aDomValue = mBoolVal;
228 0 : return;
229 :
230 : default:
231 0 : MOZ_CRASH();
232 : }
233 : }
234 :
235 0 : PrefType FromDomPrefValue(const dom::PrefValue& aDomValue)
236 : {
237 0 : switch (aDomValue.type()) {
238 : case dom::PrefValue::TnsCString:
239 0 : mStringVal = aDomValue.get_nsCString().get();
240 0 : return PrefType::String;
241 :
242 : case dom::PrefValue::Tint32_t:
243 0 : mIntVal = aDomValue.get_int32_t();
244 0 : return PrefType::Int;
245 :
246 : case dom::PrefValue::Tbool:
247 0 : mBoolVal = aDomValue.get_bool();
248 0 : return PrefType::Bool;
249 :
250 : default:
251 0 : MOZ_CRASH();
252 : }
253 : }
254 :
255 0 : void SerializeAndAppend(PrefType aType, nsCString& aStr)
256 : {
257 0 : switch (aType) {
258 : case PrefType::Bool:
259 0 : aStr.Append(mBoolVal ? 'T' : 'F');
260 0 : break;
261 :
262 : case PrefType::Int:
263 0 : aStr.AppendInt(mIntVal);
264 0 : break;
265 :
266 : case PrefType::String: {
267 0 : SerializeAndAppendString(mStringVal, aStr);
268 0 : break;
269 : }
270 :
271 : case PrefType::None:
272 : default:
273 0 : MOZ_CRASH();
274 : }
275 0 : }
276 :
277 0 : static char* Deserialize(PrefType aType,
278 : char* aStr,
279 : dom::MaybePrefValue* aDomValue)
280 : {
281 0 : char* p = aStr;
282 :
283 0 : switch (aType) {
284 : case PrefType::Bool:
285 0 : if (*p == 'T') {
286 0 : *aDomValue = true;
287 0 : } else if (*p == 'F') {
288 0 : *aDomValue = false;
289 : } else {
290 0 : *aDomValue = false;
291 0 : NS_ERROR("bad bool pref value");
292 : }
293 0 : p++;
294 0 : return p;
295 :
296 : case PrefType::Int: {
297 0 : *aDomValue = int32_t(strtol(p, &p, 10));
298 0 : return p;
299 : }
300 :
301 : case PrefType::String: {
302 0 : nsCString str;
303 0 : p = DeserializeString(p, str);
304 0 : *aDomValue = str;
305 0 : return p;
306 : }
307 :
308 : default:
309 0 : MOZ_CRASH();
310 : }
311 : }
312 : };
313 :
314 : #ifdef DEBUG
315 : const char*
316 0 : PrefTypeToString(PrefType aType)
317 : {
318 0 : switch (aType) {
319 : case PrefType::None:
320 : return "none";
321 : case PrefType::String:
322 0 : return "string";
323 : case PrefType::Int:
324 0 : return "int";
325 : case PrefType::Bool:
326 0 : return "bool";
327 : default:
328 0 : MOZ_CRASH("Unhandled enum value");
329 : }
330 : }
331 : #endif
332 :
333 : // Assign to aResult a quoted, escaped copy of aOriginal.
334 : static void
335 0 : StrEscape(const char* aOriginal, nsCString& aResult)
336 : {
337 0 : if (aOriginal == nullptr) {
338 0 : aResult.AssignLiteral("\"\"");
339 0 : return;
340 : }
341 :
342 : // JavaScript does not allow quotes, slashes, or line terminators inside
343 : // strings so we must escape them. ECMAScript defines four line terminators,
344 : // but we're only worrying about \r and \n here. We currently feed our pref
345 : // script to the JS interpreter as Latin-1 so we won't encounter \u2028
346 : // (line separator) or \u2029 (paragraph separator).
347 : //
348 : // WARNING: There are hints that we may be moving to storing prefs as utf8.
349 : // If we ever feed them to the JS compiler as UTF8 then we'll have to worry
350 : // about the multibyte sequences that would be interpreted as \u2028 and
351 : // \u2029.
352 : const char* p;
353 :
354 0 : aResult.Assign('"');
355 :
356 : // Paranoid worst case all slashes will free quickly.
357 0 : for (p = aOriginal; *p; ++p) {
358 0 : switch (*p) {
359 : case '\n':
360 0 : aResult.AppendLiteral("\\n");
361 0 : break;
362 :
363 : case '\r':
364 0 : aResult.AppendLiteral("\\r");
365 0 : break;
366 :
367 : case '\\':
368 0 : aResult.AppendLiteral("\\\\");
369 0 : break;
370 :
371 : case '\"':
372 0 : aResult.AppendLiteral("\\\"");
373 0 : break;
374 :
375 : default:
376 0 : aResult.Append(*p);
377 0 : break;
378 : }
379 : }
380 :
381 0 : aResult.Append('"');
382 : }
383 :
384 : namespace mozilla {
385 : struct PrefsSizes
386 : {
387 0 : PrefsSizes()
388 0 : : mHashTable(0)
389 : , mPrefValues(0)
390 : , mStringValues(0)
391 : , mCacheData(0)
392 : , mRootBranches(0)
393 : , mPrefNameArena(0)
394 : , mCallbacksObjects(0)
395 : , mCallbacksDomains(0)
396 0 : , mMisc(0)
397 : {
398 0 : }
399 :
400 : size_t mHashTable;
401 : size_t mPrefValues;
402 : size_t mStringValues;
403 : size_t mCacheData;
404 : size_t mRootBranches;
405 : size_t mPrefNameArena;
406 : size_t mCallbacksObjects;
407 : size_t mCallbacksDomains;
408 : size_t mMisc;
409 : };
410 : }
411 :
412 0 : static ArenaAllocator<8192, 1> gPrefNameArena;
413 :
414 : class Pref
415 : {
416 : public:
417 0 : explicit Pref(const char* aName)
418 0 : : mName(ArenaStrdup(aName, gPrefNameArena))
419 : , mType(static_cast<uint32_t>(PrefType::None))
420 : , mIsSticky(false)
421 : , mIsLocked(false)
422 : , mHasDefaultValue(false)
423 : , mHasUserValue(false)
424 : , mHasChangedSinceInit(false)
425 : , mDefaultValue()
426 0 : , mUserValue()
427 : {
428 0 : }
429 :
430 0 : ~Pref()
431 0 : {
432 : // There's no need to free mName because it's allocated in memory owned by
433 : // gPrefNameArena.
434 :
435 0 : mDefaultValue.Clear(Type());
436 0 : mUserValue.Clear(Type());
437 0 : }
438 :
439 : const char* Name() { return mName; }
440 :
441 : // Types.
442 :
443 0 : PrefType Type() const { return static_cast<PrefType>(mType); }
444 0 : void SetType(PrefType aType) { mType = static_cast<uint32_t>(aType); }
445 :
446 0 : bool IsType(PrefType aType) const { return Type() == aType; }
447 0 : bool IsTypeNone() const { return IsType(PrefType::None); }
448 0 : bool IsTypeString() const { return IsType(PrefType::String); }
449 0 : bool IsTypeInt() const { return IsType(PrefType::Int); }
450 0 : bool IsTypeBool() const { return IsType(PrefType::Bool); }
451 :
452 : // Other properties.
453 :
454 0 : bool IsLocked() const { return mIsLocked; }
455 : void SetIsLocked(bool aValue)
456 : {
457 0 : mIsLocked = aValue;
458 0 : mHasChangedSinceInit = true;
459 : }
460 :
461 0 : bool HasDefaultValue() const { return mHasDefaultValue; }
462 0 : bool HasUserValue() const { return mHasUserValue; }
463 :
464 : // When a content process is created we could tell it about every pref. But
465 : // the content process also initializes prefs from file, so we save a lot of
466 : // IPC if we only tell it about prefs that have changed since initialization.
467 : //
468 : // Specifically, we send a pref if any of the following conditions are met.
469 : //
470 : // - If the pref has changed in any way (default value, user value, or other
471 : // attribute, such as whether it is locked) since being initialized from
472 : // file.
473 : //
474 : // - If the pref has a user value. (User values are more complicated than
475 : // default values, because they can be loaded from file after
476 : // initialization with Preferences::ReadUserPrefsFromFile(), so we are
477 : // conservative with them.)
478 : //
479 : // In other words, prefs that only have a default value and haven't changed
480 : // need not be sent. One could do better with effort, but it's ok to be
481 : // conservative and this still greatly reduces the number of prefs sent.
482 : //
483 : // Note: This function is only useful in the parent process.
484 0 : bool MustSendToContentProcesses() const
485 : {
486 0 : MOZ_ASSERT(XRE_IsParentProcess());
487 0 : return mHasUserValue || mHasChangedSinceInit;
488 : }
489 :
490 : // Other operations.
491 :
492 0 : bool MatchEntry(const char* aPrefName)
493 : {
494 0 : if (!mName || !aPrefName) {
495 : return false;
496 : }
497 :
498 0 : return strcmp(mName, aPrefName) == 0;
499 : }
500 :
501 0 : nsresult GetBoolValue(PrefValueKind aKind, bool* aResult)
502 : {
503 0 : if (!IsTypeBool()) {
504 : return NS_ERROR_UNEXPECTED;
505 : }
506 :
507 0 : if (aKind == PrefValueKind::Default || IsLocked() || !mHasUserValue) {
508 : // Do we have a default?
509 0 : if (!mHasDefaultValue) {
510 : return NS_ERROR_UNEXPECTED;
511 : }
512 0 : *aResult = mDefaultValue.mBoolVal;
513 : } else {
514 0 : *aResult = mUserValue.mBoolVal;
515 : }
516 :
517 : return NS_OK;
518 : }
519 :
520 0 : nsresult GetIntValue(PrefValueKind aKind, int32_t* aResult)
521 : {
522 0 : if (!IsTypeInt()) {
523 : return NS_ERROR_UNEXPECTED;
524 : }
525 :
526 0 : if (aKind == PrefValueKind::Default || IsLocked() || !mHasUserValue) {
527 : // Do we have a default?
528 0 : if (!mHasDefaultValue) {
529 : return NS_ERROR_UNEXPECTED;
530 : }
531 0 : *aResult = mDefaultValue.mIntVal;
532 : } else {
533 0 : *aResult = mUserValue.mIntVal;
534 : }
535 :
536 : return NS_OK;
537 : }
538 :
539 0 : nsresult GetCStringValue(PrefValueKind aKind, nsACString& aResult)
540 : {
541 0 : if (!IsTypeString()) {
542 : return NS_ERROR_UNEXPECTED;
543 : }
544 :
545 0 : if (aKind == PrefValueKind::Default || IsLocked() || !mHasUserValue) {
546 : // Do we have a default?
547 0 : if (!mHasDefaultValue) {
548 : return NS_ERROR_UNEXPECTED;
549 : }
550 0 : MOZ_ASSERT(mDefaultValue.mStringVal);
551 0 : aResult = mDefaultValue.mStringVal;
552 : } else {
553 0 : MOZ_ASSERT(mUserValue.mStringVal);
554 0 : aResult = mUserValue.mStringVal;
555 : }
556 :
557 : return NS_OK;
558 : }
559 :
560 0 : void ToDomPref(dom::Pref* aDomPref)
561 : {
562 0 : MOZ_ASSERT(XRE_IsParentProcess());
563 :
564 0 : aDomPref->name() = mName;
565 :
566 0 : aDomPref->isLocked() = mIsLocked;
567 :
568 0 : if (mHasDefaultValue) {
569 0 : aDomPref->defaultValue() = dom::PrefValue();
570 0 : mDefaultValue.ToDomPrefValue(Type(),
571 0 : &aDomPref->defaultValue().get_PrefValue());
572 : } else {
573 0 : aDomPref->defaultValue() = null_t();
574 : }
575 :
576 0 : if (mHasUserValue) {
577 0 : aDomPref->userValue() = dom::PrefValue();
578 0 : mUserValue.ToDomPrefValue(Type(), &aDomPref->userValue().get_PrefValue());
579 : } else {
580 0 : aDomPref->userValue() = null_t();
581 : }
582 :
583 0 : MOZ_ASSERT(aDomPref->defaultValue().type() ==
584 : dom::MaybePrefValue::Tnull_t ||
585 : aDomPref->userValue().type() == dom::MaybePrefValue::Tnull_t ||
586 : (aDomPref->defaultValue().get_PrefValue().type() ==
587 : aDomPref->userValue().get_PrefValue().type()));
588 0 : }
589 :
590 0 : void FromDomPref(const dom::Pref& aDomPref, bool* aValueChanged)
591 : {
592 0 : MOZ_ASSERT(!XRE_IsParentProcess());
593 0 : MOZ_ASSERT(strcmp(mName, aDomPref.name().get()) == 0);
594 :
595 0 : mIsLocked = aDomPref.isLocked();
596 :
597 0 : const dom::MaybePrefValue& defaultValue = aDomPref.defaultValue();
598 0 : bool defaultValueChanged = false;
599 0 : if (defaultValue.type() == dom::MaybePrefValue::TPrefValue) {
600 : PrefValue value;
601 0 : PrefType type = value.FromDomPrefValue(defaultValue.get_PrefValue());
602 0 : if (!ValueMatches(PrefValueKind::Default, type, value)) {
603 : // Type() is PrefType::None if it's a newly added pref. This is ok.
604 0 : mDefaultValue.Replace(mHasDefaultValue, Type(), type, value);
605 0 : SetType(type);
606 0 : mHasDefaultValue = true;
607 0 : defaultValueChanged = true;
608 : }
609 : }
610 : // Note: we never clear a default value.
611 :
612 0 : const dom::MaybePrefValue& userValue = aDomPref.userValue();
613 0 : bool userValueChanged = false;
614 0 : if (userValue.type() == dom::MaybePrefValue::TPrefValue) {
615 : PrefValue value;
616 0 : PrefType type = value.FromDomPrefValue(userValue.get_PrefValue());
617 0 : if (!ValueMatches(PrefValueKind::User, type, value)) {
618 : // Type() is PrefType::None if it's a newly added pref. This is ok.
619 0 : mUserValue.Replace(mHasUserValue, Type(), type, value);
620 0 : SetType(type);
621 0 : mHasUserValue = true;
622 0 : userValueChanged = true;
623 : }
624 0 : } else if (mHasUserValue) {
625 0 : ClearUserValue();
626 0 : userValueChanged = true;
627 : }
628 :
629 0 : mHasChangedSinceInit = true;
630 :
631 0 : if (userValueChanged || (defaultValueChanged && !mHasUserValue)) {
632 0 : *aValueChanged = true;
633 : }
634 0 : }
635 :
636 0 : bool HasAdvisablySizedValues()
637 : {
638 0 : MOZ_ASSERT(XRE_IsParentProcess());
639 :
640 0 : if (!IsTypeString()) {
641 : return true;
642 : }
643 :
644 : const char* stringVal;
645 0 : if (mHasDefaultValue) {
646 0 : stringVal = mDefaultValue.mStringVal;
647 0 : if (strlen(stringVal) > MAX_ADVISABLE_PREF_LENGTH) {
648 : return false;
649 : }
650 : }
651 :
652 0 : if (mHasUserValue) {
653 0 : stringVal = mUserValue.mStringVal;
654 0 : if (strlen(stringVal) > MAX_ADVISABLE_PREF_LENGTH) {
655 : return false;
656 : }
657 : }
658 :
659 0 : return true;
660 : }
661 :
662 : private:
663 0 : bool ValueMatches(PrefValueKind aKind, PrefType aType, PrefValue aValue)
664 : {
665 0 : return IsType(aType) &&
666 : (aKind == PrefValueKind::Default
667 0 : ? mHasDefaultValue && mDefaultValue.Equals(aType, aValue)
668 0 : : mHasUserValue && mUserValue.Equals(aType, aValue));
669 : }
670 :
671 : public:
672 0 : void ClearUserValue()
673 : {
674 0 : mUserValue.Clear(Type());
675 0 : mHasUserValue = false;
676 0 : mHasChangedSinceInit = true;
677 0 : }
678 :
679 0 : nsresult SetDefaultValue(PrefType aType,
680 : PrefValue aValue,
681 : bool aIsSticky,
682 : bool aIsLocked,
683 : bool aFromInit,
684 : bool* aValueChanged)
685 : {
686 : // Types must always match when setting the default value.
687 0 : if (!IsType(aType)) {
688 : return NS_ERROR_UNEXPECTED;
689 : }
690 :
691 : // Should we set the default value? Only if the pref is not locked, and
692 : // doing so would change the default value.
693 0 : if (!IsLocked()) {
694 0 : if (aIsLocked) {
695 : SetIsLocked(true);
696 : }
697 0 : if (!ValueMatches(PrefValueKind::Default, aType, aValue)) {
698 0 : mDefaultValue.Replace(mHasDefaultValue, Type(), aType, aValue);
699 0 : mHasDefaultValue = true;
700 0 : if (!aFromInit) {
701 0 : mHasChangedSinceInit = true;
702 : }
703 0 : if (aIsSticky) {
704 0 : mIsSticky = true;
705 : }
706 0 : if (!mHasUserValue) {
707 0 : *aValueChanged = true;
708 : }
709 : // What if we change the default to be the same as the user value?
710 : // Should we clear the user value? Currently we don't.
711 : }
712 : }
713 : return NS_OK;
714 : }
715 :
716 0 : nsresult SetUserValue(PrefType aType,
717 : PrefValue aValue,
718 : bool aFromInit,
719 : bool* aValueChanged)
720 : {
721 : // If we have a default value, types must match when setting the user
722 : // value.
723 0 : if (mHasDefaultValue && !IsType(aType)) {
724 : return NS_ERROR_UNEXPECTED;
725 : }
726 :
727 : // Should we clear the user value, if present? Only if the new user value
728 : // matches the default value, and the pref isn't sticky, and we aren't
729 : // force-setting it during initialization.
730 0 : if (ValueMatches(PrefValueKind::Default, aType, aValue) && !mIsSticky &&
731 : !aFromInit) {
732 0 : if (mHasUserValue) {
733 0 : ClearUserValue();
734 0 : if (!IsLocked()) {
735 0 : *aValueChanged = true;
736 : }
737 : }
738 :
739 : // Otherwise, should we set the user value? Only if doing so would
740 : // change the user value.
741 0 : } else if (!ValueMatches(PrefValueKind::User, aType, aValue)) {
742 0 : mUserValue.Replace(mHasUserValue, Type(), aType, aValue);
743 0 : SetType(aType); // needed because we may have changed the type
744 0 : mHasUserValue = true;
745 0 : if (!aFromInit) {
746 0 : mHasChangedSinceInit = true;
747 : }
748 0 : if (!IsLocked()) {
749 0 : *aValueChanged = true;
750 : }
751 : }
752 : return NS_OK;
753 : }
754 :
755 : // Returns false if this pref doesn't have a user value worth saving.
756 0 : bool UserValueToStringForSaving(nsCString& aStr)
757 : {
758 : // Should we save the user value, if present? Only if it does not match the
759 : // default value, or it is sticky.
760 0 : if (mHasUserValue &&
761 0 : (!ValueMatches(PrefValueKind::Default, Type(), mUserValue) ||
762 0 : mIsSticky)) {
763 0 : if (IsTypeString()) {
764 0 : StrEscape(mUserValue.mStringVal, aStr);
765 :
766 0 : } else if (IsTypeInt()) {
767 0 : aStr.AppendInt(mUserValue.mIntVal);
768 :
769 0 : } else if (IsTypeBool()) {
770 0 : aStr = mUserValue.mBoolVal ? "true" : "false";
771 : }
772 : return true;
773 : }
774 :
775 : // Do not save default prefs that haven't changed.
776 : return false;
777 : }
778 :
779 : // Prefs are serialized in a manner that mirrors dom::Pref. The two should be
780 : // kept in sync. E.g. if something is added to one it should also be added to
781 : // the other. (It would be nice to be able to use the code generated from
782 : // IPDL for serializing dom::Pref here instead of writing by hand this
783 : // serialization/deserialization. Unfortunately, that generated code is
784 : // difficult to use directly, outside of the IPDL IPC code.)
785 : //
786 : // The grammar for the serialized prefs has the following form.
787 : //
788 : // <pref> = <type> <locked> ':' <name> ':' <value>? ':' <value>? '\n'
789 : // <type> = 'B' | 'I' | 'S'
790 : // <locked> = 'L' | '-'
791 : // <name> = <string-value>
792 : // <value> = <bool-value> | <int-value> | <string-value>
793 : // <bool-value> = 'T' | 'F'
794 : // <int-value> = an integer literal accepted by strtol()
795 : // <string-value> = <int-value> '/' <chars>
796 : // <chars> = any char sequence of length dictated by the preceding
797 : // <int-value>.
798 : //
799 : // No whitespace is tolerated between tokens. <type> must match the types of
800 : // the values.
801 : //
802 : // The serialization is text-based, rather than binary, for the following
803 : // reasons.
804 : //
805 : // - The size difference wouldn't be much different between text-based and
806 : // binary. Most of the space is for strings (pref names and string pref
807 : // values), which would be the same in both styles. And other differences
808 : // would be minimal, e.g. small integers are shorter in text but long
809 : // integers are longer in text.
810 : //
811 : // - Likewise, speed differences should be negligible.
812 : //
813 : // - It's much easier to debug a text-based serialization. E.g. you can
814 : // print it and inspect it easily in a debugger.
815 : //
816 : // Examples of unlocked boolean prefs:
817 : // - "B-:8/my.bool1:F:T\n"
818 : // - "B-:8/my.bool2:F:\n"
819 : // - "B-:8/my.bool3::T\n"
820 : //
821 : // Examples of locked integer prefs:
822 : // - "IL:7/my.int1:0:1\n"
823 : // - "IL:7/my.int2:123:\n"
824 : // - "IL:7/my.int3::-99\n"
825 : //
826 : // Examples of unlocked string prefs:
827 : // - "S-:10/my.string1:3/abc:4/wxyz\n"
828 : // - "S-:10/my.string2:5/1.234:\n"
829 : // - "S-:10/my.string3::7/string!\n"
830 :
831 0 : void SerializeAndAppend(nsCString& aStr)
832 : {
833 0 : switch (Type()) {
834 : case PrefType::Bool:
835 0 : aStr.Append('B');
836 0 : break;
837 :
838 : case PrefType::Int:
839 0 : aStr.Append('I');
840 0 : break;
841 :
842 : case PrefType::String: {
843 0 : aStr.Append('S');
844 0 : break;
845 : }
846 :
847 : case PrefType::None:
848 : default:
849 0 : MOZ_CRASH();
850 : }
851 :
852 0 : aStr.Append(mIsLocked ? 'L' : '-');
853 0 : aStr.Append(':');
854 :
855 0 : SerializeAndAppendString(mName, aStr);
856 0 : aStr.Append(':');
857 :
858 0 : if (mHasDefaultValue) {
859 0 : mDefaultValue.SerializeAndAppend(Type(), aStr);
860 : }
861 0 : aStr.Append(':');
862 :
863 0 : if (mHasUserValue) {
864 0 : mUserValue.SerializeAndAppend(Type(), aStr);
865 : }
866 0 : aStr.Append('\n');
867 0 : }
868 :
869 0 : static char* Deserialize(char* aStr, dom::Pref* aDomPref)
870 : {
871 0 : char* p = aStr;
872 :
873 : // The type.
874 : PrefType type;
875 0 : if (*p == 'B') {
876 : type = PrefType::Bool;
877 0 : } else if (*p == 'I') {
878 : type = PrefType::Int;
879 0 : } else if (*p == 'S') {
880 : type = PrefType::String;
881 : } else {
882 0 : NS_ERROR("bad pref type");
883 0 : type = PrefType::None;
884 : }
885 0 : p++; // move past the type char
886 :
887 : // Locked?
888 : bool isLocked;
889 0 : if (*p == 'L') {
890 0 : isLocked = true;
891 0 : } else if (*p == '-') {
892 0 : isLocked = false;
893 : } else {
894 0 : NS_ERROR("bad pref locked status");
895 0 : isLocked = false;
896 : }
897 0 : p++; // move past the isLocked char
898 :
899 0 : MOZ_ASSERT(*p == ':');
900 0 : p++; // move past the ':'
901 :
902 : // The pref name.
903 0 : nsCString name;
904 0 : p = DeserializeString(p, name);
905 :
906 0 : MOZ_ASSERT(*p == ':');
907 0 : p++; // move past the ':' preceding the default value
908 :
909 0 : dom::MaybePrefValue maybeDefaultValue;
910 0 : if (*p != ':') {
911 0 : dom::PrefValue defaultValue;
912 0 : p = PrefValue::Deserialize(type, p, &maybeDefaultValue);
913 : }
914 :
915 0 : MOZ_ASSERT(*p == ':');
916 0 : p++; // move past the ':' between the default and user values
917 :
918 0 : dom::MaybePrefValue maybeUserValue;
919 0 : if (*p != '\n') {
920 0 : dom::PrefValue userValue;
921 0 : p = PrefValue::Deserialize(type, p, &maybeUserValue);
922 : }
923 :
924 0 : MOZ_ASSERT(*p == '\n');
925 0 : p++; // move past the '\n' following the user value
926 :
927 0 : *aDomPref = dom::Pref(name, isLocked, maybeDefaultValue, maybeUserValue);
928 :
929 0 : return p;
930 : }
931 :
932 0 : void AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, PrefsSizes& aSizes)
933 : {
934 : // Note: mName is allocated in gPrefNameArena, measured elsewhere.
935 0 : aSizes.mPrefValues += aMallocSizeOf(this);
936 0 : if (IsTypeString()) {
937 0 : if (mHasDefaultValue) {
938 0 : aSizes.mStringValues += aMallocSizeOf(mDefaultValue.mStringVal);
939 : }
940 0 : if (mHasUserValue) {
941 0 : aSizes.mStringValues += aMallocSizeOf(mUserValue.mStringVal);
942 : }
943 : }
944 0 : }
945 :
946 : private:
947 : const char* mName; // allocated in gPrefNameArena
948 :
949 : uint32_t mType : 2;
950 : uint32_t mIsSticky : 1;
951 : uint32_t mIsLocked : 1;
952 : uint32_t mHasDefaultValue : 1;
953 : uint32_t mHasUserValue : 1;
954 : uint32_t mHasChangedSinceInit : 1;
955 :
956 : PrefValue mDefaultValue;
957 : PrefValue mUserValue;
958 : };
959 :
960 : class PrefEntry : public PLDHashEntryHdr
961 : {
962 : public:
963 : #ifdef DEBUG
964 : // This field is before mPref to minimize sizeof(PrefEntry) on 64-bit.
965 : uint32_t mAccessCount;
966 : #endif
967 : Pref* mPref; // Note: this is never null in a live entry.
968 :
969 0 : static bool MatchEntry(const PLDHashEntryHdr* aEntry, const void* aKey)
970 : {
971 0 : auto entry = static_cast<const PrefEntry*>(aEntry);
972 0 : auto prefName = static_cast<const char*>(aKey);
973 :
974 0 : return entry->mPref->MatchEntry(prefName);
975 : }
976 :
977 0 : static void InitEntry(PLDHashEntryHdr* aEntry, const void* aKey)
978 : {
979 0 : auto entry = static_cast<PrefEntry*>(aEntry);
980 0 : auto prefName = static_cast<const char*>(aKey);
981 :
982 : #ifdef DEBUG
983 0 : entry->mAccessCount = 0;
984 : #endif
985 0 : entry->mPref = new Pref(prefName);
986 0 : }
987 :
988 0 : static void ClearEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
989 : {
990 0 : auto entry = static_cast<PrefEntry*>(aEntry);
991 :
992 0 : delete entry->mPref;
993 0 : entry->mPref = nullptr;
994 0 : }
995 : };
996 :
997 0 : class CallbackNode
998 : {
999 : public:
1000 : CallbackNode(const char* aDomain,
1001 : PrefChangedFunc aFunc,
1002 : void* aData,
1003 : Preferences::MatchKind aMatchKind)
1004 0 : : mDomain(moz_xstrdup(aDomain))
1005 : , mFunc(aFunc)
1006 : , mData(aData)
1007 0 : , mNextAndMatchKind(aMatchKind)
1008 : {
1009 : }
1010 :
1011 : // mDomain is a UniquePtr<>, so any uses of Domain() should only be temporary
1012 : // borrows.
1013 0 : const char* Domain() const { return mDomain.get(); }
1014 :
1015 : PrefChangedFunc Func() const { return mFunc; }
1016 0 : void ClearFunc() { mFunc = nullptr; }
1017 :
1018 : void* Data() const { return mData; }
1019 :
1020 : Preferences::MatchKind MatchKind() const
1021 : {
1022 0 : return static_cast<Preferences::MatchKind>(mNextAndMatchKind &
1023 0 : kMatchKindMask);
1024 : }
1025 :
1026 : CallbackNode* Next() const
1027 : {
1028 0 : return reinterpret_cast<CallbackNode*>(mNextAndMatchKind & kNextMask);
1029 : }
1030 :
1031 0 : void SetNext(CallbackNode* aNext)
1032 : {
1033 0 : uintptr_t matchKind = mNextAndMatchKind & kMatchKindMask;
1034 0 : mNextAndMatchKind = reinterpret_cast<uintptr_t>(aNext);
1035 0 : MOZ_ASSERT((mNextAndMatchKind & kMatchKindMask) == 0);
1036 0 : mNextAndMatchKind |= matchKind;
1037 0 : }
1038 :
1039 0 : void AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, PrefsSizes& aSizes)
1040 : {
1041 0 : aSizes.mCallbacksObjects += aMallocSizeOf(this);
1042 0 : aSizes.mCallbacksDomains += aMallocSizeOf(mDomain.get());
1043 0 : }
1044 :
1045 : private:
1046 : static const uintptr_t kMatchKindMask = uintptr_t(0x1);
1047 : static const uintptr_t kNextMask = ~kMatchKindMask;
1048 :
1049 : UniqueFreePtr<const char> mDomain;
1050 :
1051 : // If someone attempts to remove the node from the callback list while
1052 : // NotifyCallbacks() is running, |func| is set to nullptr. Such nodes will
1053 : // be removed at the end of NotifyCallbacks().
1054 : PrefChangedFunc mFunc;
1055 : void* mData;
1056 :
1057 : // Conceptually this is two fields:
1058 : // - CallbackNode* mNext;
1059 : // - Preferences::MatchKind mMatchKind;
1060 : // They are combined into a tagged pointer to save memory.
1061 : uintptr_t mNextAndMatchKind;
1062 : };
1063 :
1064 : static PLDHashTable* gHashTable;
1065 :
1066 : // The callback list contains all the priority callbacks followed by the
1067 : // non-priority callbacks. gLastPriorityNode records where the first part ends.
1068 : static CallbackNode* gFirstCallback = nullptr;
1069 : static CallbackNode* gLastPriorityNode = nullptr;
1070 :
1071 : // These are only used during the call to NotifyCallbacks().
1072 : static bool gCallbacksInProgress = false;
1073 : static bool gShouldCleanupDeadNodes = false;
1074 :
1075 : static PLDHashTableOps pref_HashTableOps = {
1076 : PLDHashTable::HashStringKey, PrefEntry::MatchEntry,
1077 : PLDHashTable::MoveEntryStub, PrefEntry::ClearEntry,
1078 : PrefEntry::InitEntry,
1079 : };
1080 :
1081 : static Pref*
1082 : pref_HashTableLookup(const char* aPrefName);
1083 :
1084 : static void
1085 : NotifyCallbacks(const char* aPrefName);
1086 :
1087 : #define PREF_HASHTABLE_INITIAL_LENGTH 1024
1088 :
1089 : static PrefSaveData
1090 0 : pref_savePrefs()
1091 : {
1092 0 : MOZ_ASSERT(NS_IsMainThread());
1093 :
1094 0 : PrefSaveData savedPrefs(gHashTable->EntryCount());
1095 :
1096 0 : for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
1097 0 : Pref* pref = static_cast<PrefEntry*>(iter.Get())->mPref;
1098 :
1099 0 : nsAutoCString prefValueStr;
1100 0 : if (!pref->UserValueToStringForSaving(prefValueStr)) {
1101 0 : continue;
1102 : }
1103 :
1104 0 : nsAutoCString prefNameStr;
1105 0 : StrEscape(pref->Name(), prefNameStr);
1106 :
1107 : nsPrintfCString str(
1108 0 : "user_pref(%s, %s);", prefNameStr.get(), prefValueStr.get());
1109 :
1110 0 : savedPrefs.AppendElement(str);
1111 : }
1112 :
1113 0 : return savedPrefs;
1114 : }
1115 :
1116 : #ifdef DEBUG
1117 :
1118 : // Note that this never changes in the parent process, and is only read in
1119 : // content processes.
1120 : static bool gContentProcessPrefsAreInited = false;
1121 :
1122 : #endif // DEBUG
1123 :
1124 : static PrefEntry*
1125 0 : pref_HashTableLookupInner(const char* aPrefName)
1126 : {
1127 0 : MOZ_ASSERT(NS_IsMainThread() || mozilla::ServoStyleSet::IsInServoTraversal());
1128 :
1129 0 : MOZ_ASSERT_IF(!XRE_IsParentProcess(), gContentProcessPrefsAreInited);
1130 :
1131 0 : return static_cast<PrefEntry*>(gHashTable->Search(aPrefName));
1132 : }
1133 :
1134 : static Pref*
1135 0 : pref_HashTableLookup(const char* aPrefName)
1136 : {
1137 0 : PrefEntry* entry = pref_HashTableLookupInner(aPrefName);
1138 0 : if (!entry) {
1139 : return nullptr;
1140 : }
1141 :
1142 : #ifdef DEBUG
1143 0 : entry->mAccessCount += 1;
1144 : #endif
1145 :
1146 0 : return entry->mPref;
1147 : }
1148 :
1149 : static nsresult
1150 0 : pref_SetPref(const char* aPrefName,
1151 : PrefType aType,
1152 : PrefValueKind aKind,
1153 : PrefValue aValue,
1154 : bool aIsSticky,
1155 : bool aIsLocked,
1156 : bool aFromInit)
1157 : {
1158 0 : MOZ_ASSERT(NS_IsMainThread());
1159 :
1160 0 : if (!gHashTable) {
1161 : return NS_ERROR_OUT_OF_MEMORY;
1162 : }
1163 :
1164 0 : auto entry = static_cast<PrefEntry*>(gHashTable->Add(aPrefName, fallible));
1165 0 : if (!entry) {
1166 : return NS_ERROR_OUT_OF_MEMORY;
1167 : }
1168 :
1169 0 : Pref* pref = entry->mPref;
1170 0 : if (pref->IsTypeNone()) {
1171 : // New entry. Set the type.
1172 : pref->SetType(aType);
1173 : }
1174 :
1175 0 : bool valueChanged = false;
1176 : nsresult rv;
1177 0 : if (aKind == PrefValueKind::Default) {
1178 0 : rv = pref->SetDefaultValue(
1179 0 : aType, aValue, aIsSticky, aIsLocked, aFromInit, &valueChanged);
1180 : } else {
1181 0 : MOZ_ASSERT(!aIsLocked); // `locked` is disallowed in user pref files
1182 0 : rv = pref->SetUserValue(aType, aValue, aFromInit, &valueChanged);
1183 : }
1184 0 : if (NS_FAILED(rv)) {
1185 0 : NS_WARNING(
1186 : nsPrintfCString(
1187 : "Rejected attempt to change type of pref %s's %s value from %s to %s",
1188 : aPrefName,
1189 : (aKind == PrefValueKind::Default) ? "default" : "user",
1190 : PrefTypeToString(pref->Type()),
1191 : PrefTypeToString(aType))
1192 0 : .get());
1193 :
1194 0 : return rv;
1195 : }
1196 :
1197 0 : if (valueChanged) {
1198 0 : if (aKind == PrefValueKind::User && XRE_IsParentProcess()) {
1199 0 : Preferences::HandleDirty();
1200 : }
1201 0 : NotifyCallbacks(aPrefName);
1202 : }
1203 :
1204 : return NS_OK;
1205 : }
1206 :
1207 : // Removes |node| from callback list. Returns the node after the deleted one.
1208 : static CallbackNode*
1209 0 : pref_RemoveCallbackNode(CallbackNode* aNode, CallbackNode* aPrevNode)
1210 : {
1211 0 : MOZ_ASSERT(!aPrevNode || aPrevNode->Next() == aNode);
1212 0 : MOZ_ASSERT(aPrevNode || gFirstCallback == aNode);
1213 0 : MOZ_ASSERT(!gCallbacksInProgress);
1214 :
1215 0 : CallbackNode* next_node = aNode->Next();
1216 0 : if (aPrevNode) {
1217 0 : aPrevNode->SetNext(next_node);
1218 : } else {
1219 0 : gFirstCallback = next_node;
1220 : }
1221 0 : if (gLastPriorityNode == aNode) {
1222 0 : gLastPriorityNode = aPrevNode;
1223 : }
1224 0 : delete aNode;
1225 0 : return next_node;
1226 : }
1227 :
1228 : static void
1229 0 : NotifyCallbacks(const char* aPrefName)
1230 : {
1231 0 : bool reentered = gCallbacksInProgress;
1232 :
1233 : // Nodes must not be deleted while gCallbacksInProgress is true.
1234 : // Nodes that need to be deleted are marked for deletion by nulling
1235 : // out the |func| pointer. We release them at the end of this function
1236 : // if we haven't reentered.
1237 0 : gCallbacksInProgress = true;
1238 :
1239 0 : for (CallbackNode* node = gFirstCallback; node; node = node->Next()) {
1240 0 : if (node->Func()) {
1241 : bool matches =
1242 0 : node->MatchKind() == Preferences::ExactMatch
1243 0 : ? strcmp(node->Domain(), aPrefName) == 0
1244 0 : : strncmp(node->Domain(), aPrefName, strlen(node->Domain())) == 0;
1245 0 : if (matches) {
1246 0 : (node->Func())(aPrefName, node->Data());
1247 : }
1248 : }
1249 : }
1250 :
1251 0 : gCallbacksInProgress = reentered;
1252 :
1253 0 : if (gShouldCleanupDeadNodes && !gCallbacksInProgress) {
1254 0 : CallbackNode* prev_node = nullptr;
1255 0 : CallbackNode* node = gFirstCallback;
1256 :
1257 0 : while (node) {
1258 0 : if (!node->Func()) {
1259 0 : node = pref_RemoveCallbackNode(node, prev_node);
1260 : } else {
1261 0 : prev_node = node;
1262 0 : node = node->Next();
1263 : }
1264 : }
1265 0 : gShouldCleanupDeadNodes = false;
1266 : }
1267 0 : }
1268 :
1269 : //===========================================================================
1270 : // Prefs parsing
1271 : //===========================================================================
1272 :
1273 : struct TelemetryLoadData
1274 : {
1275 : uint32_t mFileLoadSize_B;
1276 : uint32_t mFileLoadNumPrefs;
1277 : uint32_t mFileLoadTime_us;
1278 : };
1279 :
1280 : static nsDataHashtable<nsCStringHashKey, TelemetryLoadData>* gTelemetryLoadData;
1281 :
1282 : extern "C" {
1283 :
1284 : // Keep this in sync with PrefFn in prefs_parser/src/lib.rs.
1285 : typedef void (*PrefsParserPrefFn)(const char* aPrefName,
1286 : PrefType aType,
1287 : PrefValueKind aKind,
1288 : PrefValue aValue,
1289 : bool aIsSticky,
1290 : bool aIsLocked);
1291 :
1292 : // Keep this in sync with ErrorFn in prefs_parser/src/lib.rs.
1293 : //
1294 : // `aMsg` is just a borrow of the string, and must be copied if it is used
1295 : // outside the lifetime of the prefs_parser_parse() call.
1296 : typedef void (*PrefsParserErrorFn)(const char* aMsg);
1297 :
1298 : // Keep this in sync with prefs_parser_parse() in prefs_parser/src/lib.rs.
1299 : bool
1300 : prefs_parser_parse(const char* aPath,
1301 : PrefValueKind aKind,
1302 : const char* aBuf,
1303 : size_t aLen,
1304 : PrefsParserPrefFn aPrefFn,
1305 : PrefsParserErrorFn aErrorFn);
1306 : }
1307 :
1308 : class Parser
1309 : {
1310 : public:
1311 : Parser() = default;
1312 : ~Parser() = default;
1313 :
1314 0 : bool Parse(const nsCString& aName,
1315 : PrefValueKind aKind,
1316 : const char* aPath,
1317 : const TimeStamp& aStartTime,
1318 : const nsCString& aBuf)
1319 : {
1320 0 : sNumPrefs = 0;
1321 0 : bool ok = prefs_parser_parse(
1322 0 : aPath, aKind, aBuf.get(), aBuf.Length(), HandlePref, HandleError);
1323 0 : if (!ok) {
1324 : return false;
1325 : }
1326 :
1327 0 : uint32_t loadTime_us = (TimeStamp::Now() - aStartTime).ToMicroseconds();
1328 :
1329 : // Most prefs files are read before telemetry initializes, so we have to
1330 : // save these measurements now and send them to telemetry later.
1331 0 : TelemetryLoadData loadData = { uint32_t(aBuf.Length()),
1332 : sNumPrefs,
1333 0 : loadTime_us };
1334 0 : gTelemetryLoadData->Put(aName, loadData);
1335 :
1336 : return true;
1337 : }
1338 :
1339 : private:
1340 0 : static void HandlePref(const char* aPrefName,
1341 : PrefType aType,
1342 : PrefValueKind aKind,
1343 : PrefValue aValue,
1344 : bool aIsSticky,
1345 : bool aIsLocked)
1346 : {
1347 0 : sNumPrefs++;
1348 0 : pref_SetPref(aPrefName,
1349 : aType,
1350 : aKind,
1351 : aValue,
1352 : aIsSticky,
1353 : aIsLocked,
1354 0 : /* fromInit */ true);
1355 0 : }
1356 :
1357 0 : static void HandleError(const char* aMsg)
1358 : {
1359 : nsresult rv;
1360 : nsCOMPtr<nsIConsoleService> console =
1361 0 : do_GetService("@mozilla.org/consoleservice;1", &rv);
1362 0 : if (NS_SUCCEEDED(rv)) {
1363 0 : console->LogStringMessage(NS_ConvertUTF8toUTF16(aMsg).get());
1364 : }
1365 : #ifdef DEBUG
1366 0 : NS_ERROR(aMsg);
1367 : #else
1368 : printf_stderr("%s\n", aMsg);
1369 : #endif
1370 0 : }
1371 :
1372 : // This is static so that HandlePref() can increment it easily. This is ok
1373 : // because prefs files are read one at a time.
1374 : static uint32_t sNumPrefs;
1375 : };
1376 :
1377 : uint32_t Parser::sNumPrefs = 0;
1378 :
1379 : // The following code is test code for the gtest.
1380 :
1381 : static void
1382 0 : TestParseErrorHandlePref(const char* aPrefName,
1383 : PrefType aType,
1384 : PrefValueKind aKind,
1385 : PrefValue aValue,
1386 : bool aIsSticky,
1387 : bool aIsLocked)
1388 : {
1389 0 : }
1390 :
1391 0 : static nsCString gTestParseErrorMsgs;
1392 :
1393 : static void
1394 0 : TestParseErrorHandleError(const char* aMsg)
1395 : {
1396 0 : gTestParseErrorMsgs.Append(aMsg);
1397 0 : gTestParseErrorMsgs.Append('\n');
1398 0 : }
1399 :
1400 : // Keep this in sync with the declaration in test/gtest/Parser.cpp.
1401 : void
1402 0 : TestParseError(PrefValueKind aKind, const char* aText, nsCString& aErrorMsg)
1403 : {
1404 0 : prefs_parser_parse("test",
1405 : aKind,
1406 : aText,
1407 : strlen(aText),
1408 : TestParseErrorHandlePref,
1409 0 : TestParseErrorHandleError);
1410 :
1411 : // Copy the error messages into the outparam, then clear them from
1412 : // gTestParseErrorMsgs.
1413 0 : aErrorMsg.Assign(gTestParseErrorMsgs);
1414 0 : gTestParseErrorMsgs.Truncate();
1415 0 : }
1416 :
1417 : void
1418 0 : SendTelemetryLoadData()
1419 : {
1420 0 : for (auto iter = gTelemetryLoadData->Iter(); !iter.Done(); iter.Next()) {
1421 0 : const nsCString& filename = PromiseFlatCString(iter.Key());
1422 0 : const TelemetryLoadData& data = iter.Data();
1423 : Telemetry::Accumulate(
1424 0 : Telemetry::PREFERENCES_FILE_LOAD_SIZE_B, filename, data.mFileLoadSize_B);
1425 : Telemetry::Accumulate(Telemetry::PREFERENCES_FILE_LOAD_NUM_PREFS,
1426 : filename,
1427 0 : data.mFileLoadNumPrefs);
1428 : Telemetry::Accumulate(Telemetry::PREFERENCES_FILE_LOAD_TIME_US,
1429 : filename,
1430 0 : data.mFileLoadTime_us);
1431 : }
1432 :
1433 0 : gTelemetryLoadData->Clear();
1434 0 : }
1435 :
1436 : //===========================================================================
1437 : // nsPrefBranch et al.
1438 : //===========================================================================
1439 :
1440 : namespace mozilla {
1441 : class PreferenceServiceReporter;
1442 : } // namespace mozilla
1443 :
1444 : class PrefCallback : public PLDHashEntryHdr
1445 : {
1446 : friend class mozilla::PreferenceServiceReporter;
1447 :
1448 : public:
1449 : typedef PrefCallback* KeyType;
1450 : typedef const PrefCallback* KeyTypePointer;
1451 :
1452 : static const PrefCallback* KeyToPointer(PrefCallback* aKey) { return aKey; }
1453 :
1454 0 : static PLDHashNumber HashKey(const PrefCallback* aKey)
1455 : {
1456 0 : uint32_t hash = mozilla::HashString(aKey->mDomain);
1457 0 : return mozilla::AddToHash(hash, aKey->mCanonical);
1458 : }
1459 :
1460 : public:
1461 : // Create a PrefCallback with a strong reference to its observer.
1462 0 : PrefCallback(const char* aDomain,
1463 : nsIObserver* aObserver,
1464 : nsPrefBranch* aBranch)
1465 0 : : mDomain(aDomain)
1466 : , mBranch(aBranch)
1467 : , mWeakRef(nullptr)
1468 0 : , mStrongRef(aObserver)
1469 : {
1470 0 : MOZ_COUNT_CTOR(PrefCallback);
1471 0 : nsCOMPtr<nsISupports> canonical = do_QueryInterface(aObserver);
1472 0 : mCanonical = canonical;
1473 0 : }
1474 :
1475 : // Create a PrefCallback with a weak reference to its observer.
1476 0 : PrefCallback(const char* aDomain,
1477 : nsISupportsWeakReference* aObserver,
1478 : nsPrefBranch* aBranch)
1479 0 : : mDomain(aDomain)
1480 : , mBranch(aBranch)
1481 0 : , mWeakRef(do_GetWeakReference(aObserver))
1482 0 : , mStrongRef(nullptr)
1483 : {
1484 0 : MOZ_COUNT_CTOR(PrefCallback);
1485 0 : nsCOMPtr<nsISupports> canonical = do_QueryInterface(aObserver);
1486 0 : mCanonical = canonical;
1487 0 : }
1488 :
1489 : // Copy constructor needs to be explicit or the linker complains.
1490 0 : explicit PrefCallback(const PrefCallback*& aCopy)
1491 0 : : mDomain(aCopy->mDomain)
1492 0 : , mBranch(aCopy->mBranch)
1493 0 : , mWeakRef(aCopy->mWeakRef)
1494 0 : , mStrongRef(aCopy->mStrongRef)
1495 0 : , mCanonical(aCopy->mCanonical)
1496 : {
1497 0 : MOZ_COUNT_CTOR(PrefCallback);
1498 0 : }
1499 :
1500 0 : ~PrefCallback() { MOZ_COUNT_DTOR(PrefCallback); }
1501 :
1502 0 : bool KeyEquals(const PrefCallback* aKey) const
1503 : {
1504 : // We want to be able to look up a weakly-referencing PrefCallback after
1505 : // its observer has died so we can remove it from the table. Once the
1506 : // callback's observer dies, its canonical pointer is stale -- in
1507 : // particular, we may have allocated a new observer in the same spot in
1508 : // memory! So we can't just compare canonical pointers to determine whether
1509 : // aKey refers to the same observer as this.
1510 : //
1511 : // Our workaround is based on the way we use this hashtable: When we ask
1512 : // the hashtable to remove a PrefCallback whose weak reference has expired,
1513 : // we use as the key for removal the same object as was inserted into the
1514 : // hashtable. Thus we can say that if one of the keys' weak references has
1515 : // expired, the two keys are equal iff they're the same object.
1516 :
1517 0 : if (IsExpired() || aKey->IsExpired()) {
1518 0 : return this == aKey;
1519 : }
1520 :
1521 0 : if (mCanonical != aKey->mCanonical) {
1522 : return false;
1523 : }
1524 :
1525 0 : return mDomain.Equals(aKey->mDomain);
1526 : }
1527 :
1528 : PrefCallback* GetKey() const { return const_cast<PrefCallback*>(this); }
1529 :
1530 : // Get a reference to the callback's observer, or null if the observer was
1531 : // weakly referenced and has been destroyed.
1532 0 : already_AddRefed<nsIObserver> GetObserver() const
1533 : {
1534 0 : if (!IsWeak()) {
1535 0 : nsCOMPtr<nsIObserver> copy = mStrongRef;
1536 0 : return copy.forget();
1537 : }
1538 :
1539 0 : nsCOMPtr<nsIObserver> observer = do_QueryReferent(mWeakRef);
1540 0 : return observer.forget();
1541 : }
1542 :
1543 : const nsCString& GetDomain() const { return mDomain; }
1544 :
1545 : nsPrefBranch* GetPrefBranch() const { return mBranch; }
1546 :
1547 : // Has this callback's weak reference died?
1548 0 : bool IsExpired() const
1549 : {
1550 0 : if (!IsWeak())
1551 : return false;
1552 :
1553 0 : nsCOMPtr<nsIObserver> observer(do_QueryReferent(mWeakRef));
1554 0 : return !observer;
1555 : }
1556 :
1557 0 : size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
1558 : {
1559 0 : size_t n = aMallocSizeOf(this);
1560 0 : n += mDomain.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
1561 :
1562 : // All the other fields are non-owning pointers, so we don't measure them.
1563 :
1564 0 : return n;
1565 : }
1566 :
1567 : enum
1568 : {
1569 : ALLOW_MEMMOVE = true
1570 : };
1571 :
1572 : private:
1573 : nsCString mDomain;
1574 : nsPrefBranch* mBranch;
1575 :
1576 : // Exactly one of mWeakRef and mStrongRef should be non-null.
1577 : nsWeakPtr mWeakRef;
1578 : nsCOMPtr<nsIObserver> mStrongRef;
1579 :
1580 : // We need a canonical nsISupports pointer, per bug 578392.
1581 : nsISupports* mCanonical;
1582 :
1583 0 : bool IsWeak() const { return !!mWeakRef; }
1584 : };
1585 :
1586 : class nsPrefBranch final
1587 : : public nsIPrefBranch
1588 : , public nsIObserver
1589 : , public nsSupportsWeakReference
1590 : {
1591 : friend class mozilla::PreferenceServiceReporter;
1592 :
1593 : public:
1594 : NS_DECL_ISUPPORTS
1595 : NS_DECL_NSIPREFBRANCH
1596 : NS_DECL_NSIOBSERVER
1597 :
1598 : nsPrefBranch(const char* aPrefRoot, PrefValueKind aKind);
1599 : nsPrefBranch() = delete;
1600 :
1601 : static void NotifyObserver(const char* aNewpref, void* aData);
1602 :
1603 : size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
1604 :
1605 : private:
1606 : // Helper class for either returning a raw cstring or nsCString.
1607 : typedef mozilla::Variant<const char*, const nsCString> PrefNameBase;
1608 0 : class PrefName : public PrefNameBase
1609 : {
1610 : public:
1611 : explicit PrefName(const char* aName)
1612 0 : : PrefNameBase(aName)
1613 : {
1614 : }
1615 : explicit PrefName(const nsCString& aName)
1616 0 : : PrefNameBase(aName)
1617 : {
1618 : }
1619 :
1620 : // Use default move constructors, disallow copy constructors.
1621 : PrefName(PrefName&& aOther) = default;
1622 : PrefName& operator=(PrefName&& aOther) = default;
1623 : PrefName(const PrefName&) = delete;
1624 : PrefName& operator=(const PrefName&) = delete;
1625 :
1626 : struct PtrMatcher
1627 : {
1628 : static const char* match(const char* aVal) { return aVal; }
1629 0 : static const char* match(const nsCString& aVal) { return aVal.get(); }
1630 : };
1631 :
1632 : struct LenMatcher
1633 : {
1634 0 : static size_t match(const char* aVal) { return strlen(aVal); }
1635 0 : static size_t match(const nsCString& aVal) { return aVal.Length(); }
1636 : };
1637 :
1638 : const char* get() const
1639 : {
1640 : static PtrMatcher m;
1641 0 : return match(m);
1642 : }
1643 :
1644 : size_t Length() const
1645 : {
1646 : static LenMatcher m;
1647 0 : return match(m);
1648 : }
1649 : };
1650 :
1651 : virtual ~nsPrefBranch();
1652 :
1653 0 : int32_t GetRootLength() const { return mPrefRoot.Length(); }
1654 :
1655 : nsresult GetDefaultFromPropertiesFile(const char* aPrefName,
1656 : nsAString& aReturn);
1657 :
1658 : // As SetCharPref, but without any check on the length of |aValue|.
1659 : nsresult SetCharPrefNoLengthCheck(const char* aPrefName,
1660 : const nsACString& aValue);
1661 :
1662 : // Reject strings that are more than 1Mb, warn if strings are more than 16kb.
1663 : nsresult CheckSanityOfStringLength(const char* aPrefName,
1664 : const nsAString& aValue);
1665 : nsresult CheckSanityOfStringLength(const char* aPrefName,
1666 : const nsACString& aValue);
1667 : nsresult CheckSanityOfStringLength(const char* aPrefName,
1668 : const uint32_t aLength);
1669 :
1670 : void RemoveExpiredCallback(PrefCallback* aCallback);
1671 :
1672 : PrefName GetPrefName(const char* aPrefName) const;
1673 :
1674 : void FreeObserverList(void);
1675 :
1676 : const nsCString mPrefRoot;
1677 : PrefValueKind mKind;
1678 :
1679 : bool mFreeingObserverList;
1680 : nsClassHashtable<PrefCallback, PrefCallback> mObservers;
1681 : };
1682 :
1683 0 : class nsPrefLocalizedString final : public nsIPrefLocalizedString
1684 : {
1685 : public:
1686 : nsPrefLocalizedString();
1687 :
1688 : NS_DECL_ISUPPORTS
1689 0 : NS_FORWARD_NSISUPPORTSPRIMITIVE(mUnicodeString->)
1690 0 : NS_FORWARD_NSISUPPORTSSTRING(mUnicodeString->)
1691 :
1692 : nsresult Init();
1693 :
1694 : private:
1695 : virtual ~nsPrefLocalizedString();
1696 :
1697 : nsCOMPtr<nsISupportsString> mUnicodeString;
1698 : };
1699 :
1700 0 : class nsRelativeFilePref : public nsIRelativeFilePref
1701 : {
1702 : public:
1703 : NS_DECL_ISUPPORTS
1704 : NS_DECL_NSIRELATIVEFILEPREF
1705 :
1706 : nsRelativeFilePref();
1707 :
1708 : private:
1709 : virtual ~nsRelativeFilePref();
1710 :
1711 : nsCOMPtr<nsIFile> mFile;
1712 : nsCString mRelativeToKey;
1713 : };
1714 :
1715 : //----------------------------------------------------------------------------
1716 : // nsPrefBranch
1717 : //----------------------------------------------------------------------------
1718 :
1719 0 : nsPrefBranch::nsPrefBranch(const char* aPrefRoot, PrefValueKind aKind)
1720 : : mPrefRoot(aPrefRoot)
1721 : , mKind(aKind)
1722 : , mFreeingObserverList(false)
1723 0 : , mObservers()
1724 : {
1725 : nsCOMPtr<nsIObserverService> observerService =
1726 0 : mozilla::services::GetObserverService();
1727 0 : if (observerService) {
1728 0 : ++mRefCnt; // must be > 0 when we call this, or we'll get deleted!
1729 :
1730 : // Add weakly so we don't have to clean up at shutdown.
1731 0 : observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
1732 0 : --mRefCnt;
1733 : }
1734 0 : }
1735 :
1736 0 : nsPrefBranch::~nsPrefBranch()
1737 : {
1738 0 : FreeObserverList();
1739 :
1740 : nsCOMPtr<nsIObserverService> observerService =
1741 0 : mozilla::services::GetObserverService();
1742 0 : if (observerService) {
1743 0 : observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
1744 : }
1745 0 : }
1746 :
1747 0 : NS_IMPL_ISUPPORTS(nsPrefBranch,
1748 : nsIPrefBranch,
1749 : nsIObserver,
1750 : nsISupportsWeakReference)
1751 :
1752 : NS_IMETHODIMP
1753 0 : nsPrefBranch::GetRoot(nsACString& aRoot)
1754 : {
1755 0 : aRoot = mPrefRoot;
1756 0 : return NS_OK;
1757 : }
1758 :
1759 : NS_IMETHODIMP
1760 0 : nsPrefBranch::GetPrefType(const char* aPrefName, int32_t* aRetVal)
1761 : {
1762 0 : NS_ENSURE_ARG(aPrefName);
1763 :
1764 0 : const PrefName& prefName = GetPrefName(aPrefName);
1765 0 : *aRetVal = Preferences::GetType(prefName.get());
1766 0 : return NS_OK;
1767 : }
1768 :
1769 : NS_IMETHODIMP
1770 0 : nsPrefBranch::GetBoolPrefWithDefault(const char* aPrefName,
1771 : bool aDefaultValue,
1772 : uint8_t aArgc,
1773 : bool* aRetVal)
1774 : {
1775 0 : nsresult rv = GetBoolPref(aPrefName, aRetVal);
1776 0 : if (NS_FAILED(rv) && aArgc == 1) {
1777 0 : *aRetVal = aDefaultValue;
1778 0 : return NS_OK;
1779 : }
1780 :
1781 : return rv;
1782 : }
1783 :
1784 : NS_IMETHODIMP
1785 0 : nsPrefBranch::GetBoolPref(const char* aPrefName, bool* aRetVal)
1786 : {
1787 0 : NS_ENSURE_ARG(aPrefName);
1788 :
1789 0 : const PrefName& pref = GetPrefName(aPrefName);
1790 0 : return Preferences::GetBool(pref.get(), aRetVal, mKind);
1791 : }
1792 :
1793 : NS_IMETHODIMP
1794 0 : nsPrefBranch::SetBoolPref(const char* aPrefName, bool aValue)
1795 : {
1796 0 : NS_ENSURE_ARG(aPrefName);
1797 :
1798 0 : const PrefName& pref = GetPrefName(aPrefName);
1799 0 : return Preferences::SetBool(pref.get(), aValue, mKind);
1800 : }
1801 :
1802 : NS_IMETHODIMP
1803 0 : nsPrefBranch::GetFloatPrefWithDefault(const char* aPrefName,
1804 : float aDefaultValue,
1805 : uint8_t aArgc,
1806 : float* aRetVal)
1807 : {
1808 0 : nsresult rv = GetFloatPref(aPrefName, aRetVal);
1809 :
1810 0 : if (NS_FAILED(rv) && aArgc == 1) {
1811 0 : *aRetVal = aDefaultValue;
1812 0 : return NS_OK;
1813 : }
1814 :
1815 : return rv;
1816 : }
1817 :
1818 : NS_IMETHODIMP
1819 0 : nsPrefBranch::GetFloatPref(const char* aPrefName, float* aRetVal)
1820 : {
1821 0 : NS_ENSURE_ARG(aPrefName);
1822 :
1823 0 : nsAutoCString stringVal;
1824 0 : nsresult rv = GetCharPref(aPrefName, stringVal);
1825 0 : if (NS_SUCCEEDED(rv)) {
1826 0 : *aRetVal = stringVal.ToFloat(&rv);
1827 : }
1828 :
1829 0 : return rv;
1830 : }
1831 :
1832 : NS_IMETHODIMP
1833 0 : nsPrefBranch::GetCharPrefWithDefault(const char* aPrefName,
1834 : const nsACString& aDefaultValue,
1835 : uint8_t aArgc,
1836 : nsACString& aRetVal)
1837 : {
1838 0 : nsresult rv = GetCharPref(aPrefName, aRetVal);
1839 :
1840 0 : if (NS_FAILED(rv) && aArgc == 1) {
1841 0 : aRetVal = aDefaultValue;
1842 0 : return NS_OK;
1843 : }
1844 :
1845 : return rv;
1846 : }
1847 :
1848 : NS_IMETHODIMP
1849 0 : nsPrefBranch::GetCharPref(const char* aPrefName, nsACString& aRetVal)
1850 : {
1851 0 : NS_ENSURE_ARG(aPrefName);
1852 :
1853 0 : const PrefName& pref = GetPrefName(aPrefName);
1854 0 : return Preferences::GetCString(pref.get(), aRetVal, mKind);
1855 : }
1856 :
1857 : NS_IMETHODIMP
1858 0 : nsPrefBranch::SetCharPref(const char* aPrefName, const nsACString& aValue)
1859 : {
1860 0 : nsresult rv = CheckSanityOfStringLength(aPrefName, aValue);
1861 0 : if (NS_FAILED(rv)) {
1862 : return rv;
1863 : }
1864 0 : return SetCharPrefNoLengthCheck(aPrefName, aValue);
1865 : }
1866 :
1867 : nsresult
1868 0 : nsPrefBranch::SetCharPrefNoLengthCheck(const char* aPrefName,
1869 : const nsACString& aValue)
1870 : {
1871 0 : NS_ENSURE_ARG(aPrefName);
1872 :
1873 0 : const PrefName& pref = GetPrefName(aPrefName);
1874 0 : return Preferences::SetCString(pref.get(), aValue, mKind);
1875 : }
1876 :
1877 : NS_IMETHODIMP
1878 0 : nsPrefBranch::GetStringPref(const char* aPrefName,
1879 : const nsACString& aDefaultValue,
1880 : uint8_t aArgc,
1881 : nsACString& aRetVal)
1882 : {
1883 0 : nsCString utf8String;
1884 0 : nsresult rv = GetCharPref(aPrefName, utf8String);
1885 0 : if (NS_SUCCEEDED(rv)) {
1886 0 : aRetVal = utf8String;
1887 0 : return rv;
1888 : }
1889 :
1890 0 : if (aArgc == 1) {
1891 0 : aRetVal = aDefaultValue;
1892 0 : return NS_OK;
1893 : }
1894 :
1895 : return rv;
1896 : }
1897 :
1898 : NS_IMETHODIMP
1899 0 : nsPrefBranch::SetStringPref(const char* aPrefName, const nsACString& aValue)
1900 : {
1901 0 : nsresult rv = CheckSanityOfStringLength(aPrefName, aValue);
1902 0 : if (NS_FAILED(rv)) {
1903 : return rv;
1904 : }
1905 :
1906 0 : return SetCharPrefNoLengthCheck(aPrefName, aValue);
1907 : }
1908 :
1909 : NS_IMETHODIMP
1910 0 : nsPrefBranch::GetIntPrefWithDefault(const char* aPrefName,
1911 : int32_t aDefaultValue,
1912 : uint8_t aArgc,
1913 : int32_t* aRetVal)
1914 : {
1915 0 : nsresult rv = GetIntPref(aPrefName, aRetVal);
1916 :
1917 0 : if (NS_FAILED(rv) && aArgc == 1) {
1918 0 : *aRetVal = aDefaultValue;
1919 0 : return NS_OK;
1920 : }
1921 :
1922 : return rv;
1923 : }
1924 :
1925 : NS_IMETHODIMP
1926 0 : nsPrefBranch::GetIntPref(const char* aPrefName, int32_t* aRetVal)
1927 : {
1928 0 : NS_ENSURE_ARG(aPrefName);
1929 0 : const PrefName& pref = GetPrefName(aPrefName);
1930 0 : return Preferences::GetInt(pref.get(), aRetVal, mKind);
1931 : }
1932 :
1933 : NS_IMETHODIMP
1934 0 : nsPrefBranch::SetIntPref(const char* aPrefName, int32_t aValue)
1935 : {
1936 0 : NS_ENSURE_ARG(aPrefName);
1937 :
1938 0 : const PrefName& pref = GetPrefName(aPrefName);
1939 0 : return Preferences::SetInt(pref.get(), aValue, mKind);
1940 : }
1941 :
1942 : NS_IMETHODIMP
1943 0 : nsPrefBranch::GetComplexValue(const char* aPrefName,
1944 : const nsIID& aType,
1945 : void** aRetVal)
1946 : {
1947 0 : NS_ENSURE_ARG(aPrefName);
1948 :
1949 : nsresult rv;
1950 0 : nsAutoCString utf8String;
1951 :
1952 : // We have to do this one first because it's different to all the rest.
1953 0 : if (aType.Equals(NS_GET_IID(nsIPrefLocalizedString))) {
1954 : nsCOMPtr<nsIPrefLocalizedString> theString(
1955 0 : do_CreateInstance(NS_PREFLOCALIZEDSTRING_CONTRACTID, &rv));
1956 0 : if (NS_FAILED(rv)) {
1957 : return rv;
1958 : }
1959 :
1960 0 : const PrefName& pref = GetPrefName(aPrefName);
1961 0 : bool bNeedDefault = false;
1962 :
1963 0 : if (mKind == PrefValueKind::Default) {
1964 : bNeedDefault = true;
1965 : } else {
1966 : // if there is no user (or locked) value
1967 0 : if (!Preferences::HasUserValue(pref.get()) &&
1968 0 : !Preferences::IsLocked(pref.get())) {
1969 0 : bNeedDefault = true;
1970 : }
1971 : }
1972 :
1973 : // if we need to fetch the default value, do that instead, otherwise use the
1974 : // value we pulled in at the top of this function
1975 0 : if (bNeedDefault) {
1976 0 : nsAutoString utf16String;
1977 0 : rv = GetDefaultFromPropertiesFile(pref.get(), utf16String);
1978 0 : if (NS_SUCCEEDED(rv)) {
1979 0 : theString->SetData(utf16String);
1980 : }
1981 : } else {
1982 0 : rv = GetCharPref(aPrefName, utf8String);
1983 0 : if (NS_SUCCEEDED(rv)) {
1984 0 : theString->SetData(NS_ConvertUTF8toUTF16(utf8String));
1985 : }
1986 : }
1987 :
1988 0 : if (NS_SUCCEEDED(rv)) {
1989 0 : theString.forget(reinterpret_cast<nsIPrefLocalizedString**>(aRetVal));
1990 : }
1991 :
1992 0 : return rv;
1993 : }
1994 :
1995 : // if we can't get the pref, there's no point in being here
1996 0 : rv = GetCharPref(aPrefName, utf8String);
1997 0 : if (NS_FAILED(rv)) {
1998 : return rv;
1999 : }
2000 :
2001 0 : if (aType.Equals(NS_GET_IID(nsIFile))) {
2002 0 : ENSURE_PARENT_PROCESS("GetComplexValue(nsIFile)", aPrefName);
2003 :
2004 0 : nsCOMPtr<nsIFile> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
2005 :
2006 0 : if (NS_SUCCEEDED(rv)) {
2007 0 : rv = file->SetPersistentDescriptor(utf8String);
2008 0 : if (NS_SUCCEEDED(rv)) {
2009 0 : file.forget(reinterpret_cast<nsIFile**>(aRetVal));
2010 0 : return NS_OK;
2011 : }
2012 : }
2013 0 : return rv;
2014 : }
2015 :
2016 0 : if (aType.Equals(NS_GET_IID(nsIRelativeFilePref))) {
2017 0 : ENSURE_PARENT_PROCESS("GetComplexValue(nsIRelativeFilePref)", aPrefName);
2018 :
2019 0 : nsACString::const_iterator keyBegin, strEnd;
2020 0 : utf8String.BeginReading(keyBegin);
2021 0 : utf8String.EndReading(strEnd);
2022 :
2023 : // The pref has the format: [fromKey]a/b/c
2024 0 : if (*keyBegin++ != '[') {
2025 : return NS_ERROR_FAILURE;
2026 : }
2027 :
2028 0 : nsACString::const_iterator keyEnd(keyBegin);
2029 0 : if (!FindCharInReadable(']', keyEnd, strEnd)) {
2030 : return NS_ERROR_FAILURE;
2031 : }
2032 :
2033 0 : nsAutoCString key(Substring(keyBegin, keyEnd));
2034 :
2035 0 : nsCOMPtr<nsIFile> fromFile;
2036 : nsCOMPtr<nsIProperties> directoryService(
2037 0 : do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv));
2038 0 : if (NS_FAILED(rv)) {
2039 : return rv;
2040 : }
2041 :
2042 0 : rv = directoryService->Get(
2043 0 : key.get(), NS_GET_IID(nsIFile), getter_AddRefs(fromFile));
2044 0 : if (NS_FAILED(rv)) {
2045 : return rv;
2046 : }
2047 :
2048 0 : nsCOMPtr<nsIFile> theFile;
2049 0 : rv = NS_NewNativeLocalFile(EmptyCString(), true, getter_AddRefs(theFile));
2050 0 : if (NS_FAILED(rv)) {
2051 : return rv;
2052 : }
2053 :
2054 0 : rv = theFile->SetRelativeDescriptor(fromFile, Substring(++keyEnd, strEnd));
2055 0 : if (NS_FAILED(rv)) {
2056 : return rv;
2057 : }
2058 :
2059 0 : nsCOMPtr<nsIRelativeFilePref> relativePref;
2060 0 : rv = NS_NewRelativeFilePref(theFile, key, getter_AddRefs(relativePref));
2061 0 : if (NS_FAILED(rv)) {
2062 : return rv;
2063 : }
2064 :
2065 0 : relativePref.forget(reinterpret_cast<nsIRelativeFilePref**>(aRetVal));
2066 0 : return NS_OK;
2067 : }
2068 :
2069 0 : NS_WARNING("nsPrefBranch::GetComplexValue - Unsupported interface type");
2070 0 : return NS_NOINTERFACE;
2071 : }
2072 :
2073 : nsresult
2074 0 : nsPrefBranch::CheckSanityOfStringLength(const char* aPrefName,
2075 : const nsAString& aValue)
2076 : {
2077 0 : return CheckSanityOfStringLength(aPrefName, aValue.Length());
2078 : }
2079 :
2080 : nsresult
2081 0 : nsPrefBranch::CheckSanityOfStringLength(const char* aPrefName,
2082 : const nsACString& aValue)
2083 : {
2084 0 : return CheckSanityOfStringLength(aPrefName, aValue.Length());
2085 : }
2086 :
2087 : nsresult
2088 0 : nsPrefBranch::CheckSanityOfStringLength(const char* aPrefName,
2089 : const uint32_t aLength)
2090 : {
2091 0 : if (aLength > MAX_PREF_LENGTH) {
2092 : return NS_ERROR_ILLEGAL_VALUE;
2093 : }
2094 0 : if (aLength <= MAX_ADVISABLE_PREF_LENGTH) {
2095 : return NS_OK;
2096 : }
2097 :
2098 : nsresult rv;
2099 : nsCOMPtr<nsIConsoleService> console =
2100 0 : do_GetService("@mozilla.org/consoleservice;1", &rv);
2101 0 : if (NS_FAILED(rv)) {
2102 : return rv;
2103 : }
2104 :
2105 0 : nsAutoCString message(nsPrintfCString(
2106 : "Warning: attempting to write %d bytes to preference %s. This is bad "
2107 : "for general performance and memory usage. Such an amount of data "
2108 : "should rather be written to an external file. This preference will "
2109 : "not be sent to any content processes.",
2110 : aLength,
2111 0 : GetPrefName(aPrefName).get()));
2112 :
2113 0 : rv = console->LogStringMessage(NS_ConvertUTF8toUTF16(message).get());
2114 0 : if (NS_FAILED(rv)) {
2115 0 : return rv;
2116 : }
2117 : return NS_OK;
2118 : }
2119 :
2120 : NS_IMETHODIMP
2121 0 : nsPrefBranch::SetComplexValue(const char* aPrefName,
2122 : const nsIID& aType,
2123 : nsISupports* aValue)
2124 : {
2125 0 : ENSURE_PARENT_PROCESS("SetComplexValue", aPrefName);
2126 0 : NS_ENSURE_ARG(aPrefName);
2127 :
2128 0 : nsresult rv = NS_NOINTERFACE;
2129 :
2130 0 : if (aType.Equals(NS_GET_IID(nsIFile))) {
2131 0 : nsCOMPtr<nsIFile> file = do_QueryInterface(aValue);
2132 0 : if (!file) {
2133 : return NS_NOINTERFACE;
2134 : }
2135 :
2136 0 : nsAutoCString descriptorString;
2137 0 : rv = file->GetPersistentDescriptor(descriptorString);
2138 0 : if (NS_SUCCEEDED(rv)) {
2139 0 : rv = SetCharPrefNoLengthCheck(aPrefName, descriptorString);
2140 : }
2141 0 : return rv;
2142 : }
2143 :
2144 0 : if (aType.Equals(NS_GET_IID(nsIRelativeFilePref))) {
2145 0 : nsCOMPtr<nsIRelativeFilePref> relFilePref = do_QueryInterface(aValue);
2146 0 : if (!relFilePref) {
2147 : return NS_NOINTERFACE;
2148 : }
2149 :
2150 0 : nsCOMPtr<nsIFile> file;
2151 0 : relFilePref->GetFile(getter_AddRefs(file));
2152 0 : if (!file) {
2153 : return NS_NOINTERFACE;
2154 : }
2155 :
2156 0 : nsAutoCString relativeToKey;
2157 0 : (void)relFilePref->GetRelativeToKey(relativeToKey);
2158 :
2159 0 : nsCOMPtr<nsIFile> relativeToFile;
2160 : nsCOMPtr<nsIProperties> directoryService(
2161 0 : do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv));
2162 0 : if (NS_FAILED(rv)) {
2163 : return rv;
2164 : }
2165 :
2166 0 : rv = directoryService->Get(
2167 0 : relativeToKey.get(), NS_GET_IID(nsIFile), getter_AddRefs(relativeToFile));
2168 0 : if (NS_FAILED(rv)) {
2169 : return rv;
2170 : }
2171 :
2172 0 : nsAutoCString relDescriptor;
2173 0 : rv = file->GetRelativeDescriptor(relativeToFile, relDescriptor);
2174 0 : if (NS_FAILED(rv)) {
2175 : return rv;
2176 : }
2177 :
2178 0 : nsAutoCString descriptorString;
2179 0 : descriptorString.Append('[');
2180 0 : descriptorString.Append(relativeToKey);
2181 0 : descriptorString.Append(']');
2182 0 : descriptorString.Append(relDescriptor);
2183 0 : return SetCharPrefNoLengthCheck(aPrefName, descriptorString);
2184 : }
2185 :
2186 0 : if (aType.Equals(NS_GET_IID(nsIPrefLocalizedString))) {
2187 0 : nsCOMPtr<nsISupportsString> theString = do_QueryInterface(aValue);
2188 :
2189 0 : if (theString) {
2190 0 : nsString wideString;
2191 :
2192 0 : rv = theString->GetData(wideString);
2193 0 : if (NS_SUCCEEDED(rv)) {
2194 : // Check sanity of string length before any lengthy conversion
2195 0 : rv = CheckSanityOfStringLength(aPrefName, wideString);
2196 0 : if (NS_FAILED(rv)) {
2197 0 : return rv;
2198 : }
2199 0 : rv = SetCharPrefNoLengthCheck(aPrefName,
2200 0 : NS_ConvertUTF16toUTF8(wideString));
2201 : }
2202 : }
2203 0 : return rv;
2204 : }
2205 :
2206 0 : NS_WARNING("nsPrefBranch::SetComplexValue - Unsupported interface type");
2207 0 : return NS_NOINTERFACE;
2208 : }
2209 :
2210 : NS_IMETHODIMP
2211 0 : nsPrefBranch::ClearUserPref(const char* aPrefName)
2212 : {
2213 0 : NS_ENSURE_ARG(aPrefName);
2214 :
2215 0 : const PrefName& pref = GetPrefName(aPrefName);
2216 0 : return Preferences::ClearUser(pref.get());
2217 : }
2218 :
2219 : NS_IMETHODIMP
2220 0 : nsPrefBranch::PrefHasUserValue(const char* aPrefName, bool* aRetVal)
2221 : {
2222 0 : NS_ENSURE_ARG_POINTER(aRetVal);
2223 0 : NS_ENSURE_ARG(aPrefName);
2224 :
2225 0 : const PrefName& pref = GetPrefName(aPrefName);
2226 0 : *aRetVal = Preferences::HasUserValue(pref.get());
2227 0 : return NS_OK;
2228 : }
2229 :
2230 : NS_IMETHODIMP
2231 0 : nsPrefBranch::LockPref(const char* aPrefName)
2232 : {
2233 0 : NS_ENSURE_ARG(aPrefName);
2234 :
2235 0 : const PrefName& pref = GetPrefName(aPrefName);
2236 0 : return Preferences::Lock(pref.get());
2237 : }
2238 :
2239 : NS_IMETHODIMP
2240 0 : nsPrefBranch::PrefIsLocked(const char* aPrefName, bool* aRetVal)
2241 : {
2242 0 : NS_ENSURE_ARG_POINTER(aRetVal);
2243 0 : NS_ENSURE_ARG(aPrefName);
2244 :
2245 0 : const PrefName& pref = GetPrefName(aPrefName);
2246 0 : *aRetVal = Preferences::IsLocked(pref.get());
2247 0 : return NS_OK;
2248 : }
2249 :
2250 : NS_IMETHODIMP
2251 0 : nsPrefBranch::UnlockPref(const char* aPrefName)
2252 : {
2253 0 : NS_ENSURE_ARG(aPrefName);
2254 :
2255 0 : const PrefName& pref = GetPrefName(aPrefName);
2256 0 : return Preferences::Unlock(pref.get());
2257 : }
2258 :
2259 : NS_IMETHODIMP
2260 0 : nsPrefBranch::ResetBranch(const char* aStartingAt)
2261 : {
2262 0 : return NS_ERROR_NOT_IMPLEMENTED;
2263 : }
2264 :
2265 : NS_IMETHODIMP
2266 0 : nsPrefBranch::DeleteBranch(const char* aStartingAt)
2267 : {
2268 0 : ENSURE_PARENT_PROCESS("DeleteBranch", aStartingAt);
2269 0 : NS_ENSURE_ARG(aStartingAt);
2270 :
2271 0 : MOZ_ASSERT(NS_IsMainThread());
2272 :
2273 0 : if (!gHashTable) {
2274 : return NS_ERROR_NOT_INITIALIZED;
2275 : }
2276 :
2277 0 : const PrefName& pref = GetPrefName(aStartingAt);
2278 0 : nsAutoCString branchName(pref.get());
2279 :
2280 : // Add a trailing '.' if it doesn't already have one.
2281 0 : if (branchName.Length() > 1 &&
2282 0 : !StringEndsWith(branchName, NS_LITERAL_CSTRING("."))) {
2283 : branchName += '.';
2284 : }
2285 :
2286 : const nsACString& branchNameNoDot =
2287 0 : Substring(branchName, 0, branchName.Length() - 1);
2288 :
2289 0 : for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
2290 0 : Pref* pref = static_cast<PrefEntry*>(iter.Get())->mPref;
2291 :
2292 : // The first disjunct matches branches: e.g. a branch name "foo.bar."
2293 : // matches a name "foo.bar.baz" (but it won't match "foo.barrel.baz").
2294 : // The second disjunct matches leaf nodes: e.g. a branch name "foo.bar."
2295 : // matches a name "foo.bar" (by ignoring the trailing '.').
2296 0 : nsDependentCString name(pref->Name());
2297 0 : if (StringBeginsWith(name, branchName) || name.Equals(branchNameNoDot)) {
2298 0 : iter.Remove();
2299 : }
2300 : }
2301 :
2302 0 : Preferences::HandleDirty();
2303 0 : return NS_OK;
2304 : }
2305 :
2306 : NS_IMETHODIMP
2307 0 : nsPrefBranch::GetChildList(const char* aStartingAt,
2308 : uint32_t* aCount,
2309 : char*** aChildArray)
2310 : {
2311 : char** outArray;
2312 : int32_t numPrefs;
2313 : int32_t dwIndex;
2314 0 : AutoTArray<nsCString, 32> prefArray;
2315 :
2316 0 : NS_ENSURE_ARG(aStartingAt);
2317 0 : NS_ENSURE_ARG_POINTER(aCount);
2318 0 : NS_ENSURE_ARG_POINTER(aChildArray);
2319 :
2320 0 : MOZ_ASSERT(NS_IsMainThread());
2321 :
2322 0 : *aChildArray = nullptr;
2323 0 : *aCount = 0;
2324 :
2325 : // This will contain a list of all the pref name strings. Allocated on the
2326 : // stack for speed.
2327 :
2328 0 : const PrefName& parent = GetPrefName(aStartingAt);
2329 0 : size_t parentLen = parent.Length();
2330 0 : for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
2331 0 : Pref* pref = static_cast<PrefEntry*>(iter.Get())->mPref;
2332 0 : if (strncmp(pref->Name(), parent.get(), parentLen) == 0) {
2333 0 : prefArray.AppendElement(pref->Name());
2334 : }
2335 : }
2336 :
2337 : // Now that we've built up the list, run the callback on all the matching
2338 : // elements.
2339 0 : numPrefs = prefArray.Length();
2340 :
2341 0 : if (numPrefs) {
2342 0 : outArray = (char**)moz_xmalloc(numPrefs * sizeof(char*));
2343 :
2344 0 : for (dwIndex = 0; dwIndex < numPrefs; ++dwIndex) {
2345 : // we need to lop off mPrefRoot in case the user is planning to pass this
2346 : // back to us because if they do we are going to add mPrefRoot again.
2347 0 : const nsCString& element = prefArray[dwIndex];
2348 0 : outArray[dwIndex] =
2349 0 : (char*)nsMemory::Clone(element.get() + mPrefRoot.Length(),
2350 0 : element.Length() - mPrefRoot.Length() + 1);
2351 :
2352 0 : if (!outArray[dwIndex]) {
2353 : // We ran out of memory... this is annoying.
2354 0 : NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(dwIndex, outArray);
2355 0 : return NS_ERROR_OUT_OF_MEMORY;
2356 : }
2357 : }
2358 0 : *aChildArray = outArray;
2359 : }
2360 0 : *aCount = numPrefs;
2361 :
2362 0 : return NS_OK;
2363 : }
2364 :
2365 : NS_IMETHODIMP
2366 0 : nsPrefBranch::AddObserver(const char* aDomain,
2367 : nsIObserver* aObserver,
2368 : bool aHoldWeak)
2369 : {
2370 : PrefCallback* pCallback;
2371 :
2372 0 : NS_ENSURE_ARG(aDomain);
2373 0 : NS_ENSURE_ARG(aObserver);
2374 :
2375 : // Hold a weak reference to the observer if so requested.
2376 0 : if (aHoldWeak) {
2377 : nsCOMPtr<nsISupportsWeakReference> weakRefFactory =
2378 0 : do_QueryInterface(aObserver);
2379 0 : if (!weakRefFactory) {
2380 : // The caller didn't give us a object that supports weak reference...
2381 : // tell them.
2382 0 : return NS_ERROR_INVALID_ARG;
2383 : }
2384 :
2385 : // Construct a PrefCallback with a weak reference to the observer.
2386 0 : pCallback = new PrefCallback(aDomain, weakRefFactory, this);
2387 :
2388 : } else {
2389 : // Construct a PrefCallback with a strong reference to the observer.
2390 0 : pCallback = new PrefCallback(aDomain, aObserver, this);
2391 : }
2392 :
2393 0 : auto p = mObservers.LookupForAdd(pCallback);
2394 0 : if (p) {
2395 0 : NS_WARNING("Ignoring duplicate observer.");
2396 0 : delete pCallback;
2397 : return NS_OK;
2398 : }
2399 :
2400 0 : p.OrInsert([&pCallback]() { return pCallback; });
2401 :
2402 : // We must pass a fully qualified preference name to the callback
2403 : // aDomain == nullptr is the only possible failure, and we trapped it with
2404 : // NS_ENSURE_ARG above.
2405 0 : const PrefName& pref = GetPrefName(aDomain);
2406 0 : Preferences::RegisterCallback(NotifyObserver,
2407 : pref.get(),
2408 : pCallback,
2409 : Preferences::PrefixMatch,
2410 0 : /* isPriority */ false);
2411 :
2412 0 : return NS_OK;
2413 : }
2414 :
2415 : NS_IMETHODIMP
2416 0 : nsPrefBranch::RemoveObserver(const char* aDomain, nsIObserver* aObserver)
2417 : {
2418 0 : NS_ENSURE_ARG(aDomain);
2419 0 : NS_ENSURE_ARG(aObserver);
2420 :
2421 0 : nsresult rv = NS_OK;
2422 :
2423 : // If we're in the middle of a call to FreeObserverList, don't process this
2424 : // RemoveObserver call -- the observer in question will be removed soon, if
2425 : // it hasn't been already.
2426 : //
2427 : // It's important that we don't touch mObservers in any way -- even a Get()
2428 : // which returns null might cause the hashtable to resize itself, which will
2429 : // break the iteration in FreeObserverList.
2430 0 : if (mFreeingObserverList) {
2431 : return NS_OK;
2432 : }
2433 :
2434 : // Remove the relevant PrefCallback from mObservers and get an owning pointer
2435 : // to it. Unregister the callback first, and then let the owning pointer go
2436 : // out of scope and destroy the callback.
2437 0 : PrefCallback key(aDomain, aObserver, this);
2438 0 : nsAutoPtr<PrefCallback> pCallback;
2439 0 : mObservers.Remove(&key, &pCallback);
2440 0 : if (pCallback) {
2441 : // aDomain == nullptr is the only possible failure, trapped above.
2442 0 : const PrefName& pref = GetPrefName(aDomain);
2443 0 : rv = Preferences::UnregisterCallback(
2444 0 : NotifyObserver, pref.get(), pCallback, Preferences::PrefixMatch);
2445 : }
2446 :
2447 : return rv;
2448 : }
2449 :
2450 : NS_IMETHODIMP
2451 0 : nsPrefBranch::Observe(nsISupports* aSubject,
2452 : const char* aTopic,
2453 : const char16_t* aData)
2454 : {
2455 : // Watch for xpcom shutdown and free our observers to eliminate any cyclic
2456 : // references.
2457 0 : if (!nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
2458 0 : FreeObserverList();
2459 : }
2460 0 : return NS_OK;
2461 : }
2462 :
2463 : /* static */ void
2464 0 : nsPrefBranch::NotifyObserver(const char* aNewPref, void* aData)
2465 : {
2466 0 : PrefCallback* pCallback = (PrefCallback*)aData;
2467 :
2468 0 : nsCOMPtr<nsIObserver> observer = pCallback->GetObserver();
2469 0 : if (!observer) {
2470 : // The observer has expired. Let's remove this callback.
2471 0 : pCallback->GetPrefBranch()->RemoveExpiredCallback(pCallback);
2472 0 : return;
2473 : }
2474 :
2475 : // Remove any root this string may contain so as to not confuse the observer
2476 : // by passing them something other than what they passed us as a topic.
2477 0 : uint32_t len = pCallback->GetPrefBranch()->GetRootLength();
2478 0 : nsAutoCString suffix(aNewPref + len);
2479 :
2480 0 : observer->Observe(static_cast<nsIPrefBranch*>(pCallback->GetPrefBranch()),
2481 : NS_PREFBRANCH_PREFCHANGE_TOPIC_ID,
2482 0 : NS_ConvertASCIItoUTF16(suffix).get());
2483 : }
2484 :
2485 : size_t
2486 0 : nsPrefBranch::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
2487 : {
2488 0 : size_t n = aMallocSizeOf(this);
2489 :
2490 0 : n += mPrefRoot.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
2491 :
2492 0 : n += mObservers.ShallowSizeOfExcludingThis(aMallocSizeOf);
2493 0 : for (auto iter = mObservers.ConstIter(); !iter.Done(); iter.Next()) {
2494 0 : const PrefCallback* data = iter.UserData();
2495 0 : n += data->SizeOfIncludingThis(aMallocSizeOf);
2496 : }
2497 :
2498 0 : return n;
2499 : }
2500 :
2501 : void
2502 0 : nsPrefBranch::FreeObserverList()
2503 : {
2504 : // We need to prevent anyone from modifying mObservers while we're iterating
2505 : // over it. In particular, some clients will call RemoveObserver() when
2506 : // they're removed and destructed via the iterator; we set
2507 : // mFreeingObserverList to keep those calls from touching mObservers.
2508 0 : mFreeingObserverList = true;
2509 0 : for (auto iter = mObservers.Iter(); !iter.Done(); iter.Next()) {
2510 0 : nsAutoPtr<PrefCallback>& callback = iter.Data();
2511 0 : nsPrefBranch* prefBranch = callback->GetPrefBranch();
2512 0 : const PrefName& pref = prefBranch->GetPrefName(callback->GetDomain().get());
2513 0 : Preferences::UnregisterCallback(nsPrefBranch::NotifyObserver,
2514 : pref.get(),
2515 : callback,
2516 0 : Preferences::PrefixMatch);
2517 0 : iter.Remove();
2518 : }
2519 0 : mFreeingObserverList = false;
2520 0 : }
2521 :
2522 : void
2523 0 : nsPrefBranch::RemoveExpiredCallback(PrefCallback* aCallback)
2524 : {
2525 0 : MOZ_ASSERT(aCallback->IsExpired());
2526 0 : mObservers.Remove(aCallback);
2527 0 : }
2528 :
2529 : nsresult
2530 0 : nsPrefBranch::GetDefaultFromPropertiesFile(const char* aPrefName,
2531 : nsAString& aReturn)
2532 : {
2533 : // The default value contains a URL to a .properties file.
2534 :
2535 0 : nsAutoCString propertyFileURL;
2536 : nsresult rv =
2537 0 : Preferences::GetCString(aPrefName, propertyFileURL, PrefValueKind::Default);
2538 0 : if (NS_FAILED(rv)) {
2539 : return rv;
2540 : }
2541 :
2542 : nsCOMPtr<nsIStringBundleService> bundleService =
2543 0 : mozilla::services::GetStringBundleService();
2544 0 : if (!bundleService) {
2545 : return NS_ERROR_FAILURE;
2546 : }
2547 :
2548 0 : nsCOMPtr<nsIStringBundle> bundle;
2549 : rv =
2550 0 : bundleService->CreateBundle(propertyFileURL.get(), getter_AddRefs(bundle));
2551 0 : if (NS_FAILED(rv)) {
2552 : return rv;
2553 : }
2554 :
2555 0 : return bundle->GetStringFromName(aPrefName, aReturn);
2556 : }
2557 :
2558 : nsPrefBranch::PrefName
2559 0 : nsPrefBranch::GetPrefName(const char* aPrefName) const
2560 : {
2561 0 : MOZ_ASSERT(aPrefName);
2562 :
2563 : // For speed, avoid strcpy if we can.
2564 0 : if (mPrefRoot.IsEmpty()) {
2565 0 : return PrefName(aPrefName);
2566 : }
2567 :
2568 0 : return PrefName(mPrefRoot + nsDependentCString(aPrefName));
2569 : }
2570 :
2571 : //----------------------------------------------------------------------------
2572 : // nsPrefLocalizedString
2573 : //----------------------------------------------------------------------------
2574 :
2575 : nsPrefLocalizedString::nsPrefLocalizedString() = default;
2576 :
2577 : nsPrefLocalizedString::~nsPrefLocalizedString() = default;
2578 :
2579 0 : NS_IMPL_ISUPPORTS(nsPrefLocalizedString,
2580 : nsIPrefLocalizedString,
2581 : nsISupportsString)
2582 :
2583 : nsresult
2584 0 : nsPrefLocalizedString::Init()
2585 : {
2586 : nsresult rv;
2587 0 : mUnicodeString = do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
2588 :
2589 0 : return rv;
2590 : }
2591 :
2592 : //----------------------------------------------------------------------------
2593 : // nsRelativeFilePref
2594 : //----------------------------------------------------------------------------
2595 :
2596 0 : NS_IMPL_ISUPPORTS(nsRelativeFilePref, nsIRelativeFilePref)
2597 :
2598 : nsRelativeFilePref::nsRelativeFilePref() = default;
2599 :
2600 : nsRelativeFilePref::~nsRelativeFilePref() = default;
2601 :
2602 : NS_IMETHODIMP
2603 0 : nsRelativeFilePref::GetFile(nsIFile** aFile)
2604 : {
2605 0 : NS_ENSURE_ARG_POINTER(aFile);
2606 0 : *aFile = mFile;
2607 0 : NS_IF_ADDREF(*aFile);
2608 0 : return NS_OK;
2609 : }
2610 :
2611 : NS_IMETHODIMP
2612 0 : nsRelativeFilePref::SetFile(nsIFile* aFile)
2613 : {
2614 0 : mFile = aFile;
2615 0 : return NS_OK;
2616 : }
2617 :
2618 : NS_IMETHODIMP
2619 0 : nsRelativeFilePref::GetRelativeToKey(nsACString& aRelativeToKey)
2620 : {
2621 0 : aRelativeToKey.Assign(mRelativeToKey);
2622 0 : return NS_OK;
2623 : }
2624 :
2625 : NS_IMETHODIMP
2626 0 : nsRelativeFilePref::SetRelativeToKey(const nsACString& aRelativeToKey)
2627 : {
2628 0 : mRelativeToKey.Assign(aRelativeToKey);
2629 0 : return NS_OK;
2630 : }
2631 :
2632 : //===========================================================================
2633 : // class Preferences and related things
2634 : //===========================================================================
2635 :
2636 : namespace mozilla {
2637 :
2638 : #define INITIAL_PREF_FILES 10
2639 :
2640 : static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
2641 :
2642 : void
2643 0 : Preferences::HandleDirty()
2644 : {
2645 0 : MOZ_ASSERT(XRE_IsParentProcess());
2646 :
2647 0 : if (!gHashTable || !sPreferences) {
2648 : return;
2649 : }
2650 :
2651 0 : if (sPreferences->mProfileShutdown) {
2652 0 : NS_WARNING("Setting user pref after profile shutdown.");
2653 0 : return;
2654 : }
2655 :
2656 0 : if (!sPreferences->mDirty) {
2657 0 : sPreferences->mDirty = true;
2658 :
2659 0 : if (sPreferences->mCurrentFile && sPreferences->AllowOffMainThreadSave() &&
2660 0 : !sPreferences->mSavePending) {
2661 0 : sPreferences->mSavePending = true;
2662 : static const int PREF_DELAY_MS = 500;
2663 0 : NS_DelayedDispatchToCurrentThread(
2664 0 : mozilla::NewRunnableMethod("Preferences::SavePrefFileAsynchronous",
2665 0 : sPreferences.get(),
2666 : &Preferences::SavePrefFileAsynchronous),
2667 0 : PREF_DELAY_MS);
2668 : }
2669 : }
2670 : }
2671 :
2672 : static nsresult
2673 : openPrefFile(nsIFile* aFile, PrefValueKind aKind);
2674 :
2675 : // clang-format off
2676 : static const char kPrefFileHeader[] =
2677 : "// Mozilla User Preferences"
2678 : NS_LINEBREAK
2679 : NS_LINEBREAK
2680 : "// DO NOT EDIT THIS FILE."
2681 : NS_LINEBREAK
2682 : "//"
2683 : NS_LINEBREAK
2684 : "// If you make changes to this file while the application is running,"
2685 : NS_LINEBREAK
2686 : "// the changes will be overwritten when the application exits."
2687 : NS_LINEBREAK
2688 : "//"
2689 : NS_LINEBREAK
2690 : "// To change a preference value, you can either:"
2691 : NS_LINEBREAK
2692 : "// - modify it via the UI (e.g. via about:config in the browser); or"
2693 : NS_LINEBREAK
2694 : "// - set it within a user.js file in your profile."
2695 : NS_LINEBREAK
2696 : NS_LINEBREAK;
2697 : // clang-format on
2698 :
2699 : // Note: if sShutdown is true, sPreferences will be nullptr.
2700 0 : StaticRefPtr<Preferences> Preferences::sPreferences;
2701 : bool Preferences::sShutdown = false;
2702 :
2703 : // This globally enables or disables OMT pref writing, both sync and async.
2704 : static int32_t sAllowOMTPrefWrite = -1;
2705 :
2706 : // Write the preference data to a file.
2707 : class PreferencesWriter final
2708 : {
2709 : public:
2710 : PreferencesWriter() = default;
2711 :
2712 0 : static nsresult Write(nsIFile* aFile, PrefSaveData& aPrefs)
2713 : {
2714 0 : nsCOMPtr<nsIOutputStream> outStreamSink;
2715 0 : nsCOMPtr<nsIOutputStream> outStream;
2716 : uint32_t writeAmount;
2717 : nsresult rv;
2718 :
2719 : // Execute a "safe" save by saving through a tempfile.
2720 0 : rv = NS_NewSafeLocalFileOutputStream(
2721 0 : getter_AddRefs(outStreamSink), aFile, -1, 0600);
2722 0 : if (NS_FAILED(rv)) {
2723 : return rv;
2724 : }
2725 :
2726 0 : rv = NS_NewBufferedOutputStream(
2727 0 : getter_AddRefs(outStream), outStreamSink.forget(), 4096);
2728 0 : if (NS_FAILED(rv)) {
2729 : return rv;
2730 : }
2731 :
2732 : struct CharComparator
2733 : {
2734 : bool LessThan(const nsCString& aA, const nsCString& aB) const
2735 : {
2736 0 : return aA < aB;
2737 : }
2738 :
2739 : bool Equals(const nsCString& aA, const nsCString& aB) const
2740 : {
2741 0 : return aA == aB;
2742 : }
2743 : };
2744 :
2745 : // Sort the preferences to make a readable file on disk.
2746 0 : aPrefs.Sort(CharComparator());
2747 :
2748 : // Write out the file header.
2749 0 : outStream->Write(
2750 0 : kPrefFileHeader, sizeof(kPrefFileHeader) - 1, &writeAmount);
2751 :
2752 0 : for (nsCString& pref : aPrefs) {
2753 0 : outStream->Write(pref.get(), pref.Length(), &writeAmount);
2754 0 : outStream->Write(NS_LINEBREAK, NS_LINEBREAK_LEN, &writeAmount);
2755 : }
2756 :
2757 : // Tell the safe output stream to overwrite the real prefs file.
2758 : // (It'll abort if there were any errors during writing.)
2759 0 : nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(outStream);
2760 0 : MOZ_ASSERT(safeStream, "expected a safe output stream!");
2761 0 : if (safeStream) {
2762 0 : rv = safeStream->Finish();
2763 : }
2764 :
2765 : #ifdef DEBUG
2766 0 : if (NS_FAILED(rv)) {
2767 0 : NS_WARNING("failed to save prefs file! possible data loss");
2768 : }
2769 : #endif
2770 :
2771 : return rv;
2772 : }
2773 :
2774 0 : static void Flush()
2775 : {
2776 : // This can be further optimized; instead of waiting for all of the writer
2777 : // thread to be available, we just have to wait for all the pending writes
2778 : // to be done.
2779 0 : if (!sPendingWriteData.compareExchange(nullptr, nullptr)) {
2780 0 : nsresult rv = NS_OK;
2781 : nsCOMPtr<nsIEventTarget> target =
2782 0 : do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
2783 0 : if (NS_SUCCEEDED(rv)) {
2784 0 : target->Dispatch(NS_NewRunnableFunction("Preferences_dummy", [] {}),
2785 0 : nsIEventTarget::DISPATCH_SYNC);
2786 : }
2787 : }
2788 0 : }
2789 :
2790 : // This is the data that all of the runnables (see below) will attempt
2791 : // to write. It will always have the most up to date version, or be
2792 : // null, if the up to date information has already been written out.
2793 : static Atomic<PrefSaveData*> sPendingWriteData;
2794 : };
2795 :
2796 : Atomic<PrefSaveData*> PreferencesWriter::sPendingWriteData(nullptr);
2797 :
2798 0 : class PWRunnable : public Runnable
2799 : {
2800 : public:
2801 0 : explicit PWRunnable(nsIFile* aFile)
2802 0 : : Runnable("PWRunnable")
2803 0 : , mFile(aFile)
2804 : {
2805 0 : }
2806 :
2807 0 : NS_IMETHOD Run() override
2808 : {
2809 : // If we get a nullptr on the exchange, it means that somebody
2810 : // else has already processed the request, and we can just return.
2811 : mozilla::UniquePtr<PrefSaveData> prefs(
2812 0 : PreferencesWriter::sPendingWriteData.exchange(nullptr));
2813 0 : nsresult rv = NS_OK;
2814 0 : if (prefs) {
2815 0 : rv = PreferencesWriter::Write(mFile, *prefs);
2816 :
2817 : // Make a copy of these so we can have them in runnable lambda.
2818 : // nsIFile is only there so that we would never release the
2819 : // ref counted pointer off main thread.
2820 0 : nsresult rvCopy = rv;
2821 0 : nsCOMPtr<nsIFile> fileCopy(mFile);
2822 0 : SystemGroup::Dispatch(
2823 : TaskCategory::Other,
2824 0 : NS_NewRunnableFunction("Preferences::WriterRunnable",
2825 0 : [fileCopy, rvCopy] {
2826 0 : MOZ_RELEASE_ASSERT(NS_IsMainThread());
2827 0 : if (NS_FAILED(rvCopy)) {
2828 0 : Preferences::HandleDirty();
2829 : }
2830 0 : }));
2831 : }
2832 0 : return rv;
2833 : }
2834 :
2835 : protected:
2836 : nsCOMPtr<nsIFile> mFile;
2837 : };
2838 :
2839 : struct CacheData
2840 : {
2841 : void* mCacheLocation;
2842 : union {
2843 : bool mDefaultValueBool;
2844 : int32_t mDefaultValueInt;
2845 : uint32_t mDefaultValueUint;
2846 : float mDefaultValueFloat;
2847 : };
2848 : };
2849 :
2850 : // gCacheDataDesc holds information about prefs startup. It's being used for
2851 : // diagnosing prefs startup problems in bug 1276488.
2852 : static const char* gCacheDataDesc = "untouched";
2853 :
2854 : // gCacheData holds the CacheData objects used for VarCache prefs. It owns
2855 : // those objects, and also is used to detect if multiple VarCaches get tied to
2856 : // a single global variable.
2857 : static nsTArray<nsAutoPtr<CacheData>>* gCacheData = nullptr;
2858 :
2859 : #ifdef DEBUG
2860 : static bool
2861 0 : HaveExistingCacheFor(void* aPtr)
2862 : {
2863 0 : MOZ_ASSERT(NS_IsMainThread());
2864 0 : if (gCacheData) {
2865 0 : for (size_t i = 0, count = gCacheData->Length(); i < count; ++i) {
2866 0 : if ((*gCacheData)[i]->mCacheLocation == aPtr) {
2867 : return true;
2868 : }
2869 : }
2870 : }
2871 : return false;
2872 : }
2873 : #endif
2874 :
2875 : static void
2876 0 : AssertNotAlreadyCached(const char* aPrefType, const char* aPref, void* aPtr)
2877 : {
2878 : #ifdef DEBUG
2879 0 : MOZ_ASSERT(aPtr);
2880 0 : if (HaveExistingCacheFor(aPtr)) {
2881 : fprintf_stderr(
2882 : stderr,
2883 : "Attempt to add a %s pref cache for preference '%s' at address '%p'"
2884 : "was made. However, a pref was already cached at this address.\n",
2885 : aPrefType,
2886 : aPref,
2887 0 : aPtr);
2888 0 : MOZ_ASSERT(false,
2889 : "Should not have an existing pref cache for this address");
2890 : }
2891 : #endif
2892 0 : }
2893 :
2894 : // Although this is a member of Preferences, it measures sPreferences and
2895 : // several other global structures.
2896 : /* static */ void
2897 0 : Preferences::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
2898 : PrefsSizes& aSizes)
2899 : {
2900 0 : if (!sPreferences) {
2901 : return;
2902 : }
2903 :
2904 0 : aSizes.mMisc += aMallocSizeOf(sPreferences.get());
2905 :
2906 0 : aSizes.mRootBranches +=
2907 0 : static_cast<nsPrefBranch*>(sPreferences->mRootBranch.get())
2908 0 : ->SizeOfIncludingThis(aMallocSizeOf) +
2909 0 : static_cast<nsPrefBranch*>(sPreferences->mDefaultRootBranch.get())
2910 0 : ->SizeOfIncludingThis(aMallocSizeOf);
2911 : }
2912 :
2913 0 : class PreferenceServiceReporter final : public nsIMemoryReporter
2914 : {
2915 0 : ~PreferenceServiceReporter() {}
2916 :
2917 : public:
2918 : NS_DECL_ISUPPORTS
2919 : NS_DECL_NSIMEMORYREPORTER
2920 :
2921 : protected:
2922 : static const uint32_t kSuspectReferentCount = 1000;
2923 : };
2924 :
2925 0 : NS_IMPL_ISUPPORTS(PreferenceServiceReporter, nsIMemoryReporter)
2926 :
2927 0 : MOZ_DEFINE_MALLOC_SIZE_OF(PreferenceServiceMallocSizeOf)
2928 :
2929 : NS_IMETHODIMP
2930 0 : PreferenceServiceReporter::CollectReports(
2931 : nsIHandleReportCallback* aHandleReport,
2932 : nsISupports* aData,
2933 : bool aAnonymize)
2934 : {
2935 0 : MOZ_ASSERT(NS_IsMainThread());
2936 :
2937 0 : MallocSizeOf mallocSizeOf = PreferenceServiceMallocSizeOf;
2938 0 : PrefsSizes sizes;
2939 :
2940 0 : Preferences::AddSizeOfIncludingThis(mallocSizeOf, sizes);
2941 :
2942 0 : if (gHashTable) {
2943 0 : sizes.mHashTable += gHashTable->ShallowSizeOfIncludingThis(mallocSizeOf);
2944 0 : for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
2945 0 : Pref* pref = static_cast<PrefEntry*>(iter.Get())->mPref;
2946 0 : pref->AddSizeOfIncludingThis(mallocSizeOf, sizes);
2947 : }
2948 : }
2949 :
2950 0 : if (gCacheData) {
2951 0 : sizes.mCacheData += gCacheData->ShallowSizeOfIncludingThis(mallocSizeOf);
2952 0 : for (uint32_t i = 0, count = gCacheData->Length(); i < count; ++i) {
2953 0 : sizes.mCacheData += mallocSizeOf((*gCacheData)[i]);
2954 : }
2955 : }
2956 :
2957 0 : sizes.mPrefNameArena += gPrefNameArena.SizeOfExcludingThis(mallocSizeOf);
2958 :
2959 0 : for (CallbackNode* node = gFirstCallback; node; node = node->Next()) {
2960 0 : node->AddSizeOfIncludingThis(mallocSizeOf, sizes);
2961 : }
2962 :
2963 0 : MOZ_COLLECT_REPORT("explicit/preferences/hash-table",
2964 : KIND_HEAP,
2965 : UNITS_BYTES,
2966 : sizes.mHashTable,
2967 0 : "Memory used by libpref's hash table.");
2968 :
2969 0 : MOZ_COLLECT_REPORT("explicit/preferences/pref-values",
2970 : KIND_HEAP,
2971 : UNITS_BYTES,
2972 : sizes.mPrefValues,
2973 0 : "Memory used by PrefValues hanging off the hash table.");
2974 :
2975 0 : MOZ_COLLECT_REPORT("explicit/preferences/string-values",
2976 : KIND_HEAP,
2977 : UNITS_BYTES,
2978 : sizes.mStringValues,
2979 0 : "Memory used by libpref's string pref values.");
2980 :
2981 0 : MOZ_COLLECT_REPORT("explicit/preferences/cache-data",
2982 : KIND_HEAP,
2983 : UNITS_BYTES,
2984 : sizes.mCacheData,
2985 0 : "Memory used by libpref's VarCaches.");
2986 :
2987 0 : MOZ_COLLECT_REPORT("explicit/preferences/root-branches",
2988 : KIND_HEAP,
2989 : UNITS_BYTES,
2990 : sizes.mRootBranches,
2991 0 : "Memory used by libpref's root branches.");
2992 :
2993 0 : MOZ_COLLECT_REPORT("explicit/preferences/pref-name-arena",
2994 : KIND_HEAP,
2995 : UNITS_BYTES,
2996 : sizes.mPrefNameArena,
2997 0 : "Memory used by libpref's arena for pref names.");
2998 :
2999 0 : MOZ_COLLECT_REPORT("explicit/preferences/callbacks/objects",
3000 : KIND_HEAP,
3001 : UNITS_BYTES,
3002 : sizes.mCallbacksObjects,
3003 0 : "Memory used by pref callback objects.");
3004 :
3005 0 : MOZ_COLLECT_REPORT("explicit/preferences/callbacks/domains",
3006 : KIND_HEAP,
3007 : UNITS_BYTES,
3008 : sizes.mCallbacksDomains,
3009 : "Memory used by pref callback domains (pref names and "
3010 0 : "prefixes).");
3011 :
3012 0 : MOZ_COLLECT_REPORT("explicit/preferences/misc",
3013 : KIND_HEAP,
3014 : UNITS_BYTES,
3015 : sizes.mMisc,
3016 0 : "Miscellaneous memory used by libpref.");
3017 :
3018 : nsPrefBranch* rootBranch =
3019 0 : static_cast<nsPrefBranch*>(Preferences::GetRootBranch());
3020 0 : if (!rootBranch) {
3021 : return NS_OK;
3022 : }
3023 :
3024 0 : size_t numStrong = 0;
3025 0 : size_t numWeakAlive = 0;
3026 0 : size_t numWeakDead = 0;
3027 0 : nsTArray<nsCString> suspectPreferences;
3028 : // Count of the number of referents for each preference.
3029 0 : nsDataHashtable<nsCStringHashKey, uint32_t> prefCounter;
3030 :
3031 0 : for (auto iter = rootBranch->mObservers.Iter(); !iter.Done(); iter.Next()) {
3032 0 : nsAutoPtr<PrefCallback>& callback = iter.Data();
3033 0 : nsPrefBranch* prefBranch = callback->GetPrefBranch();
3034 0 : const auto& pref = prefBranch->GetPrefName(callback->GetDomain().get());
3035 :
3036 0 : if (callback->IsWeak()) {
3037 0 : nsCOMPtr<nsIObserver> callbackRef = do_QueryReferent(callback->mWeakRef);
3038 0 : if (callbackRef) {
3039 0 : numWeakAlive++;
3040 : } else {
3041 0 : numWeakDead++;
3042 : }
3043 : } else {
3044 0 : numStrong++;
3045 : }
3046 :
3047 0 : nsDependentCString prefString(pref.get());
3048 0 : uint32_t oldCount = 0;
3049 0 : prefCounter.Get(prefString, &oldCount);
3050 0 : uint32_t currentCount = oldCount + 1;
3051 0 : prefCounter.Put(prefString, currentCount);
3052 :
3053 : // Keep track of preferences that have a suspiciously large number of
3054 : // referents (a symptom of a leak).
3055 0 : if (currentCount == kSuspectReferentCount) {
3056 0 : suspectPreferences.AppendElement(prefString);
3057 : }
3058 : }
3059 :
3060 0 : for (uint32_t i = 0; i < suspectPreferences.Length(); i++) {
3061 0 : nsCString& suspect = suspectPreferences[i];
3062 0 : uint32_t totalReferentCount = 0;
3063 0 : prefCounter.Get(suspect, &totalReferentCount);
3064 :
3065 : nsPrintfCString suspectPath("preference-service-suspect/"
3066 : "referent(pref=%s)",
3067 0 : suspect.get());
3068 :
3069 0 : aHandleReport->Callback(
3070 0 : /* process = */ EmptyCString(),
3071 : suspectPath,
3072 : KIND_OTHER,
3073 : UNITS_COUNT,
3074 : totalReferentCount,
3075 0 : NS_LITERAL_CSTRING(
3076 : "A preference with a suspiciously large number referents (symptom of a "
3077 : "leak)."),
3078 0 : aData);
3079 : }
3080 :
3081 0 : MOZ_COLLECT_REPORT(
3082 : "preference-service/referent/strong",
3083 : KIND_OTHER,
3084 : UNITS_COUNT,
3085 : numStrong,
3086 0 : "The number of strong referents held by the preference service.");
3087 :
3088 0 : MOZ_COLLECT_REPORT(
3089 : "preference-service/referent/weak/alive",
3090 : KIND_OTHER,
3091 : UNITS_COUNT,
3092 : numWeakAlive,
3093 : "The number of weak referents held by the preference service that are "
3094 0 : "still alive.");
3095 :
3096 0 : MOZ_COLLECT_REPORT(
3097 : "preference-service/referent/weak/dead",
3098 : KIND_OTHER,
3099 : UNITS_COUNT,
3100 : numWeakDead,
3101 : "The number of weak referents held by the preference service that are "
3102 0 : "dead.");
3103 :
3104 0 : return NS_OK;
3105 : }
3106 :
3107 : namespace {
3108 :
3109 0 : class AddPreferencesMemoryReporterRunnable : public Runnable
3110 : {
3111 : public:
3112 0 : AddPreferencesMemoryReporterRunnable()
3113 0 : : Runnable("AddPreferencesMemoryReporterRunnable")
3114 : {
3115 0 : }
3116 :
3117 0 : NS_IMETHOD Run() override
3118 : {
3119 0 : return RegisterStrongMemoryReporter(new PreferenceServiceReporter());
3120 : }
3121 : };
3122 :
3123 : } // namespace
3124 :
3125 : // A list of changed prefs sent from the parent via shared memory.
3126 : static InfallibleTArray<dom::Pref>* gChangedDomPrefs;
3127 :
3128 : static const char kTelemetryPref[] = "toolkit.telemetry.enabled";
3129 : static const char kChannelPref[] = "app.update.channel";
3130 :
3131 : #ifdef MOZ_WIDGET_ANDROID
3132 :
3133 : static Maybe<bool>
3134 : TelemetryPrefValue()
3135 : {
3136 : // Leave it unchanged if it's already set.
3137 : // XXX: how could it already be set?
3138 : if (Preferences::GetType(kTelemetryPref) != nsIPrefBranch::PREF_INVALID) {
3139 : return Nothing();
3140 : }
3141 :
3142 : // Determine the correct default for toolkit.telemetry.enabled. If this
3143 : // build has MOZ_TELEMETRY_ON_BY_DEFAULT *or* we're on the beta channel,
3144 : // telemetry is on by default, otherwise not. This is necessary so that
3145 : // beta users who are testing final release builds don't flipflop defaults.
3146 : #ifdef MOZ_TELEMETRY_ON_BY_DEFAULT
3147 : return Some(true);
3148 : #else
3149 : nsAutoCString channelPrefValue;
3150 : Unused << Preferences::GetCString(
3151 : kChannelPref, channelPrefValue, PrefValueKind::Default);
3152 : return Some(channelPrefValue.EqualsLiteral("beta"));
3153 : #endif
3154 : }
3155 :
3156 : /* static */ void
3157 : Preferences::SetupTelemetryPref()
3158 : {
3159 : MOZ_ASSERT(XRE_IsParentProcess());
3160 :
3161 : Maybe<bool> telemetryPrefValue = TelemetryPrefValue();
3162 : if (telemetryPrefValue.isSome()) {
3163 : Preferences::SetBool(
3164 : kTelemetryPref, *telemetryPrefValue, PrefValueKind::Default);
3165 : }
3166 : }
3167 :
3168 : #else // !MOZ_WIDGET_ANDROID
3169 :
3170 : static bool
3171 0 : TelemetryPrefValue()
3172 : {
3173 : // For platforms with Unified Telemetry (here meaning not-Android),
3174 : // toolkit.telemetry.enabled determines whether we send "extended" data.
3175 : // We only want extended data from pre-release channels due to size.
3176 :
3177 0 : NS_NAMED_LITERAL_CSTRING(channel, NS_STRINGIFY(MOZ_UPDATE_CHANNEL));
3178 :
3179 : // Easy cases: Nightly, Aurora, Beta.
3180 0 : if (channel.EqualsLiteral("nightly") || channel.EqualsLiteral("aurora") ||
3181 0 : channel.EqualsLiteral("beta")) {
3182 : return true;
3183 : }
3184 :
3185 : #ifndef MOZILLA_OFFICIAL
3186 : // Local developer builds: non-official builds on the "default" channel.
3187 : if (channel.EqualsLiteral("default")) {
3188 : return true;
3189 : }
3190 : #endif
3191 :
3192 : // Release Candidate builds: builds that think they are release builds, but
3193 : // are shipped to beta users.
3194 0 : if (channel.EqualsLiteral("release")) {
3195 0 : nsAutoCString channelPrefValue;
3196 0 : Unused << Preferences::GetCString(
3197 : kChannelPref, channelPrefValue, PrefValueKind::Default);
3198 0 : if (channelPrefValue.EqualsLiteral("beta")) {
3199 0 : return true;
3200 : }
3201 : }
3202 :
3203 : return false;
3204 : }
3205 :
3206 : /* static */ void
3207 0 : Preferences::SetupTelemetryPref()
3208 : {
3209 0 : MOZ_ASSERT(XRE_IsParentProcess());
3210 :
3211 0 : Preferences::SetBool(
3212 0 : kTelemetryPref, TelemetryPrefValue(), PrefValueKind::Default);
3213 0 : Preferences::Lock(kTelemetryPref);
3214 0 : }
3215 :
3216 : static void
3217 0 : CheckTelemetryPref()
3218 : {
3219 0 : MOZ_ASSERT(!XRE_IsParentProcess());
3220 :
3221 : // Make sure the children got passed the right telemetry pref details.
3222 0 : DebugOnly<bool> value;
3223 0 : MOZ_ASSERT(NS_SUCCEEDED(Preferences::GetBool(kTelemetryPref, &value)) &&
3224 : value == TelemetryPrefValue());
3225 0 : MOZ_ASSERT(Preferences::IsLocked(kTelemetryPref));
3226 0 : }
3227 :
3228 : #endif // MOZ_WIDGET_ANDROID
3229 :
3230 : /* static */ already_AddRefed<Preferences>
3231 0 : Preferences::GetInstanceForService()
3232 : {
3233 0 : if (sPreferences) {
3234 0 : return do_AddRef(sPreferences);
3235 : }
3236 :
3237 0 : if (sShutdown) {
3238 0 : gCacheDataDesc = "shutting down in GetInstanceForService()";
3239 : return nullptr;
3240 : }
3241 :
3242 0 : sPreferences = new Preferences();
3243 :
3244 0 : MOZ_ASSERT(!gHashTable);
3245 0 : gHashTable = new PLDHashTable(
3246 0 : &pref_HashTableOps, sizeof(PrefEntry), PREF_HASHTABLE_INITIAL_LENGTH);
3247 :
3248 0 : gTelemetryLoadData =
3249 0 : new nsDataHashtable<nsCStringHashKey, TelemetryLoadData>();
3250 :
3251 0 : gCacheData = new nsTArray<nsAutoPtr<CacheData>>();
3252 0 : gCacheDataDesc = "set by GetInstanceForService() (1)";
3253 :
3254 0 : Result<Ok, const char*> res = InitInitialObjects(/* isStartup */ true);
3255 0 : if (res.isErr()) {
3256 0 : sPreferences = nullptr;
3257 0 : gCacheDataDesc = res.unwrapErr();
3258 : return nullptr;
3259 : }
3260 :
3261 0 : if (!XRE_IsParentProcess()) {
3262 0 : MOZ_ASSERT(gChangedDomPrefs);
3263 0 : for (unsigned int i = 0; i < gChangedDomPrefs->Length(); i++) {
3264 0 : Preferences::SetPreference(gChangedDomPrefs->ElementAt(i));
3265 : }
3266 0 : delete gChangedDomPrefs;
3267 0 : gChangedDomPrefs = nullptr;
3268 :
3269 : #ifndef MOZ_WIDGET_ANDROID
3270 0 : CheckTelemetryPref();
3271 : #endif
3272 :
3273 : } else {
3274 : // Check if there is a deployment configuration file. If so, set up the
3275 : // pref config machinery, which will actually read the file.
3276 0 : nsAutoCString lockFileName;
3277 : nsresult rv = Preferences::GetCString(
3278 0 : "general.config.filename", lockFileName, PrefValueKind::User);
3279 0 : if (NS_SUCCEEDED(rv)) {
3280 : NS_CreateServicesFromCategory(
3281 : "pref-config-startup",
3282 0 : static_cast<nsISupports*>(static_cast<void*>(sPreferences)),
3283 0 : "pref-config-startup");
3284 : }
3285 :
3286 : nsCOMPtr<nsIObserverService> observerService =
3287 0 : mozilla::services::GetObserverService();
3288 0 : if (!observerService) {
3289 0 : sPreferences = nullptr;
3290 0 : gCacheDataDesc = "GetObserverService() failed (1)";
3291 0 : return nullptr;
3292 : }
3293 :
3294 0 : observerService->AddObserver(
3295 0 : sPreferences, "profile-before-change-telemetry", true);
3296 : rv =
3297 0 : observerService->AddObserver(sPreferences, "profile-before-change", true);
3298 :
3299 0 : observerService->AddObserver(
3300 0 : sPreferences, "suspend_process_notification", true);
3301 :
3302 0 : if (NS_FAILED(rv)) {
3303 0 : sPreferences = nullptr;
3304 0 : gCacheDataDesc = "AddObserver(\"profile-before-change\") failed";
3305 : return nullptr;
3306 : }
3307 : }
3308 :
3309 0 : gCacheDataDesc = "set by GetInstanceForService() (2)";
3310 :
3311 : // Preferences::GetInstanceForService() can be called from GetService(), and
3312 : // RegisterStrongMemoryReporter calls GetService(nsIMemoryReporter). To
3313 : // avoid a potential recursive GetService() call, we can't register the
3314 : // memory reporter here; instead, do it off a runnable.
3315 : RefPtr<AddPreferencesMemoryReporterRunnable> runnable =
3316 0 : new AddPreferencesMemoryReporterRunnable();
3317 0 : NS_DispatchToMainThread(runnable);
3318 :
3319 0 : return do_AddRef(sPreferences);
3320 : }
3321 :
3322 : /* static */ bool
3323 0 : Preferences::IsServiceAvailable()
3324 : {
3325 0 : return !!sPreferences;
3326 : }
3327 :
3328 : /* static */ bool
3329 0 : Preferences::InitStaticMembers()
3330 : {
3331 0 : MOZ_ASSERT(NS_IsMainThread() || mozilla::ServoStyleSet::IsInServoTraversal());
3332 :
3333 0 : if (MOZ_LIKELY(sPreferences)) {
3334 : return true;
3335 : }
3336 :
3337 0 : if (!sShutdown) {
3338 0 : MOZ_ASSERT(NS_IsMainThread());
3339 : nsCOMPtr<nsIPrefService> prefService =
3340 0 : do_GetService(NS_PREFSERVICE_CONTRACTID);
3341 : }
3342 :
3343 0 : return sPreferences != nullptr;
3344 : }
3345 :
3346 : /* static */ void
3347 0 : Preferences::Shutdown()
3348 : {
3349 0 : if (!sShutdown) {
3350 0 : sShutdown = true; // Don't create the singleton instance after here.
3351 : sPreferences = nullptr;
3352 : }
3353 0 : }
3354 :
3355 0 : Preferences::Preferences()
3356 0 : : mRootBranch(new nsPrefBranch("", PrefValueKind::User))
3357 0 : , mDefaultRootBranch(new nsPrefBranch("", PrefValueKind::Default))
3358 : {
3359 0 : }
3360 :
3361 0 : Preferences::~Preferences()
3362 : {
3363 0 : MOZ_ASSERT(!sPreferences);
3364 :
3365 0 : delete gCacheData;
3366 0 : gCacheData = nullptr;
3367 :
3368 0 : MOZ_ASSERT(!gCallbacksInProgress);
3369 :
3370 0 : CallbackNode* node = gFirstCallback;
3371 0 : while (node) {
3372 0 : CallbackNode* next_node = node->Next();
3373 0 : delete node;
3374 0 : node = next_node;
3375 : }
3376 0 : gLastPriorityNode = gFirstCallback = nullptr;
3377 :
3378 0 : delete gHashTable;
3379 0 : gHashTable = nullptr;
3380 :
3381 0 : delete gTelemetryLoadData;
3382 0 : gTelemetryLoadData = nullptr;
3383 :
3384 0 : gPrefNameArena.Clear();
3385 0 : }
3386 :
3387 0 : NS_IMPL_ISUPPORTS(Preferences,
3388 : nsIPrefService,
3389 : nsIObserver,
3390 : nsIPrefBranch,
3391 : nsISupportsWeakReference)
3392 :
3393 : /* static */ void
3394 0 : Preferences::SerializePreferences(nsCString& aStr)
3395 : {
3396 0 : MOZ_RELEASE_ASSERT(InitStaticMembers());
3397 :
3398 0 : aStr.Truncate();
3399 :
3400 0 : for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
3401 0 : Pref* pref = static_cast<PrefEntry*>(iter.Get())->mPref;
3402 0 : if (pref->MustSendToContentProcesses() && pref->HasAdvisablySizedValues()) {
3403 0 : pref->SerializeAndAppend(aStr);
3404 : }
3405 : }
3406 :
3407 0 : aStr.Append('\0');
3408 0 : }
3409 :
3410 : /* static */ void
3411 0 : Preferences::DeserializePreferences(char* aStr, size_t aPrefsLen)
3412 : {
3413 0 : MOZ_ASSERT(!XRE_IsParentProcess());
3414 :
3415 0 : MOZ_ASSERT(!gChangedDomPrefs);
3416 0 : gChangedDomPrefs = new InfallibleTArray<dom::Pref>();
3417 :
3418 0 : char* p = aStr;
3419 0 : while (*p != '\0') {
3420 0 : dom::Pref pref;
3421 0 : p = Pref::Deserialize(p, &pref);
3422 0 : gChangedDomPrefs->AppendElement(pref);
3423 : }
3424 :
3425 : // We finished parsing on a '\0'. That should be the last char in the shared
3426 : // memory. (aPrefsLen includes the '\0'.)
3427 0 : MOZ_ASSERT(p == aStr + aPrefsLen - 1);
3428 :
3429 : #ifdef DEBUG
3430 0 : MOZ_ASSERT(!gContentProcessPrefsAreInited);
3431 0 : gContentProcessPrefsAreInited = true;
3432 : #endif
3433 0 : }
3434 :
3435 : /* static */ void
3436 0 : Preferences::InitializeUserPrefs()
3437 : {
3438 0 : MOZ_ASSERT(XRE_IsParentProcess());
3439 0 : MOZ_ASSERT(!sPreferences->mCurrentFile, "Should only initialize prefs once");
3440 :
3441 : // Prefs which are set before we initialize the profile are silently
3442 : // discarded. This is stupid, but there are various tests which depend on
3443 : // this behavior.
3444 0 : sPreferences->ResetUserPrefs();
3445 :
3446 0 : nsCOMPtr<nsIFile> prefsFile = sPreferences->ReadSavedPrefs();
3447 0 : sPreferences->ReadUserOverridePrefs();
3448 :
3449 0 : sPreferences->mDirty = false;
3450 :
3451 : // Don't set mCurrentFile until we're done so that dirty flags work properly.
3452 0 : sPreferences->mCurrentFile = prefsFile.forget();
3453 :
3454 0 : sPreferences->NotifyServiceObservers(NS_PREFSERVICE_READ_TOPIC_ID);
3455 :
3456 : // At this point all the prefs files have been read and telemetry has been
3457 : // initialized. Send all the file load measurements to telemetry.
3458 0 : SendTelemetryLoadData();
3459 0 : }
3460 :
3461 : NS_IMETHODIMP
3462 0 : Preferences::Observe(nsISupports* aSubject,
3463 : const char* aTopic,
3464 : const char16_t* someData)
3465 : {
3466 0 : if (MOZ_UNLIKELY(!XRE_IsParentProcess())) {
3467 : return NS_ERROR_NOT_AVAILABLE;
3468 : }
3469 :
3470 0 : nsresult rv = NS_OK;
3471 :
3472 0 : if (!nsCRT::strcmp(aTopic, "profile-before-change")) {
3473 : // Normally prefs aren't written after this point, and so we kick off
3474 : // an asynchronous pref save so that I/O can be done in parallel with
3475 : // other shutdown.
3476 0 : if (AllowOffMainThreadSave()) {
3477 0 : SavePrefFile(nullptr);
3478 : }
3479 :
3480 0 : } else if (!nsCRT::strcmp(aTopic, "profile-before-change-telemetry")) {
3481 : // It's possible that a profile-before-change observer after ours
3482 : // set a pref. A blocking save here re-saves if necessary and also waits
3483 : // for any pending saves to complete.
3484 0 : SavePrefFileBlocking();
3485 0 : MOZ_ASSERT(!mDirty, "Preferences should not be dirty");
3486 0 : mProfileShutdown = true;
3487 :
3488 0 : } else if (!nsCRT::strcmp(aTopic, "reload-default-prefs")) {
3489 : // Reload the default prefs from file.
3490 0 : Unused << InitInitialObjects(/* isStartup */ false);
3491 :
3492 0 : } else if (!nsCRT::strcmp(aTopic, "suspend_process_notification")) {
3493 : // Our process is being suspended. The OS may wake our process later,
3494 : // or it may kill the process. In case our process is going to be killed
3495 : // from the suspended state, we save preferences before suspending.
3496 0 : rv = SavePrefFileBlocking();
3497 : }
3498 :
3499 : return rv;
3500 : }
3501 :
3502 : NS_IMETHODIMP
3503 0 : Preferences::ReadDefaultPrefsFromFile(nsIFile* aFile)
3504 : {
3505 0 : ENSURE_PARENT_PROCESS("Preferences::ReadDefaultPrefsFromFile", "all prefs");
3506 :
3507 0 : if (!aFile) {
3508 0 : NS_ERROR("ReadDefaultPrefsFromFile requires a parameter");
3509 0 : return NS_ERROR_INVALID_ARG;
3510 : }
3511 :
3512 0 : return openPrefFile(aFile, PrefValueKind::Default);
3513 : }
3514 :
3515 : NS_IMETHODIMP
3516 0 : Preferences::ReadUserPrefsFromFile(nsIFile* aFile)
3517 : {
3518 0 : ENSURE_PARENT_PROCESS("Preferences::ReadUserPrefsFromFile", "all prefs");
3519 :
3520 0 : if (!aFile) {
3521 0 : NS_ERROR("ReadUserPrefsFromFile requires a parameter");
3522 0 : return NS_ERROR_INVALID_ARG;
3523 : }
3524 :
3525 0 : return openPrefFile(aFile, PrefValueKind::User);
3526 : }
3527 :
3528 : NS_IMETHODIMP
3529 0 : Preferences::ResetPrefs()
3530 : {
3531 0 : ENSURE_PARENT_PROCESS("Preferences::ResetPrefs", "all prefs");
3532 :
3533 0 : gHashTable->ClearAndPrepareForLength(PREF_HASHTABLE_INITIAL_LENGTH);
3534 0 : gPrefNameArena.Clear();
3535 :
3536 0 : return InitInitialObjects(/* isStartup */ false).isOk() ? NS_OK
3537 0 : : NS_ERROR_FAILURE;
3538 : }
3539 :
3540 : NS_IMETHODIMP
3541 0 : Preferences::ResetUserPrefs()
3542 : {
3543 0 : ENSURE_PARENT_PROCESS("Preferences::ResetUserPrefs", "all prefs");
3544 0 : NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
3545 0 : MOZ_ASSERT(NS_IsMainThread());
3546 :
3547 0 : Vector<const char*> prefNames;
3548 0 : for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
3549 0 : Pref* pref = static_cast<PrefEntry*>(iter.Get())->mPref;
3550 :
3551 0 : if (pref->HasUserValue()) {
3552 0 : if (!prefNames.append(pref->Name())) {
3553 0 : return NS_ERROR_OUT_OF_MEMORY;
3554 : }
3555 :
3556 0 : pref->ClearUserValue();
3557 0 : if (!pref->HasDefaultValue()) {
3558 0 : iter.Remove();
3559 : }
3560 : }
3561 : }
3562 :
3563 0 : for (const char* prefName : prefNames) {
3564 0 : NotifyCallbacks(prefName);
3565 : }
3566 :
3567 0 : Preferences::HandleDirty();
3568 0 : return NS_OK;
3569 : }
3570 :
3571 : bool
3572 0 : Preferences::AllowOffMainThreadSave()
3573 : {
3574 : // Put in a preference that allows us to disable off main thread preference
3575 : // file save.
3576 0 : if (sAllowOMTPrefWrite < 0) {
3577 0 : bool value = false;
3578 0 : Preferences::GetBool("preferences.allow.omt-write", &value);
3579 0 : sAllowOMTPrefWrite = value ? 1 : 0;
3580 : }
3581 :
3582 0 : return !!sAllowOMTPrefWrite;
3583 : }
3584 :
3585 : nsresult
3586 0 : Preferences::SavePrefFileBlocking()
3587 : {
3588 0 : if (mDirty) {
3589 0 : return SavePrefFileInternal(nullptr, SaveMethod::Blocking);
3590 : }
3591 :
3592 : // If we weren't dirty to start, SavePrefFileInternal will early exit so
3593 : // there is no guarantee that we don't have oustanding async saves in the
3594 : // pipe. Since the contract of SavePrefFileOnMainThread is that the file on
3595 : // disk matches the preferences, we have to make sure those requests are
3596 : // completed.
3597 :
3598 0 : if (AllowOffMainThreadSave()) {
3599 0 : PreferencesWriter::Flush();
3600 : }
3601 :
3602 : return NS_OK;
3603 : }
3604 :
3605 : nsresult
3606 0 : Preferences::SavePrefFileAsynchronous()
3607 : {
3608 0 : return SavePrefFileInternal(nullptr, SaveMethod::Asynchronous);
3609 : }
3610 :
3611 : NS_IMETHODIMP
3612 0 : Preferences::SavePrefFile(nsIFile* aFile)
3613 : {
3614 : // This is the method accessible from service API. Make it off main thread.
3615 0 : return SavePrefFileInternal(aFile, SaveMethod::Asynchronous);
3616 : }
3617 :
3618 : /* static */ void
3619 0 : Preferences::SetPreference(const dom::Pref& aDomPref)
3620 : {
3621 0 : MOZ_ASSERT(!XRE_IsParentProcess());
3622 0 : NS_ENSURE_TRUE(InitStaticMembers(), (void)0);
3623 :
3624 0 : const char* prefName = aDomPref.name().get();
3625 :
3626 0 : auto entry = static_cast<PrefEntry*>(gHashTable->Add(prefName, fallible));
3627 0 : if (!entry) {
3628 : return;
3629 : }
3630 :
3631 0 : Pref* pref = entry->mPref;
3632 :
3633 0 : bool valueChanged = false;
3634 0 : pref->FromDomPref(aDomPref, &valueChanged);
3635 :
3636 : // When the parent process clears a pref's user value we get a DomPref here
3637 : // with no default value and no user value. There are two possibilities.
3638 : //
3639 : // - There was an existing pref with only a user value. FromDomPref() will
3640 : // have just cleared that user value, so the pref can be removed.
3641 : //
3642 : // - There was no existing pref. FromDomPref() will have done nothing, and
3643 : // `pref` will be valueless. We will end up adding and removing the value
3644 : // needlessly, but that's ok because this case is rare.
3645 : //
3646 0 : if (!pref->HasDefaultValue() && !pref->HasUserValue()) {
3647 0 : gHashTable->RemoveEntry(entry);
3648 : }
3649 :
3650 : // Note: we don't have to worry about HandleDirty() because we are setting
3651 : // prefs in the content process that have come from the parent process.
3652 :
3653 0 : if (valueChanged) {
3654 0 : NotifyCallbacks(prefName);
3655 : }
3656 : }
3657 :
3658 : /* static */ void
3659 0 : Preferences::GetPreference(dom::Pref* aDomPref)
3660 : {
3661 0 : MOZ_ASSERT(XRE_IsParentProcess());
3662 :
3663 0 : Pref* pref = pref_HashTableLookup(aDomPref->name().get());
3664 0 : if (pref && pref->HasAdvisablySizedValues()) {
3665 0 : pref->ToDomPref(aDomPref);
3666 : }
3667 0 : }
3668 :
3669 : #ifdef DEBUG
3670 : bool
3671 0 : Preferences::ArePrefsInitedInContentProcess()
3672 : {
3673 0 : MOZ_ASSERT(!XRE_IsParentProcess());
3674 0 : return gContentProcessPrefsAreInited;
3675 : }
3676 : #endif
3677 :
3678 : NS_IMETHODIMP
3679 0 : Preferences::GetBranch(const char* aPrefRoot, nsIPrefBranch** aRetVal)
3680 : {
3681 0 : if ((nullptr != aPrefRoot) && (*aPrefRoot != '\0')) {
3682 : // TODO: Cache this stuff and allow consumers to share branches (hold weak
3683 : // references, I think).
3684 : RefPtr<nsPrefBranch> prefBranch =
3685 0 : new nsPrefBranch(aPrefRoot, PrefValueKind::User);
3686 0 : prefBranch.forget(aRetVal);
3687 : } else {
3688 : // Special case: caching the default root.
3689 0 : nsCOMPtr<nsIPrefBranch> root(sPreferences->mRootBranch);
3690 0 : root.forget(aRetVal);
3691 : }
3692 :
3693 0 : return NS_OK;
3694 : }
3695 :
3696 : NS_IMETHODIMP
3697 0 : Preferences::GetDefaultBranch(const char* aPrefRoot, nsIPrefBranch** aRetVal)
3698 : {
3699 0 : if (!aPrefRoot || !aPrefRoot[0]) {
3700 0 : nsCOMPtr<nsIPrefBranch> root(sPreferences->mDefaultRootBranch);
3701 0 : root.forget(aRetVal);
3702 : return NS_OK;
3703 : }
3704 :
3705 : // TODO: Cache this stuff and allow consumers to share branches (hold weak
3706 : // references, I think).
3707 : RefPtr<nsPrefBranch> prefBranch =
3708 0 : new nsPrefBranch(aPrefRoot, PrefValueKind::Default);
3709 0 : if (!prefBranch) {
3710 : return NS_ERROR_OUT_OF_MEMORY;
3711 : }
3712 :
3713 0 : prefBranch.forget(aRetVal);
3714 0 : return NS_OK;
3715 : }
3716 :
3717 : NS_IMETHODIMP
3718 0 : Preferences::ReadStats(nsIPrefStatsCallback* aCallback)
3719 : {
3720 : #ifdef DEBUG
3721 0 : for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
3722 0 : PrefEntry* entry = static_cast<PrefEntry*>(iter.Get());
3723 0 : aCallback->Visit(entry->mPref->Name(), entry->mAccessCount);
3724 : }
3725 :
3726 0 : return NS_OK;
3727 : #else
3728 : return NS_ERROR_NOT_IMPLEMENTED;
3729 : #endif
3730 : }
3731 :
3732 : NS_IMETHODIMP
3733 0 : Preferences::ResetStats()
3734 : {
3735 : #ifdef DEBUG
3736 0 : for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
3737 0 : static_cast<PrefEntry*>(iter.Get())->mAccessCount = 0;
3738 : }
3739 0 : return NS_OK;
3740 : #else
3741 : return NS_ERROR_NOT_IMPLEMENTED;
3742 : #endif
3743 : }
3744 :
3745 : NS_IMETHODIMP
3746 0 : Preferences::GetDirty(bool* aRetVal)
3747 : {
3748 0 : *aRetVal = mDirty;
3749 0 : return NS_OK;
3750 : }
3751 :
3752 : nsresult
3753 0 : Preferences::NotifyServiceObservers(const char* aTopic)
3754 : {
3755 : nsCOMPtr<nsIObserverService> observerService =
3756 0 : mozilla::services::GetObserverService();
3757 0 : if (!observerService) {
3758 : return NS_ERROR_FAILURE;
3759 : }
3760 :
3761 0 : auto subject = static_cast<nsIPrefService*>(this);
3762 0 : observerService->NotifyObservers(subject, aTopic, nullptr);
3763 :
3764 0 : return NS_OK;
3765 : }
3766 :
3767 : already_AddRefed<nsIFile>
3768 0 : Preferences::ReadSavedPrefs()
3769 : {
3770 0 : nsCOMPtr<nsIFile> file;
3771 : nsresult rv =
3772 0 : NS_GetSpecialDirectory(NS_APP_PREFS_50_FILE, getter_AddRefs(file));
3773 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3774 : return nullptr;
3775 : }
3776 :
3777 0 : rv = openPrefFile(file, PrefValueKind::User);
3778 0 : if (rv == NS_ERROR_FILE_NOT_FOUND) {
3779 : // This is a normal case for new users.
3780 : Telemetry::ScalarSet(
3781 0 : Telemetry::ScalarID::PREFERENCES_CREATED_NEW_USER_PREFS_FILE, true);
3782 0 : rv = NS_OK;
3783 0 : } else if (NS_FAILED(rv)) {
3784 : // Save a backup copy of the current (invalid) prefs file, since all prefs
3785 : // from the error line to the end of the file will be lost (bug 361102).
3786 : // TODO we should notify the user about it (bug 523725).
3787 : Telemetry::ScalarSet(
3788 0 : Telemetry::ScalarID::PREFERENCES_PREFS_FILE_WAS_INVALID, true);
3789 0 : MakeBackupPrefFile(file);
3790 : }
3791 :
3792 0 : return file.forget();
3793 : }
3794 :
3795 : void
3796 0 : Preferences::ReadUserOverridePrefs()
3797 : {
3798 0 : nsCOMPtr<nsIFile> aFile;
3799 : nsresult rv =
3800 0 : NS_GetSpecialDirectory(NS_APP_PREFS_50_DIR, getter_AddRefs(aFile));
3801 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3802 0 : return;
3803 : }
3804 :
3805 0 : aFile->AppendNative(NS_LITERAL_CSTRING("user.js"));
3806 0 : rv = openPrefFile(aFile, PrefValueKind::User);
3807 0 : if (rv != NS_ERROR_FILE_NOT_FOUND) {
3808 : // If the file exists and was at least partially read, record that in
3809 : // telemetry as it may be a sign of pref injection.
3810 0 : Telemetry::ScalarSet(Telemetry::ScalarID::PREFERENCES_READ_USER_JS, true);
3811 : }
3812 : }
3813 :
3814 : nsresult
3815 0 : Preferences::MakeBackupPrefFile(nsIFile* aFile)
3816 : {
3817 : // Example: this copies "prefs.js" to "Invalidprefs.js" in the same directory.
3818 : // "Invalidprefs.js" is removed if it exists, prior to making the copy.
3819 0 : nsAutoString newFilename;
3820 0 : nsresult rv = aFile->GetLeafName(newFilename);
3821 0 : NS_ENSURE_SUCCESS(rv, rv);
3822 :
3823 0 : newFilename.InsertLiteral(u"Invalid", 0);
3824 0 : nsCOMPtr<nsIFile> newFile;
3825 0 : rv = aFile->GetParent(getter_AddRefs(newFile));
3826 0 : NS_ENSURE_SUCCESS(rv, rv);
3827 :
3828 0 : rv = newFile->Append(newFilename);
3829 0 : NS_ENSURE_SUCCESS(rv, rv);
3830 :
3831 0 : bool exists = false;
3832 0 : newFile->Exists(&exists);
3833 0 : if (exists) {
3834 0 : rv = newFile->Remove(false);
3835 0 : NS_ENSURE_SUCCESS(rv, rv);
3836 : }
3837 :
3838 0 : rv = aFile->CopyTo(nullptr, newFilename);
3839 0 : NS_ENSURE_SUCCESS(rv, rv);
3840 :
3841 : return rv;
3842 : }
3843 :
3844 : nsresult
3845 0 : Preferences::SavePrefFileInternal(nsIFile* aFile, SaveMethod aSaveMethod)
3846 : {
3847 0 : ENSURE_PARENT_PROCESS("Preferences::SavePrefFileInternal", "all prefs");
3848 :
3849 : // We allow different behavior here when aFile argument is not null, but it
3850 : // happens to be the same as the current file. It is not clear that we
3851 : // should, but it does give us a "force" save on the unmodified pref file
3852 : // (see the original bug 160377 when we added this.)
3853 :
3854 0 : if (nullptr == aFile) {
3855 0 : mSavePending = false;
3856 :
3857 : // Off main thread writing only if allowed.
3858 0 : if (!AllowOffMainThreadSave()) {
3859 0 : aSaveMethod = SaveMethod::Blocking;
3860 : }
3861 :
3862 : // The mDirty flag tells us if we should write to mCurrentFile. We only
3863 : // check this flag when the caller wants to write to the default.
3864 0 : if (!mDirty) {
3865 : return NS_OK;
3866 : }
3867 :
3868 : // Check for profile shutdown after mDirty because the runnables from
3869 : // HandleDirty() can still be pending.
3870 0 : if (mProfileShutdown) {
3871 0 : NS_WARNING("Cannot save pref file after profile shutdown.");
3872 0 : return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
3873 : }
3874 :
3875 : // It's possible that we never got a prefs file.
3876 0 : nsresult rv = NS_OK;
3877 0 : if (mCurrentFile) {
3878 0 : rv = WritePrefFile(mCurrentFile, aSaveMethod);
3879 : }
3880 :
3881 : // If we succeeded writing to mCurrentFile, reset the dirty flag.
3882 0 : if (NS_SUCCEEDED(rv)) {
3883 0 : mDirty = false;
3884 : }
3885 : return rv;
3886 :
3887 : } else {
3888 : // We only allow off main thread writes on mCurrentFile.
3889 0 : return WritePrefFile(aFile, SaveMethod::Blocking);
3890 : }
3891 : }
3892 :
3893 : nsresult
3894 0 : Preferences::WritePrefFile(nsIFile* aFile, SaveMethod aSaveMethod)
3895 : {
3896 0 : MOZ_ASSERT(XRE_IsParentProcess());
3897 :
3898 0 : if (!gHashTable) {
3899 : return NS_ERROR_NOT_INITIALIZED;
3900 : }
3901 :
3902 0 : AUTO_PROFILER_LABEL("Preferences::WritePrefFile", OTHER);
3903 :
3904 0 : if (AllowOffMainThreadSave()) {
3905 :
3906 0 : nsresult rv = NS_OK;
3907 : mozilla::UniquePtr<PrefSaveData> prefs =
3908 0 : MakeUnique<PrefSaveData>(pref_savePrefs());
3909 :
3910 : // Put the newly constructed preference data into sPendingWriteData
3911 : // for the next request to pick up
3912 0 : prefs.reset(PreferencesWriter::sPendingWriteData.exchange(prefs.release()));
3913 0 : if (prefs) {
3914 : // There was a previous request that hasn't been processed,
3915 : // and this is the data it had.
3916 0 : return rv;
3917 : }
3918 :
3919 : // There were no previous requests. Dispatch one since sPendingWriteData has
3920 : // the up to date information.
3921 : nsCOMPtr<nsIEventTarget> target =
3922 0 : do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
3923 0 : if (NS_SUCCEEDED(rv)) {
3924 0 : bool async = aSaveMethod == SaveMethod::Asynchronous;
3925 0 : if (async) {
3926 0 : rv = target->Dispatch(new PWRunnable(aFile),
3927 : nsIEventTarget::DISPATCH_NORMAL);
3928 : } else {
3929 : // Note that we don't get the nsresult return value here.
3930 0 : SyncRunnable::DispatchToThread(target, new PWRunnable(aFile), true);
3931 : }
3932 0 : return rv;
3933 : }
3934 :
3935 : // If we can't get the thread for writing, for whatever reason, do the main
3936 : // thread write after making some noise.
3937 0 : MOZ_ASSERT(false, "failed to get the target thread for OMT pref write");
3938 : }
3939 :
3940 : // This will do a main thread write. It is safe to do it this way because
3941 : // AllowOffMainThreadSave() returns a consistent value for the lifetime of
3942 : // the parent process.
3943 0 : PrefSaveData prefsData = pref_savePrefs();
3944 0 : return PreferencesWriter::Write(aFile, prefsData);
3945 : }
3946 :
3947 : static nsresult
3948 0 : openPrefFile(nsIFile* aFile, PrefValueKind aKind)
3949 : {
3950 0 : TimeStamp startTime = TimeStamp::Now();
3951 :
3952 0 : nsCString data;
3953 0 : MOZ_TRY_VAR(data, URLPreloader::ReadFile(aFile));
3954 :
3955 0 : nsAutoString filenameUtf16;
3956 0 : aFile->GetLeafName(filenameUtf16);
3957 0 : NS_ConvertUTF16toUTF8 filename(filenameUtf16);
3958 :
3959 0 : nsAutoString path;
3960 0 : aFile->GetPath(path);
3961 :
3962 : Parser parser;
3963 0 : if (!parser.Parse(
3964 0 : filename, aKind, NS_ConvertUTF16toUTF8(path).get(), startTime, data)) {
3965 : return NS_ERROR_FILE_CORRUPTED;
3966 : }
3967 :
3968 0 : return NS_OK;
3969 : }
3970 :
3971 : static int
3972 0 : pref_CompareFileNames(nsIFile* aFile1, nsIFile* aFile2, void* /* unused */)
3973 : {
3974 0 : nsAutoCString filename1, filename2;
3975 0 : aFile1->GetNativeLeafName(filename1);
3976 0 : aFile2->GetNativeLeafName(filename2);
3977 :
3978 0 : return Compare(filename2, filename1);
3979 : }
3980 :
3981 : // Load default pref files from a directory. The files in the directory are
3982 : // sorted reverse-alphabetically; a set of "special file names" may be
3983 : // specified which are loaded after all the others.
3984 : static nsresult
3985 0 : pref_LoadPrefsInDir(nsIFile* aDir,
3986 : char const* const* aSpecialFiles,
3987 : uint32_t aSpecialFilesCount)
3988 : {
3989 : nsresult rv, rv2;
3990 :
3991 0 : nsCOMPtr<nsIDirectoryEnumerator> dirIterator;
3992 :
3993 : // This may fail in some normal cases, such as embedders who do not use a
3994 : // GRE.
3995 0 : rv = aDir->GetDirectoryEntries(getter_AddRefs(dirIterator));
3996 0 : if (NS_FAILED(rv)) {
3997 : // If the directory doesn't exist, then we have no reason to complain. We
3998 : // loaded everything (and nothing) successfully.
3999 0 : if (rv == NS_ERROR_FILE_NOT_FOUND ||
4000 0 : rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) {
4001 0 : rv = NS_OK;
4002 : }
4003 : return rv;
4004 : }
4005 :
4006 0 : nsCOMArray<nsIFile> prefFiles(INITIAL_PREF_FILES);
4007 0 : nsCOMArray<nsIFile> specialFiles(aSpecialFilesCount);
4008 0 : nsCOMPtr<nsIFile> prefFile;
4009 :
4010 0 : while (NS_SUCCEEDED(dirIterator->GetNextFile(getter_AddRefs(prefFile))) &&
4011 0 : prefFile) {
4012 0 : nsAutoCString leafName;
4013 0 : prefFile->GetNativeLeafName(leafName);
4014 0 : MOZ_ASSERT(
4015 : !leafName.IsEmpty(),
4016 : "Failure in default prefs: directory enumerator returned empty file?");
4017 :
4018 : // Skip non-js files.
4019 0 : if (StringEndsWith(leafName,
4020 0 : NS_LITERAL_CSTRING(".js"),
4021 0 : nsCaseInsensitiveCStringComparator())) {
4022 : bool shouldParse = true;
4023 :
4024 : // Separate out special files.
4025 0 : for (uint32_t i = 0; i < aSpecialFilesCount; ++i) {
4026 0 : if (leafName.Equals(nsDependentCString(aSpecialFiles[i]))) {
4027 0 : shouldParse = false;
4028 : // Special files should be processed in order. We put them into the
4029 : // array by index, which can make the array sparse.
4030 0 : specialFiles.ReplaceObjectAt(prefFile, i);
4031 : }
4032 : }
4033 :
4034 0 : if (shouldParse) {
4035 0 : prefFiles.AppendObject(prefFile);
4036 : }
4037 : }
4038 : }
4039 :
4040 0 : if (prefFiles.Count() + specialFiles.Count() == 0) {
4041 0 : NS_WARNING("No default pref files found.");
4042 0 : if (NS_SUCCEEDED(rv)) {
4043 0 : rv = NS_SUCCESS_FILE_DIRECTORY_EMPTY;
4044 : }
4045 : return rv;
4046 : }
4047 :
4048 0 : prefFiles.Sort(pref_CompareFileNames, nullptr);
4049 :
4050 0 : uint32_t arrayCount = prefFiles.Count();
4051 : uint32_t i;
4052 0 : for (i = 0; i < arrayCount; ++i) {
4053 0 : rv2 = openPrefFile(prefFiles[i], PrefValueKind::Default);
4054 0 : if (NS_FAILED(rv2)) {
4055 0 : NS_ERROR("Default pref file not parsed successfully.");
4056 0 : rv = rv2;
4057 : }
4058 : }
4059 :
4060 0 : arrayCount = specialFiles.Count();
4061 0 : for (i = 0; i < arrayCount; ++i) {
4062 : // This may be a sparse array; test before parsing.
4063 0 : nsIFile* file = specialFiles[i];
4064 0 : if (file) {
4065 0 : rv2 = openPrefFile(file, PrefValueKind::Default);
4066 0 : if (NS_FAILED(rv2)) {
4067 0 : NS_ERROR("Special default pref file not parsed successfully.");
4068 0 : rv = rv2;
4069 : }
4070 : }
4071 : }
4072 :
4073 : return rv;
4074 : }
4075 :
4076 : static nsresult
4077 0 : pref_ReadPrefFromJar(nsZipArchive* aJarReader, const char* aName)
4078 : {
4079 0 : TimeStamp startTime = TimeStamp::Now();
4080 :
4081 0 : nsCString manifest;
4082 0 : MOZ_TRY_VAR(manifest,
4083 : URLPreloader::ReadZip(aJarReader, nsDependentCString(aName)));
4084 :
4085 : Parser parser;
4086 0 : if (!parser.Parse(nsDependentCString(aName),
4087 : PrefValueKind::Default,
4088 : aName,
4089 : startTime,
4090 : manifest)) {
4091 : return NS_ERROR_FILE_CORRUPTED;
4092 : }
4093 :
4094 0 : return NS_OK;
4095 : }
4096 :
4097 : // Initialize default preference JavaScript buffers from appropriate TEXT
4098 : // resources.
4099 : /* static */ Result<Ok, const char*>
4100 0 : Preferences::InitInitialObjects(bool aIsStartup)
4101 : {
4102 : // Initialize static prefs before prefs from data files so that the latter
4103 : // will override the former.
4104 0 : StaticPrefs::InitAll(aIsStartup);
4105 :
4106 : // In the omni.jar case, we load the following prefs:
4107 : // - jar:$gre/omni.jar!/greprefs.js
4108 : // - jar:$gre/omni.jar!/defaults/pref/*.js
4109 : //
4110 : // In the non-omni.jar case, we load:
4111 : // - $gre/greprefs.js
4112 : //
4113 : // In both cases, we also load:
4114 : // - $gre/defaults/pref/*.js
4115 : //
4116 : // This is kept for bug 591866 (channel-prefs.js should not be in omni.jar)
4117 : // in the `$app == $gre` case; we load all files instead of channel-prefs.js
4118 : // only to have the same behaviour as `$app != $gre`, where this is required
4119 : // as a supported location for GRE preferences.
4120 : //
4121 : // When `$app != $gre`, we additionally load, in the omni.jar case:
4122 : // - jar:$app/omni.jar!/defaults/preferences/*.js
4123 : // - $app/defaults/preferences/*.js
4124 : //
4125 : // and in the non-omni.jar case:
4126 : // - $app/defaults/preferences/*.js
4127 : //
4128 : // When `$app == $gre`, we additionally load, in the omni.jar case:
4129 : // - jar:$gre/omni.jar!/defaults/preferences/*.js
4130 : //
4131 : // Thus, in the omni.jar case, we always load app-specific default
4132 : // preferences from omni.jar, whether or not `$app == $gre`.
4133 :
4134 : nsresult rv;
4135 : nsZipFind* findPtr;
4136 0 : nsAutoPtr<nsZipFind> find;
4137 0 : nsTArray<nsCString> prefEntries;
4138 : const char* entryName;
4139 : uint16_t entryNameLen;
4140 :
4141 : RefPtr<nsZipArchive> jarReader =
4142 0 : mozilla::Omnijar::GetReader(mozilla::Omnijar::GRE);
4143 0 : if (jarReader) {
4144 : // Load jar:$gre/omni.jar!/greprefs.js.
4145 0 : rv = pref_ReadPrefFromJar(jarReader, "greprefs.js");
4146 0 : NS_ENSURE_SUCCESS(rv, Err("pref_ReadPrefFromJar() failed"));
4147 :
4148 : // Load jar:$gre/omni.jar!/defaults/pref/*.js.
4149 0 : rv = jarReader->FindInit("defaults/pref/*.js$", &findPtr);
4150 0 : NS_ENSURE_SUCCESS(rv, Err("jarReader->FindInit() failed"));
4151 :
4152 0 : find = findPtr;
4153 0 : while (NS_SUCCEEDED(find->FindNext(&entryName, &entryNameLen))) {
4154 0 : prefEntries.AppendElement(Substring(entryName, entryNameLen));
4155 : }
4156 :
4157 0 : prefEntries.Sort();
4158 0 : for (uint32_t i = prefEntries.Length(); i--;) {
4159 0 : rv = pref_ReadPrefFromJar(jarReader, prefEntries[i].get());
4160 0 : if (NS_FAILED(rv)) {
4161 0 : NS_WARNING("Error parsing preferences.");
4162 : }
4163 : }
4164 :
4165 : } else {
4166 : // Load $gre/greprefs.js.
4167 0 : nsCOMPtr<nsIFile> greprefsFile;
4168 0 : rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(greprefsFile));
4169 0 : NS_ENSURE_SUCCESS(rv, Err("NS_GetSpecialDirectory(NS_GRE_DIR) failed"));
4170 :
4171 0 : rv = greprefsFile->AppendNative(NS_LITERAL_CSTRING("greprefs.js"));
4172 0 : NS_ENSURE_SUCCESS(rv, Err("greprefsFile->AppendNative() failed"));
4173 :
4174 0 : rv = openPrefFile(greprefsFile, PrefValueKind::Default);
4175 0 : if (NS_FAILED(rv)) {
4176 : NS_WARNING("Error parsing GRE default preferences. Is this an old-style "
4177 0 : "embedding app?");
4178 : }
4179 : }
4180 :
4181 : // Load $gre/defaults/pref/*.js.
4182 0 : nsCOMPtr<nsIFile> defaultPrefDir;
4183 0 : rv = NS_GetSpecialDirectory(NS_APP_PREF_DEFAULTS_50_DIR,
4184 0 : getter_AddRefs(defaultPrefDir));
4185 0 : NS_ENSURE_SUCCESS(
4186 : rv, Err("NS_GetSpecialDirectory(NS_APP_PREF_DEFAULTS_50_DIR) failed"));
4187 :
4188 : // These pref file names should not be used: we process them after all other
4189 : // application pref files for backwards compatibility.
4190 : static const char* specialFiles[] = {
4191 : #if defined(XP_MACOSX)
4192 : "macprefs.js"
4193 : #elif defined(XP_WIN)
4194 : "winpref.js"
4195 : #elif defined(XP_UNIX)
4196 : "unix.js"
4197 : #if defined(_AIX)
4198 : ,
4199 : "aix.js"
4200 : #endif
4201 : #elif defined(XP_BEOS)
4202 : "beos.js"
4203 : #endif
4204 : };
4205 :
4206 0 : rv = pref_LoadPrefsInDir(
4207 : defaultPrefDir, specialFiles, ArrayLength(specialFiles));
4208 0 : if (NS_FAILED(rv)) {
4209 0 : NS_WARNING("Error parsing application default preferences.");
4210 : }
4211 :
4212 : // Load jar:$app/omni.jar!/defaults/preferences/*.js
4213 : // or jar:$gre/omni.jar!/defaults/preferences/*.js.
4214 : RefPtr<nsZipArchive> appJarReader =
4215 0 : mozilla::Omnijar::GetReader(mozilla::Omnijar::APP);
4216 :
4217 : // GetReader(mozilla::Omnijar::APP) returns null when `$app == $gre`, in
4218 : // which case we look for app-specific default preferences in $gre.
4219 0 : if (!appJarReader) {
4220 0 : appJarReader = mozilla::Omnijar::GetReader(mozilla::Omnijar::GRE);
4221 : }
4222 :
4223 0 : if (appJarReader) {
4224 0 : rv = appJarReader->FindInit("defaults/preferences/*.js$", &findPtr);
4225 0 : NS_ENSURE_SUCCESS(rv, Err("appJarReader->FindInit() failed"));
4226 0 : find = findPtr;
4227 0 : prefEntries.Clear();
4228 0 : while (NS_SUCCEEDED(find->FindNext(&entryName, &entryNameLen))) {
4229 0 : prefEntries.AppendElement(Substring(entryName, entryNameLen));
4230 : }
4231 0 : prefEntries.Sort();
4232 0 : for (uint32_t i = prefEntries.Length(); i--;) {
4233 0 : rv = pref_ReadPrefFromJar(appJarReader, prefEntries[i].get());
4234 0 : if (NS_FAILED(rv)) {
4235 0 : NS_WARNING("Error parsing preferences.");
4236 : }
4237 : }
4238 : }
4239 :
4240 : nsCOMPtr<nsIProperties> dirSvc(
4241 0 : do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv));
4242 0 : NS_ENSURE_SUCCESS(
4243 : rv, Err("do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID) failed"));
4244 :
4245 0 : nsCOMPtr<nsISimpleEnumerator> list;
4246 0 : dirSvc->Get(NS_APP_PREFS_DEFAULTS_DIR_LIST,
4247 : NS_GET_IID(nsISimpleEnumerator),
4248 0 : getter_AddRefs(list));
4249 0 : if (list) {
4250 : bool hasMore;
4251 0 : while (NS_SUCCEEDED(list->HasMoreElements(&hasMore)) && hasMore) {
4252 0 : nsCOMPtr<nsISupports> elem;
4253 0 : list->GetNext(getter_AddRefs(elem));
4254 0 : if (!elem) {
4255 0 : continue;
4256 : }
4257 :
4258 0 : nsCOMPtr<nsIFile> path = do_QueryInterface(elem);
4259 0 : if (!path) {
4260 0 : continue;
4261 : }
4262 :
4263 : // Do we care if a file provided by this process fails to load?
4264 0 : pref_LoadPrefsInDir(path, nullptr, 0);
4265 : }
4266 : }
4267 :
4268 0 : if (XRE_IsParentProcess()) {
4269 0 : SetupTelemetryPref();
4270 : }
4271 :
4272 : NS_CreateServicesFromCategory(NS_PREFSERVICE_APPDEFAULTS_TOPIC_ID,
4273 : nullptr,
4274 0 : NS_PREFSERVICE_APPDEFAULTS_TOPIC_ID);
4275 :
4276 : nsCOMPtr<nsIObserverService> observerService =
4277 0 : mozilla::services::GetObserverService();
4278 0 : NS_ENSURE_SUCCESS(rv, Err("GetObserverService() failed (2)"));
4279 :
4280 0 : observerService->NotifyObservers(
4281 0 : nullptr, NS_PREFSERVICE_APPDEFAULTS_TOPIC_ID, nullptr);
4282 :
4283 0 : return Ok();
4284 : }
4285 :
4286 : /* static */ nsresult
4287 0 : Preferences::GetBool(const char* aPrefName, bool* aResult, PrefValueKind aKind)
4288 : {
4289 0 : MOZ_ASSERT(aResult);
4290 0 : NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
4291 :
4292 0 : Pref* pref = pref_HashTableLookup(aPrefName);
4293 0 : return pref ? pref->GetBoolValue(aKind, aResult) : NS_ERROR_UNEXPECTED;
4294 : }
4295 :
4296 : /* static */ nsresult
4297 0 : Preferences::GetInt(const char* aPrefName,
4298 : int32_t* aResult,
4299 : PrefValueKind aKind)
4300 : {
4301 0 : MOZ_ASSERT(aResult);
4302 0 : NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
4303 :
4304 0 : Pref* pref = pref_HashTableLookup(aPrefName);
4305 0 : return pref ? pref->GetIntValue(aKind, aResult) : NS_ERROR_UNEXPECTED;
4306 : }
4307 :
4308 : /* static */ nsresult
4309 0 : Preferences::GetFloat(const char* aPrefName,
4310 : float* aResult,
4311 : PrefValueKind aKind)
4312 : {
4313 0 : MOZ_ASSERT(aResult);
4314 :
4315 0 : nsAutoCString result;
4316 0 : nsresult rv = Preferences::GetCString(aPrefName, result, aKind);
4317 0 : if (NS_SUCCEEDED(rv)) {
4318 0 : *aResult = result.ToFloat(&rv);
4319 : }
4320 0 : return rv;
4321 : }
4322 :
4323 : /* static */ nsresult
4324 0 : Preferences::GetCString(const char* aPrefName,
4325 : nsACString& aResult,
4326 : PrefValueKind aKind)
4327 : {
4328 0 : NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
4329 :
4330 0 : aResult.SetIsVoid(true);
4331 :
4332 0 : Pref* pref = pref_HashTableLookup(aPrefName);
4333 0 : return pref ? pref->GetCStringValue(aKind, aResult) : NS_ERROR_UNEXPECTED;
4334 : }
4335 :
4336 : /* static */ nsresult
4337 0 : Preferences::GetString(const char* aPrefName,
4338 : nsAString& aResult,
4339 : PrefValueKind aKind)
4340 : {
4341 0 : nsAutoCString result;
4342 0 : nsresult rv = Preferences::GetCString(aPrefName, result, aKind);
4343 0 : if (NS_SUCCEEDED(rv)) {
4344 0 : CopyUTF8toUTF16(result, aResult);
4345 : }
4346 0 : return rv;
4347 : }
4348 :
4349 : /* static */ nsresult
4350 0 : Preferences::GetLocalizedCString(const char* aPrefName,
4351 : nsACString& aResult,
4352 : PrefValueKind aKind)
4353 : {
4354 0 : nsAutoString result;
4355 0 : nsresult rv = GetLocalizedString(aPrefName, result, aKind);
4356 0 : if (NS_SUCCEEDED(rv)) {
4357 0 : CopyUTF16toUTF8(result, aResult);
4358 : }
4359 0 : return rv;
4360 : }
4361 :
4362 : /* static */ nsresult
4363 0 : Preferences::GetLocalizedString(const char* aPrefName,
4364 : nsAString& aResult,
4365 : PrefValueKind aKind)
4366 : {
4367 0 : NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
4368 0 : nsCOMPtr<nsIPrefLocalizedString> prefLocalString;
4369 : nsresult rv =
4370 0 : GetRootBranch(aKind)->GetComplexValue(aPrefName,
4371 : NS_GET_IID(nsIPrefLocalizedString),
4372 0 : getter_AddRefs(prefLocalString));
4373 0 : if (NS_SUCCEEDED(rv)) {
4374 0 : MOZ_ASSERT(prefLocalString, "Succeeded but the result is NULL");
4375 0 : prefLocalString->GetData(aResult);
4376 : }
4377 : return rv;
4378 : }
4379 :
4380 : /* static */ nsresult
4381 0 : Preferences::GetComplex(const char* aPrefName,
4382 : const nsIID& aType,
4383 : void** aResult,
4384 : PrefValueKind aKind)
4385 : {
4386 0 : NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
4387 0 : return GetRootBranch(aKind)->GetComplexValue(aPrefName, aType, aResult);
4388 : }
4389 :
4390 : /* static */ nsresult
4391 0 : Preferences::SetCString(const char* aPrefName,
4392 : const nsACString& aValue,
4393 : PrefValueKind aKind)
4394 : {
4395 0 : ENSURE_PARENT_PROCESS("SetCString", aPrefName);
4396 0 : NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
4397 :
4398 0 : if (aValue.Length() > MAX_PREF_LENGTH) {
4399 : return NS_ERROR_ILLEGAL_VALUE;
4400 : }
4401 :
4402 : // It's ok to stash a pointer to the temporary PromiseFlatCString's chars in
4403 : // pref because pref_SetPref() duplicates those chars.
4404 : PrefValue prefValue;
4405 0 : const nsCString& flat = PromiseFlatCString(aValue);
4406 0 : prefValue.mStringVal = flat.get();
4407 : return pref_SetPref(aPrefName,
4408 : PrefType::String,
4409 : aKind,
4410 : prefValue,
4411 : /* isSticky */ false,
4412 : /* isLocked */ false,
4413 0 : /* fromInit */ false);
4414 : }
4415 :
4416 : /* static */ nsresult
4417 0 : Preferences::SetBool(const char* aPrefName, bool aValue, PrefValueKind aKind)
4418 : {
4419 0 : ENSURE_PARENT_PROCESS("SetBool", aPrefName);
4420 0 : NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
4421 :
4422 : PrefValue prefValue;
4423 0 : prefValue.mBoolVal = aValue;
4424 : return pref_SetPref(aPrefName,
4425 : PrefType::Bool,
4426 : aKind,
4427 : prefValue,
4428 : /* isSticky */ false,
4429 : /* isLocked */ false,
4430 0 : /* fromInit */ false);
4431 : }
4432 :
4433 : /* static */ nsresult
4434 0 : Preferences::SetInt(const char* aPrefName, int32_t aValue, PrefValueKind aKind)
4435 : {
4436 0 : ENSURE_PARENT_PROCESS("SetInt", aPrefName);
4437 0 : NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
4438 :
4439 : PrefValue prefValue;
4440 0 : prefValue.mIntVal = aValue;
4441 : return pref_SetPref(aPrefName,
4442 : PrefType::Int,
4443 : aKind,
4444 : prefValue,
4445 : /* isSticky */ false,
4446 : /* isLocked */ false,
4447 0 : /* fromInit */ false);
4448 : }
4449 :
4450 : /* static */ nsresult
4451 0 : Preferences::SetComplex(const char* aPrefName,
4452 : const nsIID& aType,
4453 : nsISupports* aValue,
4454 : PrefValueKind aKind)
4455 : {
4456 0 : NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
4457 0 : return GetRootBranch(aKind)->SetComplexValue(aPrefName, aType, aValue);
4458 : }
4459 :
4460 : /* static */ nsresult
4461 0 : Preferences::Lock(const char* aPrefName)
4462 : {
4463 0 : ENSURE_PARENT_PROCESS("Lock", aPrefName);
4464 0 : NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
4465 :
4466 0 : Pref* pref = pref_HashTableLookup(aPrefName);
4467 0 : if (!pref) {
4468 : return NS_ERROR_UNEXPECTED;
4469 : }
4470 :
4471 0 : if (!pref->IsLocked()) {
4472 0 : pref->SetIsLocked(true);
4473 0 : NotifyCallbacks(aPrefName);
4474 : }
4475 :
4476 : return NS_OK;
4477 : }
4478 :
4479 : /* static */ nsresult
4480 0 : Preferences::Unlock(const char* aPrefName)
4481 : {
4482 0 : ENSURE_PARENT_PROCESS("Unlock", aPrefName);
4483 0 : NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
4484 :
4485 0 : Pref* pref = pref_HashTableLookup(aPrefName);
4486 0 : if (!pref) {
4487 : return NS_ERROR_UNEXPECTED;
4488 : }
4489 :
4490 0 : if (pref->IsLocked()) {
4491 0 : pref->SetIsLocked(false);
4492 0 : NotifyCallbacks(aPrefName);
4493 : }
4494 :
4495 : return NS_OK;
4496 : }
4497 :
4498 : /* static */ bool
4499 0 : Preferences::IsLocked(const char* aPrefName)
4500 : {
4501 0 : NS_ENSURE_TRUE(InitStaticMembers(), false);
4502 :
4503 0 : Pref* pref = pref_HashTableLookup(aPrefName);
4504 0 : return pref && pref->IsLocked();
4505 : }
4506 :
4507 : /* static */ nsresult
4508 0 : Preferences::ClearUser(const char* aPrefName)
4509 : {
4510 0 : ENSURE_PARENT_PROCESS("ClearUser", aPrefName);
4511 0 : NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
4512 :
4513 0 : PrefEntry* entry = pref_HashTableLookupInner(aPrefName);
4514 : Pref* pref;
4515 0 : if (entry && (pref = entry->mPref) && pref->HasUserValue()) {
4516 0 : pref->ClearUserValue();
4517 :
4518 0 : if (!pref->HasDefaultValue()) {
4519 0 : gHashTable->RemoveEntry(entry);
4520 : }
4521 :
4522 0 : NotifyCallbacks(aPrefName);
4523 0 : Preferences::HandleDirty();
4524 : }
4525 : return NS_OK;
4526 : }
4527 :
4528 : /* static */ bool
4529 0 : Preferences::HasUserValue(const char* aPrefName)
4530 : {
4531 0 : NS_ENSURE_TRUE(InitStaticMembers(), false);
4532 :
4533 0 : Pref* pref = pref_HashTableLookup(aPrefName);
4534 0 : return pref && pref->HasUserValue();
4535 : }
4536 :
4537 : /* static */ int32_t
4538 0 : Preferences::GetType(const char* aPrefName)
4539 : {
4540 0 : NS_ENSURE_TRUE(InitStaticMembers(), nsIPrefBranch::PREF_INVALID);
4541 :
4542 : Pref* pref;
4543 0 : if (!gHashTable || !(pref = pref_HashTableLookup(aPrefName))) {
4544 : return PREF_INVALID;
4545 : }
4546 :
4547 0 : switch (pref->Type()) {
4548 : case PrefType::String:
4549 : return PREF_STRING;
4550 :
4551 : case PrefType::Int:
4552 0 : return PREF_INT;
4553 :
4554 : case PrefType::Bool:
4555 0 : return PREF_BOOL;
4556 :
4557 : default:
4558 0 : MOZ_CRASH();
4559 : }
4560 : }
4561 :
4562 : /* static */ nsresult
4563 0 : Preferences::AddStrongObserver(nsIObserver* aObserver, const char* aPref)
4564 : {
4565 0 : MOZ_ASSERT(aObserver);
4566 0 : NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
4567 0 : return sPreferences->mRootBranch->AddObserver(aPref, aObserver, false);
4568 : }
4569 :
4570 : /* static */ nsresult
4571 0 : Preferences::AddWeakObserver(nsIObserver* aObserver, const char* aPref)
4572 : {
4573 0 : MOZ_ASSERT(aObserver);
4574 0 : NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
4575 0 : return sPreferences->mRootBranch->AddObserver(aPref, aObserver, true);
4576 : }
4577 :
4578 : /* static */ nsresult
4579 0 : Preferences::RemoveObserver(nsIObserver* aObserver, const char* aPref)
4580 : {
4581 0 : MOZ_ASSERT(aObserver);
4582 0 : if (sShutdown) {
4583 0 : MOZ_ASSERT(!sPreferences);
4584 : return NS_OK; // Observers have been released automatically.
4585 : }
4586 0 : NS_ENSURE_TRUE(sPreferences, NS_ERROR_NOT_AVAILABLE);
4587 0 : return sPreferences->mRootBranch->RemoveObserver(aPref, aObserver);
4588 : }
4589 :
4590 : /* static */ nsresult
4591 0 : Preferences::AddStrongObservers(nsIObserver* aObserver, const char** aPrefs)
4592 : {
4593 0 : MOZ_ASSERT(aObserver);
4594 0 : for (uint32_t i = 0; aPrefs[i]; i++) {
4595 0 : nsresult rv = AddStrongObserver(aObserver, aPrefs[i]);
4596 0 : NS_ENSURE_SUCCESS(rv, rv);
4597 : }
4598 : return NS_OK;
4599 : }
4600 :
4601 : /* static */ nsresult
4602 0 : Preferences::AddWeakObservers(nsIObserver* aObserver, const char** aPrefs)
4603 : {
4604 0 : MOZ_ASSERT(aObserver);
4605 0 : for (uint32_t i = 0; aPrefs[i]; i++) {
4606 0 : nsresult rv = AddWeakObserver(aObserver, aPrefs[i]);
4607 0 : NS_ENSURE_SUCCESS(rv, rv);
4608 : }
4609 : return NS_OK;
4610 : }
4611 :
4612 : /* static */ nsresult
4613 0 : Preferences::RemoveObservers(nsIObserver* aObserver, const char** aPrefs)
4614 : {
4615 0 : MOZ_ASSERT(aObserver);
4616 0 : if (sShutdown) {
4617 0 : MOZ_ASSERT(!sPreferences);
4618 : return NS_OK; // Observers have been released automatically.
4619 : }
4620 0 : NS_ENSURE_TRUE(sPreferences, NS_ERROR_NOT_AVAILABLE);
4621 :
4622 0 : for (uint32_t i = 0; aPrefs[i]; i++) {
4623 0 : nsresult rv = RemoveObserver(aObserver, aPrefs[i]);
4624 0 : NS_ENSURE_SUCCESS(rv, rv);
4625 : }
4626 : return NS_OK;
4627 : }
4628 :
4629 : /* static */ nsresult
4630 0 : Preferences::RegisterCallback(PrefChangedFunc aCallback,
4631 : const char* aPrefNode,
4632 : void* aData,
4633 : MatchKind aMatchKind,
4634 : bool aIsPriority)
4635 : {
4636 0 : NS_ENSURE_ARG(aPrefNode);
4637 0 : NS_ENSURE_ARG(aCallback);
4638 :
4639 0 : NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
4640 :
4641 0 : auto node = new CallbackNode(aPrefNode, aCallback, aData, aMatchKind);
4642 :
4643 0 : if (aIsPriority) {
4644 : // Add to the start of the list.
4645 0 : node->SetNext(gFirstCallback);
4646 0 : gFirstCallback = node;
4647 0 : if (!gLastPriorityNode) {
4648 0 : gLastPriorityNode = node;
4649 : }
4650 : } else {
4651 : // Add to the start of the non-priority part of the list.
4652 0 : if (gLastPriorityNode) {
4653 0 : node->SetNext(gLastPriorityNode->Next());
4654 0 : gLastPriorityNode->SetNext(node);
4655 : } else {
4656 0 : node->SetNext(gFirstCallback);
4657 0 : gFirstCallback = node;
4658 : }
4659 : }
4660 :
4661 : return NS_OK;
4662 : }
4663 :
4664 : /* static */ nsresult
4665 0 : Preferences::RegisterCallbackAndCall(PrefChangedFunc aCallback,
4666 : const char* aPref,
4667 : void* aClosure,
4668 : MatchKind aMatchKind)
4669 : {
4670 0 : MOZ_ASSERT(aCallback);
4671 0 : nsresult rv = RegisterCallback(aCallback, aPref, aClosure, aMatchKind);
4672 0 : if (NS_SUCCEEDED(rv)) {
4673 0 : (*aCallback)(aPref, aClosure);
4674 : }
4675 0 : return rv;
4676 : }
4677 :
4678 : /* static */ nsresult
4679 0 : Preferences::UnregisterCallback(PrefChangedFunc aCallback,
4680 : const char* aPrefNode,
4681 : void* aData,
4682 : MatchKind aMatchKind)
4683 : {
4684 0 : MOZ_ASSERT(aCallback);
4685 0 : if (sShutdown) {
4686 0 : MOZ_ASSERT(!sPreferences);
4687 : return NS_OK; // Observers have been released automatically.
4688 : }
4689 0 : NS_ENSURE_TRUE(sPreferences, NS_ERROR_NOT_AVAILABLE);
4690 :
4691 0 : nsresult rv = NS_ERROR_FAILURE;
4692 0 : CallbackNode* node = gFirstCallback;
4693 0 : CallbackNode* prev_node = nullptr;
4694 :
4695 0 : while (node) {
4696 0 : if (node->Func() == aCallback && node->Data() == aData &&
4697 0 : node->MatchKind() == aMatchKind &&
4698 0 : strcmp(node->Domain(), aPrefNode) == 0) {
4699 0 : if (gCallbacksInProgress) {
4700 : // Postpone the node removal until after callbacks enumeration is
4701 : // finished.
4702 0 : node->ClearFunc();
4703 0 : gShouldCleanupDeadNodes = true;
4704 0 : prev_node = node;
4705 0 : node = node->Next();
4706 : } else {
4707 0 : node = pref_RemoveCallbackNode(node, prev_node);
4708 : }
4709 : rv = NS_OK;
4710 : } else {
4711 0 : prev_node = node;
4712 0 : node = node->Next();
4713 : }
4714 : }
4715 : return rv;
4716 : }
4717 :
4718 : static void
4719 0 : CacheDataAppendElement(CacheData* aData)
4720 : {
4721 0 : if (!gCacheData) {
4722 0 : MOZ_CRASH_UNSAFE_PRINTF("!gCacheData: %s", gCacheDataDesc);
4723 : }
4724 0 : gCacheData->AppendElement(aData);
4725 0 : }
4726 :
4727 : static void
4728 0 : BoolVarChanged(const char* aPref, void* aClosure)
4729 : {
4730 0 : CacheData* cache = static_cast<CacheData*>(aClosure);
4731 0 : *static_cast<bool*>(cache->mCacheLocation) =
4732 0 : Preferences::GetBool(aPref, cache->mDefaultValueBool);
4733 0 : }
4734 :
4735 : /* static */ nsresult
4736 0 : Preferences::AddBoolVarCache(bool* aCache,
4737 : const char* aPref,
4738 : bool aDefault,
4739 : bool aSkipAssignment)
4740 : {
4741 0 : AssertNotAlreadyCached("bool", aPref, aCache);
4742 0 : if (!aSkipAssignment) {
4743 0 : *aCache = GetBool(aPref, aDefault);
4744 : }
4745 0 : CacheData* data = new CacheData();
4746 0 : data->mCacheLocation = aCache;
4747 0 : data->mDefaultValueBool = aDefault;
4748 0 : CacheDataAppendElement(data);
4749 : Preferences::RegisterCallback(BoolVarChanged,
4750 : aPref,
4751 : data,
4752 : Preferences::ExactMatch,
4753 0 : /* isPriority */ true);
4754 0 : return NS_OK;
4755 : }
4756 :
4757 : template<MemoryOrdering Order>
4758 : static void
4759 0 : AtomicBoolVarChanged(const char* aPref, void* aClosure)
4760 : {
4761 0 : CacheData* cache = static_cast<CacheData*>(aClosure);
4762 0 : *static_cast<Atomic<bool, Order>*>(cache->mCacheLocation) =
4763 0 : Preferences::GetBool(aPref, cache->mDefaultValueBool);
4764 0 : }
4765 :
4766 : template<MemoryOrdering Order>
4767 : /* static */ nsresult
4768 0 : Preferences::AddAtomicBoolVarCache(Atomic<bool, Order>* aCache,
4769 : const char* aPref,
4770 : bool aDefault,
4771 : bool aSkipAssignment)
4772 : {
4773 0 : AssertNotAlreadyCached("bool", aPref, aCache);
4774 0 : if (!aSkipAssignment) {
4775 0 : *aCache = GetBool(aPref, aDefault);
4776 : }
4777 0 : CacheData* data = new CacheData();
4778 0 : data->mCacheLocation = aCache;
4779 0 : data->mDefaultValueBool = aDefault;
4780 0 : CacheDataAppendElement(data);
4781 0 : Preferences::RegisterCallback(AtomicBoolVarChanged<Order>,
4782 : aPref,
4783 : data,
4784 : Preferences::ExactMatch,
4785 : /* isPriority */ true);
4786 0 : return NS_OK;
4787 : }
4788 :
4789 : static void
4790 0 : IntVarChanged(const char* aPref, void* aClosure)
4791 : {
4792 0 : CacheData* cache = static_cast<CacheData*>(aClosure);
4793 0 : *static_cast<int32_t*>(cache->mCacheLocation) =
4794 0 : Preferences::GetInt(aPref, cache->mDefaultValueInt);
4795 0 : }
4796 :
4797 : /* static */ nsresult
4798 0 : Preferences::AddIntVarCache(int32_t* aCache,
4799 : const char* aPref,
4800 : int32_t aDefault,
4801 : bool aSkipAssignment)
4802 : {
4803 0 : AssertNotAlreadyCached("int", aPref, aCache);
4804 0 : if (!aSkipAssignment) {
4805 0 : *aCache = GetInt(aPref, aDefault);
4806 : }
4807 0 : CacheData* data = new CacheData();
4808 0 : data->mCacheLocation = aCache;
4809 0 : data->mDefaultValueInt = aDefault;
4810 0 : CacheDataAppendElement(data);
4811 : Preferences::RegisterCallback(
4812 0 : IntVarChanged, aPref, data, Preferences::ExactMatch, /* isPriority */ true);
4813 0 : return NS_OK;
4814 : }
4815 :
4816 : template<MemoryOrdering Order>
4817 : static void
4818 0 : AtomicIntVarChanged(const char* aPref, void* aClosure)
4819 : {
4820 0 : CacheData* cache = static_cast<CacheData*>(aClosure);
4821 0 : *static_cast<Atomic<int32_t, Order>*>(cache->mCacheLocation) =
4822 0 : Preferences::GetInt(aPref, cache->mDefaultValueUint);
4823 0 : }
4824 :
4825 : template<MemoryOrdering Order>
4826 : /* static */ nsresult
4827 0 : Preferences::AddAtomicIntVarCache(Atomic<int32_t, Order>* aCache,
4828 : const char* aPref,
4829 : int32_t aDefault,
4830 : bool aSkipAssignment)
4831 : {
4832 0 : AssertNotAlreadyCached("int", aPref, aCache);
4833 0 : if (!aSkipAssignment) {
4834 0 : *aCache = GetInt(aPref, aDefault);
4835 : }
4836 0 : CacheData* data = new CacheData();
4837 0 : data->mCacheLocation = aCache;
4838 0 : data->mDefaultValueUint = aDefault;
4839 0 : CacheDataAppendElement(data);
4840 0 : Preferences::RegisterCallback(AtomicIntVarChanged<Order>,
4841 : aPref,
4842 : data,
4843 : Preferences::ExactMatch,
4844 : /* isPriority */ true);
4845 0 : return NS_OK;
4846 : }
4847 :
4848 : static void
4849 0 : UintVarChanged(const char* aPref, void* aClosure)
4850 : {
4851 0 : CacheData* cache = static_cast<CacheData*>(aClosure);
4852 0 : *static_cast<uint32_t*>(cache->mCacheLocation) =
4853 0 : Preferences::GetUint(aPref, cache->mDefaultValueUint);
4854 0 : }
4855 :
4856 : /* static */ nsresult
4857 0 : Preferences::AddUintVarCache(uint32_t* aCache,
4858 : const char* aPref,
4859 : uint32_t aDefault,
4860 : bool aSkipAssignment)
4861 : {
4862 0 : AssertNotAlreadyCached("uint", aPref, aCache);
4863 0 : if (!aSkipAssignment) {
4864 0 : *aCache = GetUint(aPref, aDefault);
4865 : }
4866 0 : CacheData* data = new CacheData();
4867 0 : data->mCacheLocation = aCache;
4868 0 : data->mDefaultValueUint = aDefault;
4869 0 : CacheDataAppendElement(data);
4870 : Preferences::RegisterCallback(UintVarChanged,
4871 : aPref,
4872 : data,
4873 : Preferences::ExactMatch,
4874 0 : /* isPriority */ true);
4875 0 : return NS_OK;
4876 : }
4877 :
4878 : template<MemoryOrdering Order>
4879 : static void
4880 0 : AtomicUintVarChanged(const char* aPref, void* aClosure)
4881 : {
4882 0 : CacheData* cache = static_cast<CacheData*>(aClosure);
4883 0 : *static_cast<Atomic<uint32_t, Order>*>(cache->mCacheLocation) =
4884 : Preferences::GetUint(aPref, cache->mDefaultValueUint);
4885 0 : }
4886 :
4887 : template<MemoryOrdering Order>
4888 : /* static */ nsresult
4889 0 : Preferences::AddAtomicUintVarCache(Atomic<uint32_t, Order>* aCache,
4890 : const char* aPref,
4891 : uint32_t aDefault,
4892 : bool aSkipAssignment)
4893 : {
4894 0 : AssertNotAlreadyCached("uint", aPref, aCache);
4895 0 : if (!aSkipAssignment) {
4896 0 : *aCache = GetUint(aPref, aDefault);
4897 : }
4898 0 : CacheData* data = new CacheData();
4899 0 : data->mCacheLocation = aCache;
4900 0 : data->mDefaultValueUint = aDefault;
4901 0 : CacheDataAppendElement(data);
4902 0 : Preferences::RegisterCallback(AtomicUintVarChanged<Order>,
4903 : aPref,
4904 : data,
4905 : Preferences::ExactMatch,
4906 : /* isPriority */ true);
4907 0 : return NS_OK;
4908 : }
4909 :
4910 : // Since the definition of template functions is not in a header file, we
4911 : // need to explicitly specify the instantiations that are required. Currently
4912 : // limited orders are needed and therefore implemented.
4913 : template nsresult
4914 : Preferences::AddAtomicBoolVarCache(Atomic<bool, Relaxed>*,
4915 : const char*,
4916 : bool,
4917 : bool);
4918 :
4919 : template nsresult
4920 : Preferences::AddAtomicBoolVarCache(Atomic<bool, ReleaseAcquire>*,
4921 : const char*,
4922 : bool,
4923 : bool);
4924 :
4925 : template nsresult
4926 : Preferences::AddAtomicBoolVarCache(Atomic<bool, SequentiallyConsistent>*,
4927 : const char*,
4928 : bool,
4929 : bool);
4930 :
4931 : template nsresult
4932 : Preferences::AddAtomicIntVarCache(Atomic<int32_t, Relaxed>*,
4933 : const char*,
4934 : int32_t,
4935 : bool);
4936 :
4937 : template nsresult
4938 : Preferences::AddAtomicUintVarCache(Atomic<uint32_t, Relaxed>*,
4939 : const char*,
4940 : uint32_t,
4941 : bool);
4942 :
4943 : template nsresult
4944 : Preferences::AddAtomicUintVarCache(Atomic<uint32_t, ReleaseAcquire>*,
4945 : const char*,
4946 : uint32_t,
4947 : bool);
4948 :
4949 : template nsresult
4950 : Preferences::AddAtomicUintVarCache(Atomic<uint32_t, SequentiallyConsistent>*,
4951 : const char*,
4952 : uint32_t,
4953 : bool);
4954 :
4955 : static void
4956 0 : FloatVarChanged(const char* aPref, void* aClosure)
4957 : {
4958 0 : CacheData* cache = static_cast<CacheData*>(aClosure);
4959 0 : *static_cast<float*>(cache->mCacheLocation) =
4960 0 : Preferences::GetFloat(aPref, cache->mDefaultValueFloat);
4961 0 : }
4962 :
4963 : /* static */ nsresult
4964 0 : Preferences::AddFloatVarCache(float* aCache,
4965 : const char* aPref,
4966 : float aDefault,
4967 : bool aSkipAssignment)
4968 : {
4969 0 : AssertNotAlreadyCached("float", aPref, aCache);
4970 0 : if (!aSkipAssignment) {
4971 0 : *aCache = GetFloat(aPref, aDefault);
4972 : }
4973 0 : CacheData* data = new CacheData();
4974 0 : data->mCacheLocation = aCache;
4975 0 : data->mDefaultValueFloat = aDefault;
4976 0 : CacheDataAppendElement(data);
4977 : Preferences::RegisterCallback(FloatVarChanged,
4978 : aPref,
4979 : data,
4980 : Preferences::ExactMatch,
4981 0 : /* isPriority */ true);
4982 0 : return NS_OK;
4983 : }
4984 :
4985 : // For a VarCache pref like this:
4986 : //
4987 : // VARCACHE_PREF("my.varcache", my_varcache, int32_t, 99)
4988 : //
4989 : // we generate a static variable definition:
4990 : //
4991 : // int32_t StaticPrefs::sVarCache_my_varcache(99);
4992 : //
4993 : #define PREF(name, cpp_type, value)
4994 : #define VARCACHE_PREF(name, id, cpp_type, value) \
4995 : cpp_type StaticPrefs::sVarCache_##id(value);
4996 : #include "mozilla/StaticPrefList.h"
4997 : #undef PREF
4998 : #undef VARCACHE_PREF
4999 :
5000 : // The SetPref_*() functions below end in a `_<type>` suffix because they are
5001 : // used by the PREF macro definition in InitAll() below.
5002 :
5003 : static void
5004 0 : SetPref_bool(const char* aName, bool aDefaultValue)
5005 : {
5006 : PrefValue value;
5007 0 : value.mBoolVal = aDefaultValue;
5008 : pref_SetPref(aName,
5009 : PrefType::Bool,
5010 : PrefValueKind::Default,
5011 : value,
5012 : /* isSticky */ false,
5013 : /* isLocked */ false,
5014 0 : /* fromInit */ true);
5015 0 : }
5016 :
5017 : static void
5018 0 : SetPref_int32_t(const char* aName, int32_t aDefaultValue)
5019 : {
5020 : PrefValue value;
5021 0 : value.mIntVal = aDefaultValue;
5022 : pref_SetPref(aName,
5023 : PrefType::Int,
5024 : PrefValueKind::Default,
5025 : value,
5026 : /* isSticky */ false,
5027 : /* isLocked */ false,
5028 0 : /* fromInit */ true);
5029 0 : }
5030 :
5031 : static void
5032 : SetPref_float(const char* aName, float aDefaultValue)
5033 : {
5034 : PrefValue value;
5035 : nsPrintfCString defaultValue("%f", aDefaultValue);
5036 : value.mStringVal = defaultValue.get();
5037 : pref_SetPref(aName,
5038 : PrefType::String,
5039 : PrefValueKind::Default,
5040 : value,
5041 : /* isSticky */ false,
5042 : /* isLocked */ false,
5043 : /* fromInit */ true);
5044 : }
5045 :
5046 : // XXX: this will eventually become used
5047 : MOZ_MAYBE_UNUSED static void
5048 : SetPref_String(const char* aName, const char* aDefaultValue)
5049 : {
5050 : PrefValue value;
5051 : value.mStringVal = aDefaultValue;
5052 : pref_SetPref(aName,
5053 : PrefType::String,
5054 : PrefValueKind::Default,
5055 : value,
5056 : /* isSticky */ false,
5057 : /* isLocked */ false,
5058 : /* fromInit */ true);
5059 : }
5060 :
5061 : static void
5062 0 : InitVarCachePref(const char* aName,
5063 : bool* aCache,
5064 : bool aDefaultValue,
5065 : bool aIsStartup)
5066 : {
5067 0 : SetPref_bool(aName, aDefaultValue);
5068 0 : *aCache = aDefaultValue;
5069 0 : if (aIsStartup) {
5070 0 : Preferences::AddBoolVarCache(aCache, aName, aDefaultValue, true);
5071 : }
5072 0 : }
5073 :
5074 : template<MemoryOrdering Order>
5075 : static void
5076 21 : InitVarCachePref(const char* aName,
5077 : Atomic<bool, Order>* aCache,
5078 : bool aDefaultValue,
5079 : bool aIsStartup)
5080 : {
5081 21 : SetPref_bool(aName, aDefaultValue);
5082 42 : *aCache = aDefaultValue;
5083 21 : if (aIsStartup) {
5084 21 : Preferences::AddAtomicBoolVarCache(aCache, aName, aDefaultValue, true);
5085 : }
5086 21 : }
5087 :
5088 : // XXX: this will eventually become used
5089 : MOZ_MAYBE_UNUSED static void
5090 0 : InitVarCachePref(const char* aName,
5091 : int32_t* aCache,
5092 : int32_t aDefaultValue,
5093 : bool aIsStartup)
5094 : {
5095 0 : SetPref_int32_t(aName, aDefaultValue);
5096 0 : *aCache = aDefaultValue;
5097 0 : if (aIsStartup) {
5098 0 : Preferences::AddIntVarCache(aCache, aName, aDefaultValue, true);
5099 : }
5100 0 : }
5101 :
5102 : template<MemoryOrdering Order>
5103 : static void
5104 5 : InitVarCachePref(const char* aName,
5105 : Atomic<int32_t, Order>* aCache,
5106 : int32_t aDefaultValue,
5107 : bool aIsStartup)
5108 : {
5109 5 : SetPref_int32_t(aName, aDefaultValue);
5110 10 : *aCache = aDefaultValue;
5111 5 : if (aIsStartup) {
5112 5 : Preferences::AddAtomicIntVarCache(aCache, aName, aDefaultValue, true);
5113 : }
5114 5 : }
5115 :
5116 : static void
5117 0 : InitVarCachePref(const char* aName,
5118 : uint32_t* aCache,
5119 : uint32_t aDefaultValue,
5120 : bool aIsStartup)
5121 : {
5122 0 : SetPref_int32_t(aName, static_cast<int32_t>(aDefaultValue));
5123 0 : *aCache = aDefaultValue;
5124 0 : if (aIsStartup) {
5125 0 : Preferences::AddUintVarCache(aCache, aName, aDefaultValue, true);
5126 : }
5127 0 : }
5128 :
5129 : template<MemoryOrdering Order>
5130 : static void
5131 0 : InitVarCachePref(const char* aName,
5132 : Atomic<uint32_t, Order>* aCache,
5133 : uint32_t aDefaultValue,
5134 : bool aIsStartup)
5135 : {
5136 0 : SetPref_int32_t(aName, static_cast<int32_t>(aDefaultValue));
5137 0 : *aCache = aDefaultValue;
5138 0 : if (aIsStartup) {
5139 0 : Preferences::AddAtomicUintVarCache(aCache, aName, aDefaultValue, true);
5140 : }
5141 0 : }
5142 :
5143 : // XXX: this will eventually become used
5144 : MOZ_MAYBE_UNUSED static void
5145 : InitVarCachePref(const char* aName,
5146 : float* aCache,
5147 : float aDefaultValue,
5148 : bool aIsStartup)
5149 : {
5150 : SetPref_float(aName, aDefaultValue);
5151 : *aCache = aDefaultValue;
5152 : if (aIsStartup) {
5153 : Preferences::AddFloatVarCache(aCache, aName, aDefaultValue, true);
5154 : }
5155 : }
5156 :
5157 : /* static */ void
5158 0 : StaticPrefs::InitAll(bool aIsStartup)
5159 : {
5160 : // For prefs like these:
5161 : //
5162 : // PREF("foo.bar.baz", bool, true)
5163 : // VARCACHE_PREF("my.varcache", my_varcache, int32_t, 99)
5164 : //
5165 : // we generate registration calls:
5166 : //
5167 : // SetPref_bool("foo.bar.baz", true);
5168 : // InitVarCachePref("my.varcache", &StaticPrefs::sVarCache_my_varcache, 99,
5169 : // aIsStartup);
5170 : //
5171 : // The SetPref_*() functions have a type suffix to avoid ambiguity between
5172 : // prefs having int32_t and float default values. That suffix is not needed for
5173 : // the InitVarCachePref() functions because they take a pointer parameter,
5174 : // which prevents automatic int-to-float coercion.
5175 : #define PREF(name, cpp_type, value) SetPref_##cpp_type(name, value);
5176 : #define VARCACHE_PREF(name, id, cpp_type, value) \
5177 : InitVarCachePref(name, &StaticPrefs::sVarCache_##id, value, aIsStartup);
5178 : #include "mozilla/StaticPrefList.h"
5179 : #undef PREF
5180 : #undef VARCACHE_PREF
5181 0 : }
5182 :
5183 : } // namespace mozilla
5184 :
5185 : #undef ENSURE_PARENT_PROCESS
5186 :
5187 : //===========================================================================
5188 : // Module and factory stuff
5189 : //===========================================================================
5190 :
5191 0 : NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(Preferences,
5192 : Preferences::GetInstanceForService)
5193 0 : NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrefLocalizedString, Init)
5194 0 : NS_GENERIC_FACTORY_CONSTRUCTOR(nsRelativeFilePref)
5195 :
5196 : static NS_DEFINE_CID(kPrefServiceCID, NS_PREFSERVICE_CID);
5197 : static NS_DEFINE_CID(kPrefLocalizedStringCID, NS_PREFLOCALIZEDSTRING_CID);
5198 : static NS_DEFINE_CID(kRelativeFilePrefCID, NS_RELATIVEFILEPREF_CID);
5199 :
5200 : static mozilla::Module::CIDEntry kPrefCIDs[] = {
5201 : { &kPrefServiceCID, true, nullptr, PreferencesConstructor },
5202 : { &kPrefLocalizedStringCID,
5203 : false,
5204 : nullptr,
5205 : nsPrefLocalizedStringConstructor },
5206 : { &kRelativeFilePrefCID, false, nullptr, nsRelativeFilePrefConstructor },
5207 : { nullptr }
5208 : };
5209 :
5210 : static mozilla::Module::ContractIDEntry kPrefContracts[] = {
5211 : { NS_PREFSERVICE_CONTRACTID, &kPrefServiceCID },
5212 : { NS_PREFLOCALIZEDSTRING_CONTRACTID, &kPrefLocalizedStringCID },
5213 : { NS_RELATIVEFILEPREF_CONTRACTID, &kRelativeFilePrefCID },
5214 : { nullptr }
5215 : };
5216 :
5217 : static void
5218 0 : UnloadPrefsModule()
5219 : {
5220 0 : Preferences::Shutdown();
5221 0 : }
5222 :
5223 : static const mozilla::Module kPrefModule = { mozilla::Module::kVersion,
5224 : kPrefCIDs,
5225 : kPrefContracts,
5226 : nullptr,
5227 : nullptr,
5228 : nullptr,
5229 : UnloadPrefsModule };
5230 :
5231 0 : NSMODULE_DEFN(nsPrefModule) = &kPrefModule;
|