mozilla-gconf-backend.patch
author Wolfgang Rosenauer <wr@rosenauer.org>
Mon, 10 Nov 2014 16:34:27 +0100
branchfirefox33
changeset 802 6c8dd9468bcc
parent 59 604517bb16d8
child 174 b3f909e83302
permissions -rw-r--r--
33.1 release

From: various contributors
Subject: replace gconf backend with more complete mapping for lockdown feature

diff --git a/extensions/pref/Makefile.in b/extensions/pref/Makefile.in
--- a/extensions/pref/Makefile.in
+++ b/extensions/pref/Makefile.in
@@ -40,13 +40,13 @@ DEPTH            = ../..
 topsrcdir        = @top_srcdir@
 srcdir           = @srcdir@
 VPATH            = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 DIRS             = autoconfig
 
-ifdef MOZ_ENABLE_GTK2
+ifdef MOZ_ENABLE_GCONF
 DIRS		+= system-pref
 endif
 
 include $(topsrcdir)/config/rules.mk
diff --git a/extensions/pref/system-pref/src/Makefile.in b/extensions/pref/system-pref/src/Makefile.in
--- a/extensions/pref/system-pref/src/Makefile.in
+++ b/extensions/pref/system-pref/src/Makefile.in
@@ -38,47 +38,41 @@
 DEPTH = ../../../..
 topsrcdir = @top_srcdir@
 srcdir = @srcdir@
 VPATH = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE = system-pref
-LIBRARY_NAME = system-pref_s
+LIBRARY_NAME = system-pref
 ifneq ($(OS_ARCH),WINNT)
 SHORT_LIBNAME = syspref
 endif
 
 # We want to force the creation of a static lib.
-FORCE_STATIC_LIB = 1
-LIBXUL_LIBRARY = 1
+#FORCE_STATIC_LIB = 1
+LIBXUL_LIBRARY   = 1
+MODULE_NAME      = nsSystemPrefModule
+IS_COMPONENT     = 1
+EXPORT_LIBRARY   = 1
 
 REQUIRES        = xpcom \
 		  string \
 		  embedcomponents \
 		  pref \
 		  $(NULL)
 
-ifdef MOZ_ENABLE_GTK2
-DIRS		= gconf
-endif
+CPPSRCS =   \
+  nsSystemPref.cpp	   \
+  nsSystemPrefFactory.cpp  \
+  $(NULL)
 
 EXTRA_DSO_LDOPTS = \
-		-L$(DIST)/bin \
 		$(MOZ_COMPONENT_LIBS) \
 		$(NULL)
 
-CPPSRCS =   \
-  nsSystemPref.cpp	   \
-  $(NULL)
-
 EXPORTS		= \
-		nsSystemPrefLog.h \
+		nsISystemPrefService.h \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
-ifdef MOZ_ENABLE_GTK2
-INCLUDES	+= \
-		-I$(srcdir)/gconf \
-		$(NULL)
-endif
diff --git a/extensions/pref/system-pref/src/gconf/Makefile.in b/extensions/pref/system-pref/src/gconf/Makefile.in
--- a/extensions/pref/system-pref/src/gconf/Makefile.in
+++ b/extensions/pref/system-pref/src/gconf/Makefile.in
@@ -37,50 +37,37 @@
 
 DEPTH = ../../../../..
 topsrcdir = @top_srcdir@
 srcdir = @srcdir@
 VPATH = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-MODULE = system-pref
-LIBRARY_NAME = system-pref
-LIBXUL_LIBRARY = 1
+MODULE = system-pref-gconf
+LIBRARY_NAME = system-pref-gconf
+IS_COMPONENT = 1
+MODULE_NAME = nsSystemPrefServiceModule
+FORCE_SHARED_LIB = 1
 
 REQUIRES        = pref \
 		  string \
 		  xpcom \
-		  embedcomponents \
+		  necko \
 		  $(NULL)
 
 CPPSRCS =   \
 	nsSystemPrefService.cpp       \
-	nsSystemPrefFactory.cpp \
 	$(NULL)
 
-SHARED_LIBRARY_LIBS = ../libsystem-pref_s.a
+OS_INCLUDES += $(MOZ_GCONF_CFLAGS)
+
 
 EXTRA_DSO_LDOPTS = \
-		-L$(DIST)/bin \
-		$(MOZ_COMPONENT_LIBS) \
-		$(MOZ_GTK2_LIBS) \
+		$(XPCOM_GLUE_LDOPTS) \
+		$(MOZ_GCONF_LIBS)    \
+		$(NSPR_LIBS) \
 		$(NULL)
 
-EXPORT_LIBRARY = 1
-IS_COMPONENT = 1
-MODULE_NAME	= nsSystemPrefModule
-
-EXPORTS		= \
-		nsSystemPrefService.h \
-		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
-CFLAGS		+= $(MOZ_GTK2_CFLAGS)
-CXXFLAGS	+= $(MOZ_GTK2_CFLAGS)
-
-LOCAL_INCLUDES = -I$(srcdir)/..
-
-export::
-	$(INSTALL) $(srcdir)/../nsSystemPrefFactory.cpp .
-
 GARBAGE += nsSystemPrefFactory.cpp
diff --git a/extensions/pref/system-pref/src/gconf/nsSystemPrefService.cpp b/extensions/pref/system-pref/src/gconf/nsSystemPrefService.cpp
--- a/extensions/pref/system-pref/src/gconf/nsSystemPrefService.cpp
+++ b/extensions/pref/system-pref/src/gconf/nsSystemPrefService.cpp
@@ -18,17 +18,19 @@
  * The Original Code is mozilla.org code.
  *
  * The Initial Developer of the Original Code is Sun Microsystems, Inc.
  * Portions created by Sun Microsystems are Copyright (C) 2003 Sun
  * Microsystems, Inc. All Rights Reserved.
  *
  * Original Author: Bolian Yin (bolian.yin@sun.com)
  *
- * Contributor(s):
+ * Contributor(s): Robert O'Callahan/Novell (rocallahan@novell.com)
+ *                 Hubert Figuiere (hfiguiere@novell.com)
+ *                 Wolfgang Rosenauer (wr@rosenauer.org)
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the NPL, indicate your
@@ -36,299 +38,1300 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the NPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include <glib.h>
 #include <glib-object.h>
+#include <gconf/gconf-client.h>
 
 #include "plstr.h"
 #include "nsCOMPtr.h"
-#include "nsIPrefBranch.h"
-#include "nsIPrefService.h"
+#include "nsIPref.h"
 #include "nsIServiceManager.h"
 #include "nsIObserver.h"
 #include "nsWeakReference.h"
+#include "nsIPrefBranch2.h"
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
+#include "nsICategoryManager.h"
+#include "nsIGenericFactory.h"
+#include "nsStringAPI.h"
+#include "nsIPermissionManager.h"
+#include "../nsSystemPref.h"
 
-#include "nsString.h"
-#include "nsSystemPrefLog.h"
-#include "nsSystemPrefService.h"
+#define NS_SYSTEMPREF_SERVICE_CID                  \
+  { /* {3724e748-b088-4bf8-9298-aad426b66293} */       \
+    0x3724e748,                                        \
+    0xb088,                                            \
+    0x4bf8,                                            \
+        { 0x92, 0x98, 0xaa, 0xd4, 0x26, 0xb6, 0x62, 0x93 } \
+  }
 
-/*************************************************************************
- * The strange thing here is that we load the gconf library manually and 
- * search the function pointers we need. If that process fails, no gconf
- * support is available in mozilla. The aim is to make mozilla independent
- * on gconf, in both compile time and run time.
- ************************************************************************/
+#define NS_SYSTEMPREF_SERVICE_CLASSNAME "System Preferences Platform Service"
 
-//gconf types
-extern "C" {
+NS_DEFINE_STATIC_IID_ACCESSOR(nsISystemPrefService, NS_ISYSTEMPREFSERVICE_IID)
 
-    typedef enum {
-        GCONF_VALUE_INVALID,
-        GCONF_VALUE_STRING,
-        GCONF_VALUE_INT,
-        GCONF_VALUE_FLOAT,
-        GCONF_VALUE_BOOL,
-        GCONF_VALUE_SCHEMA,
+/**
+ * We can link directly to the gconf library. If it's not available,
+ * this component just won't load and no system prefs will be offered.
+ */
 
-        GCONF_VALUE_LIST,
-        GCONF_VALUE_PAIR
+#define NUM_ELEM(a) (sizeof(a)/sizeof(a[0]))
 
-    }GConfValueType;
+class nsSystemPrefService;
 
-    typedef struct {
-        GConfValueType type;
-    }GConfValue;
+/**
+ * List the preferences that have a simple mapping between Moz and gconf.
+ * These preferences have the same meaning and their values are
+ * automatically converted.
+ */
+struct SimplePrefMapping {
+    const char *mozPrefName;
+    const char *gconfPrefName;
+    /**
+     * If this is PR_FALSE, then we never allow Mozilla to change
+     * this setting. The Mozilla pref will always be locked.
+     * If this is PR_TRUE then Mozilla will be allowed to change
+     * the setting --- but only if it is writable in gconf.
+     */
+    PRBool allowWritesFromMozilla;
+};
+typedef nsresult (* ComplexGConfPrefChanged)(nsSystemPrefService* aPrefService,
+                                             GConfClient* aClient);
+typedef nsresult (* ComplexMozPrefChanged)(nsSystemPrefService* aPrefService,
+                                           GConfClient* aClient);
+struct ComplexGConfPrefMapping {
+    const char* gconfPrefName;
+    ComplexGConfPrefChanged callback;
+};
 
-    typedef void * (*GConfClientGetDefaultType) (void);
-    typedef PRBool (*GConfClientGetBoolType) (void *client, const gchar *key,
-                                              GError **err);
-    typedef gchar* (*GConfClientGetStringType) (void *client, const gchar *key,
-                                                GError **err);
-    typedef PRInt32 (*GConfClientGetIntType) (void *client, const gchar *key,
-                                              GError **err);
-    typedef GSList* (*GConfClientGetListType) (void *client, const gchar *key,
-                                               GConfValueType list_type,
-                                               GError **err);
-    typedef  void (*GConfClientNotifyFuncType) (void* client, guint cnxn_id,
-                                                void *entry, 
-                                                gpointer user_data);
-    typedef guint (*GConfClientNotifyAddType) (void* client,
-                                               const gchar* namespace_section,
-                                               GConfClientNotifyFuncType func,
-                                               gpointer user_data,
-                                               GFreeFunc destroy_notify,
-                                               GError** err);
-    typedef void (*GConfClientNotifyRemoveType) (void *client,
-                                                 guint cnxn);
-    typedef void (*GConfClientAddDirType) (void *client,
-                                           const gchar *dir,
-                                           guint8 preload,
-                                           GError **err);
-    typedef void (*GConfClientRemoveDirType) (void *client,
-                                              const gchar *dir,
-                                              GError **err);
+struct ComplexMozPrefMapping {
+    const char* mozPrefName;
+    ComplexMozPrefChanged callback;
+};
 
-    typedef const char* (*GConfEntryGetKeyType) (const void *entry);
-    typedef GConfValue* (*GConfEntryGetValueType) (const void *entry);
+class nsSystemPrefService : public nsISystemPrefService, nsIPrefBranch
+ {
+ public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIPREFBRANCH
 
-    typedef const char* (*GConfValueGetStringType) (const GConfValue *value);
-    typedef PRInt32 (*GConfValueGetIntType) (const GConfValue *value);
-    typedef PRBool (*GConfValueGetBoolType) (const GConfValue *value);
+    nsresult Init();
 
-    
-    static void gconf_key_listener (void* client, guint cnxn_id,
-                                    void *entry, gpointer user_data);
-}
+    virtual nsresult LoadSystemPreferences(nsISystemPref* aPrefs);
+    virtual nsresult NotifyMozillaPrefChanged(const char* aPrefName);
+    virtual nsresult NotifyUnloadSystemPreferences();
 
-struct GConfCallbackData
-{
-    GConfProxy *proxy;
-    void * userData;
-    PRUint32 atom;
-    PRUint32 notifyId;
-};
-//////////////////////////////////////////////////////////////////////
-// GConPrxoy is a thin wrapper for easy use of gconf funcs. It loads the
-// gconf library and initializes the func pointers for later use.
-//////////////////////////////////////////////////////////////////////
-class GConfProxy
-{
-public:
-    GConfProxy(nsSystemPrefService* aSysPrefService);
-    ~GConfProxy();
-    PRBool Init();
+    nsSystemPrefService();
+    virtual ~nsSystemPrefService();
 
-    nsresult GetBoolPref(const char *aMozKey, PRBool *retval);
-    nsresult GetCharPref(const char *aMozKey, char **retval);
-    nsresult GetIntPref(const char *aMozKey, PRInt32 *retval);
-
-    nsresult NotifyAdd (PRUint32 aAtom, void *aUserData);
-    nsresult NotifyRemove (PRUint32 aAtom, const void *aUserData);
-
-    nsresult GetAtomForMozKey(const char *aMozKey, PRUint32 *aAtom) {
-        return GetAtom(aMozKey, 0, aAtom); 
+    nsISystemPref* GetPrefs() { return mPref; }
+    SimplePrefMapping* GetSimpleCallbackData(PRUint32 aKey) {
+      SimplePrefMapping* result = nsnull;
+      mGConfSimpleCallbacks.Get(aKey, &result);
+      return result;
     }
-    const char *GetMozKey(PRUint32 aAtom) {
-        return GetKey(aAtom, 0); 
-    }
-
-    void OnNotify(void *aClient, void * aEntry, PRUint32 aNotifyId,
-                  GConfCallbackData *aData);
+    ComplexGConfPrefMapping* GetComplexCallbackData(PRUint32 aKey) {
+      ComplexGConfPrefMapping* result = nsnull;
+      mGConfComplexCallbacks.Get(aKey, &result);
+      return result;
+     }
 
 private:
-    void *mGConfClient;
-    PRLibrary *mGConfLib;
-    PRBool mInitialized;
-    nsSystemPrefService *mSysPrefService;
+    nsISystemPref* mPref;
+    nsDataHashtable<nsUint32HashKey, SimplePrefMapping*> mGConfSimpleCallbacks;
+    nsDataHashtable<nsUint32HashKey, ComplexGConfPrefMapping*> mGConfComplexCallbacks;
+    // This is set to PR_FALSE temporarily to stop listening to gconf
+    // change notifications (while we change gconf values)
+    PRPackedBool mListenToGConf;
 
-    //listeners
-    nsAutoVoidArray *mObservers;
-
-    void InitFuncPtrs();
-    //gconf public func ptrs
-
-    //gconf client funcs
-    GConfClientGetDefaultType GConfClientGetDefault;
-    GConfClientGetBoolType GConfClientGetBool;
-    GConfClientGetStringType GConfClientGetString;
-    GConfClientGetIntType GConfClientGetInt;
-    GConfClientGetListType GConfClientGetList;
-    GConfClientNotifyAddType GConfClientNotifyAdd;
-    GConfClientNotifyRemoveType GConfClientNotifyRemove;
-    GConfClientAddDirType GConfClientAddDir;
-    GConfClientRemoveDirType GConfClientRemoveDir;
-
-    //gconf entry funcs
-    GConfEntryGetValueType GConfEntryGetValue;
-    GConfEntryGetKeyType GConfEntryGetKey;
-
-    //gconf value funcs
-    GConfValueGetBoolType GConfValueGetBool;
-    GConfValueGetStringType GConfValueGetString;
-    GConfValueGetIntType GConfValueGetInt;
-
-    //pref name translating stuff
-    nsresult GetAtom(const char *aKey, PRUint8 aNameType, PRUint32 *aAtom);
-    nsresult GetAtomForGConfKey(const char *aGConfKey, PRUint32 *aAtom) \
-    {return GetAtom(aGConfKey, 1, aAtom);}
-    const char *GetKey(PRUint32 aAtom, PRUint8 aNameType);
-    const char *GetGConfKey(PRUint32 aAtom) \
-    {return GetKey(aAtom, 1); }
-    inline const char *MozKey2GConfKey(const char *aMozKey);
-
-    //const strings
-    static const char sPrefGConfKey[];
-    static const char sDefaultLibName1[];
-    static const char sDefaultLibName2[];
+    GConfValue* GConfGet(const char *aPrefName);
 };
 
-struct SysPrefCallbackData {
-    nsISupports *observer;
-    PRBool bIsWeakRef;
-    PRUint32 prefAtom;
-};
-
-PRBool
-sysPrefDeleteObserver(void *aElement, void *aData) {
-    SysPrefCallbackData *pElement =
-        static_cast<SysPrefCallbackData *>(aElement);
-    NS_RELEASE(pElement->observer);
-    nsMemory::Free(pElement);
-    return PR_TRUE;
-}
-
-NS_IMPL_ISUPPORTS2(nsSystemPrefService, nsIPrefBranch, nsIPrefBranch2)
-
-/* public */
 nsSystemPrefService::nsSystemPrefService()
-    :mInitialized(PR_FALSE),
-     mGConf(nsnull),
-     mObservers(nsnull)
+   : mPref(nsnull), mListenToGConf(PR_TRUE)
 {
+   mGConfSimpleCallbacks.Init();
+   mGConfComplexCallbacks.Init();
 }
 
 nsSystemPrefService::~nsSystemPrefService()
 {
-    mInitialized = PR_FALSE;
-
-    if (mGConf)
-        delete mGConf;
-    if (mObservers) {
-        (void)mObservers->EnumerateForwards(sysPrefDeleteObserver, nsnull);
-        delete mObservers;
-    }
+   NotifyUnloadSystemPreferences();
 }
 
 nsresult
 nsSystemPrefService::Init()
 {
-    if (!gSysPrefLog) {
-        gSysPrefLog = PR_NewLogModule("Syspref");
-        if (!gSysPrefLog) return NS_ERROR_OUT_OF_MEMORY;
+    return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS2(nsSystemPrefService, 
+                   nsISystemPrefService, 
+                   nsIPrefBranch)
+
+static GConfClient* GetGConf() {
+    return gconf_client_get_default();
+}
+
+static PRBool VerifyMatchingTypes(nsISystemPref* aPrefs,
+                                  const char* aMozPref, GConfValue* aVal)
+{
+    nsCOMPtr<nsIPrefBranch2> prefBranch = aPrefs->GetPrefUserBranch();
+    PRInt32 type;
+    nsresult rv = prefBranch->GetPrefType(aMozPref, &type);
+    if (NS_FAILED(rv)) {
+        // pref probably doesn't exist. Let gconf set it.
+        return PR_TRUE;
     }
 
-    SYSPREF_LOG(("Init SystemPref Service\n"));
-    if (mInitialized)
+    PRBool ok;
+    switch (aVal->type) {
+    case GCONF_VALUE_STRING:
+        ok = type == nsIPrefBranch2::PREF_STRING;
+        break;
+    case GCONF_VALUE_INT:
+        ok = type == nsIPrefBranch2::PREF_INT;
+        break;
+    case GCONF_VALUE_BOOL:
+        ok = type == nsIPrefBranch2::PREF_BOOL;
+        break;
+    default:
+        NS_ERROR("Unhandled gconf preference type");
+        return PR_FALSE;
+    }
+
+    NS_ASSERTION(ok, "Mismatched gconf/Mozilla pref types");
+    return ok;
+}
+
+/**
+ * Map a gconf pref value into the corresponding Mozilla pref.
+ */
+static nsresult ApplySimpleMapping(SimplePrefMapping* aMap,
+                                   nsISystemPref* aPrefs,
+                                   GConfClient* aClient)
+{
+    GConfValue* val = gconf_client_get(aClient, aMap->gconfPrefName, nsnull);
+    if (!val) {
+        // No gconf key, so there's really nothing to do
+        return NS_OK;
+    }
+
+    VerifyMatchingTypes(aPrefs, aMap->mozPrefName, val);
+
+    PRBool locked = !aMap->allowWritesFromMozilla ||
+        !gconf_client_key_is_writable(aClient, aMap->gconfPrefName, nsnull);
+    nsresult rv;
+    switch (val->type) {
+    case GCONF_VALUE_STRING: {
+        const char* str = gconf_value_get_string(val);
+        rv = aPrefs->SetOverridingMozillaStringPref(aMap->mozPrefName, str, locked);
+        // XXX do we need to free 'str' here?
+        break;
+    }
+    case GCONF_VALUE_INT:
+        rv = aPrefs->SetOverridingMozillaIntPref(aMap->mozPrefName,
+                                                 gconf_value_get_int(val), locked);
+        break;
+    case GCONF_VALUE_BOOL:
+        rv = aPrefs->SetOverridingMozillaBoolPref(aMap->mozPrefName,
+                                                  gconf_value_get_bool(val), locked);
+        break;
+    default:
+        NS_ERROR("Unusable gconf value type");
+        rv = NS_ERROR_FAILURE;
+        break;
+    }
+
+    gconf_value_free(val);
+    return rv;
+}
+
+/**
+ * Map a Mozilla pref into the corresponding gconf pref, if
+ * that's allowed.
+ */
+static nsresult ReverseApplySimpleMapping(SimplePrefMapping* aMap,
+                                          nsISystemPref* aPrefs,
+                                          GConfClient* aClient)
+{
+    // Verify that the gconf key has the right type, if it exists
+    GConfValue* val = gconf_client_get(aClient, aMap->gconfPrefName, nsnull);
+    if (val) {
+        VerifyMatchingTypes(aPrefs, aMap->mozPrefName, val);
+        gconf_value_free(val);
+    }
+
+    PRBool writable = aMap->allowWritesFromMozilla &&
+        gconf_client_key_is_writable(aClient, aMap->gconfPrefName, nsnull);
+    if (!writable) {
+        NS_ERROR("Gconf key is not writable");
         return NS_ERROR_FAILURE;
+    }
 
-    if (!mGConf) {
-        mGConf = new GConfProxy(this);
-        if (!mGConf->Init()) {
-            delete mGConf;
-            mGConf = nsnull;
+    nsCOMPtr<nsIPrefBranch2> prefBranch = aPrefs->GetPrefUserBranch();
+    PRInt32 type;
+    nsresult rv = prefBranch->GetPrefType(aMap->mozPrefName, &type);
+    if (NS_FAILED(rv)) {
+        NS_ERROR("Writing back a pref that doesn't exist?");
+        return rv;
+    }
+
+    switch (type) {
+    case nsIPrefBranch2::PREF_STRING:
+        {
+            char* result;
+            rv = prefBranch->GetCharPref(aMap->mozPrefName, &result);
+            if (NS_FAILED(rv))
+                return rv;
+
+            gconf_client_set_string(aClient, aMap->gconfPrefName, result, nsnull);
+            nsMemory::Free(result);
+        }
+        break;
+    case nsIPrefBranch2::PREF_INT:
+        {
+            PRInt32 result;
+            rv = prefBranch->GetIntPref(aMap->mozPrefName, &result);
+            if (NS_FAILED(rv))
+                return rv;
+
+            gconf_client_set_int(aClient, aMap->gconfPrefName, result, nsnull);
+        }
+        break;
+    case nsIPrefBranch2::PREF_BOOL:
+        {
+            PRBool result;
+            rv = prefBranch->GetBoolPref(aMap->mozPrefName, &result);
+            if (NS_FAILED(rv))
+                return rv;
+
+            gconf_client_set_bool(aClient, aMap->gconfPrefName, result, nsnull);
+        }
+        break;
+    default:
+        NS_ERROR("Unhandled gconf preference type");
+        return NS_ERROR_FAILURE;
+    }
+
+    return NS_OK;
+}
+
+/* BEGIN preference mapping definition area
+ *
+ * There are a few rules that our preference maps have to obey:
+ *
+ * 1) Each mapping defines a relationship R between a set of GConf preferences and
+ * a set of Mozilla preferences that must *always* be true. Thus, when a Mozilla
+ * pref changes or a gconf pref changes, we may need to change something on the
+ * other side to preserve R. If a GConf preference is read-only, then we may
+ * need to lock one or more Mozilla preferences to avoid a situation where the
+ * Mozilla preference changes and we can't update the GConf preference to
+ * ensure R continues to hold.
+ *
+ * 2) If an unlocked Mozilla preference is changed, then we can only
+ * preserve R by changing GConf preferences; we are not allowed to
+ * change Mozilla preferences.
+ *
+ * 3) If a GConf preference is changed, then we can only preserve R by
+ * changing Moozilla preferences; we are nt allowed to change GConf
+ * preferences.
+ *
+ * For "simple" mappings, the relationship R is just of the form
+ * "GConf preference 'A' is equal to Mozilla preference 'B'". R is
+ * preserved by setting A to B when B changes, and by setting B to A
+ * when A changes. If A is read-only then we lock B (or we may just
+ * decide to lock B for other reasons). Thus rules 1-3 are satisfied.
+ *
+ * For "complex" mappings we have more complicated
+ * relationships. These are documented below.
+ */
+
+static SimplePrefMapping sSimplePrefMappings[] = {
+    // GNOME accessibility setting; never allow this to be set by Firefox
+    {"config.use_system_prefs.accessibility",
+     "/desktop/gnome/interface/accessibility", PR_FALSE},
+
+    // GConf Firefox preferences; allow these to be set through the Firefox UI
+    {"security.enable_java", "/apps/firefox/web/java_enabled", PR_TRUE},
+    {"javascript.enabled", "/apps/firefox/web/javascript_enabled", PR_TRUE},
+    {"browser.startup.homepage", "/apps/firefox/general/homepage_url", PR_TRUE},
+    {"browser.cache.disk.capacity", "/apps/firefox/web/cache_size", PR_TRUE},
+    {"network.cookie.lifetimePolicy", "/apps/firefox/web/cookie_accept", PR_TRUE},
+
+    // UI lockdown settings; never allow these to be set by Firefox. There is no
+    // Firefox UI for these but they could otherwise be set via about:config.
+    {"config.lockdown.printing", "/desktop/gnome/lockdown/disable_printing", PR_FALSE},
+    {"config.lockdown.printsetup", "/desktop/gnome/lockdown/disable_print_setup", PR_FALSE},
+    {"config.lockdown.savepage", "/desktop/gnome/lockdown/disable_save_to_disk", PR_FALSE},
+    {"config.lockdown.history", "/apps/firefox/lockdown/disable_history", PR_FALSE},
+    {"config.lockdown.toolbarediting", "/apps/firefox/lockdown/disable_toolbar_editing", PR_FALSE},
+    {"config.lockdown.urlbar", "/apps/firefox/lockdown/disable_url_bar", PR_FALSE},
+    {"config.lockdown.bookmark", "/apps/firefox/lockdown/disable_bookmark_editing", PR_FALSE},
+    {"config.lockdown.disable_themes", "/apps/firefox/lockdown/disable_themes", PR_FALSE},
+    {"config.lockdown.disable_extensions", "/apps/firefox/lockdown/disable_extensions", PR_FALSE},
+    {"config.lockdown.searchbar", "/apps/firefox/lockdown/disable_searchbar", PR_FALSE},
+    {"config.lockdown.hidebookmark", "/apps/firefox/lockdown/hide_bookmark", PR_FALSE},
+    {"config.lockdown.showsavedpasswords", "/apps/firefox/lockdown/disable_show_passwords", PR_FALSE},
+};
+
+static nsresult ApplyListPref(nsSystemPrefService* aPrefService,
+                             GConfClient* aClient,
+                             const char* aGConfKey, const char* aMozKey,
+                             char aSeparator)
+{
+    GSList* list = gconf_client_get_list(aClient, aGConfKey,
+                                         GCONF_VALUE_STRING, nsnull);
+    nsCAutoString str;
+    for (GSList* l = list; l; l = l->next) {
+        str.Append((const char*)l->data);
+        if (l->next) {
+            str.Append(aSeparator);
+        }
+    }
+    PRBool lock = !gconf_client_key_is_writable(aClient, aGConfKey, nsnull);
+    nsresult rv = aPrefService->GetPrefs()->
+        SetOverridingMozillaStringPref(aMozKey, str.get(), lock);
+    // XXX does this free the strings? Should it?
+    g_slist_free(list);
+    return rv;
+}
+static nsresult ReverseApplyListPref(nsSystemPrefService* aPrefService,
+                                    GConfClient* aClient,
+                                    const char* aGConfKey, const char* aMozKey,
+                                    char aSeparator)
+{
+    char* data = nsnull;
+    nsCOMPtr<nsIPrefBranch2> prefs =
+        aPrefService->GetPrefs()->GetPrefUserBranch();
+    prefs->GetCharPref(aMozKey, &data);
+    if (!data)
+        return NS_ERROR_FAILURE;
+    nsresult rv = NS_OK;
+    GSList* list = nsnull;
+    PRInt32 i = 0;
+    while (data[i]) {
+        const char* nextComma = strchr(data+i, ',');
+        PRInt32 tokLen = nextComma ? nextComma - (data+i) : strlen(data+i);
+        char* tok = strndup(data+i, tokLen);
+        if (!tok)
+            break;
+        GSList* newList = g_slist_append(list, tok);
+        if (!newList) {
+            rv = NS_ERROR_OUT_OF_MEMORY;
+            break;
+        }
+        list = newList;
+        if (!nextComma)
+            break;
+        i = nextComma + 1 - data;
+    }
+    nsMemory::Free(data);
+    if (NS_SUCCEEDED(rv)) {
+        if (gconf_client_key_is_writable(aClient, aGConfKey, nsnull))
+            gconf_client_set_list(aClient, aGConfKey, GCONF_VALUE_STRING, list, nsnull);
+        else
+            NS_ERROR("Gconf key is not writable");
+    }
+    for (GSList* l = list; l; l = l->next) {
+        free(l->data);
+    }
+    g_slist_free(list);
+    return rv;
+}
+
+/**
+ * The relationship R is
+ * "network.negotiate-auth.trusted-uris" is the comma-separated concatenation
+ * of the elements of the list "/apps/firefox/general/trusted_URIs"
+ */
+static const char GConfKey_TrustedURIs[] = "/apps/firefox/general/trusted_URIs";
+static const char MozKey_TrustedURIs[] = "network.negotiate-auth.trusted-uris";
+static nsresult ApplyTrustedURIs(nsSystemPrefService* aPrefService,
+                                GConfClient* aClient)
+{
+    return ApplyListPref(aPrefService, aClient,
+                        GConfKey_TrustedURIs, MozKey_TrustedURIs, ',');
+}
+static nsresult ReverseApplyTrustedURIs(nsSystemPrefService* aPrefService,
+                                       GConfClient* aClient)
+{
+    return ReverseApplyListPref(aPrefService, aClient,
+                               GConfKey_TrustedURIs, MozKey_TrustedURIs, ',');
+}
+
+/**
+ * The relationship R is
+ * "network.negotiate-auth.delegation-uris" is the comma-separated concatenation
+ * of the elements of the list "/apps/firefox/general/delegation_URIs"
+ */
+static const char GConfKey_DelegationURIs[] = "/apps/firefox/general/delegation_URIs";
+static const char MozKey_DelegationURIs[] = "network.negotiate-auth.delegation-uris";
+static nsresult ApplyDelegationURIs(nsSystemPrefService* aPrefService,
+                                   GConfClient* aClient)
+{
+    return ApplyListPref(aPrefService, aClient,
+                        GConfKey_DelegationURIs, MozKey_DelegationURIs, ',');
+}
+static nsresult ReverseApplyDelegationURIs(nsSystemPrefService* aPrefService,
+                                          GConfClient* aClient)
+{
+    return ReverseApplyListPref(aPrefService, aClient,
+                               GConfKey_DelegationURIs, MozKey_DelegationURIs, ',');
+}
+
+
+/**
+ * The relationship R is
+ * If "/apps/firefox/web/download_defaultfolder" is the empty string, then
+ * "browser.download.useDownloadDir" is false;
+ * otherwise "browser.download.useDownloadDir" is true and "browser.download.folderList"
+ * is (0 if "/apps/firefox/web/download_defaultfolder" is "Desktop";
+ *     1 if "/apps/firefox/web/download_defaultfolder" is "My Downloads";
+ *     3 if "/apps/firefox/web/download_defaultfolder" is "Home";
+ *     otherwise 2 and "browser.download.dir" = "/apps/firefox/web/download_defaultfolder")
+ */
+static const char GConfKey_DownloadFolder[] = "/apps/firefox/web/download_defaultfolder";
+static const char MozKey_UseDownloadDir[] = "browser.download.useDownloadDir";
+static const char MozKey_DownloadDirType[] = "browser.download.folderList";
+static const char MozKey_DownloadDirExplicit[] = "browser.download.dir";
+static nsresult ApplyDownloadFolder(nsSystemPrefService* aPrefService,
+                                    GConfClient* aClient)
+{
+    char* str = gconf_client_get_string(aClient, GConfKey_DownloadFolder, nsnull);
+    if (!str)
+        return NS_ERROR_FAILURE;
+    PRBool lock = !gconf_client_key_is_writable(aClient, GConfKey_DownloadFolder, nsnull);
+    nsresult rv = aPrefService->GetPrefs()->
+        SetOverridingMozillaBoolPref(MozKey_UseDownloadDir, *str != 0, lock);
+    if (NS_FAILED(rv)) {
+        g_free(str);
+        return rv;
+    }
+    PRInt32 dirType = 0;
+    if (!strcmp(str, "Desktop")) {
+        dirType = 0;
+    } else if (!strcmp(str, "My Downloads")) {
+        dirType = 1;
+    } else if (!strcmp(str, "Home")) {
+        dirType = 3;
+    } else {
+        dirType = 2;
+    }
+    // Always set all three Mozilla preferences. This is simpler and avoids
+    // problems; e.g., if the gconf value changes from "/home/rocallahan" to "Desktop"
+    // we might leave MozKey_DownloadDirType accidentally locked.
+    rv = aPrefService->GetPrefs()->
+        SetOverridingMozillaIntPref(MozKey_DownloadDirType, dirType, lock);
+    if (NS_SUCCEEDED(rv)) {
+        rv = aPrefService->GetPrefs()->
+            SetOverridingMozillaStringPref(MozKey_DownloadDirExplicit, str, lock);
+    }
+    g_free(str);
+    return rv;
+}
+
+static nsresult ReverseApplyDownloadFolder(nsSystemPrefService* aPrefService,
+                                           GConfClient* aClient)
+{
+    PRBool useDownloadDir = PR_FALSE;
+    const char* result;
+    char* explicitStr = nsnull;
+    nsCOMPtr<nsIPrefBranch2> prefs = aPrefService->GetPrefs()->GetPrefUserBranch();
+    prefs->GetBoolPref(MozKey_UseDownloadDir, &useDownloadDir);
+    if (!useDownloadDir) {
+        result = "";
+    } else {
+        PRInt32 type = -1;
+        prefs->GetIntPref(MozKey_DownloadDirType, &type);
+        if (type < 0)
+            return NS_ERROR_FAILURE;
+        switch (type) {
+        case 0: result = "Desktop"; break;
+        case 1: result = "Downloads"; break;
+        case 2:
+            prefs->GetCharPref(MozKey_DownloadDirExplicit, &explicitStr);
+            result = explicitStr;
+            break;
+        case 3: result = "Home"; break;
+        default:
+            NS_ERROR("Unknown download dir type");
             return NS_ERROR_FAILURE;
         }
     }
+    if (!result)
+        return NS_ERROR_FAILURE;
+    if (gconf_client_key_is_writable(aClient, GConfKey_DownloadFolder, nsnull))
+        gconf_client_set_string(aClient, GConfKey_DownloadFolder,
+                                result, nsnull);
+    else
+        NS_ERROR("Gconf key is not writable");
+    nsMemory::Free(explicitStr);
+    return NS_OK;
+}
 
-    mInitialized = PR_TRUE;
+/**
+ * The relationship R is
+ * "/apps/firefox/web/disable_cookies" is true if and only if
+ * "network.cookie.cookieBehavior" is 2 ('dontUse')
+ */
+static const char GConfKey_DisableCookies[] = "/apps/firefox/web/disable_cookies";
+static const char MozKey_CookieBehavior[] = "network.cookie.cookieBehavior";
+static const char MozKey_CookieExceptions[] = "network.cookie.honorExceptions";
+static const char MozKey_CookieViewExceptions[] = "pref.privacy.disable_button.cookie_exceptions";
+static nsresult ApplyDisableCookies(nsSystemPrefService* aPrefService,
+                                    GConfClient* aClient)
+{
+    gboolean disable = gconf_client_get_bool(aClient, GConfKey_DisableCookies, nsnull);
+    PRInt32 behavior = -1;
+    nsCOMPtr<nsIPrefBranch2> prefs =
+        aPrefService->GetPrefs()->GetPrefUserBranch();
+    prefs->GetIntPref(MozKey_CookieBehavior, &behavior);
+    if (behavior < 0)
+        return NS_ERROR_FAILURE;
+    if (disable) {
+        behavior = 2;
+    } else {
+        if (behavior == 2) {
+            behavior = 0;
+        }
+    }
+    PRBool lock = !gconf_client_key_is_writable(aClient, GConfKey_DisableCookies, nsnull);
+    nsresult rv = aPrefService->GetPrefs()->
+        SetOverridingMozillaBoolPref(MozKey_CookieExceptions, !lock, lock);
+    if (NS_FAILED(rv))
+      return rv;
+    rv = aPrefService->GetPrefs()->
+        SetOverridingMozillaBoolPref(MozKey_CookieViewExceptions, lock, lock);
+    if (NS_FAILED(rv))
+      return rv;
+    return aPrefService->GetPrefs()->
+        SetOverridingMozillaIntPref(MozKey_CookieBehavior, behavior, lock);
+}
+static nsresult ReverseApplyDisableCookies(nsSystemPrefService* aPrefService,
+                                           GConfClient* aClient)
+{
+    PRInt32 behavior = -1;
+    nsCOMPtr<nsIPrefBranch2> prefs =
+        aPrefService->GetPrefs()->GetPrefUserBranch();
+    prefs->GetIntPref(MozKey_CookieBehavior, &behavior);
+    if (behavior < 0)
+        return NS_ERROR_FAILURE;
+    if (gconf_client_key_is_writable(aClient, GConfKey_DisableCookies, nsnull))
+        gconf_client_set_bool(aClient, GConfKey_DisableCookies, behavior == 2, nsnull);
+    else
+        NS_ERROR("Gconf key is not writable");
     return NS_OK;
 }
 
+static char const* windowOpenFeatures[] = {
+    "dom.disable_window_open_feature.close",
+    "dom.disable_window_open_feature.directories",
+    "dom.disable_window_open_feature.location",
+    "dom.disable_window_open_feature.menubar",
+    "dom.disable_window_open_feature.minimizable",
+    "dom.disable_window_open_feature.personalbar",
+    "dom.disable_window_open_feature.resizable",
+    "dom.disable_window_open_feature.scrollbars",
+    "dom.disable_window_open_feature.status",
+    "dom.disable_window_open_feature.titlebar",
+    "dom.disable_window_open_feature.toolbar"
+};
+/**
+ * The relationship R is
+ * "/apps/firefox/lockdown/disable_javascript_chrome" is true if and only if
+ * all of windowOpenFeatures are true
+ */
+static const char GConfKey_DisableJSChrome[] =
+    "/apps/firefox/lockdown/disable_javascript_chrome";
+static nsresult ApplyWindowOpen(nsSystemPrefService* aPrefService,
+                                GConfClient* aClient)
+{
+    gboolean disable = gconf_client_get_bool(aClient, GConfKey_DisableJSChrome, nsnull);
+    PRBool lock = !gconf_client_key_is_writable(aClient, GConfKey_DisableJSChrome, nsnull);
+    PRBool curValues[NUM_ELEM(windowOpenFeatures)];
+    PRUint32 i;
+    nsCOMPtr<nsIPrefBranch2> prefs = aPrefService->GetPrefs()->GetPrefUserBranch();
+    PRBool allDisabled = PR_TRUE;
+    for (i = 0; i < NUM_ELEM(windowOpenFeatures); ++i) {
+        nsresult rv = prefs->GetBoolPref(windowOpenFeatures[i], &curValues[i]);
+        if (NS_FAILED(rv))
+            return rv;
+        if (!curValues[i]) {
+            allDisabled = PR_FALSE;
+        }
+    }
+    for (i = 0; i < NUM_ELEM(windowOpenFeatures); ++i) {
+        PRBool newVal = curValues[i];
+        if (disable) {
+            newVal = PR_TRUE;
+        } else if (allDisabled) {
+            // If all disable-window-open-feature prefs are currently
+            // PR_TRUE, then we need to set at least one of them to
+            // PR_FALSE. Set all of them to PR_FALSE.
+            newVal = PR_FALSE;
+        } // If at least one disable-window-open-feature pref is
+          // currently PR_FALSE, then we don't need to change anything
+          // when the gconf pref says don't disable
+        nsresult rv = aPrefService->GetPrefs()->
+            SetOverridingMozillaBoolPref(windowOpenFeatures[i], newVal, lock);
+        if (NS_FAILED(rv))
+            return rv;
+    }
+    return NS_OK;
+}
+
+static nsresult ReverseApplyWindowOpen(nsSystemPrefService* aPrefService,
+                                       GConfClient* aClient)
+{
+    nsCOMPtr<nsIPrefBranch2> prefs = aPrefService->GetPrefs()->GetPrefUserBranch();
+    PRBool allDisabled = PR_TRUE;
+    PRBool curValues[NUM_ELEM(windowOpenFeatures)];
+    for (PRUint32 i = 0; i < NUM_ELEM(windowOpenFeatures); ++i) {
+        nsresult rv = prefs->GetBoolPref(windowOpenFeatures[i], &curValues[i]);
+        if (NS_FAILED(rv))
+            return rv;
+        if (!curValues[i]) {
+            allDisabled = PR_FALSE;
+        }
+    }
+    if (gconf_client_key_is_writable(aClient, GConfKey_DisableJSChrome, nsnull))
+        gconf_client_set_bool(aClient, GConfKey_DisableJSChrome, allDisabled, nsnull);
+    else
+        NS_ERROR("Gconf key is not writable");
+    return NS_OK;
+}
+
+/**
+ * The relationship R is
+ * If "/apps/firefox/lockdown/disable_unsafe_protocol" is true then
+ * -- "network.protocol-handler.blocked-default" is true
+ * -- "network.protocol-handler.blocked.XYZ" is false if and only if
+ * XYZ is a builtin non-disablable protocol or in
+ * "/apps/firefox/lockdown/additional_safe_protocols"
+ * AND if "/apps/firefox/lockdown/disable_unsafe_protocol" is false then
+ * -- "network.protocol-handler.blocked-default" is false
+ * -- if "network.protocol-handler.blocked.XYZ" exists then it is false
+ */
+static const char GConfKey_DisableUnsafeProtocols[] =
+    "/apps/firefox/lockdown/disable_unsafe_protocol";
+static const char GConfKey_AdditionalSafeProtocols[] =
+    "/apps/firefox/lockdown/additional_safe_protocols";
+static const char MozKey_BlockedDefault[] =
+    "network.protocol-handler.blocked-default";
+static const char MozKey_BlockedPrefix[] =
+    "network.protocol-handler.blocked.";
+static const char* nonDisablableBuiltinProtocols[] =
+    { "about", "data", "jar", "keyword", "resource", "viewsource",
+      "chrome", "moz-icon", "javascript", "file" };
+static PRBool FindString(const char** aList, PRInt32 aCount,
+                         const char* aStr)
+{
+    for (PRInt32 i = 0; i < aCount; ++i) {
+        if (!strcmp(aStr, aList[i]))
+            return PR_TRUE;
+    }
+    return PR_FALSE;
+}
+typedef nsDataHashtable<nsCStringHashKey,int> StringSet;
+/** Collect the set of protocol names that we want to set preferences for */
+static nsresult AddAllProtocols(nsSystemPrefService* aPrefService,
+                                const char* aSafeProtocols,
+                                StringSet* aProtocolSet,
+                                StringSet* aSafeSet)
+{
+    nsCOMPtr<nsIPrefBranch2> prefs = aPrefService->GetPrefs()->GetPrefUserBranch();
+    PRUint32 childCount;
+    char **childArray = nsnull;
+    nsresult rv = prefs->GetChildList(MozKey_BlockedPrefix, &childCount, &childArray);
+    if (NS_FAILED(rv))
+        return rv;
+    PRUint32 i;
+    for (i = 0; i < childCount; ++i) {
+        nsDependentCString tmp(childArray[i] + NUM_ELEM(MozKey_BlockedPrefix)-1);
+        aProtocolSet->Put(tmp, 1); // copies
+    }
+    for (i = 0; i < NUM_ELEM(nonDisablableBuiltinProtocols); ++i) {
+        nsDependentCString tmp(nonDisablableBuiltinProtocols[i]);
+        aProtocolSet->Put(tmp, 1);
+    }
+    i = 0;
+    while (aSafeProtocols[i]) {
+        const char* nextComma = strchr(aSafeProtocols+i, ',');
+        PRUint32 tokLen = nextComma ? nextComma - (aSafeProtocols+i)
+            : strlen(aSafeProtocols+i);
+        nsCAutoString tok(aSafeProtocols+i, tokLen);
+        aProtocolSet->Put(tok, 1);
+        aSafeSet->Put(tok, 1);
+        if (nextComma) {
+            i = nextComma - aSafeProtocols + 1;
+        } else {
+            break;
+        }
+    }
+    NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(childCount, childArray);
+    return NS_OK;
+}
+
+struct ProtocolPrefClosure {
+    StringSet safeProtocolSet;
+    nsIPrefBranch2* prefs;
+    nsISystemPref* prefSetter;
+    PRPackedBool disableUnsafe;
+    PRPackedBool lock;
+};
+
+static PLDHashOperator PR_CALLBACK SetProtocolPref(const nsACString& aKey,
+                                                   int aItem,
+                                                   void* aClosure)
+{
+    ProtocolPrefClosure* closure = static_cast<ProtocolPrefClosure*>(aClosure);
+    const nsCString& protocol = PromiseFlatCString(aKey);
+    PRBool blockProtocol = PR_FALSE;
+    if (closure->disableUnsafe &&
+        !FindString(nonDisablableBuiltinProtocols,
+                    NUM_ELEM(nonDisablableBuiltinProtocols), protocol.get()) &&
+        !closure->safeProtocolSet.Get(aKey, nsnull)) {
+        blockProtocol = PR_TRUE;
+    }
+
+    nsCAutoString prefName;
+    prefName.Append(MozKey_BlockedPrefix);
+    prefName.Append(protocol);
+    closure->prefSetter->SetOverridingMozillaBoolPref(prefName.get(), blockProtocol,
+                                                      closure->lock);
+    return PL_DHASH_NEXT;
+}
+static nsresult ApplyUnsafeProtocols(nsSystemPrefService* aPrefService,
+                                     GConfClient* aClient)
+{
+    PRBool lock = !gconf_client_key_is_writable(aClient, GConfKey_DisableUnsafeProtocols, nsnull)
+        || !gconf_client_key_is_writable(aClient, GConfKey_AdditionalSafeProtocols, nsnull);
+    gboolean disable = gconf_client_get_bool(aClient, GConfKey_DisableUnsafeProtocols, nsnull);
+    char* protocols = gconf_client_get_string(aClient, GConfKey_AdditionalSafeProtocols, nsnull);
+    if (!protocols)
+        return NS_ERROR_FAILURE;
+    nsresult rv = aPrefService->GetPrefs()->
+        SetOverridingMozillaBoolPref(MozKey_BlockedDefault, disable, lock);
+    StringSet protocolSet;
+    ProtocolPrefClosure closure;
+    protocolSet.Init();
+    closure.safeProtocolSet.Init();
+    if (NS_SUCCEEDED(rv)) {
+        rv = AddAllProtocols(aPrefService, protocols, &protocolSet,
+                             &closure.safeProtocolSet);
+    }
+    if (NS_SUCCEEDED(rv)) {
+        closure.disableUnsafe = disable;
+        closure.lock = lock;
+        closure.prefSetter = aPrefService->GetPrefs();
+        nsCOMPtr<nsIPrefBranch2> prefs = aPrefService->GetPrefs()->GetPrefUserBranch();
+        closure.prefs = prefs;
+        protocolSet.EnumerateRead(SetProtocolPref, &closure);
+    }
+    g_free(protocols);
+    return rv;
+}
+
+static nsresult ReverseApplyUnsafeProtocols(nsSystemPrefService* aPrefService,
+                                            GConfClient* aClient)
+{
+    nsCOMPtr<nsIPrefBranch2> prefs = aPrefService->GetPrefs()->GetPrefUserBranch();
+    PRBool blockedDefault;
+    nsresult rv = prefs->GetBoolPref(MozKey_BlockedDefault, &blockedDefault);
+    if (NS_FAILED(rv))
+        return rv;
+    nsCAutoString enabledProtocols;
+    PRUint32 childCount;
+    char **childArray = nsnull;
+    rv = prefs->GetChildList(MozKey_BlockedPrefix, &childCount, &childArray);
+    if (NS_FAILED(rv))
+        return rv;
+    for (PRUint32 i = 0; i < childCount; ++i) {
+        PRBool val = PR_FALSE;
+        prefs->GetBoolPref(childArray[i], &val);
+        if (val) {
+            if (enabledProtocols.Length() > 0) {
+                enabledProtocols.Append(',');
+            }
+            enabledProtocols.Append(childArray[i] + NUM_ELEM(MozKey_BlockedPrefix)-1);
+        }
+    }
+    NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(childCount, childArray);
+    if (gconf_client_key_is_writable(aClient, GConfKey_DisableUnsafeProtocols, nsnull) &&
+            gconf_client_key_is_writable(aClient, GConfKey_AdditionalSafeProtocols, nsnull)) {
+        gconf_client_set_bool(aClient, GConfKey_DisableUnsafeProtocols, blockedDefault, nsnull);
+        gconf_client_set_string(aClient, GConfKey_AdditionalSafeProtocols,
+                                enabledProtocols.get(), nsnull);
+    } else {
+        NS_ERROR("Gconf key is not writable");
+    }
+    return NS_OK;
+}
+
+/**
+ * Set config.lockdown.setwallpaper if and only if
+ * /desktop/gnome/background/picture_filename is write-only. Always
+ * lock it.
+ */
+static const char MozKey_LockdownWallpaper[] = "config.lockdown.setwallpaper";
+static const char GConfKey_WallpaperSetting[] =
+    "/desktop/gnome/background/picture_filename";
+static nsresult ApplyWallpaper(nsSystemPrefService* aPrefService,
+                               GConfClient* aClient)
+{
+    PRBool canSetWallpaper =
+        gconf_client_key_is_writable(aClient, GConfKey_WallpaperSetting, nsnull);
+    return aPrefService->GetPrefs()->
+        SetOverridingMozillaBoolPref(MozKey_LockdownWallpaper,
+                                     !canSetWallpaper, PR_TRUE);
+}
+// No ReverseApplyWallpaper because this Mozilla pref can never be
+// modified
+
+/**
+ * The relationship R is
+ * "signon.rememberSignons" is true if and only if "/apps/firefox/web/disable_save_password"
+ * is false.
+ */
+static const char MozKey_RememberSignons[] = "signon.rememberSignons";
+static const char GConfKey_DisableSavePassword[] = "/apps/firefox/web/disable_save_password";
+static nsresult ApplyDisableSavePassword(nsSystemPrefService* aPrefService,
+                                         GConfClient* aClient)
+{
+    PRBool lock = !gconf_client_key_is_writable(aClient, GConfKey_DisableSavePassword, nsnull);
+    gboolean disable = gconf_client_get_bool(aClient, GConfKey_DisableSavePassword, nsnull);
+    return aPrefService->GetPrefs()->
+        SetOverridingMozillaBoolPref(MozKey_RememberSignons, !disable, lock);
+}
+
+static nsresult ReverseApplyDisableSavePassword(nsSystemPrefService* aPrefService,
+                                                GConfClient* aClient)
+{
+    nsCOMPtr<nsIPrefBranch2> prefs = aPrefService->GetPrefs()->GetPrefUserBranch();
+    PRBool remember;
+    nsresult rv = prefs->GetBoolPref(MozKey_RememberSignons, &remember);
+    if (NS_FAILED(rv))
+        return rv;
+    gconf_client_set_bool(aClient, GConfKey_DisableSavePassword, !remember, nsnull);
+    return NS_OK;
+}
+
+/**
+ * The relationship R is
+ * "permissions.default.image" is 1 (nsIPermissionManager::ALLOW_ACTION) if and only if
+ * "/apps/firefox/web/images_load" is 0, AND
+ * "permissions.default.image" is 2 (nsIPermissionManager::DENY_ACTION) if and only if
+ * "/apps/firefox/web/images_load" is 2, AND
+ * "permissions.default.image" is 3 if and only if "/apps/firefox/web/images_load" is 1
+ *
+ * Also, we set pref.advanced.images.disable_button.view_image iff
+ * /apps/firefox/web/images_load is read-only
+ * And we set permissions.default.honorExceptions iff
+ * /apps/firefox/web/images_load is not read-only
+ */
+static const char MozKey_ImagePermissions[] = "permissions.default.image";
+static const char MozKey_ImageExceptions[] = "permissions.honorExceptions.image";
+static const char MozKey_ImageViewExceptions[] = "pref.advanced.images.disable_button.view_image";
+static const char GConfKey_LoadImages[] = "/apps/firefox/web/images_load";
+static nsresult ApplyLoadImages(nsSystemPrefService* aPrefService,
+                                 GConfClient* aClient)
+{
+    PRBool lock = !gconf_client_key_is_writable(aClient, GConfKey_LoadImages, nsnull);
+    // 0 == accept, 1 == no-foreign, 2 == reject
+    gint setting = gconf_client_get_int(aClient, GConfKey_LoadImages, nsnull);
+    PRInt32 pref;
+    switch (setting) {
+      case 0: pref = nsIPermissionManager::ALLOW_ACTION; break;
+      case 2: pref = nsIPermissionManager::DENY_ACTION; break;
+      case 1: pref = 3; break;
+      default: return NS_ERROR_FAILURE;
+    }
+    nsresult rv = aPrefService->GetPrefs()->
+        SetOverridingMozillaBoolPref(MozKey_ImageExceptions, !lock, lock);
+    if (NS_FAILED(rv))
+      return rv;
+    rv = aPrefService->GetPrefs()->
+        SetOverridingMozillaBoolPref(MozKey_ImageViewExceptions, lock, lock);
+    if (NS_FAILED(rv))
+      return rv;
+    return aPrefService->GetPrefs()->
+        SetOverridingMozillaIntPref(MozKey_ImagePermissions, pref, lock);
+}
+
+static nsresult ReverseApplyLoadImages(nsSystemPrefService* aPrefService,
+                                        GConfClient* aClient)
+{
+    nsCOMPtr<nsIPrefBranch2> prefs = aPrefService->GetPrefs()->GetPrefUserBranch();
+    PRInt32 pref;
+    nsresult rv = prefs->GetIntPref(MozKey_ImagePermissions, &pref);
+    if (NS_FAILED(rv))
+        return rv;
+    gint setting;
+    switch (pref) {
+      case nsIPermissionManager::ALLOW_ACTION: setting = 0; break;
+      case nsIPermissionManager::DENY_ACTION: setting = 2; break;
+      case 3: setting = 1; break;
+      default: return NS_ERROR_FAILURE;
+    }
+    if (gconf_client_key_is_writable(aClient, GConfKey_LoadImages, nsnull))
+        gconf_client_set_int(aClient, GConfKey_LoadImages, setting, nsnull);
+    else
+        NS_ERROR("Gconf key is not writable");
+    return NS_OK;
+}
+
+/**
+ * The relationship R is
+ * "/apps/firefox/web/disable_popups" is true if and only if
+ * "dom.disable_open_during_load" is true
+ * AND if "/apps/firefox/web/disable_popups" is true then
+ * "privacy.popups.showBrowserMessage" is false.
+ */
+static const char MozKey_DisablePopups[] = "dom.disable_open_during_load";
+static const char MozKey_DisableBrowserPopupMessage[] = "privacy.popups.showBrowserMessage";
+static const char GConfKey_DisablePopups[] = "/apps/firefox/web/disable_popups";
+static nsresult ApplyDisablePopups(nsSystemPrefService* aPrefService,
+                                   GConfClient* aClient)
+{
+    PRBool lock = !gconf_client_key_is_writable(aClient, GConfKey_DisablePopups, nsnull);
+    gboolean disable = gconf_client_get_bool(aClient, GConfKey_DisablePopups, nsnull);
+    nsresult rv = aPrefService->GetPrefs()->
+        SetOverridingMozillaBoolPref(MozKey_DisablePopups, disable, lock);
+    if (NS_SUCCEEDED(rv)) {
+        if (disable) {
+            rv = aPrefService->GetPrefs()->
+                SetOverridingMozillaBoolPref(MozKey_DisableBrowserPopupMessage, PR_TRUE, lock);
+        } else {
+            rv = aPrefService->GetPrefs()->
+                StopOverridingMozillaPref(MozKey_DisableBrowserPopupMessage);
+        }
+    }
+    return rv;
+}
+
+static nsresult ReverseApplyDisablePopups(nsSystemPrefService* aPrefService,
+                                          GConfClient* aClient)
+{
+    nsCOMPtr<nsIPrefBranch2> prefs = aPrefService->GetPrefs()->GetPrefUserBranch();
+    PRBool disabled;
+    nsresult rv = prefs->GetBoolPref(MozKey_DisablePopups, &disabled);
+    if (NS_FAILED(rv))
+        return rv;
+    if (gconf_client_key_is_writable(aClient, GConfKey_DisablePopups, nsnull))
+        gconf_client_set_bool(aClient, GConfKey_DisablePopups, disabled, nsnull);
+    else
+        NS_ERROR("Gconf key is not writable");
+    return NS_OK;
+}
+
+static ComplexGConfPrefMapping sComplexGConfPrefMappings[] = {
+    {GConfKey_TrustedURIs, ApplyTrustedURIs},
+    {GConfKey_DelegationURIs, ApplyDelegationURIs},
+    {GConfKey_DownloadFolder, ApplyDownloadFolder},
+    {GConfKey_DisableCookies, ApplyDisableCookies},
+    {GConfKey_DisableJSChrome, ApplyWindowOpen},
+    {GConfKey_DisableUnsafeProtocols, ApplyUnsafeProtocols},
+    {GConfKey_AdditionalSafeProtocols, ApplyUnsafeProtocols},
+    {GConfKey_WallpaperSetting, ApplyWallpaper},
+    {GConfKey_DisableSavePassword, ApplyDisableSavePassword},
+    {GConfKey_LoadImages, ApplyLoadImages},
+    {GConfKey_DisablePopups, ApplyDisablePopups}
+};
+static ComplexMozPrefMapping sComplexMozPrefMappings[] = {
+    {MozKey_TrustedURIs, ReverseApplyTrustedURIs},
+    {MozKey_DelegationURIs, ReverseApplyDelegationURIs},
+    {MozKey_UseDownloadDir, ReverseApplyDownloadFolder},
+    {MozKey_DownloadDirType, ReverseApplyDownloadFolder},
+    {MozKey_DownloadDirExplicit, ReverseApplyDownloadFolder},
+    {MozKey_CookieBehavior, ReverseApplyDisableCookies},
+    {MozKey_RememberSignons, ReverseApplyDisableSavePassword},
+    {MozKey_ImagePermissions, ReverseApplyLoadImages},
+    {MozKey_DisablePopups, ReverseApplyDisablePopups}
+};
+// The unsafe protocol preferences are handled specially because
+// they affect an unknown number of Mozilla preferences
+// Window opener permissions are also handled specially so we don't have to
+// repeat the windowOpenFeatures list.
+
+/* END preference mapping definition area */
+
+static PR_CALLBACK void GConfSimpleNotification(GConfClient* client,
+                                                guint cnxn_id,
+                                                GConfEntry *entry,
+                                                gpointer user_data)
+{
+    nsSystemPrefService* service = static_cast<nsSystemPrefService*>(user_data);
+    SimplePrefMapping* map = static_cast<SimplePrefMapping*>(
+                                            service->GetSimpleCallbackData(cnxn_id));
+    NS_ASSERTION(map, "Can't find mapping for callback");
+    if (!map)
+        return;
+
+    ApplySimpleMapping(map, service->GetPrefs(), client);
+}
+
+static PR_CALLBACK void GConfComplexNotification(GConfClient* client,
+                                                 guint cnxn_id,
+                                                 GConfEntry *entry,
+                                                 gpointer user_data)
+{
+    nsSystemPrefService* service = static_cast<nsSystemPrefService*>(user_data);
+    ComplexGConfPrefMapping* map = static_cast<ComplexGConfPrefMapping*>(
+                                                  service->GetComplexCallbackData(cnxn_id));
+    NS_ASSERTION(map, "Can't find mapping for callback");
+    if (!map)
+        return;
+
+    map->callback(service, GetGConf());
+}
+
+nsresult nsSystemPrefService::LoadSystemPreferences(nsISystemPref* aPrefs)
+{
+    mPref = aPrefs;
+
+    GConfClient* client = GetGConf();
+    PRUint32 i;
+    // Register simple mappings and callbacks
+    for (i = 0; i < NUM_ELEM(sSimplePrefMappings); ++i) {
+        guint cx = gconf_client_notify_add(client,
+                                           sSimplePrefMappings[i].gconfPrefName,
+                                           GConfSimpleNotification, this,
+                                           nsnull, nsnull);
+        mGConfSimpleCallbacks.Put(cx, &sSimplePrefMappings[i]);
+        nsresult rv = ApplySimpleMapping(&sSimplePrefMappings[i], aPrefs, client);
+        if (NS_FAILED(rv))
+            return rv;
+    }
+
+    // Update Mozilla settings with any gconf settings that have
+    // changed from the default. Do it before we register our
+    // Mozilla notifications.
+    ComplexGConfPrefChanged lastCallback = nsnull;
+    for (i = 0; i < NUM_ELEM(sComplexGConfPrefMappings); ++i) {
+        guint cx = gconf_client_notify_add(client,
+                                           sComplexGConfPrefMappings[i].gconfPrefName,
+                                           GConfComplexNotification, this,
+                                           nsnull, nsnull);
+        mGConfComplexCallbacks.Put(cx, &sComplexGConfPrefMappings[i]);
+        ComplexGConfPrefChanged cb = sComplexGConfPrefMappings[i].callback;
+        if (cb != lastCallback) {
+            cb(this, client);
+            lastCallback = cb;
+        }
+    }
+
+    nsCOMPtr<nsIPrefBranch2> userPrefs = aPrefs->GetPrefUserBranch();
+
+    // Update gconf settings with any Mozilla settings that have
+    // changed from the default.
+    for (i = 0; i < NUM_ELEM(sSimplePrefMappings); ++i) {
+        gconf_client_add_dir(client, sSimplePrefMappings[i].gconfPrefName,
+                             GCONF_CLIENT_PRELOAD_NONE, nsnull);
+
+        PRBool hasUserPref = PR_FALSE;
+        nsresult rv =
+            userPrefs->PrefHasUserValue(sSimplePrefMappings[i].mozPrefName,
+                                        &hasUserPref);
+        if (NS_FAILED(rv))
+            return rv;
+        if (hasUserPref && sSimplePrefMappings[i].allowWritesFromMozilla) {
+            rv = ReverseApplySimpleMapping(&sSimplePrefMappings[i],
+                                           aPrefs, client);
+            if (NS_FAILED(rv))
+                return rv;
+        }
+    }
+    for (i = 0; i < NUM_ELEM(sComplexGConfPrefMappings); ++i) {
+        gconf_client_add_dir(client, sComplexGConfPrefMappings[i].gconfPrefName,
+                             GCONF_CLIENT_PRELOAD_NONE, nsnull);
+    }
+    ComplexMozPrefChanged lastMozCallback = nsnull;
+    for (i = 0; i < NUM_ELEM(sComplexMozPrefMappings); ++i) {
+        PRBool hasUserPref = PR_FALSE;
+        nsresult rv =
+            userPrefs->PrefHasUserValue(sComplexMozPrefMappings[i].mozPrefName,
+                                        &hasUserPref);
+        if (NS_FAILED(rv))
+            return rv;
+        if (hasUserPref) {
+            ComplexMozPrefChanged cb = sComplexMozPrefMappings[i].callback;
+            if (cb != lastMozCallback) {
+                cb(this, client);
+                lastMozCallback = cb;
+            }
+        }
+    }
+
+    ApplyUnsafeProtocols(this, client);
+
+    return NS_OK;
+}
+
+nsresult nsSystemPrefService::NotifyMozillaPrefChanged(const char* aPrefName)
+{
+    PRUint32 i;
+    GConfClient* client = GetGConf();
+
+    for (i = 0; i < NUM_ELEM(sSimplePrefMappings); ++i) {
+        if (!strcmp(aPrefName, sSimplePrefMappings[i].mozPrefName)) {
+            ReverseApplySimpleMapping(&sSimplePrefMappings[i],
+                                      mPref, client);
+        }
+    }
+
+    for (i = 0; i < NUM_ELEM(sComplexMozPrefMappings); ++i) {
+        if (!strcmp(aPrefName, sComplexMozPrefMappings[i].mozPrefName)) {
+            sComplexMozPrefMappings[i].callback(this, client);
+        }
+    }
+
+    for (i = 0; i < NUM_ELEM(windowOpenFeatures); ++i) {
+        if (!strcmp(aPrefName, windowOpenFeatures[i])) {
+            ReverseApplyWindowOpen(this, client);
+        }
+    }
+
+    ReverseApplyUnsafeProtocols(this, client);
+
+    return NS_OK;
+}
+
+static PLDHashOperator PR_CALLBACK UnregisterSimple(const PRUint32& aKey,
+                                                    SimplePrefMapping* aData,
+                                                    void* aClosure)
+{
+    GConfClient* client = GetGConf();
+    gconf_client_notify_remove(client, aKey);
+    gconf_client_remove_dir(client, aData->gconfPrefName, nsnull);
+    return PL_DHASH_NEXT;
+}
+
+static PLDHashOperator PR_CALLBACK UnregisterComplex(const PRUint32& aKey,
+                                                     ComplexGConfPrefMapping* aData,
+                                                     void* aClosure)
+{
+    GConfClient* client = GetGConf();
+    gconf_client_notify_remove(client, aKey);
+    gconf_client_remove_dir(client, aData->gconfPrefName, nsnull);
+    return PL_DHASH_NEXT;
+}
+
+nsresult nsSystemPrefService::NotifyUnloadSystemPreferences()
+{
+    // Unregister callbacks
+    mGConfSimpleCallbacks.EnumerateRead(UnregisterSimple, this);
+    mGConfSimpleCallbacks.Clear();
+    mGConfComplexCallbacks.EnumerateRead(UnregisterComplex, this);
+    mGConfComplexCallbacks.Clear();
+
+    return NS_OK;
+}
+
+// PrefBranch interfaces
+
+GConfValue* nsSystemPrefService::GConfGet(const char *aPrefName)
+{
+    PRUint32 i;
+    PRBool found = PR_FALSE;
+    GConfValue *val = NULL;
+    GConfClient* client = GetGConf();
+    NS_ASSERTION(client, "Could not get default GConf client");
+    if (!client) {
+        return NULL;
+    }
+    for (i = 0; i < NUM_ELEM(sSimplePrefMappings); ++i) {
+        if (!strcmp(aPrefName, sSimplePrefMappings[i].mozPrefName)) {
+            found = PR_TRUE;
+            break;
+        }
+    }
+    if (found) {
+        val = gconf_client_get(client, 
+                               sSimplePrefMappings[i].gconfPrefName, 
+                               NULL);
+    }
+    return val;
+}
+
 /* readonly attribute string root; */
 NS_IMETHODIMP nsSystemPrefService::GetRoot(char * *aRoot)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 /* long getPrefType (in string aPrefName); */
 NS_IMETHODIMP nsSystemPrefService::GetPrefType(const char *aPrefName, PRInt32 *_retval)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 /* boolean getBoolPref (in string aPrefName); */
 NS_IMETHODIMP nsSystemPrefService::GetBoolPref(const char *aPrefName, PRBool *_retval)
 {
-    return mInitialized ?
-        mGConf->GetBoolPref(aPrefName, _retval) : NS_ERROR_FAILURE;
+    GConfValue *val = GConfGet(aPrefName);
+    if (val) {
+        *_retval = (PRBool)(gconf_value_get_bool(val));
+        return NS_OK;
+    }
+    return NS_ERROR_FAILURE;
 }
 
 /* void setBoolPref (in string aPrefName, in long aValue); */
 NS_IMETHODIMP nsSystemPrefService::SetBoolPref(const char *aPrefName, PRInt32 aValue)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 /* string getCharPref (in string aPrefName); */
 NS_IMETHODIMP nsSystemPrefService::GetCharPref(const char *aPrefName, char **_retval)
 {
-    return mInitialized ?
-        mGConf->GetCharPref(aPrefName, _retval) : NS_ERROR_FAILURE;
+    GConfValue *val = GConfGet(aPrefName);
+    if (val) {
+        *_retval = (char*)(gconf_value_get_string(val));
+        return *_retval ? NS_OK : NS_ERROR_FAILURE;
+    }
+    return NS_ERROR_FAILURE;
 }
 
 /* void setCharPref (in string aPrefName, in string aValue); */
 NS_IMETHODIMP nsSystemPrefService::SetCharPref(const char *aPrefName, const char *aValue)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 /* long getIntPref (in string aPrefName); */
 NS_IMETHODIMP nsSystemPrefService::GetIntPref(const char *aPrefName, PRInt32 *_retval)
 {
-    return mInitialized ?
-        mGConf->GetIntPref(aPrefName, _retval) : NS_ERROR_FAILURE;
+    GConfValue *val = GConfGet(aPrefName);
+    if (val) {
+        *_retval = (PRInt32)(gconf_value_get_int(val));
+        return NS_OK;
+    }
+    return NS_ERROR_FAILURE;
 }
 
 /* void setIntPref (in string aPrefName, in long aValue); */
 NS_IMETHODIMP nsSystemPrefService::SetIntPref(const char *aPrefName, PRInt32 aValue)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
-/* void getComplexValue (in string aPrefName, in nsIIDRef aType, [iid_is (aType), retval] out nsQIResult aValue); */
+/* void getComplexValue (in string aPrefName, in nsIIDRef aType, [iid_is
+ * (aType), retval] out nsQIResult aValue); */
 NS_IMETHODIMP nsSystemPrefService::GetComplexValue(const char *aPrefName, const nsIID & aType, void * *aValue)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
-
-/* void setComplexValue (in string aPrefName, in nsIIDRef aType, in nsISupports aValue); */
+/* void setComplexValue (in string aPrefName, in nsIIDRef aType, in
+ * nsISupports aValue); */
 NS_IMETHODIMP nsSystemPrefService::SetComplexValue(const char *aPrefName, const nsIID & aType, nsISupports *aValue)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 /* void clearUserPref (in string aPrefName); */
 NS_IMETHODIMP nsSystemPrefService::ClearUserPref(const char *aPrefName)
 {
@@ -360,557 +1363,36 @@ NS_IMETHODIMP nsSystemPrefService::Unloc
 }
 
 /* void deleteBranch (in string aStartingAt); */
 NS_IMETHODIMP nsSystemPrefService::DeleteBranch(const char *aStartingAt)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
-/* void getChildList (in string aStartingAt, out unsigned long aCount, [array, size_is (aCount), retval] out string aChildArray); */
+/* void getChildList (in string aStartingAt, out unsigned long aCount, [array,
+ * size_is (aCount), retval] out string aChildArray); */
 NS_IMETHODIMP nsSystemPrefService::GetChildList(const char *aStartingAt, PRUint32 *aCount, char ***aChildArray)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 /* void resetBranch (in string aStartingAt); */
 NS_IMETHODIMP nsSystemPrefService::ResetBranch(const char *aStartingAt)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
-/* void addObserver (in string aDomain, in nsIObserver aObserver, in boolean aHoldWeak); */
-NS_IMETHODIMP nsSystemPrefService::AddObserver(const char *aDomain, nsIObserver *aObserver, PRBool aHoldWeak)
-{
-    nsresult rv;
 
-    NS_ENSURE_ARG_POINTER(aDomain);
-    NS_ENSURE_ARG_POINTER(aObserver);
+// Factory stuff
 
-    NS_ENSURE_TRUE(mInitialized, NS_ERROR_FAILURE);
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsSystemPrefService, Init)
 
-    PRUint32 prefAtom;
-    // make sure the pref name is supported
-    rv = mGConf->GetAtomForMozKey(aDomain, &prefAtom);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (!mObservers) {
-        mObservers = new nsAutoVoidArray();
-        if (mObservers == nsnull)
-            return NS_ERROR_OUT_OF_MEMORY;
-    }
-
-    SysPrefCallbackData *pCallbackData = (SysPrefCallbackData *)
-        nsMemory::Alloc(sizeof(SysPrefCallbackData));
-    if (pCallbackData == nsnull)
-        return NS_ERROR_OUT_OF_MEMORY;
-
-    pCallbackData->bIsWeakRef = aHoldWeak;
-    pCallbackData->prefAtom = prefAtom;
-    // hold a weak reference to the observer if so requested
-    nsCOMPtr<nsISupports> observerRef;
-    if (aHoldWeak) {
-        nsCOMPtr<nsISupportsWeakReference> weakRefFactory = 
-            do_QueryInterface(aObserver);
-        if (!weakRefFactory) {
-            // the caller didn't give us a object that supports weak reference.
-            // ... tell them
-            nsMemory::Free(pCallbackData);
-            return NS_ERROR_INVALID_ARG;
-        }
-        nsCOMPtr<nsIWeakReference> tmp = do_GetWeakReference(weakRefFactory);
-        observerRef = tmp;
-    } else {
-        observerRef = aObserver;
-    }
-
-    rv = mGConf->NotifyAdd(prefAtom, pCallbackData);
-    if (NS_FAILED(rv)) {
-        nsMemory::Free(pCallbackData);
-        return rv;
-    }
-
-    pCallbackData->observer = observerRef;
-    NS_ADDREF(pCallbackData->observer);
-
-    mObservers->AppendElement(pCallbackData);
-    return NS_OK;
-}
-
-/* void removeObserver (in string aDomain, in nsIObserver aObserver); */
-NS_IMETHODIMP nsSystemPrefService::RemoveObserver(const char *aDomain, nsIObserver *aObserver)
-{
-    nsresult rv;
-
-    NS_ENSURE_ARG_POINTER(aDomain);
-    NS_ENSURE_ARG_POINTER(aObserver);
-    NS_ENSURE_TRUE(mInitialized, NS_ERROR_FAILURE);
-
-    if (!mObservers)
-        return NS_OK;
-    
-    PRUint32 prefAtom;
-    // make sure the pref name is supported
-    rv = mGConf->GetAtomForMozKey(aDomain, &prefAtom);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // need to find the index of observer, so we can remove it
-    PRIntn count = mObservers->Count();
-    if (count <= 0)
-        return NS_OK;
-
-    PRIntn i;
-    SysPrefCallbackData *pCallbackData;
-    for (i = 0; i < count; ++i) {
-        pCallbackData = (SysPrefCallbackData *)mObservers->ElementAt(i);
-        if (pCallbackData) {
-            nsCOMPtr<nsISupports> observerRef;
-            if (pCallbackData->bIsWeakRef) {
-                nsCOMPtr<nsISupportsWeakReference> weakRefFactory =
-                    do_QueryInterface(aObserver);
-                if (weakRefFactory) {
-                    nsCOMPtr<nsIWeakReference> tmp =
-                        do_GetWeakReference(aObserver);
-                    observerRef = tmp;
-                }
-            }
-            if (!observerRef)
-                observerRef = aObserver;
-
-            if (pCallbackData->observer == observerRef &&
-                pCallbackData->prefAtom == prefAtom) {
-                rv = mGConf->NotifyRemove(prefAtom, pCallbackData);
-                if (NS_SUCCEEDED(rv)) {
-                    mObservers->RemoveElementAt(i);
-                    NS_RELEASE(pCallbackData->observer);
-                    nsMemory::Free(pCallbackData);
-                }
-                return rv;
-            }
-        }
-    }
-    return NS_OK;
-}
-
-void
-nsSystemPrefService::OnPrefChange(PRUint32 aPrefAtom, void *aData)
-{
-    if (!mInitialized)
-        return;
-
-    SysPrefCallbackData *pData = (SysPrefCallbackData *)aData;
-    if (pData->prefAtom != aPrefAtom)
-        return;
-
-    nsCOMPtr<nsIObserver> observer;
-    if (pData->bIsWeakRef) {
-        nsCOMPtr<nsIWeakReference> weakRef =
-            do_QueryInterface(pData->observer);
-        if(weakRef)
-            observer = do_QueryReferent(weakRef);
-        if (!observer) {
-            // this weak referenced observer went away, remove it from the list
-            nsresult rv = mGConf->NotifyRemove(aPrefAtom, pData);
-            if (NS_SUCCEEDED(rv)) {
-                mObservers->RemoveElement(pData);
-                NS_RELEASE(pData->observer);
-                nsMemory::Free(pData);
-            }
-            return;
-        }
-    }
-    else
-        observer = do_QueryInterface(pData->observer);
-
-    if (observer)
-        observer->Observe(static_cast<nsIPrefBranch *>(this),
-                          NS_SYSTEMPREF_PREFCHANGE_TOPIC_ID,
-                          NS_ConvertUTF8toUTF16(mGConf->GetMozKey(aPrefAtom)).
-                          get());
-}
-
-/*************************************************************
- *  GConfProxy
- *
- ************************************************************/
-
-struct GConfFuncListType {
-    const char *FuncName;
-    PRFuncPtr  FuncPtr;
+static const nsModuleComponentInfo components[] = {
+    { NS_SYSTEMPREF_SERVICE_CLASSNAME,
+      NS_SYSTEMPREF_SERVICE_CID,
+      NS_SYSTEMPREF_SERVICE_CONTRACTID,
+      nsSystemPrefServiceConstructor,
+    },
 };
 
-struct PrefNamePair {
-    const char *mozPrefName;
-    const char *gconfPrefName;
-};
+NS_IMPL_NSGETMODULE(nsSystemPrefServiceModule, components)
 
-const char
-GConfProxy::sPrefGConfKey[] = "accessibility.unix.gconf2.shared-library";
-const char GConfProxy::sDefaultLibName1[] = "libgconf-2.so.4";
-const char GConfProxy::sDefaultLibName2[] = "libgconf-2.so";
-
-#define GCONF_FUNCS_POINTER_BEGIN \
-    static GConfFuncListType sGConfFuncList[] = {
-#define GCONF_FUNCS_POINTER_ADD(func_name) \
-    {func_name, nsnull},
-#define GCONF_FUNCS_POINTER_END \
-    {nsnull, nsnull}, };
-
-GCONF_FUNCS_POINTER_BEGIN
-    GCONF_FUNCS_POINTER_ADD("gconf_client_get_default")        // 0
-    GCONF_FUNCS_POINTER_ADD("gconf_client_get_bool")       // 1
-    GCONF_FUNCS_POINTER_ADD("gconf_client_get_string")     //2
-    GCONF_FUNCS_POINTER_ADD("gconf_client_get_int")       //3
-    GCONF_FUNCS_POINTER_ADD("gconf_client_notify_add")   //4
-    GCONF_FUNCS_POINTER_ADD("gconf_client_notify_remove")   //5
-    GCONF_FUNCS_POINTER_ADD("gconf_client_add_dir")   //6
-    GCONF_FUNCS_POINTER_ADD("gconf_client_remove_dir")   //7
-    GCONF_FUNCS_POINTER_ADD("gconf_entry_get_value")       //8
-    GCONF_FUNCS_POINTER_ADD("gconf_entry_get_key")       //9
-    GCONF_FUNCS_POINTER_ADD("gconf_value_get_bool")      //10
-    GCONF_FUNCS_POINTER_ADD("gconf_value_get_string")     //11
-    GCONF_FUNCS_POINTER_ADD("gconf_value_get_int")       //12
-    GCONF_FUNCS_POINTER_ADD("gconf_client_get_list")       //13
-GCONF_FUNCS_POINTER_END
-
-/////////////////////////////////////////////////////////////////////////////
-// the list is the mapping table, between mozilla prefs and gconf prefs
-// It is expected to include all the pref pairs that are related in mozilla
-// and gconf. 
-//
-// Note: the prefs listed here are not neccessarily be read from gconf, they
-//       are the prefs that could be read from gconf. Mozilla has another
-//       list (see sSysPrefList in nsSystemPref.cpp) that decide which prefs
-//       are really read.
-//////////////////////////////////////////////////////////////////////////////
-
-static const PrefNamePair sPrefNameMapping[] = {
-#include "gconf_pref_list.inc"
-    {nsnull, nsnull},
-};
-
-PRBool
-gconfDeleteObserver(void *aElement, void *aData) {
-    nsMemory::Free(aElement);
-    return PR_TRUE;
-}
-
-GConfProxy::GConfProxy(nsSystemPrefService *aSysPrefService):
-    mGConfClient(nsnull),
-    mGConfLib(nsnull),
-    mInitialized(PR_FALSE),
-    mSysPrefService(aSysPrefService),
-    mObservers(nsnull)
-{
-}
-
-GConfProxy::~GConfProxy()
-{
-    if (mGConfClient)
-        g_object_unref(G_OBJECT(mGConfClient));
-
-    if (mObservers) {
-        (void)mObservers->EnumerateForwards(gconfDeleteObserver, nsnull);
-        delete mObservers;
-    }
-
-    // bug 379666: can't unload GConf-2 since it registers atexit handlers
-    //PR_UnloadLibrary(mGConfLib);
-}
-
-PRBool
-GConfProxy::Init()
-{
-    SYSPREF_LOG(("GConfProxy:: Init GConfProxy\n"));
-    if (!mSysPrefService)
-        return PR_FALSE;
-    if (mInitialized)
-        return PR_TRUE;
-
-    nsCOMPtr<nsIPrefBranch> pref = do_GetService(NS_PREFSERVICE_CONTRACTID); 
-
-    if (!pref)
-        return PR_FALSE;
-
-    nsXPIDLCString gconfLibName;
-    nsresult rv;
-
-    //check if gconf-2 library is given in prefs
-    rv = pref->GetCharPref(sPrefGConfKey, getter_Copies(gconfLibName));
-    if (NS_SUCCEEDED(rv)) {
-        //use the library name in the preference
-        SYSPREF_LOG(("GConf library in prefs is %s\n", gconfLibName.get()));
-        mGConfLib = PR_LoadLibrary(gconfLibName.get());
-    }
-    else {
-        SYSPREF_LOG(("GConf library not specified in prefs, try the default: "
-                     "%s and %s\n", sDefaultLibName1, sDefaultLibName2));
-        mGConfLib = PR_LoadLibrary(sDefaultLibName1);
-        if (!mGConfLib)
-            mGConfLib = PR_LoadLibrary(sDefaultLibName2);
-    }
-
-    if (!mGConfLib) {
-        SYSPREF_LOG(("Fail to load GConf library\n"));
-        return PR_FALSE;
-    }
-
-    //check every func we need in the gconf library
-    GConfFuncListType *funcList;
-    PRFuncPtr func;
-    for (funcList = sGConfFuncList; funcList->FuncName; ++funcList) {
-        func = PR_FindFunctionSymbol(mGConfLib, funcList->FuncName);
-        if (!func) {
-            SYSPREF_LOG(("Check GConf Func Error: %s", funcList->FuncName));
-            goto init_failed_unload;
-        }
-        funcList->FuncPtr = func;
-    }
-
-    InitFuncPtrs();
-
-    mGConfClient = GConfClientGetDefault();
-
-    // Don't unload past this point, since GConf's initialization of ORBit
-    // causes atexit handlers to be registered.
-
-    if (!mGConfClient) {
-        SYSPREF_LOG(("Fail to Get default gconf client\n"));
-        goto init_failed;
-    }
-    mInitialized = PR_TRUE;
-    return PR_TRUE;
-
- init_failed_unload:
-    PR_UnloadLibrary(mGConfLib);
- init_failed:
-    mGConfLib = nsnull;
-    return PR_FALSE;
-}
-
-nsresult
-GConfProxy::GetBoolPref(const char *aMozKey, PRBool *retval)
-{
-    NS_ENSURE_TRUE(mInitialized, NS_ERROR_FAILURE);
-    *retval = GConfClientGetBool(mGConfClient, MozKey2GConfKey(aMozKey), NULL);
-    return NS_OK;
-}
-
-nsresult
-GConfProxy::GetCharPref(const char *aMozKey, char **retval)
-{
-    NS_ENSURE_TRUE(mInitialized, NS_ERROR_FAILURE);
-
-    const gchar *gconfkey = MozKey2GConfKey(aMozKey);
-
-    if (!strcmp (aMozKey, "network.proxy.no_proxies_on")) {
-        GSList *s;
-        nsCString noproxy;
-        GSList *gslist = GConfClientGetList(mGConfClient, gconfkey,
-                                            GCONF_VALUE_STRING, NULL);
-
-        for (s = gslist; s; s = g_slist_next(s)) {
-            noproxy += (char *)s->data;
-            noproxy += ", ";
-            g_free ((char *)s->data);
-        }
-        g_slist_free (gslist);
-
-        *retval = PL_strdup(noproxy.get());
-    } else {
-        gchar *str = GConfClientGetString(mGConfClient, gconfkey, NULL);
-        if (str) {
-            *retval = PL_strdup(str);
-            g_free (str);
-        }
-    }
-
-    return NS_OK;
-}
-
-nsresult
-GConfProxy::GetIntPref(const char *aMozKey, PRInt32 *retval)
-{
-    NS_ENSURE_TRUE(mInitialized, NS_ERROR_FAILURE);
-    if (strcmp (aMozKey, "network.proxy.type") == 0) {
-	gchar *str;
-
-	str = GConfClientGetString(mGConfClient,
-	                           MozKey2GConfKey (aMozKey), NULL);
-
-	if (str) {
-		if (strcmp (str, "manual") == 0)
-			*retval = 1;
-		else if (strcmp (str, "auto") == 0)
-			*retval = 2;
-		else
-			*retval = 0;
-
-		g_free (str);
-	} else
-		*retval = 0;
-    } else {
-    	*retval = GConfClientGetInt(mGConfClient, 
-	                            MozKey2GConfKey(aMozKey), NULL);
-    }
-
-    return NS_OK;
-}
-
-nsresult
-GConfProxy::NotifyAdd (PRUint32 aAtom, void *aUserData)
-{
-    NS_ENSURE_TRUE(mInitialized, NS_ERROR_FAILURE);
-
-    const char *gconfKey = GetGConfKey(aAtom);
-    if (!gconfKey)
-        return NS_ERROR_FAILURE;
-
-    if (!mObservers) {
-        mObservers = new nsAutoVoidArray();
-        if (mObservers == nsnull)
-            return NS_ERROR_OUT_OF_MEMORY;
-    }
- 
-    GConfCallbackData *pData = (GConfCallbackData *)
-        nsMemory::Alloc(sizeof(GConfCallbackData));
-    NS_ENSURE_TRUE(pData, NS_ERROR_OUT_OF_MEMORY);
-
-    pData->proxy = this;
-    pData->userData = aUserData;
-    pData->atom = aAtom;
-    mObservers->AppendElement(pData);
-
-    GConfClientAddDir(mGConfClient, gconfKey,
-                      0, // GCONF_CLIENT_PRELOAD_NONE,  don't preload anything 
-                      NULL);
-
-    pData->notifyId = GConfClientNotifyAdd(mGConfClient, gconfKey,
-                                           gconf_key_listener, pData,
-                                           NULL, NULL);
-    return NS_OK;
-}
-
-nsresult
-GConfProxy::NotifyRemove (PRUint32 aAtom, const void *aUserData)
-{
-    NS_ENSURE_TRUE(mInitialized, NS_ERROR_FAILURE);
-
-    PRIntn count = mObservers->Count();
-    if (count <= 0)
-        return NS_OK;
-
-    PRIntn i;
-    GConfCallbackData *pData;
-    for (i = 0; i < count; ++i) {
-        pData = (GConfCallbackData *)mObservers->ElementAt(i);
-        if (pData && pData->atom == aAtom && pData->userData == aUserData) {
-            GConfClientNotifyRemove(mGConfClient, pData->notifyId);
-            GConfClientRemoveDir(mGConfClient,
-                                 GetGConfKey(pData->atom), NULL);
-            mObservers->RemoveElementAt(i);
-            nsMemory::Free(pData);
-            break;
-        }
-    }
-    return NS_OK;
-}
-
-void
-GConfProxy::InitFuncPtrs()
-{
-    //gconf client funcs
-    GConfClientGetDefault =
-        (GConfClientGetDefaultType) sGConfFuncList[0].FuncPtr;
-    GConfClientGetBool =
-        (GConfClientGetBoolType) sGConfFuncList[1].FuncPtr;
-    GConfClientGetString =
-        (GConfClientGetStringType) sGConfFuncList[2].FuncPtr;
-    GConfClientGetInt =
-        (GConfClientGetIntType) sGConfFuncList[3].FuncPtr;
-    GConfClientNotifyAdd =
-        (GConfClientNotifyAddType) sGConfFuncList[4].FuncPtr;
-    GConfClientNotifyRemove =
-        (GConfClientNotifyRemoveType) sGConfFuncList[5].FuncPtr;
-    GConfClientAddDir =
-        (GConfClientAddDirType) sGConfFuncList[6].FuncPtr;
-    GConfClientRemoveDir =
-        (GConfClientRemoveDirType) sGConfFuncList[7].FuncPtr;
-
-    //gconf entry funcs
-    GConfEntryGetValue = (GConfEntryGetValueType) sGConfFuncList[8].FuncPtr;
-    GConfEntryGetKey = (GConfEntryGetKeyType) sGConfFuncList[9].FuncPtr;
-
-    //gconf value funcs
-    GConfValueGetBool = (GConfValueGetBoolType) sGConfFuncList[10].FuncPtr;
-    GConfValueGetString = (GConfValueGetStringType) sGConfFuncList[11].FuncPtr;
-    GConfValueGetInt = (GConfValueGetIntType) sGConfFuncList[12].FuncPtr;
-
-    //gconf client list func
-    GConfClientGetList =
-        (GConfClientGetListType) sGConfFuncList[13].FuncPtr;
-}
-
-void
-GConfProxy::OnNotify(void *aClient, void * aEntry, PRUint32 aNotifyId,
-                     GConfCallbackData *aData)
-{
-    if (!mInitialized || !aEntry || (mGConfClient != aClient) || !aData)
-        return;
-
-    if (GConfEntryGetValue(aEntry) == NULL)
-        return;
-
-    PRUint32 prefAtom;
-    nsresult rv = GetAtomForGConfKey(GConfEntryGetKey(aEntry), &prefAtom);
-    if (NS_FAILED(rv))
-        return;
-
-    mSysPrefService->OnPrefChange(prefAtom, aData->userData);
-}
-
-nsresult
-GConfProxy::GetAtom(const char *aKey, PRUint8 aNameType, PRUint32 *aAtom)
-{
-    if (!aKey)
-        return NS_ERROR_FAILURE;
-    PRUint32 prefSize = sizeof(sPrefNameMapping) / sizeof(sPrefNameMapping[0]);
-    for (PRUint32 index = 0; index < prefSize; ++index) {
-        if (!strcmp((aNameType == 0) ? sPrefNameMapping[index].mozPrefName :
-                    sPrefNameMapping[index].gconfPrefName, aKey)) {
-            *aAtom = index;
-            return NS_OK;
-        }
-    }
-    return NS_ERROR_FAILURE;
-}
-
-const char *
-GConfProxy::GetKey(PRUint32 aAtom, PRUint8 aNameType)
-{
-    PRUint32 mapSize = sizeof(sPrefNameMapping) / sizeof(sPrefNameMapping[0]);
-    if (aAtom >= 0 && aAtom < mapSize)
-        return (aNameType == 0) ? sPrefNameMapping[aAtom].mozPrefName :
-            sPrefNameMapping[aAtom].gconfPrefName;
-    return NULL;
-}
-
-inline const char *
-GConfProxy::MozKey2GConfKey(const char *aMozKey)
-{
-    PRUint32 atom;
-    nsresult rv = GetAtomForMozKey(aMozKey, &atom);
-    if (NS_SUCCEEDED(rv))
-        return GetGConfKey(atom);
-    return NULL;
-}
-
-/* static */
-void gconf_key_listener (void* client, guint cnxn_id,
-                         void *entry, gpointer user_data)
-{
-    SYSPREF_LOG(("...SYSPREF_LOG...key listener get called \n"));
-    if (!user_data)
-        return;
-    GConfCallbackData *pData = reinterpret_cast<GConfCallbackData *>
-                                               (user_data);
-    pData->proxy->OnNotify(client, entry, cnxn_id, pData);
-}
diff --git a/extensions/pref/system-pref/src/nsISystemPrefService.h b/extensions/pref/system-pref/src/nsISystemPrefService.h
new file mode 100644
--- /dev/null
+++ b/extensions/pref/system-pref/src/nsISystemPrefService.h
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ *
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Novell
+ * Portions created by Novell are Copyright (C) 2005 Novell,
+ * All Rights Reserved.
+ *
+ * Original Author: Robert O'Callahan (rocallahan@novell.com)
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the NPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the NPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsISystemPrefService_h__
+#define nsISystemPrefService_h__
+
+#include "nsCOMPtr.h"
+#include "nsIPrefBranchInternal.h"
+
+#define NS_SYSTEMPREF_SERVICE_CONTRACTID "@mozilla.org/system-preference-service;1"
+
+#define NS_ISYSTEMPREFSERVICE_IID   \
+{ 0x006e1cfd, 0xd66a, 0x40b9, \
+    { 0x84, 0xa1, 0x84, 0xf3, 0xe6, 0xa2, 0xca, 0xbc } }
+
+class nsISystemPref {
+public:
+    /**
+     * Call one of these three methods to override a Mozilla
+     * preference with a system value. You can call it multiple
+     * times to change the value of a given preference to track
+     * the underlying system value.
+     *
+     * If aLocked is true then we set the default preference and
+     * lock it so the user value is ignored. If aLocked is false
+     * then we unlock the Mozilla preference and set the Mozilla
+     * user value.
+     */
+    virtual nsresult SetOverridingMozillaBoolPref(const char* aPrefName,
+                                                  PRBool aValue, PRBool aLocked,
+                                                  PRBool aPresent = PR_TRUE) = 0;
+    virtual nsresult SetOverridingMozillaIntPref(const char* aPrefName,
+                                                 PRInt32 aValue, PRBool aLocked,
+                                                 PRBool aPresent = PR_TRUE) = 0;
+    virtual nsresult SetOverridingMozillaStringPref(const char* aPrefName,
+                                                    const char* aValue, PRBool aLocked,
+                                                    PRBool aPresent = PR_TRUE) = 0;
+    virtual nsresult StopOverridingMozillaPref(const char* aPrefName) = 0;
+    virtual already_AddRefed<nsIPrefBranch2> GetPrefUserBranch() = 0;
+    virtual already_AddRefed<nsIPrefBranch> GetPrefDefaultBranch() = 0;
+};
+
+class nsISystemPrefService : public nsISupports {
+public:
+    NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISYSTEMPREFSERVICE_IID)
+
+    /**
+     * Load the system prefs from the store into their corresponding
+     * Mozilla prefs, calling SetOverridingMozillaPref on each
+     * such pref.
+     */
+    virtual nsresult LoadSystemPreferences(nsISystemPref* aPrefs) = 0;
+
+    /**
+     * Notify that a Mozilla user pref that is being overridden by the
+     * store has changed.  The new value of the Mozilla pref should be
+     * written back to the store.
+     */
+    virtual nsresult NotifyMozillaPrefChanged(const char* aPrefName) = 0;
+
+    /**
+     * Notify that we're about to stop using the system prefs.  After
+     * this, nsSystemPref will automatically stop overriding all
+     * Mozilla prefs that are being overridden.
+     */
+    virtual nsresult NotifyUnloadSystemPreferences() = 0;
+};
+
+#endif
diff --git a/extensions/pref/system-pref/src/nsSystemPref.cpp b/extensions/pref/system-pref/src/nsSystemPref.cpp
--- a/extensions/pref/system-pref/src/nsSystemPref.cpp
+++ b/extensions/pref/system-pref/src/nsSystemPref.cpp
@@ -19,16 +19,17 @@
  *
  * The Initial Developer of the Original Code is Sun Microsystems, Inc.
  * Portions created by Sun Microsystems are Copyright (C) 2003 Sun
  * Microsystems, Inc. All Rights Reserved.
  *
  * Original Author: Bolian Yin (bolian.yin@sun.com)
  *
  * Contributor(s):
+ *   Robert O'Callahan (rocallahan@novell.com)
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the NPL, indicate your
@@ -36,76 +37,72 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the NPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsSystemPref.h"
 #include "nsIObserverService.h"
+#include "nsIAppStartupNotifier.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "nsICategoryManager.h"
+#include "nsIServiceManager.h"
 
 #include "nsSystemPrefLog.h"
-#include "nsSystemPrefService.h"
 #include "nsString.h"
 
-const char sSysPrefString[] = "config.use_system_prefs";
-union MozPrefValue {
-    char *      stringVal;
-    PRInt32     intVal;
-    PRBool      boolVal;
-};
+#include <stdlib.h>
 
 struct SysPrefItem {
-    const char *prefName;       // mozilla pref string name
-    MozPrefValue defaultValue;  // store the mozilla default value
-    PRBool isLocked;  // store the mozilla lock status
+    // Saved values on both branches
+    PRInt32      savedUserValueScalar;
+    char*        savedUserValueString;
+    PRInt32      savedDefaultValueScalar;
+    char*        savedDefaultValueString;
+    // When this is true, then the value was locked originally
+    PRPackedBool savedLocked;
+    // When this is true, then there was a user value
+    PRPackedBool savedUserPresent;
+    PRPackedBool ignore;
+    
     SysPrefItem() {
-        prefName = nsnull;
-        defaultValue.intVal = 0;
-        defaultValue.stringVal = nsnull;
-        defaultValue.boolVal = PR_FALSE;
-        isLocked = PR_FALSE;
+        savedUserValueScalar = 0;
+        savedUserValueString = nsnull;
+        savedDefaultValueScalar = 0;
+        savedDefaultValueString = nsnull;
+        savedUserPresent = PR_FALSE;
+        savedLocked = PR_FALSE;
+        ignore = PR_FALSE;
     }
-    void SetPrefName(const char *aPrefName) {
-        prefName = aPrefName;
+
+    virtual ~SysPrefItem() {
+        nsMemory::Free(savedUserValueString);
+        nsMemory::Free(savedDefaultValueString);
     }
 };
 
-// all prefs that mozilla need to read from host system if they are available
-static const char *sSysPrefList[] = {
-    "network.proxy.http",
-    "network.proxy.http_port",
-    "network.proxy.ftp",
-    "network.proxy.ftp_port",
-    "network.proxy.ssl",
-    "network.proxy.ssl_port",
-    "network.proxy.socks",
-    "network.proxy.socks_port",
-    "network.proxy.no_proxies_on",
-    "network.proxy.autoconfig_url",
-    "network.proxy.type",
-    "config.use_system_prefs.accessibility",
-};
+NS_DEFINE_STATIC_IID_ACCESSOR(nsISystemPrefService, NS_ISYSTEMPREFSERVICE_IID)
+
+static const char sSysPrefString[] = "config.use_system_prefs";
 
 PRLogModuleInfo *gSysPrefLog = NULL;
 
 NS_IMPL_ISUPPORTS2(nsSystemPref, nsIObserver, nsISupportsWeakReference)
 
-nsSystemPref::nsSystemPref():
-    mSysPrefService(nsnull),
-    mEnabled(PR_FALSE),
-    mSysPrefs(nsnull)
+nsSystemPref::nsSystemPref() : mIgnorePrefSetting(PR_FALSE)
 {
+   mSavedPrefs.Init();
+   mCachedUserPrefBranch = nsnull;
+   mCachedDefaultPrefBranch = nsnull;
 }
 
 nsSystemPref::~nsSystemPref()
 {
-    mSysPrefService = nsnull;
-    mEnabled = PR_FALSE;
-    delete [] mSysPrefs;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // nsSystemPref::Init
 // Setup log and listen on NS_PREFSERVICE_READ_TOPIC_ID from pref service
 ///////////////////////////////////////////////////////////////////////////////
 nsresult
 nsSystemPref::Init(void)
@@ -126,349 +123,519 @@ nsSystemPref::Init(void)
                                           PR_FALSE);
         rv = observerService->AddObserver(this, "profile-before-change",
                                           PR_FALSE);
         SYSPREF_LOG(("Add Observer for %s\n", NS_PREFSERVICE_READ_TOPIC_ID));
     }
     return(rv);
 }
 
+already_AddRefed<nsIPrefBranch2>
+nsSystemPref::GetPrefUserBranch()
+{
+    if (mCachedUserPrefBranch) {
+        NS_ADDREF(mCachedUserPrefBranch);
+        return mCachedUserPrefBranch;
+    }
+
+    nsresult rv;
+    nsCOMPtr<nsIPrefService> prefService = 
+        do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
+    if (NS_FAILED(rv))
+        return nsnull;
+    nsCOMPtr<nsIPrefBranch> prefBranch;
+    rv = prefService->GetBranch(nsnull, getter_AddRefs(prefBranch));
+    if (NS_FAILED(rv))
+        return nsnull;
+    nsCOMPtr<nsIPrefBranch2> pb2(do_QueryInterface(prefBranch));
+    if (!pb2)
+        return nsnull;
+    
+    nsIPrefBranch2* result = nsnull;
+    pb2.swap(result);
+    return result;
+}
+
+already_AddRefed<nsIPrefBranch>
+nsSystemPref::GetPrefDefaultBranch()
+{
+    if (mCachedDefaultPrefBranch) {
+        NS_ADDREF(mCachedDefaultPrefBranch);
+        return mCachedDefaultPrefBranch;
+    }
+
+    nsresult rv;
+    nsCOMPtr<nsIPrefService> prefService = 
+        do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
+    if (NS_FAILED(rv))
+        return nsnull;
+    nsCOMPtr<nsIPrefBranch> prefBranch;
+    rv = prefService->GetDefaultBranch(nsnull, getter_AddRefs(prefBranch));
+    if (NS_FAILED(rv))
+        return nsnull;
+    nsIPrefBranch* pb = nsnull;
+    prefBranch.swap(pb);
+    return pb;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // nsSystemPref::Observe
 // Observe notifications from mozilla pref system and system prefs (if enabled)
 ///////////////////////////////////////////////////////////////////////////////
 NS_IMETHODIMP
 nsSystemPref::Observe(nsISupports *aSubject,
                       const char *aTopic,
                       const PRUnichar *aData)
 {
     nsresult rv = NS_OK;
 
     if (!aTopic)
         return NS_OK;
 
-    // if we are notified by pref service
-    // check the system pref settings
+    nsCOMPtr<nsIPrefBranch2> userBranch = GetPrefUserBranch();
+    nsCOMPtr<nsIPrefBranch> defaultBranch = GetPrefDefaultBranch();
+ 
+    // Check the default branch first. If system prefs are enabled
+    // by default, then don't check the user prefs; we don't want
+    // to allow users to change the default.
+    PRBool defaultEnabled;
+    rv = defaultBranch->GetBoolPref(sSysPrefString, &defaultEnabled);
+    if (NS_FAILED(rv)) {
+        SYSPREF_LOG(("...Failed to Get %s\n", sSysPrefString));
+        return rv;
+    }
+    PRBool enabled = defaultEnabled;
+    if (!enabled) {
+        rv = userBranch->GetBoolPref(sSysPrefString, &enabled);
+
+        if (NS_FAILED(rv)) {
+            SYSPREF_LOG(("...Failed to Get %s\n", sSysPrefString));
+            return rv;
+        }
+    }
     if (!nsCRT::strcmp(aTopic, NS_PREFSERVICE_READ_TOPIC_ID)) {
+        // The prefs have just loaded. This is the first thing that
+        // happens to us.
         SYSPREF_LOG(("Observed: %s\n", aTopic));
+ 
 
-        nsCOMPtr<nsIPrefBranch2> prefBranch =
-            do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
+        // listen on changes to use_system_pref. It's OK to
+        // hold a strong reference because we don't keep a reference
+        // to the pref branch.
+        rv = userBranch->AddObserver(sSysPrefString, this, PR_TRUE);
+
+        if (NS_FAILED(rv)) {
+            SYSPREF_LOG(("...Failed to add observer for %s\n", sSysPrefString));
+            return rv;
+        }
+        NS_ASSERTION(!mSysPrefService, "Should not be already enabled");
+        if (!enabled) {
+            // Don't load the system pref service if the preference is
+            // not set.
+            return NS_OK;
+        }
+
+        SYSPREF_LOG(("%s is enabled\n", sSysPrefString));
+
+	rv = LoadSystemPrefs();
         if (NS_FAILED(rv))
             return rv;
 
-        rv = prefBranch->GetBoolPref(sSysPrefString, &mEnabled);
-        if (NS_FAILED(rv)) {
-            SYSPREF_LOG(("...FAil to Get %s\n", sSysPrefString));
-            return rv;
-        }
-
-        // if there is no system pref service, assume nothing happen to us
-        mSysPrefService = do_GetService(NS_SYSTEMPREF_SERVICE_CONTRACTID, &rv);
-        if (NS_FAILED(rv) || !mSysPrefService) {
-            SYSPREF_LOG(("...No System Pref Service\n"));
-            return NS_OK;
-        }
-
-        // listen on its changes
-        rv = prefBranch->AddObserver(sSysPrefString, this, PR_TRUE);
-        if (NS_FAILED(rv)) {
-            SYSPREF_LOG(("...FAil to add observer for %s\n", sSysPrefString));
-            return rv;
-        }
-
-        if (!mEnabled) {
-            SYSPREF_LOG(("%s is disabled\n", sSysPrefString));
-            return NS_OK;
-        }
-        SYSPREF_LOG(("%s is enabled\n", sSysPrefString));
-        rv = UseSystemPrefs();
-
-    }
-    // sSysPrefString value was changed, update ...
-    else if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) &&
-             NS_ConvertUTF8toUTF16(sSysPrefString).Equals(aData)) {
-        SYSPREF_LOG(("++++++ Notify: topic=%s data=%s\n",
-                     aTopic, NS_ConvertUTF16toUTF8(aData).get()));
-
-        nsCOMPtr<nsIPrefBranch> prefBranch =
-            do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
-        if (NS_FAILED(rv))
-            return rv;
-
-        PRBool enabled = mEnabled;
-        rv = prefBranch->GetBoolPref(sSysPrefString, &mEnabled);
-        if (enabled != mEnabled) {
-            if (mEnabled)
-                //read prefs from system
-                rv = UseSystemPrefs();
-            else
-                //roll back to mozilla prefs
-                rv = UseMozillaPrefs();
+        // Lock config.use_system_prefs so the user can't undo
+        // it. But only do this if it was set by in the default prefs;
+        // if it was not set by default, then locking it would actually
+        // unset the value! And the user should be allowed to turn off
+        // something they set themselves.
+        if (NS_SUCCEEDED(rv) && defaultEnabled) {
+            userBranch->LockPref(sSysPrefString);
         }
     }
 
-    // if the system pref notify us that some pref has been changed by user
-    // outside mozilla. We need to read it again.
-    else if (!nsCRT::strcmp(aTopic, NS_SYSTEMPREF_PREFCHANGE_TOPIC_ID) &&
-             aData) {
-        NS_ASSERTION(mEnabled == PR_TRUE, "Should not listen when disabled");
-        SYSPREF_LOG(("====== System Pref Notify topic=%s data=%s\n",
-                     aTopic, (char*)aData));
-        rv = ReadSystemPref(NS_LossyConvertUTF16toASCII(aData).get());
+    if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) &&
+        nsDependentString(aData).EqualsASCII(sSysPrefString)) {
+        // sSysPrefString value was changed, update...
+        SYSPREF_LOG(("++++++ Notify: topic=%s data=%s\n",
+                     aTopic, NS_ConvertUTF16toUTF8(aData).get()));
+        if (mSysPrefService && !enabled)
+            return RestoreMozillaPrefs();
+        if (!mSysPrefService && enabled) {
+            // Don't lock it. If the user enabled use_system_prefs,
+            // they should be allowed to unlock it.
+            return LoadSystemPrefs();
+        }
+ 
+        // didn't change?
         return NS_OK;
-    } else if (!nsCRT::strcmp(aTopic,"profile-before-change")) {
-      //roll back to mozilla prefs
-      if (mEnabled)
-        UseMozillaPrefs();
-      mEnabled = PR_FALSE;
-      mSysPrefService = nsnull;
-      delete [] mSysPrefs;
-      mSysPrefs = nsnull;
-    } else
-        SYSPREF_LOG(("Not needed topic Received %s\n", aTopic));
+    }
+
+    if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
+        // some other pref changed, tell the backend if there is one
+        if (mSysPrefService && !mIgnorePrefSetting) {
+            NS_LossyConvertUTF16toASCII tmp(aData);
+#ifdef DEBUG
+            PRBool isLocked;
+            userBranch->PrefIsLocked(tmp.get(), &isLocked);
+            NS_ASSERTION(!isLocked, "Locked pref is changing?");
+#endif
+            SysPrefItem* item;
+            if (!mSavedPrefs.Get(tmp, &item)) {
+                NS_ERROR("Notified about pref change that we didn't ask about?");
+            } else {
+                if (!item->ignore) {
+                    mSysPrefService->NotifyMozillaPrefChanged(tmp.get());
+                }
+            }
+        }
+        return NS_OK;
+    }
+
+    if (!nsCRT::strcmp(aTopic,"profile-before-change"))
+        return RestoreMozillaPrefs();
+
+    SYSPREF_LOG(("Not needed topic Received %s\n", aTopic));
+
     return rv;
 }
 
-/* private */
+nsresult
+nsSystemPref::SetOverridingMozillaBoolPref(const char* aPrefName,
+                                           PRBool aValue, PRBool aLock, PRBool aPresent)
+{
+    return OverridePref(aPrefName, nsIPrefBranch::PREF_BOOL,
+                        (void*)aValue, aLock, aPresent);
+}
 
-////////////////////////////////////////////////////////////////
-// nsSystemPref::UseSystemPrefs
-// Read all the prefs in the table from system, listen for their
-// changes in system pref service.
-////////////////////////////////////////////////////////////////
 nsresult
-nsSystemPref::UseSystemPrefs()
+nsSystemPref::SetOverridingMozillaIntPref(const char* aPrefName,
+                                          PRInt32 aValue, PRBool aLock, PRBool aPresent)
 {
-    SYSPREF_LOG(("\n====Now Use system prefs==\n"));
-    nsresult rv = NS_OK;
-    if (!mSysPrefService) {
+    return OverridePref(aPrefName, nsIPrefBranch::PREF_INT,
+                        (void*)aValue, aLock, aPresent);
+}
+
+
+nsresult
+nsSystemPref::SetOverridingMozillaStringPref(const char* aPrefName,
+                                             const char* aValue, PRBool aLock, PRBool aPresent)
+{
+    return OverridePref(aPrefName, nsIPrefBranch::PREF_STRING,
+                        (void*)aValue, aLock, aPresent);
+}
+
+static nsresult RestorePrefValue(PRInt32 aPrefType,
+                                 const char* aPrefName,
+                                 SysPrefItem* aItem,
+                                 nsIPrefBranch* aUser,
+                                 nsIPrefBranch* aDefault)
+{
+    switch (aPrefType) {
+
+    case nsIPrefBranch::PREF_STRING:
+        aDefault->SetCharPref(aPrefName,
+                              aItem->savedDefaultValueString);
+        if (aItem->savedUserPresent) {
+            aUser->SetCharPref(aPrefName, aItem->savedUserValueString);
+        }
+        break;
+    case nsIPrefBranch::PREF_INT:
+        aDefault->SetBoolPref(aPrefName, aItem->savedDefaultValueScalar);
+        if (aItem->savedUserPresent) {
+            aUser->SetBoolPref(aPrefName, aItem->savedUserValueScalar);
+        }
+        break;
+    case nsIPrefBranch::PREF_BOOL:
+        aDefault->SetBoolPref(aPrefName, aItem->savedDefaultValueScalar);
+        if (aItem->savedUserPresent) {
+            aUser->SetBoolPref(aPrefName, aItem->savedUserValueScalar);
+        }
+        break;
+    default:
+        NS_ERROR("Unknown preference type");
         return NS_ERROR_FAILURE;
     }
 
-    PRIntn sysPrefCount= sizeof(sSysPrefList) / sizeof(sSysPrefList[0]);
-
-    if (!mSysPrefs) {
-        mSysPrefs = new SysPrefItem[sysPrefCount];
-        if (!mSysPrefs)
-            return NS_ERROR_OUT_OF_MEMORY;
-        for (PRIntn index = 0; index < sysPrefCount; ++index)
-            mSysPrefs[index].SetPrefName(sSysPrefList[index]);
+    if (!aItem->savedUserPresent) {
+        aUser->DeleteBranch(aPrefName);
     }
 
-    for (PRIntn index = 0; index < sysPrefCount; ++index) {
-        // save mozilla prefs
-        SaveMozDefaultPref(mSysPrefs[index].prefName,
-                           &mSysPrefs[index].defaultValue,
-                           &mSysPrefs[index].isLocked);
+    return NS_OK;
+}
 
-        // get the system prefs
-        ReadSystemPref(mSysPrefs[index].prefName);
-        SYSPREF_LOG(("Add Listener on %s\n", mSysPrefs[index].prefName));
-        mSysPrefService->AddObserver(mSysPrefs[index].prefName,
-                                     this, PR_TRUE);
+static PLDHashOperator PR_CALLBACK RestorePref(const nsACString& aKey,
+                                               SysPrefItem* aItem,
+                                               void* aClosure)
+{
+    nsSystemPref* prefs = static_cast<nsSystemPref*>(aClosure);
+    nsCOMPtr<nsIPrefBranch2> userBranch = prefs->GetPrefUserBranch();
+    const nsCString& prefName = PromiseFlatCString(aKey);
+    
+    PRInt32 prefType = nsIPrefBranch::PREF_INVALID;
+    nsresult rv = userBranch->GetPrefType(prefName.get(), &prefType);
+    if (NS_FAILED(rv))
+        return PL_DHASH_NEXT;
+    PRBool isLocked;
+    userBranch->PrefIsLocked(prefName.get(), &isLocked);
+    if (NS_FAILED(rv))
+        return PL_DHASH_NEXT;
+
+
+    // Remove our observer before we change the value
+    userBranch->RemoveObserver(prefName.get(), prefs);
+    // Remember to ignore this item. Because some prefs start with "config.use_system_prefs",
+    // which we always observe, even after we remove the observer, changes to the pref will
+    // still be observed by us. We must ignore them.
+    aItem->ignore = PR_TRUE;
+
+
+    // Unlock the pref so we can set it
+    if (isLocked) {
+        userBranch->UnlockPref(prefName.get());
     }
+
+    nsCOMPtr<nsIPrefBranch> defaultBranch = prefs->GetPrefDefaultBranch();
+
+    RestorePrefValue(prefType, prefName.get(), aItem,
+                     userBranch, defaultBranch);
+
+    if (aItem->savedLocked) {
+        userBranch->LockPref(prefName.get());
+    }
+
+    return PL_DHASH_NEXT;
+}
+
+nsresult
+nsSystemPref::StopOverridingMozillaPref(const char* aPrefName)
+{
+    SysPrefItem* item;
+    nsDependentCString prefNameStr(aPrefName);
+    if (!mSavedPrefs.Get(prefNameStr, &item))
+        return NS_OK;
+
+    RestorePref(prefNameStr, item, this);
+    mSavedPrefs.Remove(prefNameStr);
+    delete item;
+    return NS_OK;
+}
+
+/* private */
+ 
+nsresult
+nsSystemPref::OverridePref(const char* aPrefName, PRInt32 aType,
+                           void* aValue, PRBool aLock, PRBool aPresent)
+{
+    nsCOMPtr<nsIPrefBranch2> userBranch = GetPrefUserBranch();
+    nsCOMPtr<nsIPrefBranch> defaultBranch = GetPrefDefaultBranch();
+
+    PRInt32 prefType = nsIPrefBranch::PREF_INVALID;
+    nsresult rv = userBranch->GetPrefType(aPrefName, &prefType);
+    PRBool isLocked;
+    rv = userBranch->PrefIsLocked(aPrefName, &isLocked);
+    if (NS_FAILED(rv))
+        return rv;
+    PRBool hasUserValue;
+    rv = userBranch->PrefHasUserValue(aPrefName, &hasUserValue);
+    if (NS_FAILED(rv))
+        return rv;
+    if (prefType == 0) {
+        // Preference does not exist. Allow the system prefs to
+        // set it.
+    } else {
+        NS_ASSERTION(aType == prefType,
+                     "System pref engine passed incorrect type for Mozilla pref");
+        if (aType != prefType)
+            return NS_ERROR_FAILURE;
+    }
+
+    if (prefType != 0) {
+        nsDependentCString prefNameStr(aPrefName);
+        SysPrefItem* item = nsnull;
+        if (!mSavedPrefs.Get(prefNameStr, &item)) {
+            // Need to save the existing value away
+            item = new SysPrefItem();
+            if (!item)
+                return NS_ERROR_OUT_OF_MEMORY;
+
+            item->savedLocked = isLocked;
+            item->savedUserPresent = hasUserValue;
+        
+            switch (prefType) {
+            case nsIPrefBranch::PREF_STRING:
+                if (hasUserValue) {
+                    userBranch->GetCharPref(aPrefName, &item->savedUserValueString);
+                }
+                defaultBranch->GetCharPref(aPrefName, &item->savedDefaultValueString);
+                break;
+            case nsIPrefBranch::PREF_INT:
+                if (hasUserValue) {
+                    userBranch->GetIntPref(aPrefName, &item->savedUserValueScalar);
+                }
+                defaultBranch->GetIntPref(aPrefName, &item->savedDefaultValueScalar);
+                break;
+            case nsIPrefBranch::PREF_BOOL:
+                if (hasUserValue) {
+                    userBranch->GetBoolPref(aPrefName, &item->savedUserValueScalar);
+                }
+                defaultBranch->GetBoolPref(aPrefName, &item->savedDefaultValueScalar);
+                break;
+            default:
+                NS_ERROR("Unknown preference type");
+                delete item;
+                return NS_ERROR_FAILURE;
+            }
+
+            mSavedPrefs.Put(prefNameStr, item);
+
+            // Watch the user value in case it changes on the Mozilla side
+            // If 'aLock' is true then it shouldn't change and we don't
+            // need the observer, but don't bother optimizing for that.
+            userBranch->AddObserver(aPrefName, this, PR_TRUE);
+        } else {
+            if (isLocked != aLock) {
+                // restore pref value on user and default branches
+                RestorePrefValue(prefType, aPrefName, item,
+                                 userBranch, defaultBranch);
+            }
+        }
+    }
+
+    // We need to ignore pref changes due to our own calls here
+    mIgnorePrefSetting = PR_TRUE;
+ 
+    // Unlock it if it's locked, so we can set it
+    if (isLocked) {
+        rv = userBranch->UnlockPref(aPrefName);
+        if (NS_FAILED(rv))
+            return rv;
+    }
+
+    // Set the pref on the default branch if we're locking it, because
+    // only the default branch gets used when the pref is locked.
+    // Set the pref on the user branch if we're not locking it, because
+    // that's where the user change will go.
+    nsIPrefBranch* settingBranch =
+        aLock ? defaultBranch.get() : static_cast<nsIPrefBranch*>(userBranch.get());
+
+    if (!aPresent) {
+        rv = settingBranch->DeleteBranch(aPrefName);
+    } else {
+        switch (aType) {
+        case nsIPrefBranch::PREF_STRING:
+            rv = settingBranch->SetCharPref(aPrefName, (const char*)aValue);
+            break;
+        case nsIPrefBranch::PREF_INT:
+            rv = settingBranch->SetIntPref(aPrefName, (PRInt32)(NS_PTR_TO_INT32(aValue)));
+            break;
+        case nsIPrefBranch::PREF_BOOL:
+            rv = settingBranch->SetBoolPref(aPrefName, (PRBool)(NS_PTR_TO_INT32(aValue)));
+            break;
+        default:
+            NS_ERROR("Unknown preference type");
+            mIgnorePrefSetting = PR_FALSE;
+            return NS_ERROR_FAILURE;
+        }
+    }
+
+    if (NS_FAILED(rv))
+        return rv;
+    if (aLock) {
+        rv = userBranch->LockPref(aPrefName);
+    }
+
+    mIgnorePrefSetting = PR_FALSE;
     return rv;
 }
 
-//////////////////////////////////////////////////////////////////////
-// nsSystemPref::ReadSystemPref
-// Read a pref value from system pref service, and lock it in mozilla.
-//////////////////////////////////////////////////////////////////////
+
 nsresult
-nsSystemPref::ReadSystemPref(const char *aPrefName)
+nsSystemPref::FixupLockdownPrefs()
 {
-    if (!mSysPrefService)
-        return NS_ERROR_FAILURE;
-    nsresult rv;
-
-    nsCOMPtr<nsIPrefBranch> prefBranch
-        (do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+    nsCOMPtr<nsIPrefBranch2> userPrefs = GetPrefUserBranch();
+    nsCOMPtr<nsIPrefBranch2> defaultPrefs = GetPrefUserBranch();
+    PRUint32 childCount;
+    char **childArray = nsnull;
+    nsresult rv = userPrefs->GetChildList("config.lockdown.",
+                                          &childCount, &childArray);
     if (NS_FAILED(rv))
         return rv;
-
-    SYSPREF_LOG(("about to read aPrefName %s\n", aPrefName));
-
-    prefBranch->UnlockPref(aPrefName);
-
-    PRInt32 prefType = nsIPrefBranch::PREF_INVALID;
-    nsXPIDLCString strValue;
-    PRInt32 intValue = 0;
-    PRBool boolValue = PR_FALSE;
-
-    rv = prefBranch->GetPrefType(aPrefName, &prefType);
-    if (NS_FAILED(rv))
-        return rv;
-    switch (prefType) {
-    case nsIPrefBranch::PREF_STRING:
-        mSysPrefService->GetCharPref(aPrefName, getter_Copies(strValue));
-        SYSPREF_LOG(("system value is %s\n", strValue.get()));
-
-        prefBranch->SetCharPref(aPrefName, strValue.get());
-        break;
-    case nsIPrefBranch::PREF_INT:
-        mSysPrefService->GetIntPref(aPrefName, &intValue);
-        SYSPREF_LOG(("system value is %d\n", intValue));
-
-        prefBranch->SetIntPref(aPrefName, intValue);
-        break;
-    case nsIPrefBranch::PREF_BOOL:
-        mSysPrefService->GetBoolPref(aPrefName, &boolValue);
-        SYSPREF_LOG(("system value is %s\n", boolValue ? "TRUE" : "FALSE"));
-
-        prefBranch->SetBoolPref(aPrefName, boolValue);
-        break;
-    default:
-        SYSPREF_LOG(("Fail to system value for it\n"));
-        return NS_ERROR_FAILURE;
+    for (PRUint32 i = 0; i < childCount; ++i) {
+        PRInt32 type;
+        rv = defaultPrefs->GetPrefType(childArray[i], &type);
+        if (NS_FAILED(rv))
+            return rv;
+        NS_ASSERTION(type == nsIPrefBranch2::PREF_BOOL,
+                     "All config.lockdown.* prefs should be boolean");
+        if (type == nsIPrefBranch2::PREF_BOOL) {
+            rv = defaultPrefs->SetBoolPref(childArray[i], PR_FALSE);
+            if (NS_FAILED(rv))
+                return rv;
+        }
     }
-    prefBranch->LockPref(aPrefName);
+    NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(childCount, childArray);
     return NS_OK;
 }
 
-//////////////////////////////////////////////////////////////////////
-// nsSystemPref::UseMozillaPrefs
-// Restore mozilla default prefs, remove system pref listeners
-/////////////////////////////////////////////////////////////////////
+
 nsresult
-nsSystemPref::UseMozillaPrefs()
+nsSystemPref::LoadSystemPrefs()
 {
-    nsresult rv = NS_OK;
-    SYSPREF_LOG(("\n====Now rollback to Mozilla prefs==\n"));
+    SYSPREF_LOG(("\n====Now Use system prefs==\n"));
+    NS_ASSERTION(!mSysPrefService,
+                 "Shouldn't have the pref service here");
+    nsresult rv;
+    mSysPrefService = do_GetService(NS_SYSTEMPREF_SERVICE_CONTRACTID, &rv);
+    if (NS_FAILED(rv) || !mSysPrefService) {
+        FixupLockdownPrefs();
+        SYSPREF_LOG(("...No System Pref Service\n"));
+        return NS_OK;
+    }
 
-    // if we did not use system prefs, do nothing
-    if (!mSysPrefService)
-        return NS_OK;
+    // Cache the pref-branch while we load up the system prefs.
+    NS_ASSERTION(!mCachedUserPrefBranch,
+                 "Shouldn't have a cache here");
+    nsCOMPtr<nsIPrefBranch2> userBranch = GetPrefUserBranch();
+    nsCOMPtr<nsIPrefBranch> defaultBranch = GetPrefDefaultBranch();
+    mCachedDefaultPrefBranch = defaultBranch;
+    mCachedUserPrefBranch = userBranch;
+    rv = mSysPrefService->LoadSystemPreferences(this);
+    mCachedDefaultPrefBranch = nsnull;
+    mCachedUserPrefBranch = nsnull;
 
-    PRIntn sysPrefCount= sizeof(sSysPrefList) / sizeof(sSysPrefList[0]);
-    for (PRIntn index = 0; index < sysPrefCount; ++index) {
-        // restore mozilla default value and free string memory if needed
-        RestoreMozDefaultPref(mSysPrefs[index].prefName,
-                              &mSysPrefs[index].defaultValue,
-                              mSysPrefs[index].isLocked);
-        SYSPREF_LOG(("stop listening on %s\n", mSysPrefs[index].prefName));
-        mSysPrefService->RemoveObserver(mSysPrefs[index].prefName,
-                                        this);
+    if (NS_FAILED(rv)) {
+        // Restore all modified preferences to their original values
+        mSavedPrefs.EnumerateRead(RestorePref, this);
+        mSavedPrefs.Clear();
+        mSysPrefService = nsnull;
     }
+        
     return rv;
 }
 
-////////////////////////////////////////////////////////////////////////////
-// nsSystemPref::RestoreMozDefaultPref
-// Save the saved mozilla default value.
-// It is also responsible for allocate the string memory when needed, because
-// this method know what type of value is stored.
-/////////////////////////////////////////////////////////////////////////////
+
 nsresult
-nsSystemPref::SaveMozDefaultPref(const char *aPrefName,
-                                 MozPrefValue *aPrefValue,
-                                 PRBool *aLocked)
+nsSystemPref::RestoreMozillaPrefs()
 {
-    NS_ENSURE_ARG_POINTER(aPrefName);
-    NS_ENSURE_ARG_POINTER(aPrefValue);
-    NS_ENSURE_ARG_POINTER(aLocked);
+    SYSPREF_LOG(("\n====Now rollback to Mozilla prefs==\n"));
 
-    nsresult rv;
+    NS_ASSERTION(mSysPrefService,
+                 "Should have the pref service here");
+    if (!mSysPrefService)
+        return NS_ERROR_FAILURE;
 
-    nsCOMPtr<nsIPrefBranch> prefBranch =
-        do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
-    if (NS_FAILED(rv))
-        return rv;
+    nsCOMPtr<nsIPrefBranch2> userBranch = GetPrefUserBranch();
+    nsCOMPtr<nsIPrefBranch> defaultBranch = GetPrefDefaultBranch();
+    mCachedDefaultPrefBranch = defaultBranch;
+    mCachedUserPrefBranch = userBranch;
 
-    SYSPREF_LOG(("Save Mozilla value for %s\n", aPrefName));
+    mSysPrefService->NotifyUnloadSystemPreferences();
+    // Restore all modified preferences to their original values
+    mSavedPrefs.EnumerateRead(RestorePref, this);
+    mSavedPrefs.Clear();
 
-    PRInt32 prefType = nsIPrefBranch::PREF_INVALID;
-    nsXPIDLCString strValue;
+    mCachedDefaultPrefBranch = nsnull;
+    mCachedUserPrefBranch = nsnull;
+    
+    mSysPrefService = nsnull;
 
-    rv = prefBranch->GetPrefType(aPrefName, &prefType);
-    if (NS_FAILED(rv))
-        return rv;
-    switch (prefType) {
-    case nsIPrefBranch::PREF_STRING:
-        prefBranch->GetCharPref(aPrefName,
-                                getter_Copies(strValue));
-        SYSPREF_LOG(("Mozilla value is %s", strValue.get()));
+    FixupLockdownPrefs();
 
-        if (aPrefValue->stringVal)
-            PL_strfree(aPrefValue->stringVal);
-        aPrefValue->stringVal = PL_strdup(strValue.get());
-        break;
-    case nsIPrefBranch::PREF_INT:
-        prefBranch->GetIntPref(aPrefName, &aPrefValue->intVal);
-        SYSPREF_LOG(("Mozilla value is %d\n", aPrefValue->intVal));
-
-        break;
-    case nsIPrefBranch::PREF_BOOL:
-        prefBranch->GetBoolPref(aPrefName, &aPrefValue->boolVal);
-        SYSPREF_LOG(("Mozilla value is %s\n",
-                     aPrefValue->boolVal ? "TRUE" : "FALSE"));
-
-        break;
-    default:
-        SYSPREF_LOG(("Fail to Read Mozilla value for it\n"));
-        return NS_ERROR_FAILURE;
-    }
-    rv = prefBranch->PrefIsLocked(aPrefName, aLocked);
-    SYSPREF_LOG((" (%s).\n", aLocked ? "Locked" : "NOT Locked"));
-    return rv;
-}
-
-////////////////////////////////////////////////////////////////////////////
-// nsSystemPref::RestoreMozDefaultPref
-// Restore the saved mozilla default value to pref service.
-// It is also responsible for free the string memory when needed, because
-// this method know what type of value is stored.
-/////////////////////////////////////////////////////////////////////////////
-nsresult
-nsSystemPref::RestoreMozDefaultPref(const char *aPrefName,
-                                    MozPrefValue *aPrefValue,
-                                    PRBool aLocked)
-{
-    NS_ENSURE_ARG_POINTER(aPrefName);
-
-    nsresult rv;
-
-    nsCOMPtr<nsIPrefBranch> prefBranch =
-        do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
-    if (NS_FAILED(rv))
-        return rv;
-
-    SYSPREF_LOG(("Restore Mozilla value for %s\n", aPrefName));
-
-    PRInt32 prefType = nsIPrefBranch::PREF_INVALID;
-    rv = prefBranch->GetPrefType(aPrefName, &prefType);
-    if (NS_FAILED(rv))
-        return rv;
-
-    // unlock, if it is locked
-    prefBranch->UnlockPref(aPrefName);
-
-    switch (prefType) {
-    case nsIPrefBranch::PREF_STRING:
-        prefBranch->SetCharPref(aPrefName,
-                                aPrefValue->stringVal);
-        SYSPREF_LOG(("Mozilla value is %s\n", aPrefValue->stringVal));
-
-        PL_strfree(aPrefValue->stringVal);
-        aPrefValue->stringVal = nsnull;
-
-        break;
-    case nsIPrefBranch::PREF_INT:
-        prefBranch->SetIntPref(aPrefName, aPrefValue->intVal);
-        SYSPREF_LOG(("Mozilla value is %d\n", aPrefValue->intVal));
-
-        break;
-    case nsIPrefBranch::PREF_BOOL:
-        prefBranch->SetBoolPref(aPrefName, aPrefValue->boolVal);
-        SYSPREF_LOG(("Mozilla value is %s\n",
-                     aPrefValue->boolVal ? "TRUE" : "FALSE"));
-
-        break;
-    default:
-        SYSPREF_LOG(("Fail to Restore Mozilla value for it\n"));
-        return NS_ERROR_FAILURE;
-    }
-
-    // restore its old lock status
-    if (aLocked)
-        prefBranch->LockPref(aPrefName);
     return NS_OK;
 }
diff --git a/extensions/pref/system-pref/src/nsSystemPref.h b/extensions/pref/system-pref/src/nsSystemPref.h
--- a/extensions/pref/system-pref/src/nsSystemPref.h
+++ b/extensions/pref/system-pref/src/nsSystemPref.h
@@ -18,17 +18,17 @@
  * The Original Code is mozilla.org code.
  *
  * The Initial Developer of the Original Code is Sun Microsystems, Inc.
  * Portions created by Sun Microsystems are Copyright (C) 2003 Sun
  * Microsystems, Inc. All Rights Reserved.
  *
  * Original Author: Bolian Yin (bolian.yin@sun.com)
  *
- * Contributor(s):
+ * Contributor(s): Robert O'Callahan/Novell (rocallahan@novell.com)
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the NPL, indicate your
@@ -40,71 +40,101 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef __SYSTEM_PREF_H__
 #define __SYSTEM_PREF_H__
 
 #include "nsCOMPtr.h"
 #include "nsXPCOM.h"
 #include "nsCRT.h"
-#include "nsIAppStartupNotifier.h"
-#include "nsICategoryManager.h"
-#include "nsIServiceManager.h"
 #include "nsWeakReference.h"
-#include "nsIPrefService.h"
-#include "nsIPrefBranch2.h"
+#include "nsClassHashtable.h"
+#include "nsHashKeys.h"
+#include "nsMemory.h"
 
-#include <nsIObserver.h>
+#include "nsISystemPrefService.h"
+#include "nsIObserver.h"
 
-union MozPrefValue;
 struct SysPrefItem;
 
 //////////////////////////////////////////////////////////////////////////
 //
 // nsSystemPref, as an extension of mozilla pref service, reads some mozilla
 // prefs from host system when the feature is enabled ("config.system-pref").
 //
-// nsSystemPref listens on NS_PREFSERVICE_READ_TOPIC_ID. When notified,
-// nsSystemPref will start the nsSystemPrefService (platform specific) to
-// read all the interested prefs (listed in sSysPrefList table) from system
-// and lock these prefs from user's modification. 
+// nsSystemPref listens on NS_PREFSERVICE_READ_TOPIC_ID. When
+// notified, nsSystemPref will start the nsSystemPrefService (platform
+// specific) and tell it to override Mozilla prefs with its own
+// settings.
 //
-// This feature will make mozilla integrated better into host platforms. If
-// users want to change the prefs read from system, the system provided pref
-// editor (i.e. gconf-editor in gnome) should be used.
+// When overriding a Mozilla preference the prefservice can request the
+// pref be locked or unlocked. If the pref is locked then we set the default
+// value and lock it in Mozilla so the user value is ignored and the user cannot
+// change the value. If the pref is unlocked then we set the user value
+// and unlock it in Mozilla so the user can change it. If the user changes it,
+// then the prefservice is notified so it can copy the value back to its
+// underlying store.
+//
+// We detect changes to Mozilla prefs by observing pref changes in the
+// user branch.
+//
+// For testing purposes, if the user toggles on
+// config.use_system_prefs then we save the current preferences before
+// overriding them from gconf, and if the user toggles off
+// config.use_system_prefs *in the same session* then we restore the
+// preferences. If the user exits without turning off use_system_prefs
+// then the saved values are lost and the new values are permanent.
+//
 //////////////////////////////////////////////////////////////////////////
 
 class nsSystemPref : public nsIObserver,
-                     public nsSupportsWeakReference
+                     public nsSupportsWeakReference,
+                     public nsISystemPref
 {
 public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIOBSERVER
 
     nsSystemPref();
     virtual ~nsSystemPref();
     nsresult Init(void);
 
+    // nsISystemPref
+    virtual nsresult SetOverridingMozillaBoolPref(const char* aPrefName,
+                                                  PRBool aValue, PRBool aLocked,
+                                                  PRBool aPresent = PR_TRUE);
+    virtual nsresult SetOverridingMozillaIntPref(const char* aPrefName,
+                                                 PRInt32 aValue, PRBool aLocked,
+                                                 PRBool aPresent = PR_TRUE);
+    virtual nsresult SetOverridingMozillaStringPref(const char* aPrefName,
+                                                    const char* aValue, PRBool aLocked,
+                                                    PRBool aPresent = PR_TRUE);
+    virtual nsresult StopOverridingMozillaPref(const char* aPrefName);
+    virtual already_AddRefed<nsIPrefBranch2> GetPrefUserBranch();
+    virtual already_AddRefed<nsIPrefBranch> GetPrefDefaultBranch();
+
 private:
-    // funcs used to load system prefs and save mozilla default prefs
-    nsresult UseSystemPrefs();
-    nsresult ReadSystemPref(const char *aPrefName);
-    nsresult SaveMozDefaultPref(const char *aPrefName,
-                                MozPrefValue *aPrefVal,
-                                PRBool *aLocked);
+    // If we don't load the system prefs for any reason, then
+    // set all config.lockdown.* preferences to PR_FALSE so that
+    // residual lockdown settings are removed.
+    nsresult FixupLockdownPrefs();
 
-    // funcs used to load mozilla default prefs
-    nsresult UseMozillaPrefs();
-    nsresult RestoreMozDefaultPref(const char *aPrefName,
-                                   MozPrefValue *aPrefVal,
-                                   PRBool aLocked);
+    nsresult LoadSystemPrefs();
 
-    nsCOMPtr<nsIPrefBranch2>  mSysPrefService;
-    PRBool mEnabled;  // system pref is enabled or not
-    SysPrefItem *mSysPrefs;
+    nsresult RestoreMozillaPrefs();
+
+    nsresult OverridePref(const char* aPrefName, PRInt32 aType,
+                          void* aValue, PRBool aLock, PRBool aPresent);
+
+    nsCOMPtr<nsISystemPrefService>  mSysPrefService;
+    nsClassHashtable<nsCStringHashKey,SysPrefItem> mSavedPrefs;
+    // weak pointers to cached prefbranches
+    nsIPrefBranch2* mCachedUserPrefBranch;
+    nsIPrefBranch* mCachedDefaultPrefBranch;
+    PRPackedBool mIgnorePrefSetting;
 };
 
 #define NS_SYSTEMPREF_CID                  \
   { /* {549abb24-7c9d-4aba-915e-7ce0b716b32f} */       \
     0x549abb24,                                        \
     0x7c9d,                                            \
     0x4aba,                                            \
     { 0x91, 0x5e, 0x7c, 0xe0, 0xb7, 0x16, 0xb3, 0x2f } \
diff --git a/extensions/pref/system-pref/src/nsSystemPrefFactory.cpp b/extensions/pref/system-pref/src/nsSystemPrefFactory.cpp
--- a/extensions/pref/system-pref/src/nsSystemPrefFactory.cpp
+++ b/extensions/pref/system-pref/src/nsSystemPrefFactory.cpp
@@ -37,20 +37,20 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the NPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsICategoryManager.h"
 #include "nsIGenericFactory.h"
 #include "nsSystemPref.h"
-#include "nsSystemPrefService.h"
+#include "nsIServiceManager.h"
+#include "nsIAppStartupNotifier.h"
 
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsSystemPref, Init)
-NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsSystemPrefService, Init)
 
 // Registering nsSystemPref module as part of the app-startup category to get 
 // it instantiated.
 
 static NS_METHOD
 RegisterSystemPref(nsIComponentManager *aCompMgr,
                    nsIFile *aPath,
                    const char *registryLocation,
@@ -91,16 +91,11 @@ UnRegisterSystemPref(nsIComponentManager
 static const nsModuleComponentInfo components[] = {
     { NS_SYSTEMPREF_CLASSNAME,
       NS_SYSTEMPREF_CID,
       NS_SYSTEMPREF_CONTRACTID,
       nsSystemPrefConstructor,
       RegisterSystemPref,
       UnRegisterSystemPref,
     },
-    { NS_SYSTEMPREF_SERVICE_CLASSNAME,
-      NS_SYSTEMPREF_SERVICE_CID,
-      NS_SYSTEMPREF_SERVICE_CONTRACTID,
-      nsSystemPrefServiceConstructor,
-    },
 };
 
 NS_IMPL_NSGETMODULE(nsSystemPrefModule, components)
diff --git a/toolkit/library/Makefile.in b/toolkit/library/Makefile.in
--- a/toolkit/library/Makefile.in
+++ b/toolkit/library/Makefile.in
@@ -225,16 +225,20 @@ endif
 ifdef MOZ_ENABLE_LIBCONIC
 EXTRA_DSO_LDOPTS += $(LIBCONIC_LIBS)
 endif
 
 ifdef MOZ_ENABLE_DBUS
 EXTRA_DSO_LDOPTS += $(MOZ_DBUS_GLIB_LIBS)
 endif
 
+ifdef MOZ_ENABLE_GCONF
+EXTRA_DSO_LDOPTS += $(MOZ_GCONF_LIBS)
+endif
+
 ifeq (gtk2,$(MOZ_WIDGET_TOOLKIT))
 EXTRA_DSO_LDOPTS += $(XLDFLAGS) $(XLIBS) $(XEXT_LIBS) $(XCOMPOSITE_LIBS) $(MOZ_PANGO_LIBS) $(MOZ_GTK2_LIBS) $(XT_LIBS) -lgthread-2.0
 EXTRA_DSO_LDOPTS += $(FT2_LIBS)
 endif
 
 ifeq (qt,$(MOZ_WIDGET_TOOLKIT))
 EXTRA_DSO_LDOPTS += $(XLDFLAGS) $(XLIBS) $(XT_LIBS) $(MOZ_QT_LIBS) -lgthread-2.0
 EXTRA_DSO_LDOPTS += $(FT2_LIBS)
diff --git a/toolkit/toolkit-tiers.mk b/toolkit/toolkit-tiers.mk
--- a/toolkit/toolkit-tiers.mk
+++ b/toolkit/toolkit-tiers.mk
@@ -254,16 +254,23 @@ endif
 ifdef MOZ_ENABLE_LIBXUL
 tier_toolkit_dirs += xpcom/stub
 endif
 
 ifdef NS_TRACE_MALLOC
 tier_toolkit_dirs += tools/trace-malloc
 endif
 
+# gconf module is external
+ifdef MOZ_PREF_EXTENSIONS
+ifdef MOZ_ENABLE_GCONF
+tier_toolkit_dirs += extensions/pref/system-pref/src/gconf
+endif
+endif
+
 ifdef MOZ_ENABLE_GNOME_COMPONENT
 tier_toolkit_dirs    += toolkit/system/gnome
 endif
 
 ifndef MOZ_ENABLE_LIBCONIC
 # if libconic is present, it will do its own network monitoring
 ifdef MOZ_ENABLE_DBUS
 tier_toolkit_dirs    += toolkit/system/dbus