Bug 1353936 - Implement PREF_RegisterPriorityCallback. r?froydnj
MozReview-Commit-ID: 1tdE6boBUVO
--- a/modules/libpref/prefapi.cpp
+++ b/modules/libpref/prefapi.cpp
@@ -65,17 +65,18 @@ matchPrefEntry(const PLDHashEntryHdr* en
const char *otherKey = reinterpret_cast<const char*>(key);
return (strcmp(prefEntry->key, otherKey) == 0);
}
PLDHashTable* gHashTable;
static ArenaAllocator<8192,4> gPrefNameArena;
-static struct CallbackNode* gCallbacks = nullptr;
+static struct CallbackNode* gFirstCallback = nullptr;
+static struct CallbackNode* gLastPriorityNode = 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 = {
PLDHashTable::HashStringKey,
@@ -152,27 +153,27 @@ void PREF_Init()
}
}
/* Frees the callback list. */
void PREF_Cleanup()
{
NS_ASSERTION(!gCallbacksInProgress,
"PREF_Cleanup was called while gCallbacksInProgress is true!");
- struct CallbackNode* node = gCallbacks;
+ struct CallbackNode* node = gFirstCallback;
struct CallbackNode* next_node;
while (node)
{
next_node = node->next;
PL_strfree(node->domain);
free(node);
node = next_node;
}
- gCallbacks = nullptr;
+ gLastPriorityNode = gFirstCallback = nullptr;
PREF_CleanupPrefs();
}
/* Frees up all the objects except the callback list. */
void PREF_CleanupPrefs()
{
if (gHashTable) {
@@ -856,17 +857,17 @@ nsresult pref_HashPref(const char *key,
}
return NS_OK;
}
size_t
pref_SizeOfPrivateData(MallocSizeOf aMallocSizeOf)
{
size_t n = gPrefNameArena.SizeOfExcludingThis(aMallocSizeOf);
- for (struct CallbackNode* node = gCallbacks; node; node = node->next) {
+ for (struct CallbackNode* node = gFirstCallback; node; node = node->next) {
n += aMallocSizeOf(node);
n += aMallocSizeOf(node->domain);
}
return n;
}
PrefType
PREF_GetPrefType(const char *pref_name)
@@ -893,77 +894,108 @@ PREF_PrefIsLocked(const char *pref_name)
}
}
return result;
}
/* Adds a node to the beginning of the callback list. */
void
+PREF_RegisterPriorityCallback(const char *pref_node,
+ PrefChangedFunc callback,
+ void * instance_data)
+{
+ NS_PRECONDITION(pref_node, "pref_node must not be nullptr");
+ NS_PRECONDITION(callback, "callback must not be nullptr");
+
+ struct CallbackNode* node = (struct CallbackNode*) malloc(sizeof(struct CallbackNode));
+ if (node)
+ {
+ node->domain = PL_strdup(pref_node);
+ node->func = callback;
+ node->data = instance_data;
+ node->next = gFirstCallback;
+ gFirstCallback = node;
+ if (!gLastPriorityNode) {
+ gLastPriorityNode = node;
+ }
+ }
+}
+
+/* Adds a node to the end of the callback list. */
+void
PREF_RegisterCallback(const char *pref_node,
PrefChangedFunc callback,
void * instance_data)
{
NS_PRECONDITION(pref_node, "pref_node must not be nullptr");
NS_PRECONDITION(callback, "callback must not be nullptr");
struct CallbackNode* node = (struct CallbackNode*) malloc(sizeof(struct CallbackNode));
if (node)
{
node->domain = PL_strdup(pref_node);
node->func = callback;
node->data = instance_data;
- node->next = gCallbacks;
- gCallbacks = node;
+ if (gLastPriorityNode) {
+ node->next = gLastPriorityNode->next;
+ gLastPriorityNode->next = node;
+ } else {
+ node->next = gFirstCallback;
+ gFirstCallback = node;
+ }
}
- return;
}
-/* Removes |node| from gCallbacks list.
+/* Removes |node| from callback list.
Returns the node after the deleted one. */
struct CallbackNode*
pref_RemoveCallbackNode(struct CallbackNode* node,
struct CallbackNode* prev_node)
{
NS_PRECONDITION(!prev_node || prev_node->next == node, "invalid params");
- NS_PRECONDITION(prev_node || gCallbacks == node, "invalid params");
+ NS_PRECONDITION(prev_node || gFirstCallback == node, "invalid params");
NS_ASSERTION(!gCallbacksInProgress,
"modifying the callback list while gCallbacksInProgress is true");
struct CallbackNode* next_node = node->next;
- if (prev_node)
+ if (prev_node) {
prev_node->next = next_node;
- else
- gCallbacks = next_node;
+ } else {
+ gFirstCallback = next_node;
+ }
+ if (gLastPriorityNode == node) {
+ gLastPriorityNode = prev_node;
+ }
PL_strfree(node->domain);
free(node);
return next_node;
}
/* Deletes a node from the callback list or marks it for deletion. */
nsresult
PREF_UnregisterCallback(const char *pref_node,
PrefChangedFunc callback,
void * instance_data)
{
nsresult rv = NS_ERROR_FAILURE;
- struct CallbackNode* node = gCallbacks;
+ struct CallbackNode* node = gFirstCallback;
struct CallbackNode* prev_node = nullptr;
while (node != nullptr)
{
if ( node->func == callback &&
node->data == instance_data &&
strcmp(node->domain, pref_node) == 0)
{
if (gCallbacksInProgress)
{
// postpone the node removal until after
- // gCallbacks enumeration is finished.
+ // callbacks enumeration is finished.
node->func = nullptr;
gShouldCleanupDeadNodes = true;
prev_node = node;
node = node->next;
}
else
{
node = pref_RemoveCallbackNode(node, prev_node);
@@ -986,33 +1018,33 @@ static nsresult pref_DoCallback(const ch
bool reentered = gCallbacksInProgress;
gCallbacksInProgress = true;
// Nodes must not be deleted while gCallbacksInProgress is true.
// Nodes that need to be deleted are marked for deletion by nulling
// out the |func| pointer. We release them at the end of this function
// if we haven't reentered.
- for (node = gCallbacks; node != nullptr; node = node->next)
+ for (node = gFirstCallback; node != nullptr; node = node->next)
{
if ( node->func &&
PL_strncmp(changed_pref,
node->domain,
strlen(node->domain)) == 0 )
{
(*node->func) (changed_pref, node->data);
}
}
gCallbacksInProgress = reentered;
if (gShouldCleanupDeadNodes && !gCallbacksInProgress)
{
struct CallbackNode* prev_node = nullptr;
- node = gCallbacks;
+ node = gFirstCallback;
while (node != nullptr)
{
if (!node->func)
{
node = pref_RemoveCallbackNode(node, prev_node);
}
else