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