Bug 789945 - do async omt pref writes, r?bsmedberg
--- a/modules/libpref/Preferences.cpp
+++ b/modules/libpref/Preferences.cpp
@@ -23,16 +23,17 @@
#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 "nsITimer.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"
@@ -80,16 +81,38 @@ static nsresult openPrefFile(nsIFile* aF
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;
+
+static const uint32_t kPrefDelayedSaveTimeoutMs = 15000;
+
Preferences* Preferences::sPreferences = nullptr;
nsIPrefBranch* Preferences::sRootBranch = nullptr;
nsIPrefBranch* Preferences::sDefaultRootBranch = nullptr;
bool Preferences::sShutdown = false;
class ValueObserverHashKey : public PLDHashEntryHdr {
public:
typedef ValueObserverHashKey* KeyType;
@@ -170,16 +193,127 @@ ValueObserver::Observe(nsISupports *
NS_ConvertUTF16toUTF8 data(aData);
for (uint32_t i = 0; i < mClosures.Length(); i++) {
mCallback(data.get(), mClosures.ElementAt(i));
}
return NS_OK;
}
+class PreferenceRunnableSaveDone final : public nsRunnable
+{
+public:
+ PreferenceRunnableSaveDone() {}
+ NS_IMETHOD Run() {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (Preferences::sPreferences) {
+ Preferences::sPreferences->mCurrentOMTSaver = nullptr;
+ Preferences::sPreferences->mCurrentOMTSaveTimer = nullptr;
+ if (gDirty) {
+ Preferences::sPreferences->SavePrefsToDefaultFileOMT();
+ }
+ }
+ return NS_OK;
+ }
+};
+
+class PreferenceRunnableSaver final : public nsRunnable
+{
+public:
+ PreferenceRunnableSaver(nsIFile* aFile)
+ : mFile(aFile)
+ , mPrefs(pref_savePrefs(gHashTable, &mPrefCount))
+ {
+ }
+
+ NS_IMETHOD Run()
+ {
+ nsCOMPtr<nsIOutputStream> outStreamSink;
+ nsCOMPtr<nsIOutputStream> outStream;
+ uint32_t writeAmount;
+ nsresult rv;
+ // execute a "safe" save by saving through a tempfile
+ rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(outStreamSink), mFile,
+ -1, 0600);
+ if (NS_FAILED(rv)) {
+ // We set gDirty to false earlier, but we're not writing, so re-set it:
+ //XXXgijs this is racy, because I'm potentially setting it from
+ // non-mainthread. OTOH, there shouldn't be re-entrancy here
+ // because there's already a monitor and stuff. We only set to
+ // false when we re-start async writes (can't happen until
+ // after NotifyMainThreadIfNecessary() has finished on the main
+ // thread) and when we finish sync writes (can't happen until
+ // NotifyMainThreadIfNecessary() calls Notify() on the monitor).
+ gDirty = true;
+ NotifyMainThreadIfNecessary();
+ return rv;
+ }
+ rv = NS_NewBufferedOutputStream(getter_AddRefs(outStream), outStreamSink,
+ 4096);
+ if (NS_FAILED(rv)) {
+ gDirty = true;
+ NotifyMainThreadIfNecessary();
+ return rv;
+ }
+
+ /* Sort the preferences to make a readable file on disk */
+ NS_QuickSort(mPrefs.get(), mPrefCount, sizeof(char *), pref_CompareStrings,
+ nullptr);
+
+ // write out the file header
+ outStream->Write(kPrefFileHeader, sizeof(kPrefFileHeader) - 1,
+ &writeAmount);
+
+ for (uint32_t valueIdx = 0; valueIdx < mPrefCount; valueIdx++) {
+ char*& pref = mPrefs[valueIdx];
+ if (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");
+ }
+ }
+
+ NotifyMainThreadIfNecessary();
+ return rv;
+ }
+
+ void
+ NotifyMainThreadIfNecessary()
+ {
+ if (!NS_IsMainThread()) {
+ // Notify the main thread we're done
+ mozilla::MonitorAutoLock mon(Preferences::sPreferences->mOMTSaveMonitor);
+ Preferences::sPreferences->mAreWaitingForOMTSave = false;
+
+ mon.Notify();
+
+ // Now dispatch something to finish us off on the main thread,
+ // and if necessary start a new timer for a new save
+ NS_DispatchToMainThread(new PreferenceRunnableSaveDone());
+ }
+ }
+
+private:
+ nsCOMPtr<nsIFile> mFile;
+ UniquePtr<char*[]> mPrefs;
+ uint32_t mPrefCount;
+};
+
struct CacheData {
void* cacheLocation;
union {
bool defaultValueBool;
int32_t defaultValueInt;
uint32_t defaultValueUint;
float defaultValueFloat;
};
@@ -447,16 +581,18 @@ Preferences::Shutdown()
//-----------------------------------------------------------------------------
/*
* Constructor/Destructor
*/
Preferences::Preferences()
+ : mAreWaitingForOMTSave(false),
+ mOMTSaveMonitor("Preferences::OMTSaveMonitor")
{
}
Preferences::~Preferences()
{
NS_ASSERTION(sPreferences == this, "Isn't this the singleton instance?");
delete gObserverTable;
@@ -538,16 +674,18 @@ Preferences::Init()
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);
+ gDirtyCallback = &PrefsDirtyCallback;
+
return(rv);
}
// static
nsresult
Preferences::ResetAndReadUserPrefs()
{
sPreferences->ResetUserPrefs();
@@ -848,16 +986,18 @@ Preferences::MakeBackupPrefFile(nsIFile
NS_ENSURE_SUCCESS(rv, rv);
return rv;
}
nsresult
Preferences::ReadAndOwnUserPrefFile(nsIFile *aFile)
{
NS_ENSURE_ARG(aFile);
+
+ bool hadCurrentFile = !!mCurrentFile;
if (mCurrentFile == aFile)
return NS_OK;
mCurrentFile = aFile;
nsresult rv = NS_OK;
bool exists = false;
mCurrentFile->Exists(&exists);
@@ -868,117 +1008,119 @@ Preferences::ReadAndOwnUserPrefFile(nsIF
// 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;
}
+ // If we had a current file before, save now if we're dirty
+ if (gDirty && hadCurrentFile) {
+ SavePrefsToDefaultFileOMT();
+ }
+
return rv;
}
nsresult
Preferences::SavePrefFileInternal(nsIFile *aFile)
{
if (nullptr == aFile) {
// the gDirty flag tells us if we should write to mCurrentFile
// we only check this flag when the caller wants to write to the default
if (!gDirty)
return NS_OK;
-
+
+ // Cancel a pending timeout if we're saving anyway.
+ if (mCurrentOMTSaveTimer) {
+ if (!mAreWaitingForOMTSave) {
+#ifndef B2G
+ MOZ_ASSERT(NS_IsMainThread());
+#endif
+ // The timer fires on the main thread, and we should be on the main
+ // thread, so this shouldn't race:
+ mCurrentOMTSaveTimer->Cancel();
+ mCurrentOMTSaveTimer = nullptr;
+ } else {
+ // The async save is running in another thread, so wait for the monitor:
+ mozilla::MonitorAutoLock mon(mOMTSaveMonitor);
+
+ if (mAreWaitingForOMTSave) {
+ mon.Wait();
+ }
+ }
+ }
+
// 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)
{
- const char outHeader[] =
- "# 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;
-
- 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
- UniquePtr<char*[]> valueArray = pref_savePrefs(gHashTable);
-
- /* Sort the preferences to make a readable file on disk */
- NS_QuickSort(valueArray.get(), gHashTable->EntryCount(), sizeof(char *),
- pref_CompareStrings, nullptr);
-
- // write out the file header
- outStream->Write(outHeader, sizeof(outHeader) - 1, &writeAmount);
-
- for (uint32_t valueIdx = 0; valueIdx < gHashTable->EntryCount(); valueIdx++) {
- char*& pref = valueArray[valueIdx];
- if (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;
- }
- }
+ PreferenceRunnableSaver* saver = new PreferenceRunnableSaver(aFile);
+ saver->Run();
gDirty = false;
return NS_OK;
}
+nsresult
+Preferences::SavePrefsToDefaultFileOMT()
+{
+ // Don't do one if one is still in progress. We'll fire a new one off when
+ // the previous one finds a gDirty state again.
+ if (!mCurrentFile || mCurrentOMTSaveTimer || mCurrentOMTSaver) {
+ return NS_OK;
+ }
+ mCurrentOMTSaveTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+
+ nsTimerCallbackFunc callback = [](nsITimer* aTimer, void* aClosure) {
+ auto prefs = static_cast<Preferences*>(aClosure);
+ nsresult rv;
+ nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
+ // We might have done a sync save and no longer be in a dirty state
+ if (NS_SUCCEEDED(rv) && gDirty) {
+ gDirty = false;
+ prefs->mAreWaitingForOMTSave = true;
+ prefs->mCurrentOMTSaver = new PreferenceRunnableSaver(prefs->mCurrentFile);
+ target->Dispatch(prefs->mCurrentOMTSaver, nsIEventTarget::DISPATCH_NORMAL);
+ } else {
+ prefs->mCurrentOMTSaver = nullptr;
+ prefs->mCurrentOMTSaveTimer = nullptr;
+ }
+ };
+
+ mCurrentOMTSaveTimer->InitWithNamedFuncCallback(callback, this,
+ kPrefDelayedSaveTimeoutMs, nsITimer::TYPE_ONE_SHOT, "DelayedPrefSaver");
+ return NS_OK;
+}
+
+
+// static
+void
+Preferences::PrefsDirtyCallback()
+{
+ if (sPreferences && gHashTable) {
+ sPreferences->SavePrefsToDefaultFileOMT();
+ }
+}
+
+
static nsresult openPrefFile(nsIFile* aFile)
{
nsCOMPtr<nsIInputStream> inStr;
nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(inStr), aFile);
if (NS_FAILED(rv))
return rv;
--- a/modules/libpref/Preferences.h
+++ b/modules/libpref/Preferences.h
@@ -9,36 +9,41 @@
#ifndef MOZILLA_INTERNAL_API
#error "This header is only usable from within libxul (MOZILLA_INTERNAL_API)."
#endif
#include "nsIPrefService.h"
#include "nsIPrefBranch.h"
#include "nsIPrefBranchInternal.h"
#include "nsIObserver.h"
+#include "nsITimer.h"
#include "nsCOMPtr.h"
#include "nsTArray.h"
#include "nsWeakReference.h"
#include "mozilla/MemoryReporting.h"
+#include "mozilla/Monitor.h"
class nsIFile;
class nsAdoptingString;
class nsAdoptingCString;
#ifndef have_PrefChangedFunc_typedef
typedef void (*PrefChangedFunc)(const char *, void *);
#define have_PrefChangedFunc_typedef
#endif
namespace mozilla {
namespace dom {
class PrefSetting;
} // namespace dom
+class PreferenceRunnableSaver;
+class PreferenceRunnableSaveDone;
+
class Preferences final : public nsIPrefService,
public nsIObserver,
public nsIPrefBranchInternal,
public nsSupportsWeakReference
{
public:
typedef mozilla::dom::PrefSetting PrefSetting;
@@ -344,16 +349,18 @@ public:
// Used to synchronise preferences between chrome and content processes.
static void GetPreferences(InfallibleTArray<PrefSetting>* aPrefs);
static void GetPreference(PrefSetting* aPref);
static void SetPreference(const PrefSetting& aPref);
static int64_t SizeOfIncludingThisAndOtherStuff(mozilla::MallocSizeOf aMallocSizeOf);
+ static void PrefsDirtyCallback();
+
protected:
virtual ~Preferences();
nsresult NotifyServiceObservers(const char *aSubject);
/**
* Reads the default pref file or, if that failed, try to save a new one.
*
* @return NS_OK if either action succeeded,
@@ -361,26 +368,35 @@ protected:
*/
nsresult UseDefaultPrefFile();
nsresult UseUserPrefFile();
nsresult ReadAndOwnUserPrefFile(nsIFile *aFile);
nsresult ReadAndOwnSharedUserPrefFile(nsIFile *aFile);
nsresult SavePrefFileInternal(nsIFile* aFile);
nsresult WritePrefFile(nsIFile* aFile);
nsresult MakeBackupPrefFile(nsIFile *aFile);
+ nsresult SavePrefsToDefaultFileOMT();
private:
nsCOMPtr<nsIFile> mCurrentFile;
+ nsCOMPtr<nsITimer> mCurrentOMTSaveTimer;
+ RefPtr<PreferenceRunnableSaver> mCurrentOMTSaver;
+ bool mAreWaitingForOMTSave;
+ mozilla::Monitor mOMTSaveMonitor;
+
static Preferences* sPreferences;
static nsIPrefBranch* sRootBranch;
static nsIPrefBranch* sDefaultRootBranch;
static bool sShutdown;
/**
* Init static members. TRUE if it succeeded. Otherwise, FALSE.
*/
static bool InitStaticMembers();
+
+ friend class PreferenceRunnableSaver;
+ friend class PreferenceRunnableSaveDone;
};
} // namespace mozilla
#endif // mozilla_Preferences_h
--- a/modules/libpref/prefapi.cpp
+++ b/modules/libpref/prefapi.cpp
@@ -67,16 +67,18 @@ matchPrefEntry(PLDHashTable*, const PLDH
const char *otherKey = reinterpret_cast<const char*>(key);
return (strcmp(prefEntry->key, otherKey) == 0);
}
PLDHashTable* gHashTable;
static PLArenaPool gPrefNameArena;
bool gDirty = false;
+PrefsDirtyFunc gDirtyCallback = nullptr;
+
static struct CallbackNode* gCallbacks = nullptr;
static bool gIsAnyPrefLocked = false;
// These are only used during the call to pref_DoCallback
static bool gCallbacksInProgress = false;
static bool gShouldCleanupDeadNodes = false;
static PLDHashTableOps pref_HashTableOps = {
@@ -314,17 +316,17 @@ pref_SetPref(const dom::PrefSetting& aPr
// NB: we should never try to clear a default value, that doesn't
// make sense
return rv;
}
UniquePtr<char*[]>
-pref_savePrefs(PLDHashTable* aTable)
+pref_savePrefs(PLDHashTable* aTable, uint32_t* aPrefCount)
{
auto savedPrefs = MakeUnique<char*[]>(aTable->EntryCount());
memset(savedPrefs.get(), 0, aTable->EntryCount() * sizeof(char*));
int32_t j = 0;
for (auto iter = aTable->Iter(); !iter.Done(); iter.Next()) {
auto pref = static_cast<PrefHashEntry*>(iter.Get());
@@ -364,16 +366,17 @@ pref_savePrefs(PLDHashTable* aTable)
str_escape(pref->key, prefName);
savedPrefs[j++] = ToNewCString(prefPrefix +
prefName +
NS_LITERAL_CSTRING("\", ") +
prefValue +
NS_LITERAL_CSTRING(");"));
}
+ *aPrefCount = j;
return savedPrefs;
}
static void
GetPrefValueFromEntry(PrefHashEntry *aHashEntry, dom::PrefSetting* aPref,
WhichValue aWhich)
{
@@ -567,17 +570,17 @@ PREF_DeleteBranch(const char *branch_nam
and "ldap" (if such a leaf node exists) but not "ldap_1.xxx" */
if (PL_strncmp(entry->key, to_delete, (uint32_t) len) == 0 ||
(len-1 == (int)strlen(entry->key) &&
PL_strncmp(entry->key, to_delete, (uint32_t)(len-1)) == 0)) {
iter.Remove();
}
}
- gDirty = true;
+ PREF_MarkDirty();
return NS_OK;
}
nsresult
PREF_ClearUserPref(const char *pref_name)
{
if (!gHashTable)
@@ -588,17 +591,17 @@ PREF_ClearUserPref(const char *pref_name
{
pref->flags &= ~PREF_USERSET;
if (!(pref->flags & PREF_HAS_DEFAULT)) {
gHashTable->RemoveEntry(pref);
}
pref_DoCallback(pref_name);
- gDirty = true;
+ PREF_MarkDirty();
}
return NS_OK;
}
nsresult
PREF_ClearAllUserPrefs()
{
#ifndef MOZ_B2G
@@ -621,17 +624,17 @@ PREF_ClearAllUserPrefs()
}
}
}
for (std::string& prefString : prefStrings) {
pref_DoCallback(prefString.c_str());
}
- gDirty = true;
+ PREF_MarkDirty();
return NS_OK;
}
nsresult PREF_LockPref(const char *key, bool lockit)
{
if (!gHashTable)
return NS_ERROR_NOT_INITIALIZED;
@@ -763,29 +766,29 @@ nsresult pref_HashPref(const char *key,
!pref_ValueChanged(pref->defaultPref, value, type) &&
!(flags & kPrefForceSet))
{
if (PREF_HAS_USER_VALUE(pref))
{
/* XXX should we free a user-set string value if there is one? */
pref->flags &= ~PREF_USERSET;
if (!PREF_IS_LOCKED(pref)) {
- gDirty = true;
+ PREF_MarkDirty();
valueChanged = true;
}
}
}
else if (!PREF_HAS_USER_VALUE(pref) ||
PREF_TYPE(pref) != type ||
pref_ValueChanged(pref->userPref, value, type) )
{
pref_SetValue(&pref->userPref, &pref->flags, value, type);
pref->flags |= PREF_USERSET;
if (!PREF_IS_LOCKED(pref)) {
- gDirty = true;
+ PREF_MarkDirty();
valueChanged = true;
}
}
}
if (valueChanged) {
return pref_DoCallback(key);
}
@@ -975,8 +978,19 @@ void PREF_ReaderCallback(void *clo
bool isStickyDefault)
{
uint32_t flags = isDefault ? kPrefSetDefault : kPrefForceSet;
if (isDefault && isStickyDefault) {
flags |= kPrefStickyDefault;
}
pref_HashPref(pref, value, type, flags);
}
+
+void PREF_MarkDirty()
+{
+ if (!gDirty) {
+ gDirty = true;
+ if (gDirtyCallback) {
+ gDirtyCallback();
+ }
+ }
+}
+
--- a/modules/libpref/prefapi.h
+++ b/modules/libpref/prefapi.h
@@ -183,12 +183,19 @@ nsresult PREF_UnregisterCallback( const
*/
void PREF_ReaderCallback( void *closure,
const char *pref,
PrefValue value,
PrefType type,
bool isDefault,
bool isStickyDefault);
+/*
+ * Mark the prefs as dirty (triggers the dirty callback if the prefs were not
+ * previously dirty)
+ */
+void PREF_MarkDirty();
+
+typedef void (*PrefsDirtyFunc) ();
#ifdef __cplusplus
}
#endif
#endif
--- a/modules/libpref/prefapi_private_data.h
+++ b/modules/libpref/prefapi_private_data.h
@@ -8,25 +8,27 @@
#ifndef prefapi_private_data_h
#define prefapi_private_data_h
#include "mozilla/MemoryReporting.h"
#include "mozilla/UniquePtr.h"
extern PLDHashTable* gHashTable;
extern bool gDirty;
+typedef void (*PrefsDirtyFunc) ();
+extern PrefsDirtyFunc gDirtyCallback;
namespace mozilla {
namespace dom {
class PrefSetting;
} // namespace dom
} // namespace mozilla
mozilla::UniquePtr<char*[]>
-pref_savePrefs(PLDHashTable* aTable);
+pref_savePrefs(PLDHashTable* aTable, uint32_t* aPrefCount);
nsresult
pref_SetPref(const mozilla::dom::PrefSetting& aPref);
int pref_CompareStrings(const void *v1, const void *v2, void* unused);
PrefHashEntry* pref_HashTableLookup(const char *key);
void pref_GetPrefFromEntry(PrefHashEntry *aHashEntry,