Mypal/modules/libpref/Preferences.cpp

2016 lines
56 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/MemoryReporting.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Attributes.h"
#include "mozilla/HashFunctions.h"
#include "mozilla/UniquePtrExtensions.h"
#include "nsXULAppAPI.h"
#include "mozilla/Preferences.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsDataHashtable.h"
#include "nsDirectoryServiceDefs.h"
#include "nsICategoryManager.h"
#include "nsCategoryManagerUtils.h"
#include "nsNetUtil.h"
#include "nsIFile.h"
#include "nsIInputStream.h"
#include "nsIObserverService.h"
#include "nsIOutputStream.h"
#include "nsISafeOutputStream.h"
#include "nsISimpleEnumerator.h"
#include "nsIStringEnumerator.h"
#include "nsIZipReader.h"
#include "nsPrefBranch.h"
#include "nsXPIDLString.h"
#include "nsCRT.h"
#include "nsCOMArray.h"
#include "nsXPCOMCID.h"
#include "nsAutoPtr.h"
#include "nsPrintfCString.h"
#include "nsQuickSort.h"
#include "PLDHashTable.h"
#include "prefapi.h"
#include "prefread.h"
#include "prefapi_private_data.h"
#include "mozilla/Omnijar.h"
#include "nsZipArchive.h"
#include "nsTArray.h"
#include "nsRefPtrHashtable.h"
#include "nsIMemoryReporter.h"
#include "nsThreadUtils.h"
#ifdef DEBUG
#define ENSURE_MAIN_PROCESS(message, pref) do { \
if (MOZ_UNLIKELY(!XRE_IsParentProcess())) { \
nsPrintfCString msg("ENSURE_MAIN_PROCESS failed. %s %s", message, pref); \
NS_WARNING(msg.get()); \
return NS_ERROR_NOT_AVAILABLE; \
} \
} while (0);
#else
#define ENSURE_MAIN_PROCESS(message, pref) \
if (MOZ_UNLIKELY(!XRE_IsParentProcess())) { \
return NS_ERROR_NOT_AVAILABLE; \
}
#endif
class PrefCallback;
namespace mozilla {
// Definitions
#define INITIAL_PREF_FILES 10
static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
void
Preferences::DirtyCallback()
{
if (gHashTable && sPreferences && !sPreferences->mDirty) {
sPreferences->mDirty = true;
}
}
// Prototypes
static nsresult openPrefFile(nsIFile* aFile);
static nsresult pref_InitInitialObjects(void);
static nsresult pref_LoadPrefsInDirList(const char *listId);
static nsresult ReadExtensionPrefs(nsIFile *aFile);
static const char kTelemetryPref[] = "toolkit.telemetry.enabled";
static const char kOldTelemetryPref[] = "toolkit.telemetry.enabledPreRelease";
static const char kChannelPref[] = "app.update.channel";
static const char kPrefFileHeader[] =
"# Mozilla User Preferences"
NS_LINEBREAK
NS_LINEBREAK
"/* Do not edit this file."
NS_LINEBREAK
" *"
NS_LINEBREAK
" * If you make changes to this file while the application is running,"
NS_LINEBREAK
" * the changes will be overwritten when the application exits."
NS_LINEBREAK
" *"
NS_LINEBREAK
" * To make a manual change to preferences, you can visit the URL about:config"
NS_LINEBREAK
" */"
NS_LINEBREAK
NS_LINEBREAK;
Preferences* Preferences::sPreferences = nullptr;
nsIPrefBranch* Preferences::sRootBranch = nullptr;
nsIPrefBranch* Preferences::sDefaultRootBranch = nullptr;
bool Preferences::sShutdown = false;
class ValueObserverHashKey : public PLDHashEntryHdr {
public:
typedef ValueObserverHashKey* KeyType;
typedef const ValueObserverHashKey* KeyTypePointer;
static const ValueObserverHashKey* KeyToPointer(ValueObserverHashKey *aKey)
{
return aKey;
}
static PLDHashNumber HashKey(const ValueObserverHashKey *aKey)
{
PLDHashNumber hash = HashString(aKey->mPrefName);
hash = AddToHash(hash, aKey->mMatchKind);
return AddToHash(hash, aKey->mCallback);
}
ValueObserverHashKey(const char *aPref, PrefChangedFunc aCallback, Preferences::MatchKind aMatchKind) :
mPrefName(aPref), mCallback(aCallback), mMatchKind(aMatchKind) { }
explicit ValueObserverHashKey(const ValueObserverHashKey *aOther) :
mPrefName(aOther->mPrefName),
mCallback(aOther->mCallback),
mMatchKind(aOther->mMatchKind)
{ }
bool KeyEquals(const ValueObserverHashKey *aOther) const
{
return mCallback == aOther->mCallback &&
mPrefName == aOther->mPrefName &&
mMatchKind == aOther->mMatchKind;
}
ValueObserverHashKey *GetKey() const
{
return const_cast<ValueObserverHashKey*>(this);
}
enum { ALLOW_MEMMOVE = true };
nsCString mPrefName;
PrefChangedFunc mCallback;
Preferences::MatchKind mMatchKind;
};
class ValueObserver final : public nsIObserver,
public ValueObserverHashKey
{
~ValueObserver() {
Preferences::RemoveObserver(this, mPrefName.get());
}
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
ValueObserver(const char *aPref, PrefChangedFunc aCallback, Preferences::MatchKind aMatchKind)
: ValueObserverHashKey(aPref, aCallback, aMatchKind) { }
void AppendClosure(void *aClosure) {
mClosures.AppendElement(aClosure);
}
void RemoveClosure(void *aClosure) {
mClosures.RemoveElement(aClosure);
}
bool HasNoClosures() {
return mClosures.Length() == 0;
}
nsTArray<void*> mClosures;
};
NS_IMPL_ISUPPORTS(ValueObserver, nsIObserver)
NS_IMETHODIMP
ValueObserver::Observe(nsISupports *aSubject,
const char *aTopic,
const char16_t *aData)
{
NS_ASSERTION(!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID),
"invalid topic");
NS_ConvertUTF16toUTF8 data(aData);
if (mMatchKind == Preferences::ExactMatch && !mPrefName.EqualsASCII(data.get())) {
return NS_OK;
}
for (uint32_t i = 0; i < mClosures.Length(); i++) {
mCallback(data.get(), mClosures.ElementAt(i));
}
return NS_OK;
}
struct CacheData {
void* cacheLocation;
union {
bool defaultValueBool;
int32_t defaultValueInt;
uint32_t defaultValueUint;
float defaultValueFloat;
};
};
static nsTArray<nsAutoPtr<CacheData> >* gCacheData = nullptr;
static nsRefPtrHashtable<ValueObserverHashKey,
ValueObserver>* gObserverTable = nullptr;
#ifdef DEBUG
static bool
HaveExistingCacheFor(void* aPtr)
{
MOZ_ASSERT(NS_IsMainThread());
if (gCacheData) {
for (size_t i = 0, count = gCacheData->Length(); i < count; ++i) {
if ((*gCacheData)[i]->cacheLocation == aPtr) {
return true;
}
}
}
return false;
}
static void
AssertNotAlreadyCached(const char* aPrefType,
const char* aPref,
void* aPtr)
{
if (HaveExistingCacheFor(aPtr)) {
fprintf_stderr(stderr,
"Attempt to add a %s pref cache for preference '%s' at address '%p'"
"was made. However, a pref was already cached at this address.\n",
aPrefType, aPref, aPtr);
MOZ_ASSERT(false, "Should not have an existing pref cache for this address");
}
}
#endif
static void
ReportToConsole(const char* aMessage, int aLine, bool aError)
{
nsPrintfCString message("** Preference parsing %s (line %d) = %s **\n",
(aError ? "error" : "warning"), aLine, aMessage);
nsPrefBranch::ReportToConsole(NS_ConvertUTF8toUTF16(message.get()));
}
// Although this is a member of Preferences, it measures sPreferences and
// several other global structures.
/* static */ int64_t
Preferences::SizeOfIncludingThisAndOtherStuff(mozilla::MallocSizeOf aMallocSizeOf)
{
NS_ENSURE_TRUE(InitStaticMembers(), 0);
size_t n = aMallocSizeOf(sPreferences);
if (gHashTable) {
// pref keys are allocated in a private arena, which we count elsewhere.
// pref stringvals are allocated out of the same private arena.
n += gHashTable->ShallowSizeOfIncludingThis(aMallocSizeOf);
}
if (gCacheData) {
n += gCacheData->ShallowSizeOfIncludingThis(aMallocSizeOf);
for (uint32_t i = 0, count = gCacheData->Length(); i < count; ++i) {
n += aMallocSizeOf((*gCacheData)[i]);
}
}
if (gObserverTable) {
n += gObserverTable->ShallowSizeOfIncludingThis(aMallocSizeOf);
for (auto iter = gObserverTable->Iter(); !iter.Done(); iter.Next()) {
n += iter.Key()->mPrefName.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
n += iter.Data()->mClosures.ShallowSizeOfExcludingThis(aMallocSizeOf);
}
}
if (sRootBranch) {
n += reinterpret_cast<nsPrefBranch*>(sRootBranch)->SizeOfIncludingThis(aMallocSizeOf);
}
if (sDefaultRootBranch) {
n += reinterpret_cast<nsPrefBranch*>(sDefaultRootBranch)->SizeOfIncludingThis(aMallocSizeOf);
}
n += pref_SizeOfPrivateData(aMallocSizeOf);
return n;
}
class PreferenceServiceReporter final : public nsIMemoryReporter
{
~PreferenceServiceReporter() {}
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIMEMORYREPORTER
protected:
static const uint32_t kSuspectReferentCount = 1000;
};
NS_IMPL_ISUPPORTS(PreferenceServiceReporter, nsIMemoryReporter)
MOZ_DEFINE_MALLOC_SIZE_OF(PreferenceServiceMallocSizeOf)
NS_IMETHODIMP
PreferenceServiceReporter::CollectReports(
nsIHandleReportCallback* aHandleReport, nsISupports* aData, bool aAnonymize)
{
MOZ_COLLECT_REPORT(
"explicit/preferences", KIND_HEAP, UNITS_BYTES,
Preferences::SizeOfIncludingThisAndOtherStuff(PreferenceServiceMallocSizeOf),
"Memory used by the preferences system.");
nsPrefBranch* rootBranch =
static_cast<nsPrefBranch*>(Preferences::GetRootBranch());
if (!rootBranch) {
return NS_OK;
}
size_t numStrong = 0;
size_t numWeakAlive = 0;
size_t numWeakDead = 0;
nsTArray<nsCString> suspectPreferences;
// Count of the number of referents for each preference.
nsDataHashtable<nsCStringHashKey, uint32_t> prefCounter;
for (auto iter = rootBranch->mObservers.Iter(); !iter.Done(); iter.Next()) {
nsAutoPtr<PrefCallback>& callback = iter.Data();
nsPrefBranch* prefBranch = callback->GetPrefBranch();
const char* pref = prefBranch->getPrefName(callback->GetDomain().get());
if (callback->IsWeak()) {
nsCOMPtr<nsIObserver> callbackRef = do_QueryReferent(callback->mWeakRef);
if (callbackRef) {
numWeakAlive++;
} else {
numWeakDead++;
}
} else {
numStrong++;
}
nsDependentCString prefString(pref);
uint32_t oldCount = 0;
prefCounter.Get(prefString, &oldCount);
uint32_t currentCount = oldCount + 1;
prefCounter.Put(prefString, currentCount);
// Keep track of preferences that have a suspiciously large number of
// referents (a symptom of a leak).
if (currentCount == kSuspectReferentCount) {
suspectPreferences.AppendElement(prefString);
}
}
for (uint32_t i = 0; i < suspectPreferences.Length(); i++) {
nsCString& suspect = suspectPreferences[i];
uint32_t totalReferentCount = 0;
prefCounter.Get(suspect, &totalReferentCount);
nsPrintfCString suspectPath("preference-service-suspect/"
"referent(pref=%s)", suspect.get());
aHandleReport->Callback(
/* process = */ EmptyCString(),
suspectPath, KIND_OTHER, UNITS_COUNT, totalReferentCount,
NS_LITERAL_CSTRING(
"A preference with a suspiciously large number referents (symptom of a "
"leak)."),
aData);
}
MOZ_COLLECT_REPORT(
"preference-service/referent/strong", KIND_OTHER, UNITS_COUNT,
numStrong,
"The number of strong referents held by the preference service.");
MOZ_COLLECT_REPORT(
"preference-service/referent/weak/alive", KIND_OTHER, UNITS_COUNT,
numWeakAlive,
"The number of weak referents held by the preference service that are "
"still alive.");
MOZ_COLLECT_REPORT(
"preference-service/referent/weak/dead", KIND_OTHER, UNITS_COUNT,
numWeakDead,
"The number of weak referents held by the preference service that are "
"dead.");
return NS_OK;
}
namespace {
class AddPreferencesMemoryReporterRunnable : public Runnable
{
NS_IMETHOD Run() override
{
return RegisterStrongMemoryReporter(new PreferenceServiceReporter());
}
};
} // namespace
// static
Preferences*
Preferences::GetInstanceForService()
{
if (sPreferences) {
NS_ADDREF(sPreferences);
return sPreferences;
}
NS_ENSURE_TRUE(!sShutdown, nullptr);
sRootBranch = new nsPrefBranch("", false);
NS_ADDREF(sRootBranch);
sDefaultRootBranch = new nsPrefBranch("", true);
NS_ADDREF(sDefaultRootBranch);
sPreferences = new Preferences();
NS_ADDREF(sPreferences);
if (NS_FAILED(sPreferences->Init())) {
// The singleton instance will delete sRootBranch and sDefaultRootBranch.
NS_RELEASE(sPreferences);
return nullptr;
}
gCacheData = new nsTArray<nsAutoPtr<CacheData> >();
gObserverTable = new nsRefPtrHashtable<ValueObserverHashKey, ValueObserver>();
// Preferences::GetInstanceForService() can be called from GetService(), and
// RegisterStrongMemoryReporter calls GetService(nsIMemoryReporter). To
// avoid a potential recursive GetService() call, we can't register the
// memory reporter here; instead, do it off a runnable.
RefPtr<AddPreferencesMemoryReporterRunnable> runnable =
new AddPreferencesMemoryReporterRunnable();
NS_DispatchToMainThread(runnable);
NS_ADDREF(sPreferences);
return sPreferences;
}
// static
bool
Preferences::IsServiceAvailable()
{
return !!sPreferences;
}
// static
bool
Preferences::InitStaticMembers()
{
MOZ_ASSERT(NS_IsMainThread());
if (!sShutdown && !sPreferences) {
nsCOMPtr<nsIPrefService> prefService =
do_GetService(NS_PREFSERVICE_CONTRACTID);
}
return sPreferences != nullptr;
}
// static
void
Preferences::Shutdown()
{
if (!sShutdown) {
sShutdown = true; // Don't create the singleton instance after here.
// Don't set sPreferences to nullptr here. The instance may be grabbed by
// other modules. The utility methods of Preferences should be available
// until the singleton instance actually released.
if (sPreferences) {
sPreferences->Release();
}
}
}
//-----------------------------------------------------------------------------
/*
* Constructor/Destructor
*/
Preferences::Preferences()
: mDirty(false)
{
}
Preferences::~Preferences()
{
NS_ASSERTION(sPreferences == this, "Isn't this the singleton instance?");
delete gObserverTable;
gObserverTable = nullptr;
delete gCacheData;
gCacheData = nullptr;
NS_RELEASE(sRootBranch);
NS_RELEASE(sDefaultRootBranch);
sPreferences = nullptr;
PREF_Cleanup();
}
/*
* nsISupports Implementation
*/
NS_IMPL_ADDREF(Preferences)
NS_IMPL_RELEASE(Preferences)
NS_INTERFACE_MAP_BEGIN(Preferences)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPrefService)
NS_INTERFACE_MAP_ENTRY(nsIPrefService)
NS_INTERFACE_MAP_ENTRY(nsIObserver)
NS_INTERFACE_MAP_ENTRY(nsIPrefBranch)
NS_INTERFACE_MAP_ENTRY(nsIPrefBranch2)
NS_INTERFACE_MAP_ENTRY(nsIPrefBranchInternal)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_END
/*
* nsIPrefService Implementation
*/
nsresult
Preferences::Init()
{
nsresult rv;
PREF_SetDirtyCallback(&DirtyCallback);
PREF_Init();
rv = pref_InitInitialObjects();
NS_ENSURE_SUCCESS(rv, rv);
using mozilla::dom::ContentChild;
if (XRE_IsContentProcess()) {
InfallibleTArray<PrefSetting> prefs;
ContentChild::GetSingleton()->SendReadPrefsArray(&prefs);
// Store the array
for (uint32_t i = 0; i < prefs.Length(); ++i) {
pref_SetPref(prefs[i]);
}
return NS_OK;
}
nsXPIDLCString lockFileName;
/*
* The following is a small hack which will allow us to only load the library
* which supports the netscape.cfg file if the preference is defined. We
* test for the existence of the pref, set in the all.js (mozilla) or
* all-ns.js (netscape 6), and if it exists we startup the pref config
* category which will do the rest.
*/
rv = PREF_CopyCharPref("general.config.filename", getter_Copies(lockFileName), false);
if (NS_SUCCEEDED(rv))
NS_CreateServicesFromCategory("pref-config-startup",
static_cast<nsISupports *>(static_cast<void *>(this)),
"pref-config-startup");
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (!observerService)
return NS_ERROR_FAILURE;
rv = observerService->AddObserver(this, "profile-before-change", true);
observerService->AddObserver(this, "load-extension-defaults", true);
observerService->AddObserver(this, "suspend_process_notification", true);
return(rv);
}
// static
nsresult
Preferences::ResetAndReadUserPrefs()
{
sPreferences->ResetUserPrefs();
return sPreferences->ReadUserPrefs(nullptr);
}
NS_IMETHODIMP
Preferences::Observe(nsISupports *aSubject, const char *aTopic,
const char16_t *someData)
{
if (XRE_IsContentProcess())
return NS_ERROR_NOT_AVAILABLE;
nsresult rv = NS_OK;
if (!nsCRT::strcmp(aTopic, "profile-before-change")) {
rv = SavePrefFile(nullptr);
} else if (!strcmp(aTopic, "load-extension-defaults")) {
pref_LoadPrefsInDirList(NS_EXT_PREFS_DEFAULTS_DIR_LIST);
} else if (!nsCRT::strcmp(aTopic, "reload-default-prefs")) {
// Reload the default prefs from file.
pref_InitInitialObjects();
} else if (!nsCRT::strcmp(aTopic, "suspend_process_notification")) {
// Our process is being suspended. The OS may wake our process later,
// or it may kill the process. In case our process is going to be killed
// from the suspended state, we save preferences before suspending.
rv = SavePrefFile(nullptr);
}
return rv;
}
NS_IMETHODIMP
Preferences::ReadUserPrefs(nsIFile *aFile)
{
if (XRE_IsContentProcess()) {
NS_ERROR("cannot load prefs from content process");
return NS_ERROR_NOT_AVAILABLE;
}
nsresult rv;
if (nullptr == aFile) {
rv = UseDefaultPrefFile();
// A user pref file is optional.
// Ignore all errors related to it, so we retain 'rv' value :-|
(void) UseUserPrefFile();
// Migrate the old prerelease telemetry pref
if (!Preferences::GetBool(kOldTelemetryPref, true)) {
Preferences::SetBool(kTelemetryPref, false);
Preferences::ClearUser(kOldTelemetryPref);
}
NotifyServiceObservers(NS_PREFSERVICE_READ_TOPIC_ID);
} else {
rv = ReadAndOwnUserPrefFile(aFile);
}
return rv;
}
NS_IMETHODIMP
Preferences::ResetPrefs()
{
if (XRE_IsContentProcess()) {
NS_ERROR("cannot reset prefs from content process");
return NS_ERROR_NOT_AVAILABLE;
}
NotifyServiceObservers(NS_PREFSERVICE_RESET_TOPIC_ID);
PREF_CleanupPrefs();
PREF_Init();
return pref_InitInitialObjects();
}
NS_IMETHODIMP
Preferences::ResetUserPrefs()
{
if (XRE_IsContentProcess()) {
NS_ERROR("cannot reset user prefs from content process");
return NS_ERROR_NOT_AVAILABLE;
}
PREF_ClearAllUserPrefs();
return NS_OK;
}
NS_IMETHODIMP
Preferences::SavePrefFile(nsIFile *aFile)
{
if (XRE_IsContentProcess()) {
NS_ERROR("cannot save pref file from content process");
return NS_ERROR_NOT_AVAILABLE;
}
return SavePrefFileInternal(aFile);
}
static nsresult
ReadExtensionPrefs(nsIFile *aFile)
{
nsresult rv;
nsCOMPtr<nsIZipReader> reader = do_CreateInstance(kZipReaderCID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = reader->Open(aFile);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIUTF8StringEnumerator> files;
rv = reader->FindEntries(nsDependentCString("defaults/preferences/*.(J|j)(S|s)$"),
getter_AddRefs(files));
NS_ENSURE_SUCCESS(rv, rv);
char buffer[4096];
bool more;
while (NS_SUCCEEDED(rv = files->HasMore(&more)) && more) {
nsAutoCString entry;
rv = files->GetNext(entry);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIInputStream> stream;
rv = reader->GetInputStream(entry, getter_AddRefs(stream));
NS_ENSURE_SUCCESS(rv, rv);
uint64_t avail;
uint32_t read;
PrefParseState ps;
PREF_InitParseState(&ps, PREF_ReaderCallback, ReportToConsole, nullptr);
while (NS_SUCCEEDED(rv = stream->Available(&avail)) && avail) {
rv = stream->Read(buffer, 4096, &read);
if (NS_FAILED(rv)) {
NS_WARNING("Pref stream read failed");
break;
}
PREF_ParseBuf(&ps, buffer, read);
}
PREF_FinalizeParseState(&ps);
}
return rv;
}
void
Preferences::SetPreference(const PrefSetting& aPref)
{
pref_SetPref(aPref);
}
void
Preferences::GetPreference(PrefSetting* aPref)
{
PrefHashEntry *entry = pref_HashTableLookup(aPref->name().get());
if (!entry)
return;
if (pref_EntryHasAdvisablySizedValues(entry)) {
pref_GetPrefFromEntry(entry, aPref);
}
}
void
Preferences::GetPreferences(InfallibleTArray<PrefSetting>* aPrefs)
{
aPrefs->SetCapacity(gHashTable->Capacity());
for (auto iter = gHashTable->Iter(); !iter.Done(); iter.Next()) {
auto entry = static_cast<PrefHashEntry*>(iter.Get());
if (!pref_EntryHasAdvisablySizedValues(entry)) {
continue;
}
dom::PrefSetting *pref = aPrefs->AppendElement();
pref_GetPrefFromEntry(entry, pref);
}
}
NS_IMETHODIMP
Preferences::GetBranch(const char *aPrefRoot, nsIPrefBranch **_retval)
{
nsresult rv;
if ((nullptr != aPrefRoot) && (*aPrefRoot != '\0')) {
// TODO: - cache this stuff and allow consumers to share branches (hold weak references I think)
RefPtr<nsPrefBranch> prefBranch = new nsPrefBranch(aPrefRoot, false);
prefBranch.forget(_retval);
rv = NS_OK;
} else {
// special case caching the default root
nsCOMPtr<nsIPrefBranch> root(sRootBranch);
root.forget(_retval);
rv = NS_OK;
}
return rv;
}
NS_IMETHODIMP
Preferences::GetDefaultBranch(const char *aPrefRoot, nsIPrefBranch **_retval)
{
if (!aPrefRoot || !aPrefRoot[0]) {
nsCOMPtr<nsIPrefBranch> root(sDefaultRootBranch);
root.forget(_retval);
return NS_OK;
}
// TODO: - cache this stuff and allow consumers to share branches (hold weak references I think)
RefPtr<nsPrefBranch> prefBranch = new nsPrefBranch(aPrefRoot, true);
if (!prefBranch)
return NS_ERROR_OUT_OF_MEMORY;
prefBranch.forget(_retval);
return NS_OK;
}
NS_IMETHODIMP
Preferences::GetDirty(bool *_retval) {
*_retval = mDirty;
return NS_OK;
}
nsresult
Preferences::NotifyServiceObservers(const char *aTopic)
{
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (!observerService)
return NS_ERROR_FAILURE;
nsISupports *subject = (nsISupports *)((nsIPrefService *)this);
observerService->NotifyObservers(subject, aTopic, nullptr);
return NS_OK;
}
nsresult
Preferences::UseDefaultPrefFile()
{
nsCOMPtr<nsIFile> aFile;
nsresult rv = NS_GetSpecialDirectory(NS_APP_PREFS_50_FILE, getter_AddRefs(aFile));
if (NS_SUCCEEDED(rv)) {
rv = ReadAndOwnUserPrefFile(aFile);
// Most likely cause of failure here is that the file didn't
// exist, so save a new one. mUserPrefReadFailed will be
// used to catch an error in actually reading the file.
if (NS_FAILED(rv)) {
if (NS_FAILED(SavePrefFileInternal(aFile)))
NS_ERROR("Failed to save new shared pref file");
else
rv = NS_OK;
}
}
return rv;
}
nsresult
Preferences::UseUserPrefFile()
{
nsresult rv = NS_OK;
nsCOMPtr<nsIFile> aFile;
nsDependentCString prefsDirProp(NS_APP_PREFS_50_DIR);
rv = NS_GetSpecialDirectory(prefsDirProp.get(), getter_AddRefs(aFile));
if (NS_SUCCEEDED(rv) && aFile) {
rv = aFile->AppendNative(NS_LITERAL_CSTRING("user.js"));
if (NS_SUCCEEDED(rv)) {
bool exists = false;
aFile->Exists(&exists);
if (exists) {
rv = openPrefFile(aFile);
} else {
rv = NS_ERROR_FILE_NOT_FOUND;
}
}
}
return rv;
}
nsresult
Preferences::MakeBackupPrefFile(nsIFile *aFile)
{
// Example: this copies "prefs.js" to "Invalidprefs.js" in the same directory.
// "Invalidprefs.js" is removed if it exists, prior to making the copy.
nsAutoString newFilename;
nsresult rv = aFile->GetLeafName(newFilename);
NS_ENSURE_SUCCESS(rv, rv);
newFilename.Insert(NS_LITERAL_STRING("Invalid"), 0);
nsCOMPtr<nsIFile> newFile;
rv = aFile->GetParent(getter_AddRefs(newFile));
NS_ENSURE_SUCCESS(rv, rv);
rv = newFile->Append(newFilename);
NS_ENSURE_SUCCESS(rv, rv);
bool exists = false;
newFile->Exists(&exists);
if (exists) {
rv = newFile->Remove(false);
NS_ENSURE_SUCCESS(rv, rv);
}
rv = aFile->CopyTo(nullptr, newFilename);
NS_ENSURE_SUCCESS(rv, rv);
return rv;
}
nsresult
Preferences::ReadAndOwnUserPrefFile(nsIFile *aFile)
{
NS_ENSURE_ARG(aFile);
if (mCurrentFile == aFile)
return NS_OK;
mCurrentFile = aFile;
nsresult rv = NS_OK;
bool exists = false;
mCurrentFile->Exists(&exists);
if (exists) {
rv = openPrefFile(mCurrentFile);
if (NS_FAILED(rv)) {
// Save a backup copy of the current (invalid) prefs file, since all prefs
// from the error line to the end of the file will be lost (bug 361102).
// TODO we should notify the user about it (bug 523725).
MakeBackupPrefFile(mCurrentFile);
}
} else {
rv = NS_ERROR_FILE_NOT_FOUND;
}
return rv;
}
nsresult
Preferences::SavePrefFileInternal(nsIFile *aFile)
{
if (nullptr == aFile) {
// the mDirty flag tells us if we should write to mCurrentFile
// we only check this flag when the caller wants to write to the default
if (!mDirty) {
return NS_OK;
}
// It's possible that we never got a prefs file.
nsresult rv = NS_OK;
if (mCurrentFile)
rv = WritePrefFile(mCurrentFile);
return rv;
} else {
return WritePrefFile(aFile);
}
}
nsresult
Preferences::WritePrefFile(nsIFile* aFile)
{
nsCOMPtr<nsIOutputStream> outStreamSink;
nsCOMPtr<nsIOutputStream> outStream;
uint32_t writeAmount;
nsresult rv;
if (!gHashTable)
return NS_ERROR_NOT_INITIALIZED;
// execute a "safe" save by saving through a tempfile
rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(outStreamSink),
aFile,
-1,
0600);
if (NS_FAILED(rv))
return rv;
rv = NS_NewBufferedOutputStream(getter_AddRefs(outStream), outStreamSink, 4096);
if (NS_FAILED(rv))
return rv;
// get the lines that we're supposed to be writing to the file
uint32_t prefCount;
UniquePtr<char*[]> valueArray = pref_savePrefs(gHashTable, &prefCount);
/* Sort the preferences to make a readable file on disk */
NS_QuickSort(valueArray.get(), prefCount, sizeof(char *),
pref_CompareStrings, nullptr);
// write out the file header
outStream->Write(kPrefFileHeader, sizeof(kPrefFileHeader) - 1, &writeAmount);
for (uint32_t valueIdx = 0; valueIdx < prefCount; valueIdx++) {
char*& pref = valueArray[valueIdx];
MOZ_ASSERT(pref);
outStream->Write(pref, strlen(pref), &writeAmount);
outStream->Write(NS_LINEBREAK, NS_LINEBREAK_LEN, &writeAmount);
free(pref);
pref = nullptr;
}
// tell the safe output stream to overwrite the real prefs file
// (it'll abort if there were any errors during writing)
nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(outStream);
NS_ASSERTION(safeStream, "expected a safe output stream!");
if (safeStream) {
rv = safeStream->Finish();
if (NS_FAILED(rv)) {
NS_WARNING("failed to save prefs file! possible data loss");
return rv;
}
}
mDirty = false;
return NS_OK;
}
static nsresult openPrefFile(nsIFile* aFile)
{
nsCOMPtr<nsIInputStream> inStr;
nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(inStr), aFile);
if (NS_FAILED(rv))
return rv;
int64_t fileSize64;
rv = aFile->GetFileSize(&fileSize64);
if (NS_FAILED(rv))
return rv;
NS_ENSURE_TRUE(fileSize64 <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
uint32_t fileSize = (uint32_t)fileSize64;
auto fileBuffer = MakeUniqueFallible<char[]>(fileSize);
if (fileBuffer == nullptr)
return NS_ERROR_OUT_OF_MEMORY;
PrefParseState ps;
PREF_InitParseState(&ps, PREF_ReaderCallback, ReportToConsole, nullptr);
// Read is not guaranteed to return a buf the size of fileSize,
// but usually will.
nsresult rv2 = NS_OK;
uint32_t offset = 0;
for (;;) {
uint32_t amtRead = 0;
rv = inStr->Read(fileBuffer.get(), fileSize, &amtRead);
if (NS_FAILED(rv) || amtRead == 0)
break;
if (!PREF_ParseBuf(&ps, fileBuffer.get(), amtRead))
rv2 = NS_ERROR_FILE_CORRUPTED;
offset += amtRead;
if (offset == fileSize) {
break;
}
}
PREF_FinalizeParseState(&ps);
return NS_FAILED(rv) ? rv : rv2;
}
/*
* some stuff that gets called from Pref_Init()
*/
static int
pref_CompareFileNames(nsIFile* aFile1, nsIFile* aFile2, void* /*unused*/)
{
nsAutoCString filename1, filename2;
aFile1->GetNativeLeafName(filename1);
aFile2->GetNativeLeafName(filename2);
return Compare(filename2, filename1);
}
/**
* Load default pref files from a directory. The files in the
* directory are sorted reverse-alphabetically; a set of "special file
* names" may be specified which are loaded after all the others.
*/
static nsresult
pref_LoadPrefsInDir(nsIFile* aDir, char const *const *aSpecialFiles, uint32_t aSpecialFilesCount)
{
nsresult rv, rv2;
bool hasMoreElements;
nsCOMPtr<nsISimpleEnumerator> dirIterator;
// this may fail in some normal cases, such as embedders who do not use a GRE
rv = aDir->GetDirectoryEntries(getter_AddRefs(dirIterator));
if (NS_FAILED(rv)) {
// If the directory doesn't exist, then we have no reason to complain. We
// loaded everything (and nothing) successfully.
if (rv == NS_ERROR_FILE_NOT_FOUND || rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)
rv = NS_OK;
return rv;
}
rv = dirIterator->HasMoreElements(&hasMoreElements);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMArray<nsIFile> prefFiles(INITIAL_PREF_FILES);
nsCOMArray<nsIFile> specialFiles(aSpecialFilesCount);
nsCOMPtr<nsIFile> prefFile;
while (hasMoreElements && NS_SUCCEEDED(rv)) {
nsAutoCString leafName;
nsCOMPtr<nsISupports> supports;
rv = dirIterator->GetNext(getter_AddRefs(supports));
prefFile = do_QueryInterface(supports);
if (NS_FAILED(rv)) {
break;
}
prefFile->GetNativeLeafName(leafName);
NS_ASSERTION(!leafName.IsEmpty(), "Failure in default prefs: directory enumerator returned empty file?");
// Skip non-js files
if (StringEndsWith(leafName, NS_LITERAL_CSTRING(".js"),
nsCaseInsensitiveCStringComparator())) {
bool shouldParse = true;
// separate out special files
for (uint32_t i = 0; i < aSpecialFilesCount; ++i) {
if (leafName.Equals(nsDependentCString(aSpecialFiles[i]))) {
shouldParse = false;
// special files should be process in order; we put them into
// the array by index; this can make the array sparse
specialFiles.ReplaceObjectAt(prefFile, i);
}
}
if (shouldParse) {
prefFiles.AppendObject(prefFile);
}
}
rv = dirIterator->HasMoreElements(&hasMoreElements);
}
if (prefFiles.Count() + specialFiles.Count() == 0) {
NS_WARNING("No default pref files found.");
if (NS_SUCCEEDED(rv)) {
rv = NS_SUCCESS_FILE_DIRECTORY_EMPTY;
}
return rv;
}
prefFiles.Sort(pref_CompareFileNames, nullptr);
uint32_t arrayCount = prefFiles.Count();
uint32_t i;
for (i = 0; i < arrayCount; ++i) {
rv2 = openPrefFile(prefFiles[i]);
if (NS_FAILED(rv2)) {
NS_ERROR("Default pref file not parsed successfully.");
rv = rv2;
}
}
arrayCount = specialFiles.Count();
for (i = 0; i < arrayCount; ++i) {
// this may be a sparse array; test before parsing
nsIFile* file = specialFiles[i];
if (file) {
rv2 = openPrefFile(file);
if (NS_FAILED(rv2)) {
NS_ERROR("Special default pref file not parsed successfully.");
rv = rv2;
}
}
}
return rv;
}
static nsresult pref_LoadPrefsInDirList(const char *listId)
{
nsresult rv;
nsCOMPtr<nsIProperties> dirSvc(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv))
return rv;
nsCOMPtr<nsISimpleEnumerator> list;
dirSvc->Get(listId,
NS_GET_IID(nsISimpleEnumerator),
getter_AddRefs(list));
if (!list)
return NS_OK;
bool hasMore;
while (NS_SUCCEEDED(list->HasMoreElements(&hasMore)) && hasMore) {
nsCOMPtr<nsISupports> elem;
list->GetNext(getter_AddRefs(elem));
if (!elem)
continue;
nsCOMPtr<nsIFile> path = do_QueryInterface(elem);
if (!path)
continue;
nsAutoCString leaf;
path->GetNativeLeafName(leaf);
// Do we care if a file provided by this process fails to load?
if (Substring(leaf, leaf.Length() - 4).EqualsLiteral(".xpi"))
ReadExtensionPrefs(path);
else
pref_LoadPrefsInDir(path, nullptr, 0);
}
return NS_OK;
}
static nsresult pref_ReadPrefFromJar(nsZipArchive* jarReader, const char *name)
{
nsZipItemPtr<char> manifest(jarReader, name, true);
NS_ENSURE_TRUE(manifest.Buffer(), NS_ERROR_NOT_AVAILABLE);
PrefParseState ps;
PREF_InitParseState(&ps, PREF_ReaderCallback, ReportToConsole, nullptr);
PREF_ParseBuf(&ps, manifest, manifest.Length());
PREF_FinalizeParseState(&ps);
return NS_OK;
}
//----------------------------------------------------------------------------------------
// Initialize default preference JavaScript buffers from
// appropriate TEXT resources
//----------------------------------------------------------------------------------------
static nsresult pref_InitInitialObjects()
{
nsresult rv;
// In omni.jar case, we load the following prefs:
// - jar:$gre/omni.jar!/greprefs.js
// - jar:$gre/omni.jar!/defaults/pref/*.js
// In non omni.jar case, we load:
// - $gre/greprefs.js
//
// In both cases, we also load:
// - $gre/defaults/pref/*.js
// This is kept for bug 591866 (channel-prefs.js should not be in omni.jar)
// on $app == $gre case ; we load all files instead of channel-prefs.js only
// to have the same behaviour as $app != $gre, where this is required as
// a supported location for GRE preferences.
//
// When $app != $gre, we additionally load, in omni.jar case:
// - jar:$app/omni.jar!/defaults/preferences/*.js
// - $app/defaults/preferences/*.js
// and in non omni.jar case:
// - $app/defaults/preferences/*.js
// When $app == $gre, we additionally load, in omni.jar case:
// - jar:$gre/omni.jar!/defaults/preferences/*.js
// Thus, in omni.jar case, we always load app-specific default preferences
// from omni.jar, whether or not $app == $gre.
nsZipFind *findPtr;
nsAutoPtr<nsZipFind> find;
nsTArray<nsCString> prefEntries;
const char *entryName;
uint16_t entryNameLen;
RefPtr<nsZipArchive> jarReader = mozilla::Omnijar::GetReader(mozilla::Omnijar::GRE);
if (jarReader) {
// Load jar:$gre/omni.jar!/greprefs.js
rv = pref_ReadPrefFromJar(jarReader, "greprefs.js");
NS_ENSURE_SUCCESS(rv, rv);
// Load jar:$gre/omni.jar!/defaults/pref/*.js
rv = jarReader->FindInit("defaults/pref/*.js$", &findPtr);
NS_ENSURE_SUCCESS(rv, rv);
find = findPtr;
while (NS_SUCCEEDED(find->FindNext(&entryName, &entryNameLen))) {
prefEntries.AppendElement(Substring(entryName, entryNameLen));
}
prefEntries.Sort();
for (uint32_t i = prefEntries.Length(); i--; ) {
rv = pref_ReadPrefFromJar(jarReader, prefEntries[i].get());
if (NS_FAILED(rv))
NS_WARNING("Error parsing preferences.");
}
} else {
// Load $gre/greprefs.js
nsCOMPtr<nsIFile> greprefsFile;
rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(greprefsFile));
NS_ENSURE_SUCCESS(rv, rv);
rv = greprefsFile->AppendNative(NS_LITERAL_CSTRING("greprefs.js"));
NS_ENSURE_SUCCESS(rv, rv);
rv = openPrefFile(greprefsFile);
if (NS_FAILED(rv))
NS_WARNING("Error parsing GRE default preferences. Is this an old-style embedding app?");
}
// Load $gre/defaults/pref/*.js
nsCOMPtr<nsIFile> defaultPrefDir;
rv = NS_GetSpecialDirectory(NS_APP_PREF_DEFAULTS_50_DIR, getter_AddRefs(defaultPrefDir));
NS_ENSURE_SUCCESS(rv, rv);
/* these pref file names should not be used: we process them after all other application pref files for backwards compatibility */
static const char* specialFiles[] = {
#if defined(XP_MACOSX)
"macprefs.js"
#elif defined(XP_WIN)
"winpref.js"
#elif defined(XP_UNIX)
"unix.js"
#endif
};
rv = pref_LoadPrefsInDir(defaultPrefDir, specialFiles, ArrayLength(specialFiles));
if (NS_FAILED(rv))
NS_WARNING("Error parsing application default preferences.");
// Load jar:$app/omni.jar!/defaults/preferences/*.js
// or jar:$gre/omni.jar!/defaults/preferences/*.js.
RefPtr<nsZipArchive> appJarReader = mozilla::Omnijar::GetReader(mozilla::Omnijar::APP);
// GetReader(mozilla::Omnijar::APP) returns null when $app == $gre, in which
// case we look for app-specific default preferences in $gre.
if (!appJarReader)
appJarReader = mozilla::Omnijar::GetReader(mozilla::Omnijar::GRE);
if (appJarReader) {
rv = appJarReader->FindInit("defaults/preferences/*.js$", &findPtr);
NS_ENSURE_SUCCESS(rv, rv);
find = findPtr;
prefEntries.Clear();
while (NS_SUCCEEDED(find->FindNext(&entryName, &entryNameLen))) {
prefEntries.AppendElement(Substring(entryName, entryNameLen));
}
prefEntries.Sort();
for (uint32_t i = prefEntries.Length(); i--; ) {
rv = pref_ReadPrefFromJar(appJarReader, prefEntries[i].get());
if (NS_FAILED(rv))
NS_WARNING("Error parsing preferences.");
}
}
rv = pref_LoadPrefsInDirList(NS_APP_PREFS_DEFAULTS_DIR_LIST);
NS_ENSURE_SUCCESS(rv, rv);
// Set up the correct default for toolkit.telemetry.enabled.
// If this build has MOZ_TELEMETRY_ON_BY_DEFAULT *or* we're on the beta
// channel, telemetry is on by default, otherwise not. This is necessary
// so that beta users who are testing final release builds don't flipflop
// defaults.
if (Preferences::GetDefaultType(kTelemetryPref) == nsIPrefBranch::PREF_INVALID) {
bool prerelease = false;
#ifdef MOZ_TELEMETRY_ON_BY_DEFAULT
prerelease = true;
#else
if (Preferences::GetDefaultCString(kChannelPref).EqualsLiteral("beta")) {
prerelease = true;
}
#endif
PREF_SetBoolPref(kTelemetryPref, prerelease, true);
}
NS_CreateServicesFromCategory(NS_PREFSERVICE_APPDEFAULTS_TOPIC_ID,
nullptr, NS_PREFSERVICE_APPDEFAULTS_TOPIC_ID);
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (!observerService)
return NS_ERROR_FAILURE;
observerService->NotifyObservers(nullptr, NS_PREFSERVICE_APPDEFAULTS_TOPIC_ID, nullptr);
return pref_LoadPrefsInDirList(NS_EXT_PREFS_DEFAULTS_DIR_LIST);
}
/******************************************************************************
*
* static utilities
*
******************************************************************************/
// static
nsresult
Preferences::GetBool(const char* aPref, bool* aResult)
{
NS_PRECONDITION(aResult, "aResult must not be NULL");
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
return PREF_GetBoolPref(aPref, aResult, false);
}
// static
nsresult
Preferences::GetInt(const char* aPref, int32_t* aResult)
{
NS_PRECONDITION(aResult, "aResult must not be NULL");
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
return PREF_GetIntPref(aPref, aResult, false);
}
// static
nsresult
Preferences::GetFloat(const char* aPref, float* aResult)
{
NS_PRECONDITION(aResult, "aResult must not be NULL");
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
nsAutoCString result;
nsresult rv = PREF_CopyCharPref(aPref, getter_Copies(result), false);
if (NS_SUCCEEDED(rv)) {
*aResult = result.ToFloat(&rv);
}
return rv;
}
// static
nsAdoptingCString
Preferences::GetCString(const char* aPref)
{
nsAdoptingCString result;
PREF_CopyCharPref(aPref, getter_Copies(result), false);
return result;
}
// static
nsAdoptingString
Preferences::GetString(const char* aPref)
{
nsAdoptingString result;
GetString(aPref, &result);
return result;
}
// static
nsresult
Preferences::GetCString(const char* aPref, nsACString* aResult)
{
NS_PRECONDITION(aResult, "aResult must not be NULL");
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
nsAutoCString result;
nsresult rv = PREF_CopyCharPref(aPref, getter_Copies(result), false);
if (NS_SUCCEEDED(rv)) {
*aResult = result;
}
return rv;
}
// static
nsresult
Preferences::GetString(const char* aPref, nsAString* aResult)
{
NS_PRECONDITION(aResult, "aResult must not be NULL");
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
nsAutoCString result;
nsresult rv = PREF_CopyCharPref(aPref, getter_Copies(result), false);
if (NS_SUCCEEDED(rv)) {
CopyUTF8toUTF16(result, *aResult);
}
return rv;
}
// static
nsAdoptingCString
Preferences::GetLocalizedCString(const char* aPref)
{
nsAdoptingCString result;
GetLocalizedCString(aPref, &result);
return result;
}
// static
nsAdoptingString
Preferences::GetLocalizedString(const char* aPref)
{
nsAdoptingString result;
GetLocalizedString(aPref, &result);
return result;
}
// static
nsresult
Preferences::GetLocalizedCString(const char* aPref, nsACString* aResult)
{
NS_PRECONDITION(aResult, "aResult must not be NULL");
nsAutoString result;
nsresult rv = GetLocalizedString(aPref, &result);
if (NS_SUCCEEDED(rv)) {
CopyUTF16toUTF8(result, *aResult);
}
return rv;
}
// static
nsresult
Preferences::GetLocalizedString(const char* aPref, nsAString* aResult)
{
NS_PRECONDITION(aResult, "aResult must not be NULL");
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
nsCOMPtr<nsIPrefLocalizedString> prefLocalString;
nsresult rv = sRootBranch->GetComplexValue(aPref,
NS_GET_IID(nsIPrefLocalizedString),
getter_AddRefs(prefLocalString));
if (NS_SUCCEEDED(rv)) {
NS_ASSERTION(prefLocalString, "Succeeded but the result is NULL");
prefLocalString->GetData(getter_Copies(*aResult));
}
return rv;
}
// static
nsresult
Preferences::GetComplex(const char* aPref, const nsIID &aType, void** aResult)
{
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
return sRootBranch->GetComplexValue(aPref, aType, aResult);
}
// static
nsresult
Preferences::SetCString(const char* aPref, const char* aValue)
{
ENSURE_MAIN_PROCESS("Cannot SetCString from content process:", aPref);
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
return PREF_SetCharPref(aPref, aValue, false);
}
// static
nsresult
Preferences::SetCString(const char* aPref, const nsACString &aValue)
{
ENSURE_MAIN_PROCESS("Cannot SetCString from content process:", aPref);
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
return PREF_SetCharPref(aPref, PromiseFlatCString(aValue).get(), false);
}
// static
nsresult
Preferences::SetString(const char* aPref, const char16ptr_t aValue)
{
ENSURE_MAIN_PROCESS("Cannot SetString from content process:", aPref);
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
return PREF_SetCharPref(aPref, NS_ConvertUTF16toUTF8(aValue).get(), false);
}
// static
nsresult
Preferences::SetString(const char* aPref, const nsAString &aValue)
{
ENSURE_MAIN_PROCESS("Cannot SetString from content process:", aPref);
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
return PREF_SetCharPref(aPref, NS_ConvertUTF16toUTF8(aValue).get(), false);
}
// static
nsresult
Preferences::SetBool(const char* aPref, bool aValue)
{
ENSURE_MAIN_PROCESS("Cannot SetBool from content process:", aPref);
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
return PREF_SetBoolPref(aPref, aValue, false);
}
// static
nsresult
Preferences::SetInt(const char* aPref, int32_t aValue)
{
ENSURE_MAIN_PROCESS("Cannot SetInt from content process:", aPref);
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
return PREF_SetIntPref(aPref, aValue, false);
}
// static
nsresult
Preferences::SetFloat(const char* aPref, float aValue)
{
return SetCString(aPref, nsPrintfCString("%f", aValue).get());
}
// static
nsresult
Preferences::SetComplex(const char* aPref, const nsIID &aType,
nsISupports* aValue)
{
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
return sRootBranch->SetComplexValue(aPref, aType, aValue);
}
// static
nsresult
Preferences::ClearUser(const char* aPref)
{
ENSURE_MAIN_PROCESS("Cannot ClearUser from content process:", aPref);
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
return PREF_ClearUserPref(aPref);
}
// static
bool
Preferences::HasUserValue(const char* aPref)
{
NS_ENSURE_TRUE(InitStaticMembers(), false);
return PREF_HasUserPref(aPref);
}
// static
int32_t
Preferences::GetType(const char* aPref)
{
NS_ENSURE_TRUE(InitStaticMembers(), nsIPrefBranch::PREF_INVALID);
int32_t result;
return NS_SUCCEEDED(sRootBranch->GetPrefType(aPref, &result)) ?
result : nsIPrefBranch::PREF_INVALID;
}
// static
nsresult
Preferences::AddStrongObserver(nsIObserver* aObserver,
const char* aPref)
{
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
return sRootBranch->AddObserver(aPref, aObserver, false);
}
// static
nsresult
Preferences::AddWeakObserver(nsIObserver* aObserver,
const char* aPref)
{
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
return sRootBranch->AddObserver(aPref, aObserver, true);
}
// static
nsresult
Preferences::RemoveObserver(nsIObserver* aObserver,
const char* aPref)
{
if (!sPreferences && sShutdown) {
return NS_OK; // Observers have been released automatically.
}
NS_ENSURE_TRUE(sPreferences, NS_ERROR_NOT_AVAILABLE);
return sRootBranch->RemoveObserver(aPref, aObserver);
}
// static
nsresult
Preferences::AddStrongObservers(nsIObserver* aObserver,
const char** aPrefs)
{
for (uint32_t i = 0; aPrefs[i]; i++) {
nsresult rv = AddStrongObserver(aObserver, aPrefs[i]);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
// static
nsresult
Preferences::AddWeakObservers(nsIObserver* aObserver,
const char** aPrefs)
{
for (uint32_t i = 0; aPrefs[i]; i++) {
nsresult rv = AddWeakObserver(aObserver, aPrefs[i]);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
// static
nsresult
Preferences::RemoveObservers(nsIObserver* aObserver,
const char** aPrefs)
{
if (!sPreferences && sShutdown) {
return NS_OK; // Observers have been released automatically.
}
NS_ENSURE_TRUE(sPreferences, NS_ERROR_NOT_AVAILABLE);
for (uint32_t i = 0; aPrefs[i]; i++) {
nsresult rv = RemoveObserver(aObserver, aPrefs[i]);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
// static
nsresult
Preferences::RegisterCallback(PrefChangedFunc aCallback,
const char* aPref,
void* aClosure,
MatchKind aMatchKind)
{
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
ValueObserverHashKey hashKey(aPref, aCallback, aMatchKind);
RefPtr<ValueObserver> observer;
gObserverTable->Get(&hashKey, getter_AddRefs(observer));
if (observer) {
observer->AppendClosure(aClosure);
return NS_OK;
}
observer = new ValueObserver(aPref, aCallback, aMatchKind);
observer->AppendClosure(aClosure);
nsresult rv = AddStrongObserver(observer, aPref);
NS_ENSURE_SUCCESS(rv, rv);
gObserverTable->Put(observer, observer);
return NS_OK;
}
// static
nsresult
Preferences::RegisterCallbackAndCall(PrefChangedFunc aCallback,
const char* aPref,
void* aClosure,
MatchKind aMatchKind)
{
nsresult rv = RegisterCallback(aCallback, aPref, aClosure, aMatchKind);
if (NS_SUCCEEDED(rv)) {
(*aCallback)(aPref, aClosure);
}
return rv;
}
// static
nsresult
Preferences::UnregisterCallback(PrefChangedFunc aCallback,
const char* aPref,
void* aClosure,
MatchKind aMatchKind)
{
if (!sPreferences && sShutdown) {
return NS_OK; // Observers have been released automatically.
}
NS_ENSURE_TRUE(sPreferences, NS_ERROR_NOT_AVAILABLE);
ValueObserverHashKey hashKey(aPref, aCallback, aMatchKind);
RefPtr<ValueObserver> observer;
gObserverTable->Get(&hashKey, getter_AddRefs(observer));
if (!observer) {
return NS_OK;
}
observer->RemoveClosure(aClosure);
if (observer->HasNoClosures()) {
// Delete the callback since its list of closures is empty.
gObserverTable->Remove(observer);
}
return NS_OK;
}
static void BoolVarChanged(const char* aPref, void* aClosure)
{
CacheData* cache = static_cast<CacheData*>(aClosure);
*((bool*)cache->cacheLocation) =
Preferences::GetBool(aPref, cache->defaultValueBool);
}
// static
nsresult
Preferences::AddBoolVarCache(bool* aCache,
const char* aPref,
bool aDefault)
{
NS_ASSERTION(aCache, "aCache must not be NULL");
#ifdef DEBUG
AssertNotAlreadyCached("bool", aPref, aCache);
#endif
*aCache = GetBool(aPref, aDefault);
CacheData* data = new CacheData();
data->cacheLocation = aCache;
data->defaultValueBool = aDefault;
gCacheData->AppendElement(data);
return RegisterCallback(BoolVarChanged, aPref, data, ExactMatch);
}
static void IntVarChanged(const char* aPref, void* aClosure)
{
CacheData* cache = static_cast<CacheData*>(aClosure);
*((int32_t*)cache->cacheLocation) =
Preferences::GetInt(aPref, cache->defaultValueInt);
}
// static
nsresult
Preferences::AddIntVarCache(int32_t* aCache,
const char* aPref,
int32_t aDefault)
{
NS_ASSERTION(aCache, "aCache must not be NULL");
#ifdef DEBUG
AssertNotAlreadyCached("int", aPref, aCache);
#endif
*aCache = Preferences::GetInt(aPref, aDefault);
CacheData* data = new CacheData();
data->cacheLocation = aCache;
data->defaultValueInt = aDefault;
gCacheData->AppendElement(data);
return RegisterCallback(IntVarChanged, aPref, data, ExactMatch);
}
static void UintVarChanged(const char* aPref, void* aClosure)
{
CacheData* cache = static_cast<CacheData*>(aClosure);
*((uint32_t*)cache->cacheLocation) =
Preferences::GetUint(aPref, cache->defaultValueUint);
}
// static
nsresult
Preferences::AddUintVarCache(uint32_t* aCache,
const char* aPref,
uint32_t aDefault)
{
NS_ASSERTION(aCache, "aCache must not be NULL");
#ifdef DEBUG
AssertNotAlreadyCached("uint", aPref, aCache);
#endif
*aCache = Preferences::GetUint(aPref, aDefault);
CacheData* data = new CacheData();
data->cacheLocation = aCache;
data->defaultValueUint = aDefault;
gCacheData->AppendElement(data);
return RegisterCallback(UintVarChanged, aPref, data, ExactMatch);
}
template <MemoryOrdering Order>
static void AtomicUintVarChanged(const char* aPref, void* aClosure)
{
CacheData* cache = static_cast<CacheData*>(aClosure);
*((Atomic<uint32_t, Order>*)cache->cacheLocation) =
Preferences::GetUint(aPref, cache->defaultValueUint);
}
template <MemoryOrdering Order>
// static
nsresult
Preferences::AddAtomicUintVarCache(Atomic<uint32_t, Order>* aCache,
const char* aPref,
uint32_t aDefault)
{
NS_ASSERTION(aCache, "aCache must not be NULL");
#ifdef DEBUG
AssertNotAlreadyCached("uint", aPref, aCache);
#endif
*aCache = Preferences::GetUint(aPref, aDefault);
CacheData* data = new CacheData();
data->cacheLocation = aCache;
data->defaultValueUint = aDefault;
gCacheData->AppendElement(data);
return RegisterCallback(AtomicUintVarChanged<Order>, aPref, data, ExactMatch);
}
// Since the definition of this template function is not in a header file,
// we need to explicitly specify the instantiations that are required.
// Currently only the order=Relaxed variant is needed.
template
nsresult Preferences::AddAtomicUintVarCache(Atomic<uint32_t,Relaxed>*,
const char*, uint32_t);
static void FloatVarChanged(const char* aPref, void* aClosure)
{
CacheData* cache = static_cast<CacheData*>(aClosure);
*((float*)cache->cacheLocation) =
Preferences::GetFloat(aPref, cache->defaultValueFloat);
}
// static
nsresult
Preferences::AddFloatVarCache(float* aCache,
const char* aPref,
float aDefault)
{
NS_ASSERTION(aCache, "aCache must not be NULL");
#ifdef DEBUG
AssertNotAlreadyCached("float", aPref, aCache);
#endif
*aCache = Preferences::GetFloat(aPref, aDefault);
CacheData* data = new CacheData();
data->cacheLocation = aCache;
data->defaultValueFloat = aDefault;
gCacheData->AppendElement(data);
return RegisterCallback(FloatVarChanged, aPref, data, ExactMatch);
}
// static
nsresult
Preferences::GetDefaultBool(const char* aPref, bool* aResult)
{
NS_PRECONDITION(aResult, "aResult must not be NULL");
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
return PREF_GetBoolPref(aPref, aResult, true);
}
// static
nsresult
Preferences::GetDefaultInt(const char* aPref, int32_t* aResult)
{
NS_PRECONDITION(aResult, "aResult must not be NULL");
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
return PREF_GetIntPref(aPref, aResult, true);
}
// static
nsresult
Preferences::GetDefaultCString(const char* aPref, nsACString* aResult)
{
NS_PRECONDITION(aResult, "aResult must not be NULL");
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
nsAutoCString result;
nsresult rv = PREF_CopyCharPref(aPref, getter_Copies(result), true);
if (NS_SUCCEEDED(rv)) {
*aResult = result;
}
return rv;
}
// static
nsresult
Preferences::GetDefaultString(const char* aPref, nsAString* aResult)
{
NS_PRECONDITION(aResult, "aResult must not be NULL");
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
nsAutoCString result;
nsresult rv = PREF_CopyCharPref(aPref, getter_Copies(result), true);
if (NS_SUCCEEDED(rv)) {
CopyUTF8toUTF16(result, *aResult);
}
return rv;
}
// static
nsresult
Preferences::GetDefaultLocalizedCString(const char* aPref,
nsACString* aResult)
{
nsAutoString result;
nsresult rv = GetDefaultLocalizedString(aPref, &result);
if (NS_SUCCEEDED(rv)) {
CopyUTF16toUTF8(result, *aResult);
}
return rv;
}
// static
nsresult
Preferences::GetDefaultLocalizedString(const char* aPref,
nsAString* aResult)
{
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
nsCOMPtr<nsIPrefLocalizedString> prefLocalString;
nsresult rv =
sDefaultRootBranch->GetComplexValue(aPref,
NS_GET_IID(nsIPrefLocalizedString),
getter_AddRefs(prefLocalString));
if (NS_SUCCEEDED(rv)) {
NS_ASSERTION(prefLocalString, "Succeeded but the result is NULL");
prefLocalString->GetData(getter_Copies(*aResult));
}
return rv;
}
// static
nsAdoptingString
Preferences::GetDefaultString(const char* aPref)
{
nsAdoptingString result;
GetDefaultString(aPref, &result);
return result;
}
// static
nsAdoptingCString
Preferences::GetDefaultCString(const char* aPref)
{
nsAdoptingCString result;
PREF_CopyCharPref(aPref, getter_Copies(result), true);
return result;
}
// static
nsAdoptingString
Preferences::GetDefaultLocalizedString(const char* aPref)
{
nsAdoptingString result;
GetDefaultLocalizedString(aPref, &result);
return result;
}
// static
nsAdoptingCString
Preferences::GetDefaultLocalizedCString(const char* aPref)
{
nsAdoptingCString result;
GetDefaultLocalizedCString(aPref, &result);
return result;
}
// static
nsresult
Preferences::GetDefaultComplex(const char* aPref, const nsIID &aType,
void** aResult)
{
NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
return sDefaultRootBranch->GetComplexValue(aPref, aType, aResult);
}
// static
int32_t
Preferences::GetDefaultType(const char* aPref)
{
NS_ENSURE_TRUE(InitStaticMembers(), nsIPrefBranch::PREF_INVALID);
int32_t result;
return NS_SUCCEEDED(sDefaultRootBranch->GetPrefType(aPref, &result)) ?
result : nsIPrefBranch::PREF_INVALID;
}
} // namespace mozilla
#undef ENSURE_MAIN_PROCESS