Coverage Report

Created: 2018-09-25 14:53

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