diff --git a/modules/libpref/src/Makefile.in b/modules/libpref/src/Makefile.in
--- a/modules/libpref/src/Makefile.in
+++ b/modules/libpref/src/Makefile.in
@@ -90,14 +90,16 @@ GREPREF_FILES = $(topsrcdir)/netwerk/bas
 # Optimizer bug with GCC 3.2.2 on OS/2
 ifeq ($(OS_ARCH), OS2)
 nsPrefService.$(OBJ_SUFFIX): nsPrefService.cpp
 	$(REPORT_BUILD)
 	@$(MAKE_DEPS_AUTO_CXX)
 	$(ELOG) $(CCC) $(OUTOPTION)$@ -c $(COMPILE_CXXFLAGS:-O2=-O1) $(_VPATH_SRCS)
 endif
 
+LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/xre
+
 
 greprefs.js: $(GREPREF_FILES)
 	$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(PREF_PPFLAGS) $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) $^ > $@
 
 libs:: greprefs.js
 	$(INSTALL) $^ $(DIST)/bin/
diff --git a/modules/libpref/src/nsPrefService.cpp b/modules/libpref/src/nsPrefService.cpp
--- a/modules/libpref/src/nsPrefService.cpp
+++ b/modules/libpref/src/nsPrefService.cpp
@@ -55,16 +55,17 @@
 #include "nsIStringEnumerator.h"
 #include "nsIZipReader.h"
 #include "nsPrefBranch.h"
 #include "nsXPIDLString.h"
 #include "nsCRT.h"
 #include "nsCOMArray.h"
 #include "nsXPCOMCID.h"
 #include "nsAutoPtr.h"
+#include "nsKDEUtils.h"
 
 #include "nsQuickSort.h"
 #include "prmem.h"
 #include "pldhash.h"
 
 #include "prefapi.h"
 #include "prefread.h"
 #include "prefapi_private_data.h"
@@ -760,30 +761,48 @@ pref_LoadPrefsInDir(nsIFile* aDir, char 
 }
 
 static nsresult pref_LoadPrefsInDirList(const char *listId)
 {
   nsresult rv;
   nsCOMPtr<nsIProperties> dirSvc(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv));
   if (NS_FAILED(rv)) return rv;
 
+  // make sure we load these special files after all the others
+  static const char* specialFiles[] = {
+#if defined(XP_UNIX)
+    ""
+#endif
+  };
+
+  if (nsKDEUtils::kdeSession()) {
+    for(int i = 0;
+        i < NS_ARRAY_LENGTH(specialFiles);
+        ++i ) {
+      if (*specialFiles[ i ] == '\0') {
+        specialFiles[ i ] = "kde.js";
+        break;
+      }
+    }
+  }
+
   nsCOMPtr<nsISimpleEnumerator> dirList;
   dirSvc->Get(listId,
               NS_GET_IID(nsISimpleEnumerator),
               getter_AddRefs(dirList));
   if (dirList) {
     PRBool hasMore;
     while (NS_SUCCEEDED(dirList->HasMoreElements(&hasMore)) && hasMore) {
       nsCOMPtr<nsISupports> elem;
       dirList->GetNext(getter_AddRefs(elem));
       if (elem) {
         nsCOMPtr<nsIFile> dir = do_QueryInterface(elem);
         if (dir) {
           // Do we care if a file provided by this process fails to load?
-          pref_LoadPrefsInDir(dir, nsnull, 0); 
+          pref_LoadPrefsInDir(dir, specialFiles, NS_ARRAY_LENGTH(specialFiles));
         }
       }
     }
   }
   return NS_OK;
 }
 
 //----------------------------------------------------------------------------------------
@@ -814,28 +833,40 @@ static nsresult pref_InitDefaults()
   /* these pref file names should not be used: we process them after all other application pref files for backwards compatibility */
   static const char* specialFiles[] = {
 #if defined(XP_MAC) || defined(XP_MACOSX)
       "macprefs.js"
 #elif defined(XP_WIN)
       "winpref.js"
 #elif defined(XP_UNIX)
       "unix.js"
+      , "" // placeholder for KDE  (empty is otherwise harmless)
 #if defined(VMS)
       , "openvms.js"
 #elif defined(_AIX)
       , "aix.js"
 #endif
 #elif defined(XP_OS2)
       "os2pref.js"
 #elif defined(XP_BEOS)
       "beos.js"
 #endif
   };
 
+  if(nsKDEUtils::kdeSession()) { // TODO what if some setup actually requires the helper?
+    for( int i = 0;
+         i < NS_ARRAY_LENGTH(specialFiles);
+         ++i ) {
+      if( *specialFiles[ i ] == '\0' ) {
+        specialFiles[ i ] = "kde.js";
+        break;
+      }
+    }
+  }
+
   rv = pref_LoadPrefsInDir(defaultPrefDir, specialFiles, NS_ARRAY_LENGTH(specialFiles));
   if (NS_FAILED(rv)) {
     NS_WARNING("Error parsing application default preferences.");
   }
 
   return NS_OK;
 }
 
diff --git a/toolkit/components/downloads/src/Makefile.in b/toolkit/components/downloads/src/Makefile.in
--- a/toolkit/components/downloads/src/Makefile.in
+++ b/toolkit/components/downloads/src/Makefile.in
@@ -71,8 +71,9 @@ EXTRA_COMPONENTS = \
   nsDownloadManagerUI.manifest \
   $(NULL)
 endif
 
 include $(topsrcdir)/config/rules.mk
 
 EXTRA_DSO_LDOPTS += $(MOZ_COMPONENT_LIBS)
 
+LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/xre
diff --git a/toolkit/components/downloads/src/nsDownloadManager.cpp b/toolkit/components/downloads/src/nsDownloadManager.cpp
--- a/toolkit/components/downloads/src/nsDownloadManager.cpp
+++ b/toolkit/components/downloads/src/nsDownloadManager.cpp
@@ -71,16 +71,20 @@
 
 #if defined(XP_WIN) && !defined(WINCE)
 #include <shlobj.h>
 #ifdef DOWNLOAD_SCANNER
 #include "nsDownloadScanner.h"
 #endif
 #endif
 
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+#include "nsKDEUtils.h"
+#endif
+
 #ifdef XP_MACOSX
 #include <CoreFoundation/CoreFoundation.h>
 #endif
 
 #define DOWNLOAD_MANAGER_BUNDLE "chrome://mozapps/locale/downloads/downloads.properties"
 #define DOWNLOAD_MANAGER_ALERT_ICON "chrome://mozapps/skin/downloads/downloadIcon.png"
 #define PREF_BDM_SHOWALERTONCOMPLETE "browser.download.manager.showAlertOnComplete"
 #define PREF_BDM_SHOWALERTINTERVAL "browser.download.manager.showAlertInterval"
@@ -2192,16 +2196,25 @@ nsDownload::SetState(DownloadState aStat
       nsCOMPtr<nsIPrefBranch> pref(do_GetService(NS_PREFSERVICE_CONTRACTID));
 
       // Master pref to control this function.
       PRBool showTaskbarAlert = PR_TRUE;
       if (pref)
         pref->GetBoolPref(PREF_BDM_SHOWALERTONCOMPLETE, &showTaskbarAlert);
 
       if (showTaskbarAlert) {
+        if( nsKDEUtils::kdeSupport()) {
+            nsCStringArray command;
+            command.AppendCString( NS_LITERAL_CSTRING( "DOWNLOADFINISHED" ));
+            nsAutoString displayName;
+            GetDisplayName( displayName );
+            command.AppendCString( nsCAutoString( ToNewUTF8String( displayName )));
+            nsKDEUtils::command( command );
+        } else {
+        // begin non-KDE block
         PRInt32 alertInterval = 2000;
         if (pref)
           pref->GetIntPref(PREF_BDM_SHOWALERTINTERVAL, &alertInterval);
 
         PRInt64 alertIntervalUSec = alertInterval * PR_USEC_PER_MSEC;
         PRInt64 goat = PR_Now() - mStartTime;
         showTaskbarAlert = goat > alertIntervalUSec;
 
@@ -2227,16 +2240,17 @@ nsDownload::SetState(DownloadState aStat
               // because if it is, they'll click open the download manager and
               // the items they downloaded will have been removed.
               alerts->ShowAlertNotification(
                   NS_LITERAL_STRING(DOWNLOAD_MANAGER_ALERT_ICON), title,
                   message, !removeWhenDone, EmptyString(), mDownloadManager,
                   EmptyString());
             }
         }
+        } // end non-KDE block
       }
 
 #if (defined(XP_WIN) && !defined(WINCE)) || defined(XP_MACOSX)
       nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mTarget);
       nsCOMPtr<nsIFile> file;
       nsAutoString path;
 
       if (fileURL &&
diff --git a/toolkit/content/jar.mn b/toolkit/content/jar.mn
--- a/toolkit/content/jar.mn
+++ b/toolkit/content/jar.mn
@@ -40,29 +40,33 @@ toolkit.jar:
 *+ content/global/viewZoomOverlay.js          (viewZoomOverlay.js)
 *+ content/global/bindings/autocomplete.xml    (widgets/autocomplete.xml)
 *+ content/global/bindings/browser.xml         (widgets/browser.xml)
 *+ content/global/bindings/button.xml          (widgets/button.xml)
 *+ content/global/bindings/checkbox.xml        (widgets/checkbox.xml)
 *+ content/global/bindings/colorpicker.xml     (widgets/colorpicker.xml)
 *+ content/global/bindings/datetimepicker.xml  (widgets/datetimepicker.xml)
 *+ content/global/bindings/dialog.xml          (widgets/dialog.xml)
+*+ content/global/bindings/dialog-kde.xml      (widgets/dialog-kde.xml)
+% override chrome://global/content/bindings/dialog.xml chrome://global/content/bindings/dialog-kde.xml desktop=kde
 *+ content/global/bindings/editor.xml          (widgets/editor.xml)
 *  content/global/bindings/expander.xml        (widgets/expander.xml)
 *  content/global/bindings/filefield.xml       (widgets/filefield.xml)
 *+ content/global/bindings/findbar.xml         (widgets/findbar.xml)
 *+ content/global/bindings/general.xml         (widgets/general.xml)
 *+ content/global/bindings/groupbox.xml        (widgets/groupbox.xml)
 *+ content/global/bindings/listbox.xml         (widgets/listbox.xml)
 *+ content/global/bindings/menu.xml            (widgets/menu.xml)
 *+ content/global/bindings/menulist.xml        (widgets/menulist.xml)
 *+ content/global/bindings/notification.xml    (widgets/notification.xml)
 *+ content/global/bindings/numberbox.xml       (widgets/numberbox.xml)
 *+ content/global/bindings/popup.xml           (widgets/popup.xml)
 *+ content/global/bindings/preferences.xml     (widgets/preferences.xml)
+*+ content/global/bindings/preferences-kde.xml (widgets/preferences-kde.xml)
+% override chrome://global/content/bindings/preferences.xml chrome://global/content/bindings/preferences-kde.xml desktop=kde
 *+ content/global/bindings/progressmeter.xml   (widgets/progressmeter.xml)
 *+ content/global/bindings/radio.xml           (widgets/radio.xml)
 *+ content/global/bindings/resizer.xml         (widgets/resizer.xml)
 *+ content/global/bindings/richlistbox.xml     (widgets/richlistbox.xml)
 *+ content/global/bindings/scale.xml           (widgets/scale.xml)
 *+ content/global/bindings/scrollbar.xml       (widgets/scrollbar.xml)
 *+ content/global/bindings/scrollbox.xml       (widgets/scrollbox.xml)
 *+ content/global/bindings/splitter.xml        (widgets/splitter.xml)
diff --git a/toolkit/content/widgets/dialog-kde.xml b/toolkit/content/widgets/dialog-kde.xml
new file mode 100644
--- /dev/null
+++ b/toolkit/content/widgets/dialog-kde.xml
@@ -0,0 +1,447 @@
+<?xml version="1.0"?>
+
+<bindings id="dialogBindings"
+          xmlns="http://www.mozilla.org/xbl"
+          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+          xmlns:xbl="http://www.mozilla.org/xbl">
+  
+  <binding id="dialog" extends="chrome://global/content/bindings/general.xml#root-element">
+    <resources>
+      <stylesheet src="chrome://global/skin/dialog.css"/>
+    </resources>
+    <content>
+      <xul:vbox class="box-inherit dialog-content-box" flex="1">
+        <children/>
+      </xul:vbox>
+          
+      <xul:hbox class="dialog-button-box" anonid="buttons"
+                xbl:inherits="pack=buttonpack,align=buttonalign,dir=buttondir,orient=buttonorient"
+#ifdef XP_UNIX_GNOME
+                >
+        <xul:button dlgtype="disclosure" class="dialog-button" hidden="true"/>
+        <xul:button dlgtype="help" class="dialog-button" hidden="true"/>
+        <xul:button dlgtype="extra2" class="dialog-button" hidden="true"/>
+        <xul:button dlgtype="extra1" class="dialog-button" hidden="true"/>
+        <xul:spacer anonid="spacer" flex="1"/>
+        <xul:button dlgtype="cancel" class="dialog-button"/>
+        <xul:button dlgtype="accept" class="dialog-button" xbl:inherits="disabled=buttondisabledaccept"/>
+#elif XP_UNIX
+                pack="end">
+	<xul:button dlgtype="help" class="dialog-button" hidden="true"/>
+	<xul:button dlgtype="extra2" class="dialog-button" hidden="true"/>
+	<xul:spacer anonid="spacer" flex="1" hidden="true"/>
+	<xul:button dlgtype="accept" class="dialog-button" xbl:inherits="disabled=buttondisabledaccept"/>
+	<xul:button dlgtype="extra1" class="dialog-button" hidden="true"/>
+	<xul:button dlgtype="cancel" class="dialog-button"/>
+	<xul:button dlgtype="disclosure" class="dialog-button" hidden="true"/>
+#else
+                pack="end">
+        <xul:button dlgtype="extra2" class="dialog-button" hidden="true"/>
+        <xul:spacer anonid="spacer" flex="1" hidden="true"/>
+        <xul:button dlgtype="accept" class="dialog-button" xbl:inherits="disabled=buttondisabledaccept"/>
+        <xul:button dlgtype="extra1" class="dialog-button" hidden="true"/>
+        <xul:button dlgtype="cancel" class="dialog-button"/>
+        <xul:button dlgtype="help" class="dialog-button" hidden="true"/>
+        <xul:button dlgtype="disclosure" class="dialog-button" hidden="true"/>
+#endif
+      </xul:hbox>
+    </content>
+
+    <implementation>
+      <field name="_mStrBundle">null</field>
+      <field name="_closeHandler">(function(event) {
+        if (!document.documentElement.cancelDialog())
+          event.preventDefault();
+      })</field>
+
+      <property name="buttons"
+                onget="return this.getAttribute('buttons');"
+                onset="this._configureButtons(val); return val;"/>
+
+      <property name="defaultButton">
+        <getter>
+        <![CDATA[
+          if (this.hasAttribute("defaultButton"))
+            return this.getAttribute("defaultButton");
+          else // default to the accept button
+            return "accept";
+        ]]>
+        </getter>
+        <setter>
+        <![CDATA[
+          this._setDefaultButton(val);
+          return val;
+        ]]>
+        </setter>
+      </property>
+
+      <method name="acceptDialog">
+        <body>
+        <![CDATA[
+          return this._doButtonCommand("accept");
+        ]]>
+        </body>
+      </method>
+      
+      <method name="cancelDialog">
+        <body>
+        <![CDATA[
+          return this._doButtonCommand("cancel");
+        ]]>
+        </body>
+      </method>
+      
+      <method name="getButton">
+        <parameter name="aDlgType"/>
+        <body>
+        <![CDATA[
+          return this._buttons[aDlgType];
+        ]]>
+        </body>
+      </method>
+
+      <method name="moveToAlertPosition">
+        <body>
+        <![CDATA[
+          // hack. we need this so the window has something like its final size
+          if (window.outerWidth == 1) {
+            dump("Trying to position a sizeless window; caller should have called sizeToContent() or sizeTo(). See bug 75649.\n");
+            sizeToContent();
+          }
+
+          var xOffset = (opener.outerWidth - window.outerWidth) / 2;
+          var yOffset = opener.outerHeight / 5;
+
+          var newX = opener.screenX + xOffset;
+          var newY = opener.screenY + yOffset;
+
+          // ensure the window is fully onscreen (if smaller than the screen)
+          if (newX < screen.availLeft)
+            newX = screen.availLeft + 20;
+          if ((newX + window.outerWidth) > (screen.availLeft + screen.availWidth))
+            newX = (screen.availLeft + screen.availWidth) - window.outerWidth - 20;
+
+          if (newY < screen.availTop)
+            newY = screen.availTop + 20;
+          if ((newY + window.outerHeight) > (screen.availTop + screen.availHeight))
+            newY = (screen.availTop + screen.availHeight) - window.outerHeight - 60;
+
+          window.moveTo( newX, newY );
+        ]]>
+        </body>
+      </method>
+
+      <method name="centerWindowOnScreen">
+        <body>
+        <![CDATA[
+          var xOffset = screen.availWidth/2 - window.outerWidth/2;
+          var yOffset = screen.availHeight/2 - window.outerHeight/2; //(opener.outerHeight *2)/10;
+  
+          xOffset = xOffset > 0 ? xOffset : 0;
+          yOffset = yOffset > 0 ? yOffset : 0;
+          window.moveTo(xOffset, yOffset);
+        ]]>
+        </body>
+      </method>
+
+      <constructor>
+      <![CDATA[
+        this._configureButtons(this.buttons);
+
+        // listen for when window is closed via native close buttons
+        window.addEventListener("close", this._closeHandler, false);
+
+        // for things that we need to initialize after onload fires
+        window.addEventListener("load", this.postLoadInit, false);
+
+        window.moveToAlertPosition = this.moveToAlertPosition;
+        window.centerWindowOnScreen = this.centerWindowOnScreen;
+      ]]>
+      </constructor>
+
+      <method name="postLoadInit">
+        <parameter name="aEvent"/>
+        <body>
+        <![CDATA[
+          function focusInit() {
+            const dialog = document.documentElement;
+            const defaultButton = dialog.getButton(dialog.defaultButton);
+            // give focus to the first focusable element in the dialog
+            if (!document.commandDispatcher.focusedElement) {
+              document.commandDispatcher.advanceFocusIntoSubtree(dialog);
+
+              var focusedElt = document.commandDispatcher.focusedElement;
+              if (focusedElt) {
+                var initialFocusedElt = focusedElt;
+                while (focusedElt.localName == "tab" ||
+                       focusedElt.getAttribute("noinitialfocus") == "true") {
+                  document.commandDispatcher.advanceFocusIntoSubtree(focusedElt);
+                  focusedElt = document.commandDispatcher.focusedElement;
+                  if (focusedElt == initialFocusedElt)
+                    break;
+                }
+
+                if (initialFocusedElt.localName == "tab") {
+                  if (focusedElt.hasAttribute("dlgtype")) {
+                    // We don't want to focus on anonymous OK, Cancel, etc. buttons,
+                    // so return focus to the tab itself
+                    initialFocusedElt.focus();
+                  }
+                }
+#ifndef XP_MACOSX
+                else if (focusedElt.hasAttribute("dlgtype") && focusedElt != defaultButton) {
+                  defaultButton.focus();
+                }
+#endif
+              }
+            }
+
+            try {
+              if (defaultButton)
+                window.notifyDefaultButtonLoaded(defaultButton);
+            } catch (e) { }
+          }
+
+          // Give focus after onload completes, see bug 103197.
+          setTimeout(focusInit, 0);
+        ]]>
+        </body>
+      </method>                
+
+      <property name="mStrBundle">
+        <getter>
+        <![CDATA[
+          if (!this._mStrBundle) {
+            // need to create string bundle manually instead of using <xul:stringbundle/>
+            // see bug 63370 for details
+            this._mStrBundle = Components.classes["@mozilla.org/intl/stringbundle;1"]
+                                         .getService(Components.interfaces.nsIStringBundleService)
+                                         .createBundle("chrome://global/locale/dialog.properties");
+          }
+          return this._mStrBundle;
+        ]]></getter>
+      </property>
+      
+      <method name="_configureButtons">
+        <parameter name="aButtons"/>
+        <body>
+        <![CDATA[
+          // by default, get all the anonymous button elements
+          var buttons = {};
+          this._buttons = buttons;
+          buttons.accept = document.getAnonymousElementByAttribute(this, "dlgtype", "accept");
+          buttons.cancel = document.getAnonymousElementByAttribute(this, "dlgtype", "cancel");
+          buttons.extra1 = document.getAnonymousElementByAttribute(this, "dlgtype", "extra1");
+          buttons.extra2 = document.getAnonymousElementByAttribute(this, "dlgtype", "extra2");
+          buttons.help = document.getAnonymousElementByAttribute(this, "dlgtype", "help");
+          buttons.disclosure = document.getAnonymousElementByAttribute(this, "dlgtype", "disclosure");
+
+          // look for any overriding explicit button elements
+          var exBtns = this.getElementsByAttribute("dlgtype", "*");
+          var dlgtype;
+          var i;
+          for (i = 0; i < exBtns.length; ++i) {
+            dlgtype = exBtns[i].getAttribute("dlgtype");
+            buttons[dlgtype].hidden = true; // hide the anonymous button
+            buttons[dlgtype] = exBtns[i];
+          }
+
+          // add the label and oncommand handler to each button
+          for (dlgtype in buttons) {
+            var button = buttons[dlgtype];
+            button.addEventListener("command", this._handleButtonCommand, true);
+
+            // don't override custom labels with pre-defined labels on explicit buttons
+            if (!button.hasAttribute("label")) {
+              // dialog attributes override the default labels in dialog.properties
+              if (this.hasAttribute("buttonlabel"+dlgtype)) {
+                button.setAttribute("label", this.getAttribute("buttonlabel"+dlgtype));
+                if (this.hasAttribute("buttonaccesskey"+dlgtype))
+                  button.setAttribute("accesskey", this.getAttribute("buttonaccesskey"+dlgtype));
+              } else if (dlgtype != "extra1" && dlgtype != "extra2") {
+                button.setAttribute("label", this.mStrBundle.GetStringFromName("button-"+dlgtype));
+                var accessKey = this.mStrBundle.GetStringFromName("accesskey-"+dlgtype);
+                if (accessKey)
+                  button.setAttribute("accesskey", accessKey);
+              }
+            }
+            // allow specifying alternate icons in the dialog header
+            if (!button.hasAttribute("icon")) {
+              // if there's an icon specified, use that
+              if (this.hasAttribute("buttonicon"+dlgtype))
+                button.setAttribute("icon", this.getAttribute("buttonicon"+dlgtype));
+              // otherwise set defaults
+              else
+                switch (dlgtype) {
+                  case "accept":
+                    button.setAttribute("icon","accept");
+                    break;
+                  case "cancel":
+                    button.setAttribute("icon","cancel");
+                    break;
+                  case "disclosure":
+                    button.setAttribute("icon","properties");
+                    break;
+                  case "help":
+                    button.setAttribute("icon","help");
+                    break;
+                  default:
+                    break;
+                }
+            }
+          }
+
+          // ensure that hitting enter triggers the default button command
+          this.defaultButton = this.defaultButton;
+          
+          // if there is a special button configuration, use it
+          if (aButtons) {
+            // expect a comma delimited list of dlgtype values
+            var list = aButtons.split(",");
+
+            // mark shown dlgtypes as true
+            var shown = { accept: false, cancel: false, help: false,
+                          disclosure: false, extra1: false, extra2: false };
+            for (i = 0; i < list.length; ++i)
+              shown[list[i].replace(/ /g, "")] = true;
+
+            // hide/show the buttons we want
+            for (dlgtype in buttons) 
+              buttons[dlgtype].hidden = !shown[dlgtype];
+
+#ifdef XP_WIN
+#           show the spacer on Windows only when the extra2 button is present
+            var spacer = document.getAnonymousElementByAttribute(this, "anonid", "spacer");
+            spacer.removeAttribute("hidden");
+            spacer.setAttribute("flex", shown["extra2"]?"1":"0");
+#endif
+
+          }
+        ]]>
+        </body>
+      </method>
+
+      <method name="_setDefaultButton">
+        <parameter name="aNewDefault"/>
+        <body>
+        <![CDATA[
+          // remove the default attribute from the previous default button, if any
+          var oldDefaultButton = this.getButton(this.defaultButton);
+          if (oldDefaultButton)
+            oldDefaultButton.removeAttribute("default");
+
+          var newDefaultButton = this.getButton(aNewDefault);
+          if (newDefaultButton) {
+            this.setAttribute("defaultButton", aNewDefault);
+            newDefaultButton.setAttribute("default", "true");
+          }
+          else {
+            this.setAttribute("defaultButton", "none");
+            if (aNewDefault != "none")
+              dump("invalid new default button: " +  aNewDefault + ", assuming: none\n");
+          }
+        ]]>
+        </body>
+      </method>
+
+      <method name="_handleButtonCommand">
+        <parameter name="aEvent"/>
+        <body>
+        <![CDATA[
+          return document.documentElement._doButtonCommand(
+                                        aEvent.target.getAttribute("dlgtype"));
+        ]]>
+        </body>
+      </method>
+      
+      <method name="_doButtonCommand">
+        <parameter name="aDlgType"/>
+        <body>
+        <![CDATA[
+          var button = this.getButton(aDlgType);
+          if (!button.disabled) {
+            var noCancel = this._fireButtonEvent(aDlgType);
+            if (noCancel) {
+              if (aDlgType == "accept" || aDlgType == "cancel")
+                window.close();
+            }
+            return noCancel;
+          }
+          return true;
+        ]]>
+        </body>
+      </method>
+      
+      <method name="_fireButtonEvent">
+        <parameter name="aDlgType"/>
+        <body>
+        <![CDATA[
+          var event = document.createEvent("Events");
+          event.initEvent("dialog"+aDlgType, true, true);
+          
+          // handle dom event handlers
+          var noCancel = this.dispatchEvent(event);
+          
+          // handle any xml attribute event handlers
+          var handler = this.getAttribute("ondialog"+aDlgType);
+          if (handler != "") {
+            var fn = new Function("event", handler);
+            var returned = fn(event);
+            if (returned == false)
+              noCancel = false;
+          }
+          
+          return noCancel;
+        ]]>
+        </body>
+      </method>
+
+      <method name="_hitEnter">
+        <parameter name="evt"/>
+        <body>
+        <![CDATA[
+          if (evt.getPreventDefault())
+            return;
+
+          var btn = this.getButton(this.defaultButton);
+          if (btn)
+            this._doButtonCommand(this.defaultButton);
+        ]]>
+        </body>
+      </method>
+
+    </implementation>
+    
+    <handlers>
+      <handler event="keypress" keycode="VK_ENTER"
+               group="system" action="this._hitEnter(event);"/>
+      <handler event="keypress" keycode="VK_RETURN"
+               group="system" action="this._hitEnter(event);"/>
+      <handler event="keypress" keycode="VK_ESCAPE" group="system">
+        if (!event.getPreventDefault())
+          this.cancelDialog();
+      </handler>
+#ifdef XP_MACOSX
+      <handler event="keypress" key="." modifiers="meta" phase="capturing" action="this.cancelDialog();"/>
+#else
+      <handler event="focus" phase="capturing">
+        var btn = this.getButton(this.defaultButton);
+        if (btn)
+          btn.setAttribute("default", event.originalTarget == btn || !(event.originalTarget instanceof Components.interfaces.nsIDOMXULButtonElement));
+      </handler>
+#endif
+    </handlers>
+
+  </binding>
+
+  <binding id="dialogheader">
+    <resources>
+      <stylesheet src="chrome://global/skin/dialog.css"/>
+    </resources>
+    <content>
+      <xul:label class="dialogheader-title" xbl:inherits="value=title,crop" crop="right" flex="1"/>
+      <xul:label class="dialogheader-description" xbl:inherits="value=description"/>
+    </content>
+  </binding>
+
+</bindings>
diff --git a/toolkit/content/widgets/preferences-kde.xml b/toolkit/content/widgets/preferences-kde.xml
new file mode 100644
--- /dev/null
+++ b/toolkit/content/widgets/preferences-kde.xml
@@ -0,0 +1,1371 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE bindings [
+  <!ENTITY % preferencesDTD SYSTEM "chrome://global/locale/preferences.dtd">
+  %preferencesDTD;
+  <!ENTITY % globalKeysDTD SYSTEM "chrome://global/locale/globalKeys.dtd">
+  %globalKeysDTD;
+]>
+
+<bindings id="preferencesBindings"
+          xmlns="http://www.mozilla.org/xbl"
+          xmlns:xbl="http://www.mozilla.org/xbl"
+          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+#
+# = Preferences Window Framework
+#
+#   The syntax for use looks something like:
+#
+#   <prefwindow>
+#     <prefpane id="prefPaneA">
+#       <preferences>
+#         <preference id="preference1" name="app.preference1" type="bool" onchange="foo();"/>
+#         <preference id="preference2" name="app.preference2" type="bool" useDefault="true"/>
+#       </preferences>
+#       <checkbox label="Preference" preference="preference1"/>
+#     </prefpane>
+#   </prefwindow>
+#
+
+  <binding id="preferences">
+    <implementation implements="nsIObserver">
+      <method name="observe">
+        <parameter name="aSubject"/>
+        <parameter name="aTopic"/>
+        <parameter name="aData"/>
+        <body>
+        <![CDATA[
+          for (var i = 0; i < this.childNodes.length; ++i) {
+            var preference = this.childNodes[i];
+            if (preference.name == aData) {
+              preference.value = preference.valueFromPreferences;
+            }
+          }
+        ]]>
+        </body>
+      </method>
+      
+      <method name="fireChangedEvent">
+        <parameter name="aPreference"/>
+        <body>
+        <![CDATA[
+          // Value changed, synthesize an event
+          try {
+            var event = document.createEvent("Events");
+            event.initEvent("change", true, true);
+            aPreference.dispatchEvent(event);
+          }
+          catch (e) {
+            Components.utils.reportError(e);
+          }
+        ]]>
+        </body>
+      </method>
+      
+      <field name="service">
+        Components.classes["@mozilla.org/preferences-service;1"]
+                  .getService(Components.interfaces.nsIPrefService);
+      </field>
+      <field name="rootBranch">
+        Components.classes["@mozilla.org/preferences-service;1"]
+                  .getService(Components.interfaces.nsIPrefBranch);
+      </field>
+      <field name="defaultBranch">
+        this.service.getDefaultBranch("");
+      </field>
+      <field name="rootBranchInternal">
+        Components.classes["@mozilla.org/preferences-service;1"]
+                  .getService(Components.interfaces.nsIPrefBranchInternal);
+      </field>
+      <property name="type" readonly="true">
+        <getter>
+          <![CDATA[
+            return document.documentElement.type || "";
+          ]]>
+        </getter>
+      </property>
+      <property name="instantApply" readonly="true">
+        <getter>
+          <![CDATA[
+            var doc = document.documentElement;
+            return this.type == "child" ? doc.instantApply
+                                        : doc.instantApply || this.rootBranch.getBoolPref("browser.preferences.instantApply");
+          ]]>
+        </getter>
+      </property>
+    </implementation>
+  </binding>
+
+  <binding id="preference">
+    <implementation>
+      <constructor>
+      <![CDATA[
+        // if the element has been inserted without the name attribute set,
+        // we have nothing to do here
+        if (!this.name)
+          return;
+
+        this.preferences.rootBranchInternal
+            .addObserver(this.name, this.preferences, false);
+        // In non-instant apply mode, we must try and use the last saved state
+        // from any previous opens of a child dialog instead of the value from
+        // preferences, to pick up any edits a user may have made. 
+        if (this.preferences.type == "child" && 
+            !this.instantApply && window.opener) {
+          var pdoc = window.opener.document;
+
+          // Try to find a preference element for the same preference.
+          var preference = null;
+          var parentPreferences = pdoc.getElementsByTagName("preferences");
+          for (var k = 0; (k < parentPreferences.length && !preference); ++k) {
+            var parentPrefs = parentPreferences[k]
+                                    .getElementsByAttribute("name", this.name);
+            for (var l = 0; (l < parentPrefs.length && !preference); ++l) {
+              if (parentPrefs[l].localName == "preference")
+                preference = parentPrefs[l];
+            }
+          }
+          this._setValue(preference ? preference.value 
+                                    : this.valueFromPreferences, false);
+        }
+        else
+          this._setValue(this.valueFromPreferences, false);
+      ]]>
+      </constructor>
+      <destructor>
+        this.preferences.rootBranchInternal
+            .removeObserver(this.name, this.preferences);
+      </destructor>
+      
+      <property name="instantApply">
+        <getter>
+          return this.getAttribute("instantApply") == "true" || this.preferences.instantApply;
+        </getter>
+      </property>
+
+      <property name="preferences" onget="return this.parentNode"/>
+      <property name="name" onget="return this.getAttribute('name');">
+        <setter>
+          if (val == this.name)
+            return val;
+            
+          this.preferences.rootBranchInternal
+              .removeObserver(this.name, this.preferences);
+          this.setAttribute('name', val);
+          this.preferences.rootBranchInternal
+              .addObserver(val, this.preferences, false);
+              
+          return val;
+        </setter>
+      </property>
+      <property name="type" onget="return this.getAttribute('type');"
+                            onset="this.setAttribute('type', val); return val;"/>
+      <property name="inverted" onget="return this.getAttribute('inverted') == 'true';"
+                                onset="this.setAttribute('inverted', val); return val;"/>
+      <property name="readonly" onget="return this.getAttribute('readonly') == 'true';"
+                                onset="this.setAttribute('readonly', val); return val;"/>
+
+      <field name="_value">null</field>
+      <method name="_setValue">
+        <parameter name="aValue"/>
+        <parameter name="aUpdate"/>
+        <body>
+        <![CDATA[
+          if (aUpdate && this.value !== aValue) {
+            this._value = aValue;
+            if (this.instantApply)
+              this.valueFromPreferences = aValue;
+            this.preferences.fireChangedEvent(this);
+          }
+          else if (!aUpdate) {
+            this._value = aValue;
+            this.updateElements();
+          }
+          return aValue;
+        ]]>
+        </body>
+      </method>
+      <property name="value" onget="return this._value" onset="return this._setValue(val, true);"/>
+      
+      <property name="locked">
+        <getter>
+          return this.preferences.rootBranch.prefIsLocked(this.name);
+        </getter>
+      </property>
+      
+      <property name="disabled">
+        <getter>
+          return this.getAttribute("disabled") == "true";
+        </getter>
+        <setter>
+        <![CDATA[
+          if (val) 
+            this.setAttribute("disabled", "true");
+          else
+            this.removeAttribute("disabled");
+
+          if (!this.id)
+            return val;
+
+          var elements = document.getElementsByAttribute("preference", this.id);
+          for (var i = 0; i < elements.length; ++i) {
+            elements[i].disabled = val;
+            
+            var labels = document.getElementsByAttribute("control", elements[i].id);
+            for (var j = 0; j < labels.length; ++j)
+              labels[j].disabled = val;
+          }
+            
+          return val;
+        ]]>
+        </setter>
+      </property>
+      
+      <property name="tabIndex">
+        <getter>
+          return parseInt(this.getAttribute("tabindex"));
+        </getter>
+        <setter>
+        <![CDATA[
+          if (val) 
+            this.setAttribute("tabindex", val);
+          else
+            this.removeAttribute("tabindex");
+
+          if (!this.id)
+            return val;
+
+          var elements = document.getElementsByAttribute("preference", this.id);
+          for (var i = 0; i < elements.length; ++i) {
+            elements[i].tabIndex = val;
+            
+            var labels = document.getElementsByAttribute("control", elements[i].id);
+            for (var j = 0; j < labels.length; ++j)
+              labels[j].tabIndex = val;
+          }
+            
+          return val;
+        ]]>
+        </setter>
+      </property>
+
+      <property name="hasUserValue">
+        <getter>
+        <![CDATA[
+          return this.preferences.rootBranch.prefHasUserValue(this.name) &&
+                 this.value !== undefined;
+        ]]>
+        </getter>
+      </property>
+      
+      <method name="reset">
+        <body>
+          // defer reset until preference update
+          this.value = undefined;
+        </body>
+      </method>
+
+      <field name="_useDefault">false</field>      
+      <property name="defaultValue">
+        <getter>
+        <![CDATA[
+          this._useDefault = true;
+          var val = this.valueFromPreferences;
+          this._useDefault = false;
+          return val;
+        ]]>
+        </getter>
+      </property>
+      
+      <property name="_branch">
+        <getter>
+          return this._useDefault ? this.preferences.defaultBranch : this.preferences.rootBranch;
+        </getter>
+      </property>
+      
+      <field name="batching">false</field>
+      
+      <method name="_reportUnknownType">
+        <body>
+        <![CDATA[
+          var consoleService = Components.classes["@mozilla.org/consoleservice;1"]
+                                         .getService(Components.interfaces.nsIConsoleService);
+          var msg = "<preference> with id='" + this.id + "' and name='" + 
+                    this.name + "' has unknown type '" + this.type + "'.";
+          consoleService.logStringMessage(msg);
+        ]]>
+        </body>
+      </method>
+      
+      <property name="valueFromPreferences">
+        <getter>
+        <![CDATA[
+          try {
+            // Force a resync of value with preferences.
+            switch (this.type) {
+            case "int":
+              return this._branch.getIntPref(this.name);
+            case "bool":
+              var val = this._branch.getBoolPref(this.name);
+              return this.inverted ? !val : val;
+            case "wstring":
+              return this._branch
+                         .getComplexValue(this.name, Components.interfaces.nsIPrefLocalizedString)
+                         .data;
+            case "string":
+            case "unichar":
+              return this._branch
+                         .getComplexValue(this.name, Components.interfaces.nsISupportsString)
+                         .data;
+            case "fontname":
+              var family = this._branch
+                               .getComplexValue(this.name, Components.interfaces.nsISupportsString)
+                               .data;
+              var fontEnumerator = Components.classes["@mozilla.org/gfx/fontenumerator;1"]
+                                             .createInstance(Components.interfaces.nsIFontEnumerator);
+              return fontEnumerator.getStandardFamilyName(family);
+            case "file":
+              var f = this._branch
+                          .getComplexValue(this.name, Components.interfaces.nsILocalFile);
+              return f;
+            default:
+              this._reportUnknownType();
+            }
+          }
+          catch (e) { }
+          return null;
+        ]]>
+        </getter>
+        <setter>
+        <![CDATA[
+          // Exit early if nothing to do.
+          if (this.readonly || this.valueFromPreferences == val)
+            return val;
+
+          // The special value undefined means 'reset preference to default'.
+          if (val === undefined) {
+            this.preferences.rootBranch.clearUserPref(this.name);
+            return val;
+          }
+
+          // Force a resync of preferences with value.
+          switch (this.type) {
+          case "int":
+            this.preferences.rootBranch.setIntPref(this.name, val);
+            break;
+          case "bool":
+            this.preferences.rootBranch.setBoolPref(this.name, this.inverted ? !val : val);
+            break;
+          case "wstring":
+            var pls = Components.classes["@mozilla.org/pref-localizedstring;1"]
+                                .createInstance(Components.interfaces.nsIPrefLocalizedString);
+            pls.data = val;
+            this.preferences.rootBranch
+                .setComplexValue(this.name, Components.interfaces.nsIPrefLocalizedString, pls);
+            break;
+          case "string":
+          case "unichar":
+          case "fontname":
+            var iss = Components.classes["@mozilla.org/supports-string;1"]
+                                .createInstance(Components.interfaces.nsISupportsString);
+            iss.data = val;
+            this.preferences.rootBranch
+                .setComplexValue(this.name, Components.interfaces.nsISupportsString, iss);
+            break;
+          case "file":
+            var lf;
+            if (typeof(val) == "string") {
+              lf = Components.classes["@mozilla.org/file/local;1"]
+                             .createInstance(Components.interfaces.nsILocalFile);
+              lf.persistentDescriptor = val;
+              if (!lf.exists())
+                lf.initWithPath(val);
+            }
+            else 
+              lf = val.QueryInterface(Components.interfaces.nsILocalFile);
+            this.preferences.rootBranch
+                .setComplexValue(this.name, Components.interfaces.nsILocalFile, lf);
+            break;
+          default:
+            this._reportUnknownType();
+          }
+          if (!this.batching)
+            this.preferences.service.savePrefFile(null);
+          return val;
+        ]]>
+        </setter>
+      </property>
+      
+      <method name="setElementValue">
+        <parameter name="aElement"/>
+        <body>
+        <![CDATA[
+          if (this.locked)
+            aElement.disabled = true;
+
+          if (!this.isElementEditable(aElement))
+            return;
+
+          var rv = undefined;
+          if (aElement.hasAttribute("onsyncfrompreference")) {
+            // Value changed, synthesize an event
+            try {
+              var event = document.createEvent("Events");
+              event.initEvent("syncfrompreference", true, true);
+              var f = new Function ("event", 
+                                    aElement.getAttribute("onsyncfrompreference"));
+              rv = f.call(aElement, event);
+            }
+            catch (e) {
+              Components.utils.reportError(e);
+            }
+          }
+          var val = rv !== undefined ? rv : (this.instantApply ? this.valueFromPreferences : this.value);
+          // if the preference is marked for reset, show default value in UI
+          if (val === undefined)
+            val = this.defaultValue;
+
+          /**
+           * Initialize a UI element property with a value. Handles the case 
+           * where an element has not yet had a XBL binding attached for it and
+           * the property setter does not yet exist by setting the same attribute
+           * on the XUL element using DOM apis and assuming the element's 
+           * constructor or property getters appropriately handle this state. 
+           */
+          function setValue(element, attribute, value) {
+            if (attribute in element) 
+              element[attribute] = value;
+            else
+              element.setAttribute(attribute, value);
+          }
+          if (aElement.localName == "checkbox" ||
+              aElement.localName == "listitem")
+            setValue(aElement, "checked", val);
+          else if (aElement.localName == "colorpicker")
+            setValue(aElement, "color", val);
+          else if (aElement.localName == "textbox") {
+            // XXXmano Bug 303998: Avoid a caret placement issue if either the
+            // preference observer or its setter calls updateElements as a result
+            // of the input event handler.
+            if (aElement.value !== val)
+              setValue(aElement, "value", val);
+          }
+          else
+            setValue(aElement, "value", val);
+        ]]>
+        </body>
+      </method>
+
+      <method name="getElementValue">
+        <parameter name="aElement"/>
+        <body>
+        <![CDATA[
+          if (aElement.hasAttribute("onsynctopreference")) {
+            // Value changed, synthesize an event
+            try {
+              var event = document.createEvent("Events");
+              event.initEvent("synctopreference", true, true);
+              var f = new Function ("event", 
+                                    aElement.getAttribute("onsynctopreference"));
+              var rv = f.call(aElement, event);
+              if (rv !== undefined) 
+                return rv;
+            }
+            catch (e) {
+              Components.utils.reportError(e);
+            }
+          }
+          
+          /**
+           * Read the value of an attribute from an element, assuming the 
+           * attribute is a property on the element's node API. If the property
+           * is not present in the API, then assume its value is contained in
+           * an attribute, as is the case before a binding has been attached.
+           */
+          function getValue(element, attribute) {
+            if (attribute in element)
+              return element[attribute];
+            return element.getAttribute(attribute);
+          }
+          if (aElement.localName == "checkbox" ||
+              aElement.localName == "listitem")
+            var value = getValue(aElement, "checked");
+          else if (aElement.localName == "colorpicker")
+            value = getValue(aElement, "color");
+          else
+            value = getValue(aElement, "value");
+
+          switch (this.type) {
+          case "int":
+            return parseInt(value, 10) || 0;
+          case "bool":
+            return typeof(value) == "boolean" ? value : value == "true";
+          }
+          return value;
+        ]]>
+        </body>
+      </method>
+      
+      <method name="isElementEditable">
+        <parameter name="aElement"/>
+        <body>
+        <![CDATA[
+          switch (aElement.localName) {
+          case "checkbox":
+          case "colorpicker":
+          case "radiogroup":
+          case "textbox":
+          case "listitem":
+          case "listbox":
+          case "menulist":
+            return true;
+          }
+          return aElement.getAttribute("preference-editable") == "true";
+        ]]> 
+        </body>
+      </method>
+      
+      <method name="updateElements">
+        <body>
+        <![CDATA[
+          if (!this.id)
+            return;
+
+          // This "change" event handler tracks changes made to preferences by 
+          // sources other than the user in this window. 
+          var elements = document.getElementsByAttribute("preference", this.id);
+          for (var i = 0; i < elements.length; ++i) 
+            this.setElementValue(elements[i]);
+        ]]>
+        </body>
+      </method>
+    </implementation>
+    
+    <handlers>
+      <handler event="change">
+        this.updateElements();
+      </handler>
+    </handlers>
+  </binding>
+
+  <binding id="prefwindow"
+           extends="chrome://global/content/bindings/dialog.xml#dialog">
+    <resources>
+      <stylesheet src="chrome://global/skin/preferences.css"/>
+    </resources>
+    <content dlgbuttons="accept,cancel" persist="lastSelected screenX screenY"
+             closebuttonlabel="&preferencesCloseButton.label;"
+             closebuttonaccesskey="&preferencesCloseButton.accesskey;"
+             role="dialog"
+#ifdef XP_WIN
+             title="&preferencesDefaultTitleWin.title;">
+#else
+             title="&preferencesDefaultTitleMac.title;">
+#endif
+      <xul:windowdragbox orient="vertical">
+        <xul:radiogroup anonid="selector" orient="horizontal" class="paneSelector chromeclass-toolbar"
+                        role="listbox"/> <!-- Expose to accessibility APIs as a listbox -->
+      </xul:windowdragbox>
+      <xul:hbox flex="1" class="paneDeckContainer">
+        <xul:deck anonid="paneDeck" flex="1">
+          <children includes="prefpane"/>
+        </xul:deck>
+      </xul:hbox>
+      <xul:hbox anonid="dlg-buttons" class="prefWindow-dlgbuttons"
+#ifdef XP_UNIX_GNOME
+                >
+        <xul:button dlgtype="disclosure" class="dialog-button" hidden="true"/>
+        <xul:button dlgtype="help" class="dialog-button" hidden="true" icon="help"/>
+        <xul:button dlgtype="extra2" class="dialog-button" hidden="true"/>
+        <xul:button dlgtype="extra1" class="dialog-button" hidden="true"/>
+        <xul:spacer anonid="spacer" flex="1"/>
+        <xul:button dlgtype="cancel" class="dialog-button" icon="cancel"/>
+        <xul:button dlgtype="accept" class="dialog-button" icon="accept"/>
+#elif XP_UNIX
+                pack="end">
+	<xul:button dlgtype="help" class="dialog-button" hidden="true" icon="help"/>
+	<xul:button dlgtype="extra2" class="dialog-button" hidden="true"/>
+	<xul:spacer anonid="spacer" flex="1"/>
+	<xul:button dlgtype="accept" class="dialog-button" icon="accept"/>
+	<xul:button dlgtype="extra1" class="dialog-button" hidden="true"/>
+	<xul:button dlgtype="cancel" class="dialog-button" icon="cancel"/>
+	<xul:button dlgtype="disclosure" class="dialog-button" hidden="true"/>
+#else
+                pack="end">
+        <xul:button dlgtype="extra2" class="dialog-button" hidden="true"/>
+        <xul:spacer anonid="spacer" flex="1"/>
+        <xul:button dlgtype="accept" class="dialog-button" icon="accept"/>
+        <xul:button dlgtype="extra1" class="dialog-button" hidden="true"/>
+        <xul:button dlgtype="cancel" class="dialog-button" icon="cancel"/>
+        <xul:button dlgtype="help" class="dialog-button" hidden="true" icon="help"/>
+        <xul:button dlgtype="disclosure" class="dialog-button" hidden="true"/>
+#endif
+      </xul:hbox>
+      <xul:hbox>
+        <children/>
+      </xul:hbox>
+    </content>
+    <implementation implements="nsITimerCallback">
+      <constructor>
+      <![CDATA[
+        if (this.type != "child") {
+          var psvc = Components.classes["@mozilla.org/preferences-service;1"]
+                               .getService(Components.interfaces.nsIPrefBranch);
+          this.instantApply = psvc.getBoolPref("browser.preferences.instantApply");
+          if (this.instantApply) {
+            var docElt = document.documentElement;
+            var acceptButton = docElt.getButton("accept");
+            acceptButton.hidden = true;
+            var cancelButton  = docElt.getButton("cancel");
+#ifdef XP_MACOSX
+            // no buttons on Mac except Help
+            cancelButton.hidden = true;
+            // Also, don't fire onDialogAccept on enter
+            acceptButton.disabled = true;
+#else
+            // morph the Cancel button into the Close button
+            cancelButton.setAttribute ("icon", "close");
+            cancelButton.label = docElt.getAttribute("closebuttonlabel");
+            cancelButton.accesskey = docElt.getAttribute("closebuttonaccesskey");
+#endif
+          }
+        }
+        this.setAttribute("animated", this._shouldAnimate ? "true" : "false");
+        var panes = this.preferencePanes;
+
+        var lastPane = null;
+        if (this.lastSelected) {
+          lastPane = document.getElementById(this.lastSelected);
+          if (!lastPane) {
+            this.lastSelected = null;
+          }
+        }
+
+        var paneToLoad;
+        if ("arguments" in window && window.arguments[0] && document.getElementById(window.arguments[0]) && document.getElementById(window.arguments[0]).nodeName == "prefpane") {
+          paneToLoad = document.getElementById(window.arguments[0]);
+          this.lastSelected = paneToLoad.id;
+        }
+        else if (lastPane)
+          paneToLoad = lastPane;
+        else
+          paneToLoad = panes[0];
+
+        for (var i = 0; i < panes.length; ++i) {
+          this._makePaneButton(panes[i]);
+          if (panes[i].loaded) {
+            // Inline pane content, fire load event to force initialization.
+            this._fireEvent("paneload", panes[i]);
+          }
+        }
+        this.showPane(paneToLoad);
+
+        if (panes.length == 1)
+          this._selector.setAttribute("collapsed", "true");
+      ]]>
+      </constructor>
+
+      <destructor>
+      <![CDATA[
+        // Release timers to avoid reference cycles.
+        if (this._animateTimer) {
+          this._animateTimer.cancel();
+          this._animateTimer = null;
+        }
+        if (this._fadeTimer) {
+          this._fadeTimer.cancel();
+          this._fadeTimer = null;
+        }
+      ]]>
+      </destructor>
+
+      <field name="instantApply">false</field>
+      
+      <property name="preferencePanes"
+                onget="return this.getElementsByTagName('prefpane');"/>
+
+      <property name="type" onget="return this.getAttribute('type');"/>
+      <property name="_paneDeck"
+                onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'paneDeck');"/>
+      <property name="_paneDeckContainer"
+                onget="return document.getAnonymousElementByAttribute(this, 'class', 'paneDeckContainer');"/>
+      <property name="_selector"
+                onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'selector');"/>
+      <property name="lastSelected" 
+                onget="return this.getAttribute('lastSelected');">
+        <setter>
+          this.setAttribute("lastSelected", val); 
+          document.persist(this.id, "lastSelected");
+          return val;
+        </setter>          
+      </property>
+      <property name="currentPane"
+                onset="return this._currentPane = val;">
+        <getter>
+          if (!this._currentPane)
+            this._currentPane = this.preferencePanes[0];
+          
+          return this._currentPane;
+        </getter> 
+      </property>
+      <field name="_currentPane">null</field>
+      
+      
+      <method name="_makePaneButton">
+        <parameter name="aPaneElement"/>
+        <body>
+        <![CDATA[
+          var radio = document.createElement("radio");
+          radio.setAttribute("pane", aPaneElement.id);
+          radio.setAttribute("label", aPaneElement.label);
+          // Expose preference group choice to accessibility APIs as an unchecked list item
+          // The parent group is exposed to accessibility APIs as a list
+          if (aPaneElement.image)
+            radio.setAttribute("src", aPaneElement.image);
+          radio.style.listStyleImage = aPaneElement.style.listStyleImage;
+          this._selector.appendChild(radio);
+          return radio;
+        ]]>
+        </body>
+      </method>
+
+      <method name="showPane">
+        <parameter name="aPaneElement"/>
+        <body>
+        <![CDATA[
+          if (!aPaneElement)
+            return;
+
+          this._selector.selectedItem = document.getAnonymousElementByAttribute(this, "pane", aPaneElement.id);
+          if (!aPaneElement.loaded) {
+            function OverlayLoadObserver(aPane)
+            {
+              this._pane = aPane;
+            }
+            OverlayLoadObserver.prototype = { 
+              _outer: this,
+              observe: function (aSubject, aTopic, aData) 
+              {
+                this._pane.loaded = true;
+                this._outer._fireEvent("paneload", this._pane);
+                this._outer._selectPane(this._pane);
+              }
+            };
+            
+            var obs = new OverlayLoadObserver(aPaneElement);
+            document.loadOverlay(aPaneElement.src, obs);
+          }
+          else
+            this._selectPane(aPaneElement);
+        ]]>
+        </body>
+      </method>
+      
+      <method name="_fireEvent">
+        <parameter name="aEventName"/>
+        <parameter name="aTarget"/>
+        <body>
+        <![CDATA[
+          // Panel loaded, synthesize a load event. 
+          try {
+            var event = document.createEvent("Events");
+            event.initEvent(aEventName, true, true);
+            var cancel = !aTarget.dispatchEvent(event);
+            if (aTarget.hasAttribute("on" + aEventName)) {
+              var fn = new Function ("event", aTarget.getAttribute("on" + aEventName));
+              var rv = fn.call(aTarget, event);
+              if (rv == false)
+                cancel = true;
+            }
+            return !cancel;  
+          }
+          catch (e) { 
+            Components.utils.reportError(e);
+          }
+          return false;
+        ]]>
+        </body>
+      </method>
+      
+      <field name="_initialized">false</field>
+      <method name="_selectPane">
+        <parameter name="aPaneElement"/>
+        <body>
+        <![CDATA[
+#ifdef XP_MACOSX
+          var paneTitle = aPaneElement.label;
+          if (paneTitle != "")
+            document.title = paneTitle;
+#endif
+          var helpButton = document.documentElement.getButton("help");
+          if (aPaneElement.helpTopic)
+            helpButton.hidden = false;
+          else
+            helpButton.hidden = true;
+
+          // Find this pane's index in the deck and set the deck's 
+          // selectedIndex to that value to switch to it.
+          var prefpanes = this.preferencePanes;
+          for (var i = 0; i < prefpanes.length; ++i) {
+            if (prefpanes[i] == aPaneElement) {
+              this._paneDeck.selectedIndex = i;
+              
+              if (this.type != "child") {
+                if (aPaneElement.hasAttribute("flex") && this._shouldAnimate &&
+                    prefpanes.length > 1)
+                  aPaneElement.removeAttribute("flex");
+                // Calling sizeToContent after the first prefpane is loaded
+                // will size the windows contents so style information is
+                // available to calculate correct sizing.
+                if (!this._initialized && prefpanes.length > 1) {
+                  if (this._shouldAnimate)
+                    this.style.minHeight = 0;
+                  window.sizeToContent();
+                }
+
+                var oldPane = this.lastSelected ? document.getElementById(this.lastSelected) : this.preferencePanes[0];
+                oldPane.selected = !(aPaneElement.selected = true);
+                this.lastSelected = aPaneElement.id;
+                this.currentPane = aPaneElement;
+                this._initialized = true;
+
+                // Only animate if we've switched between prefpanes
+                if (this._shouldAnimate && oldPane.id != aPaneElement.id) {
+                  aPaneElement.style.opacity = 0.0;
+                  this.animate(oldPane, aPaneElement);
+                }
+                else if (!this._shouldAnimate && prefpanes.length > 1) {
+                  var targetHeight = parseInt(window.getComputedStyle(this._paneDeckContainer, "").height);
+                  var verticalPadding = parseInt(window.getComputedStyle(aPaneElement, "").paddingTop);
+                  verticalPadding += parseInt(window.getComputedStyle(aPaneElement, "").paddingBottom);
+                  if (aPaneElement.contentHeight > targetHeight - verticalPadding) {
+                    // To workaround the bottom border of a groupbox from being
+                    // cutoff an hbox with a class of bottomBox may enclose it.
+                    // This needs to include its padding to resize properly.
+                    // See bug 394433
+                    var bottomPadding = 0;
+                    var bottomBox = aPaneElement.getElementsByAttribute("class", "bottomBox")[0];
+                    if (bottomBox)
+                      bottomPadding = parseInt(window.getComputedStyle(bottomBox, "").paddingBottom);
+                    window.innerHeight += bottomPadding + verticalPadding + aPaneElement.contentHeight - targetHeight;
+                  }
+
+                  // XXX rstrong - extend the contents of the prefpane to
+                  // prevent elements from being cutoff (see bug 349098).
+                  if (aPaneElement.contentHeight + verticalPadding < targetHeight)
+                    aPaneElement._content.style.height = targetHeight - verticalPadding + "px";
+                }
+              }
+              break;
+            }
+          }
+        ]]>
+        </body>
+      </method>
+      
+      <property name="_shouldAnimate">
+        <getter>
+        <![CDATA[
+          var psvc = Components.classes["@mozilla.org/preferences-service;1"]
+                               .getService(Components.interfaces.nsIPrefBranch);
+#ifdef XP_MACOSX
+          var animate = true;
+#else
+          var animate = false;
+#endif
+          try {
+            animate = psvc.getBoolPref("browser.preferences.animateFadeIn");
+          }
+          catch (e) { }
+          return animate;
+        ]]>
+        </getter>
+      </property>
+      
+      <method name="animate">
+        <parameter name="aOldPane"/>
+        <parameter name="aNewPane"/>
+        <body>
+        <![CDATA[
+          // if we are already resizing, use currentHeight
+          var oldHeight = this._currentHeight ? this._currentHeight : aOldPane.contentHeight;
+          
+          this._multiplier = aNewPane.contentHeight > oldHeight ? 1 : -1;
+          var sizeDelta = Math.abs(oldHeight - aNewPane.contentHeight);
+          this._animateRemainder = sizeDelta % this._animateIncrement;
+
+          this._setUpAnimationTimer(oldHeight);
+        ]]>
+        </body>
+      </method>
+      
+      <property name="_sizeIncrement">
+        <getter>
+        <![CDATA[
+          var lastSelectedPane = document.getElementById(this.lastSelected);
+          var increment = this._animateIncrement * this._multiplier;
+          var newHeight = this._currentHeight + increment;
+          if ((this._multiplier > 0 && this._currentHeight >= lastSelectedPane.contentHeight) ||
+              (this._multiplier < 0 && this._currentHeight <= lastSelectedPane.contentHeight))
+            return 0;
+          
+          if ((this._multiplier > 0 && newHeight > lastSelectedPane.contentHeight) ||
+              (this._multiplier < 0 && newHeight < lastSelectedPane.contentHeight))
+            increment = this._animateRemainder * this._multiplier;
+          return increment;
+        ]]>
+        </getter>
+      </property>
+      
+      <method name="notify">
+        <parameter name="aTimer"/>
+        <body>
+        <![CDATA[
+          if (!document)
+            aTimer.cancel();
+          
+          if (aTimer == this._animateTimer) {
+            var increment = this._sizeIncrement;
+            if (increment != 0) {
+              window.innerHeight += increment;
+              this._currentHeight += increment;
+            }
+            else {
+              aTimer.cancel();
+              this._setUpFadeTimer();
+            }
+          } else if (aTimer == this._fadeTimer) {
+            var elt = document.getElementById(this.lastSelected);
+            var newOpacity = parseFloat(window.getComputedStyle(elt, "").opacity) + this._fadeIncrement;
+            if (newOpacity < 1.0)
+              elt.style.opacity = newOpacity;
+            else {
+              aTimer.cancel();
+              elt.style.opacity = 1.0;
+            }
+          }
+        ]]>
+        </body>
+      </method>
+      
+      <method name="_setUpAnimationTimer">
+        <parameter name="aStartHeight"/>
+        <body>
+        <![CDATA[
+          if (!this._animateTimer) 
+            this._animateTimer = Components.classes["@mozilla.org/timer;1"]
+                                           .createInstance(Components.interfaces.nsITimer);
+          else
+            this._animateTimer.cancel();
+          this._currentHeight = aStartHeight;
+          
+          this._animateTimer.initWithCallback(this, this._animateDelay, 
+                                              Components.interfaces.nsITimer.TYPE_REPEATING_SLACK);
+        ]]>
+        </body>
+      </method>
+      
+      <method name="_setUpFadeTimer">
+        <body>
+        <![CDATA[
+          if (!this._fadeTimer) 
+            this._fadeTimer = Components.classes["@mozilla.org/timer;1"]
+                                        .createInstance(Components.interfaces.nsITimer);
+          else
+            this._fadeTimer.cancel();
+          
+          this._fadeTimer.initWithCallback(this, this._fadeDelay, 
+                                           Components.interfaces.nsITimer.TYPE_REPEATING_SLACK);
+        ]]>
+        </body>
+      </method>
+      
+      <field name="_animateTimer">null</field>
+      <field name="_fadeTimer">null</field>
+      <field name="_animateDelay">15</field>
+      <field name="_animateIncrement">40</field>
+      <field name="_fadeDelay">5</field>
+      <field name="_fadeIncrement">0.40</field>
+      <field name="_animateRemainder">0</field>
+      <field name="_currentHeight">0</field>
+      <field name="_multiplier">0</field>
+
+      <method name="addPane">
+        <parameter name="aPaneElement"/>
+        <body>
+        <![CDATA[
+          this.appendChild(aPaneElement);
+          
+          // Set up pane button
+          this._makePaneButton(aPaneElement);
+        ]]>
+        </body>
+      </method>
+      
+      <method name="openSubDialog">
+        <parameter name="aURL"/>
+        <parameter name="aFeatures"/>
+        <parameter name="aParams"/>
+        <body>
+          return openDialog(aURL, "", "modal,centerscreen,resizable=no" + (aFeatures != "" ? ("," + aFeatures) : ""), aParams);
+        </body>
+      </method>
+      
+      <method name="openWindow">
+        <parameter name="aWindowType"/>
+        <parameter name="aURL"/>
+        <parameter name="aFeatures"/>
+        <parameter name="aParams"/>
+        <body>
+        <![CDATA[
+          var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
+                             .getService(Components.interfaces.nsIWindowMediator);
+          var win = aWindowType ? wm.getMostRecentWindow(aWindowType) : null;
+          if (win) {
+            if ("initWithParams" in win)
+              win.initWithParams(aParams);
+            win.focus();
+          }
+          else {
+            var features = "resizable,dialog=no,centerscreen" + (aFeatures != "" ? ("," + aFeatures) : "");
+            var parentWindow = (this.instantApply || !window.opener || window.opener.closed) ? window : window.opener;
+            win = parentWindow.openDialog(aURL, "_blank", features, aParams);
+          }
+          return win;
+        ]]>
+        </body>
+      </method>
+    </implementation>
+    <handlers>
+      <handler event="dialogaccept">
+      <![CDATA[
+        if (!this._fireEvent("beforeaccept", this)) 
+          return;
+        
+        if (this.type == "child" && window.opener) {
+          var psvc = Components.classes["@mozilla.org/preferences-service;1"]
+                               .getService(Components.interfaces.nsIPrefBranch);
+          var instantApply = psvc.getBoolPref("browser.preferences.instantApply");
+          if (instantApply) {
+            var panes = this.preferencePanes;
+            for (var i = 0; i < panes.length; ++i)
+              panes[i].writePreferences(true);
+          }
+          else {
+            // Clone all the preferences elements from the child document and
+            // insert them into the pane collection of the parent. 
+            var pdoc = window.opener.document;
+            if (pdoc.documentElement.localName == "prefwindow") {
+              var currentPane = pdoc.documentElement.currentPane;
+              var id = window.location.href + "#childprefs";
+              var childPrefs = pdoc.getElementById(id);
+              if (!childPrefs) {
+                var childPrefs = pdoc.createElement("preferences");
+                currentPane.appendChild(childPrefs);
+                childPrefs.id = id;
+              }
+              var panes = this.preferencePanes;
+              for (var i = 0; i < panes.length; ++i) {
+                var preferences = panes[i].preferences;
+                for (var j = 0; j < preferences.length; ++j) {
+                  // Try to find a preference element for the same preference.
+                  var preference = null;
+                  var parentPreferences = pdoc.getElementsByTagName("preferences");
+                  for (var k = 0; (k < parentPreferences.length && !preference); ++k) {
+                    var parentPrefs = parentPreferences[k]
+                                         .getElementsByAttribute("name", preferences[j].name);
+                    for (var l = 0; (l < parentPrefs.length && !preference); ++l) {
+                      if (parentPrefs[l].localName == "preference")
+                        preference = parentPrefs[l];
+                    }
+                  }
+                  if (!preference) {
+                    // No matching preference in the parent window.
+                    preference = pdoc.createElement("preference");
+                    childPrefs.appendChild(preference);
+                    preference.name     = preferences[j].name;
+                    preference.type     = preferences[j].type;
+                    preference.inverted = preferences[j].inverted;
+                    preference.readonly = preferences[j].readonly;
+                    preference.disabled = preferences[j].disabled;
+                  }
+                  preference.value = preferences[j].value;
+                }
+              }
+            }
+          }
+        }
+        else {
+          var panes = this.preferencePanes;
+          for (var i = 0; i < panes.length; ++i)
+            panes[i].writePreferences(false);
+
+          var psvc = Components.classes["@mozilla.org/preferences-service;1"]
+                               .getService(Components.interfaces.nsIPrefService);
+          psvc.savePrefFile(null);
+        }
+      ]]>
+      </handler>
+      <handler event="command">
+        if (event.originalTarget.hasAttribute("pane")) {
+          var pane = document.getElementById(event.originalTarget.getAttribute("pane"));
+          this.showPane(pane);
+        }
+      </handler>
+
+      <handler event="keypress" key="&windowClose.key;" modifiers="accel" phase="capturing">
+      <![CDATA[
+        if (this.instantApply)
+          window.close();
+        event.stopPropagation();
+        event.preventDefault();
+      ]]>
+      </handler>
+
+      <handler event="keypress"
+#ifdef XP_MACOSX
+               key="&openHelpMac.commandkey;" modifiers="accel"
+#else
+               keycode="&openHelp.commandkey;"
+#endif
+               phase="capturing">
+      <![CDATA[
+        var helpButton = this.getButton("help");
+        if (helpButton.disabled || helpButton.hidden)
+          return;
+        this._fireEvent("dialoghelp", this);
+        event.stopPropagation();
+        event.preventDefault();
+      ]]>
+      </handler>
+    </handlers>
+  </binding>
+  
+  <binding id="prefpane">
+    <resources>
+      <stylesheet src="chrome://global/skin/preferences.css"/>
+    </resources>
+    <content>
+      <xul:vbox class="content-box" xbl:inherits="flex">
+        <children/>
+      </xul:vbox>
+    </content>
+    <implementation>
+      <method name="writePreferences">
+        <parameter name="aFlushToDisk"/>
+        <body>
+        <![CDATA[
+          // Write all values to preferences.
+          var preferences = this.preferences;
+          for (var i = 0; i < preferences.length; ++i) {
+            var preference = preferences[i];
+            preference.batching = true;
+            preference.valueFromPreferences = preference.value;
+            preference.batching = false;
+          }
+          if (aFlushToDisk) {
+            var psvc = Components.classes["@mozilla.org/preferences-service;1"]
+                                 .getService(Components.interfaces.nsIPrefService);
+            psvc.savePrefFile(null);
+          }
+        ]]>
+        </body>
+      </method>
+      
+      <property name="src" 
+                onget="return this.getAttribute('src');"
+                onset="this.setAttribute('src', val); return val;"/>
+      <property name="selected" 
+                onget="return this.getAttribute('selected') == 'true';"
+                onset="this.setAttribute('selected', val); return val;"/>
+      <property name="image" 
+                onget="return this.getAttribute('image');"
+                onset="this.setAttribute('image', val); return val;"/>
+      <property name="label" 
+                onget="return this.getAttribute('label');"
+                onset="this.setAttribute('label', val); return val;"/>
+      
+      <property name="preferenceElements"
+                onget="return this.getElementsByAttribute('preference', '*');"/>
+      <property name="preferences"
+                onget="return this.getElementsByTagName('preference');"/>
+
+      <property name="helpTopic">
+        <getter>
+        <![CDATA[
+          // if there are tabs, and the selected tab provides a helpTopic, return that
+          var box = this.getElementsByTagName("tabbox");
+          if (box[0]) {
+            var tab = box[0].selectedTab;
+            if (tab && tab.hasAttribute("helpTopic"))
+              return tab.getAttribute("helpTopic");
+          }
+
+          // otherwise, return the helpTopic of the current panel
+          return this.getAttribute("helpTopic");
+        ]]>
+        </getter>
+      </property>
+
+      <field name="_loaded">false</field>
+      <property name="loaded" 
+                onget="return !this.src ? true : this._loaded;"
+                onset="this._loaded = val; return val;"/>
+      
+      <method name="preferenceForElement">
+        <parameter name="aElement"/>
+        <body>
+          return document.getElementById(aElement.getAttribute("preference"));
+        </body>
+      </method>
+      
+      <method name="getPreferenceElement">
+        <parameter name="aStartElement"/>
+        <body>
+        <![CDATA[
+          var temp = aStartElement;
+          while (temp && temp.nodeType == Node.ELEMENT_NODE && 
+                 !temp.hasAttribute("preference"))
+            temp = temp.parentNode;
+          return temp.nodeType == Node.ELEMENT_NODE ? temp : aStartElement;
+        ]]>
+        </body>
+      </method>
+      
+      <method name="userChangedValue">
+        <parameter name="aElement"/>
+        <body>
+        <![CDATA[
+          var element = this.getPreferenceElement(aElement);
+          if (element.hasAttribute("preference")) {
+            var preference = document.getElementById(element.getAttribute("preference"));
+            var prefVal = preference.getElementValue(element);
+            preference.value = prefVal;
+          }
+        ]]>
+        </body>
+      </method>
+      
+      <property name="contentHeight">
+        <getter>
+          var targetHeight = parseInt(window.getComputedStyle(this._content, "").height);
+          targetHeight += parseInt(window.getComputedStyle(this._content, "").marginTop);
+          targetHeight += parseInt(window.getComputedStyle(this._content, "").marginBottom);
+          return targetHeight;
+        </getter>
+      </property>
+      <field name="_content">
+        document.getAnonymousElementByAttribute(this, "class", "content-box");
+      </field>
+    </implementation>
+    <handlers>
+      <handler event="command">
+        // This "command" event handler tracks changes made to preferences by 
+        // the user in this window. 
+        this.userChangedValue(event.target);
+      </handler>
+      <handler event="select">
+        // This "select" event handler tracks changes made to colorpicker 
+        // preferences by the user in this window.
+        if (event.target.localName == "colorpicker") 
+          this.userChangedValue(event.target);
+      </handler>
+      <handler event="change">
+        // This "change" event handler tracks changes made to preferences by 
+        // the user in this window. 
+        this.userChangedValue(event.target);
+      </handler>
+      <handler event="input">
+        // This "input" event handler tracks changes made to preferences by 
+        // the user in this window.
+        this.userChangedValue(event.target);
+      </handler>
+      <handler event="paneload">
+      <![CDATA[
+        // Initialize all values from preferences.
+        var elements = this.preferenceElements;
+        for (var i = 0; i < elements.length; ++i) {
+          try {
+            var preference = this.preferenceForElement(elements[i]);
+            preference.setElementValue(elements[i]);
+          }
+          catch (e) {
+            dump("*** No preference found for " + elements[i].getAttribute("preference") + "\n");
+          }
+        }
+      ]]>      
+      </handler>
+    </handlers>
+  </binding>
+          
+  <binding id="panebutton" extends="chrome://global/content/bindings/radio.xml#radio">
+    <resources>
+      <stylesheet src="chrome://global/skin/preferences.css"/>
+    </resources>
+    <content>
+      <xul:image class="paneButtonIcon" xbl:inherits="src"/>
+      <xul:label class="paneButtonLabel" xbl:inherits="value=label"/>
+    </content>
+    <implementation implements="nsIAccessible">
+      <property name="accessibleType" readonly="true">
+        <getter>
+          <![CDATA[
+            return Components.interfaces.nsIAccessibleProvider.XULListitem;
+          ]]>
+        </getter>
+      </property>
+    </implementation>
+  </binding>
+
+</bindings>
+
+# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 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 the Preferences System.
+#
+# The Initial Developer of the Original Code is
+# Ben Goodger.
+# Portions created by the Initial Developer are Copyright (C) 2005
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Ben Goodger <ben@mozilla.org>
+#   Josh Aas <josh@mozilla.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 MPL, 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 MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+#
+# This is PrefWindow 6. The Code Could Well Be Ready, Are You?
+#
+#    Historical References:
+#    PrefWindow V   (February 1, 2003)
+#    PrefWindow IV  (April 24, 2000)
+#    PrefWindow III (January 6, 2000)
+#    PrefWindow II  (???)
+#    PrefWindow I   (June 4, 1999)
+#
diff --git a/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp b/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp
--- a/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp
+++ b/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp
@@ -44,32 +44,35 @@
 #include "nsIURI.h"
 #include "nsReadableUtils.h"
 #include "nsArrayUtils.h"
 #include "prnetdb.h"
 #include "prenv.h"
 #include "nsPrintfCString.h"
 #include "nsNetUtil.h"
 #include "nsISupportsPrimitives.h"
+#include "nsVoidArray.h"
+#include "nsKDEUtils.h"
 
 class nsUnixSystemProxySettings : public nsISystemProxySettings {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSISYSTEMPROXYSETTINGS
 
   nsUnixSystemProxySettings() {}
   nsresult Init();
 
 private:
   ~nsUnixSystemProxySettings() {}
   
   nsCOMPtr<nsIGConfService> mGConf;
   PRBool IsProxyMode(const char* aMode);
   nsresult SetProxyResultFromGConf(const char* aKeyBase, const char* aType, nsACString& aResult);
   nsresult GetProxyFromGConf(const nsACString& aScheme, const nsACString& aHost, PRInt32 aPort, nsACString& aResult);
+  nsresult GetProxyFromKDE(const nsACString& aScheme, const nsACString& aHost, PRInt32 aPort, nsACString& aResult);
 };
 
 NS_IMPL_ISUPPORTS1(nsUnixSystemProxySettings, nsISystemProxySettings)
 
 nsresult
 nsUnixSystemProxySettings::Init()
 {
   // If this is a GNOME session, load gconf and try to use its preferences.
@@ -407,16 +410,19 @@ nsUnixSystemProxySettings::GetProxyForUR
   nsCAutoString host;
   rv = aURI->GetHost(host);
   NS_ENSURE_SUCCESS(rv, rv);
 
   PRInt32 port;
   rv = aURI->GetPort(&port);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  if( nsKDEUtils::kdeSupport())
+    return GetProxyFromKDE( scheme, host, port, aResult );
+
   if (!mGConf)
     return GetProxyFromEnvironment(scheme, host, port, aResult);
 
   return GetProxyFromGConf(scheme, host, port, aResult);
 }
 
 #define NS_UNIXSYSTEMPROXYSERVICE_CID  /* 0fa3158c-d5a7-43de-9181-a285e74cf1d4 */\
      { 0x0fa3158c, 0xd5a7, 0x43de, \
diff --git a/toolkit/xre/Makefile.in b/toolkit/xre/Makefile.in
--- a/toolkit/xre/Makefile.in
+++ b/toolkit/xre/Makefile.in
@@ -97,17 +97,18 @@ CMMSRCS = nsNativeAppSupportCocoa.mm
 else
 ifeq ($(MOZ_WIDGET_TOOLKIT),os2)
 CPPSRCS += nsNativeAppSupportOS2.cpp
 else
 ifeq ($(MOZ_WIDGET_TOOLKIT),beos)
 CPPSRCS += nsNativeAppSupportBeOS.cpp
 else
 ifeq ($(MOZ_WIDGET_TOOLKIT),gtk2)
-CPPSRCS += nsNativeAppSupportUnix.cpp
+CPPSRCS += nsNativeAppSupportUnix.cpp nsKDEUtils.cpp
+EXPORTS += nsKDEUtils.h
 else
 ifeq ($(MOZ_WIDGET_TOOLKIT),qt)
 CPPSRCS += nsNativeAppSupportQt.cpp
 CPPSRCS += nsQAppInstance.cpp
 EXPORTS += nsQAppInstance.h
 ifdef MOZ_ENABLE_MEEGOTOUCH
 MOCSRCS += moc_MozMeegoAppService.cpp
 CPPSRCS += moc_MozMeegoAppService.cpp
diff --git a/toolkit/xre/nsKDEUtils.cpp b/toolkit/xre/nsKDEUtils.cpp
new file mode 100644
--- /dev/null
+++ b/toolkit/xre/nsKDEUtils.cpp
@@ -0,0 +1,334 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 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 Unix Native App Support.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * 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 MPL, 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 MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsKDEUtils.h"
+#include "nsIWidget.h"
+
+#include <gtk/gtk.h>
+
+#include <limits.h>
+#include <stdio.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <X11/Xlib.h>
+
+//#define DEBUG_KDE
+#ifdef DEBUG_KDE
+#define KMOZILLAHELPER "kmozillahelper"
+#else
+// not need for lib64, it's a binary
+#define KMOZILLAHELPER "/usr/lib/mozilla/kmozillahelper"
+#endif
+
+#define KMOZILLAHELPER_VERSION 6
+#define MAKE_STR2( n ) #n
+#define MAKE_STR( n ) MAKE_STR2( n )
+
+static bool getKdeSession()
+    {
+    Display* dpy = XOpenDisplay( NULL );
+    if( dpy == NULL )
+        return false;
+    Atom kde_full_session = XInternAtom( dpy, "KDE_FULL_SESSION", True );
+    bool kde = false;
+    if( kde_full_session != None )
+        {
+        int cnt;
+        if( Atom* props = XListProperties( dpy, DefaultRootWindow( dpy ), &cnt ))
+            {
+            for( int i = 0;
+                 i < cnt;
+                 ++i )
+                {
+                if( props[ i ] == kde_full_session )
+                    {
+                    kde = true;
+#ifdef DEBUG_KDE
+                    fprintf( stderr, "KDE SESSION %d\n", kde );
+#endif
+                    break;
+                    }
+                }
+            XFree( props );
+            }
+        }
+    XCloseDisplay( dpy );
+    return kde;
+    }
+
+static bool getKdeSupport()
+    {
+    nsCStringArray command;
+    command.AppendCString( NS_LITERAL_CSTRING( "CHECK" ));
+    command.AppendCString( NS_LITERAL_CSTRING( MAKE_STR( KMOZILLAHELPER_VERSION )));
+    bool kde = nsKDEUtils::command( command );
+#ifdef DEBUG_KDE
+    fprintf( stderr, "KDE RUNNING %d\n", kde );
+#endif
+    return kde;
+    }
+
+nsKDEUtils::nsKDEUtils()
+    : commandFile( NULL )
+    , replyFile( NULL )
+    {
+    }
+
+nsKDEUtils::~nsKDEUtils()
+    {
+//    closeHelper(); not actually useful, exiting will close the fd too
+    }
+
+nsKDEUtils* nsKDEUtils::self()
+    {
+    static nsKDEUtils s;
+    return &s;
+    }
+
+static bool helperRunning = false;
+static bool helperFailed = false;
+
+bool nsKDEUtils::kdeSession()
+    {
+    static bool session = getKdeSession();
+    return session;
+    }
+
+bool nsKDEUtils::kdeSupport()
+    {
+    static bool support = kdeSession() && getKdeSupport();
+    return support && helperRunning;
+    }
+
+struct nsKDECommandData
+    {
+    FILE* file;
+    nsCStringArray* output;
+    GMainLoop* loop;
+    bool success;
+    };
+
+static gboolean kdeReadFunc( GIOChannel*, GIOCondition, gpointer data )
+    {
+    nsKDECommandData* p = static_cast< nsKDECommandData* >( data );
+    char buf[ 8192 ]; // TODO big enough
+    bool command_done = false;
+    bool command_failed = false;
+    while( !command_done && !command_failed && fgets( buf, 8192, p->file ) != NULL )
+        { // TODO what if the kernel splits a line into two chunks?
+//#ifdef DEBUG_KDE
+//        fprintf( stderr, "READ: %s %d\n", buf, feof( p->file ));
+//#endif
+        if( char* eol = strchr( buf, '\n' ))
+            *eol = '\0';
+        command_done = ( strcmp( buf, "\\1" ) == 0 );
+        command_failed = ( strcmp( buf, "\\0" ) == 0 );
+        nsCAutoString line( buf );
+        line.ReplaceSubstring( "\\n", "\n" );
+        line.ReplaceSubstring( "\\" "\\", "\\" ); //  \\ -> \ , i.e. unescape
+        if( p->output && !( command_done || command_failed ))
+            p->output->AppendCString( nsCString( buf )); // TODO utf8?
+        }
+    bool quit = false;
+    if( feof( p->file ) || command_failed )
+        {
+        quit = true;
+        p->success = false;
+        }
+    if( command_done )
+        { // reading one reply finished
+        quit = true;
+        p->success = true;
+        }
+    if( quit )
+        {
+        if( p->loop )
+            g_main_loop_quit( p->loop );
+        return FALSE;
+        }
+    return TRUE;
+    }
+
+bool nsKDEUtils::command( const nsCStringArray& command, nsCStringArray* output )
+    {
+    return self()->internalCommand( command, NULL, false, output );
+    }
+
+bool nsKDEUtils::commandBlockUi( const nsCStringArray& command, const GtkWindow* parent, nsCStringArray* output )
+    {
+    return self()->internalCommand( command, parent, true, output );
+    }
+
+bool nsKDEUtils::internalCommand( const nsCStringArray& command, const GtkWindow* parent, bool blockUi,
+    nsCStringArray* output )
+    {
+    if( !startHelper())
+        return false;
+    // if Gtk has meanwhile gotten a grab (bnc#555202, somehow the file dialog
+    // is called with a delay), then do not do anything, as this app would keep
+    // the grabs but block waiting for the helper, which would be unable to do
+    // anything because of the grab
+    if( blockUi && gdk_pointer_is_grabbed())
+        return false;
+    feedCommand( command );
+    // do not store the data in 'this' but in extra structure, just in case there
+    // is reentrancy (can there be? the event loop is re-entered)
+    nsKDECommandData data;
+    data.file = replyFile;
+    data.output = output;
+    data.success = false;
+    if( blockUi )
+        {
+        data.loop = g_main_loop_new( NULL, FALSE );
+        GtkWidget* window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
+        if( parent && parent->group )
+            gtk_window_group_add_window( parent->group, GTK_WINDOW( window ));
+        gtk_widget_realize( window );
+        gtk_widget_set_sensitive( window, TRUE );
+        gtk_grab_add( window );
+        GIOChannel* channel = g_io_channel_unix_new( fileno( data.file ));
+        g_io_add_watch( channel, static_cast< GIOCondition >( G_IO_IN | G_IO_ERR | G_IO_HUP ), kdeReadFunc, &data );
+        g_io_channel_unref( channel );
+        g_main_loop_run( data.loop );
+        g_main_loop_unref( data.loop );
+        gtk_grab_remove( window );
+        gtk_widget_destroy( window );
+        }
+    else
+        {
+        data.loop = NULL;
+        while( kdeReadFunc( NULL, static_cast< GIOCondition >( 0 ), &data ))
+            ;
+        }
+    return data.success;
+    }
+
+bool nsKDEUtils::startHelper()
+    {
+    if( helperRunning )
+        return true;
+    if( helperFailed )
+        return false;
+    helperFailed = true;
+    int fdcommand[ 2 ];
+    int fdreply[ 2 ];
+    if( pipe( fdcommand ) < 0 )
+        return false;
+    if( pipe( fdreply ) < 0 )
+        {
+        close( fdcommand[ 0 ] );
+        close( fdcommand[ 1 ] );
+        return false;
+        }
+    char* args[ 2 ] = { const_cast< char* >( KMOZILLAHELPER ), NULL };
+    switch( fork())
+        {
+        case -1:
+            {
+            close( fdcommand[ 0 ] );
+            close( fdcommand[ 1 ] );
+            close( fdreply[ 0 ] );
+            close( fdreply[ 1 ] );
+            return false;
+            }
+        case 0: // child
+            {
+            if( dup2( fdcommand[ 0 ], STDIN_FILENO ) < 0 )
+                _exit( 1 );
+            if( dup2( fdreply[ 1 ], STDOUT_FILENO ) < 0 )
+                _exit( 1 );
+            int maxfd = 1024; // close all other fds
+            struct rlimit rl;
+            if( getrlimit( RLIMIT_NOFILE, &rl ) == 0 )
+                maxfd = rl.rlim_max;
+            for( int i = 3;
+                 i < maxfd;
+                 ++i )
+                close( i );
+#ifdef DEBUG_KDE
+            execvp( KMOZILLAHELPER, args );
+#else
+            execv( KMOZILLAHELPER, args );
+#endif
+            _exit( 1 ); // failed
+            }
+        default: // parent
+            {
+            commandFile = fdopen( fdcommand[ 1 ], "w" );
+            replyFile = fdopen( fdreply[ 0 ], "r" );
+            close( fdcommand[ 0 ] );
+            close( fdreply[ 1 ] );
+            if( commandFile == NULL || replyFile == NULL )
+                {
+                closeHelper();
+                return false;
+                }
+            // ok, helper ready, getKdeRunning() will check if it works
+            }
+        }
+    helperFailed = false;
+    helperRunning = true;
+    return true;
+    }
+
+void nsKDEUtils::closeHelper()
+    {
+    if( commandFile != NULL )
+        fclose( commandFile ); // this will also make the helper quit
+    if( replyFile != NULL )
+        fclose( replyFile );
+    helperRunning = false;
+    }
+
+void nsKDEUtils::feedCommand( const nsCStringArray& command )
+    {
+    for( int i = 0;
+         i < command.Count();
+         ++i )
+        {
+        nsCString line = *command[ i ];
+        line.ReplaceSubstring( "\\", "\\" "\\" ); // \ -> \\ , i.e. escape
+        line.ReplaceSubstring( "\n", "\\n" );
+#ifdef DEBUG_KDE
+        fprintf( stderr, "COMM: %s\n", line.get());
+#endif
+        fputs( line.get(), commandFile );
+        fputs( "\n", commandFile );
+        }
+    fputs( "\\E\n", commandFile ); // done as \E, so it cannot happen in normal data
+    fflush( commandFile );
+    }
diff --git a/toolkit/xre/nsKDEUtils.h b/toolkit/xre/nsKDEUtils.h
new file mode 100644
--- /dev/null
+++ b/toolkit/xre/nsKDEUtils.h
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * ***** 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 Communicator client code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 MPL, 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 MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsKDEUtils_h__
+#define nsKDEUtils_h__
+
+#include "nsStringGlue.h"
+#include "nsVoidArray.h"
+#include <stdio.h>
+
+typedef struct _GtkWindow GtkWindow;
+
+class NS_EXPORT nsKDEUtils
+    {
+    public:
+        /* Returns true if running inside a KDE session (regardless of whether there is KDE
+           support available for Firefox). This should be used e.g. when determining
+           dialog button order but not for code that requires the KDE support. */
+        static bool kdeSession();
+        /* Returns true if running inside a KDE session and KDE support is available
+           for Firefox. This should be used everywhere where the external helper is needed. */
+        static bool kdeSupport();
+        /* Executes the given helper command, returns true if helper returned success. */
+        static bool command( const nsCStringArray& command, nsCStringArray* output = NULL );
+        /* Like command(), but additionally blocks the parent widget like if there was
+           a modal dialog shown and enters the event loop (i.e. there are still paint updates,
+           this is for commands that take long). */
+        static bool commandBlockUi( const nsCStringArray& command, const GtkWindow* parent, nsCStringArray* output = NULL );
+    private:
+        nsKDEUtils();
+        ~nsKDEUtils();
+        static nsKDEUtils* self();
+        bool startHelper();
+        void closeHelper();
+        void feedCommand( const nsCStringArray& command );
+        bool internalCommand( const nsCStringArray& command, const GtkWindow* parent, bool isParent,
+            nsCStringArray* output );
+        FILE* commandFile;
+        FILE* replyFile;
+    };
+
+#endif // nsKDEUtils
diff --git a/uriloader/exthandler/Makefile.in b/uriloader/exthandler/Makefile.in
--- a/uriloader/exthandler/Makefile.in
+++ b/uriloader/exthandler/Makefile.in
@@ -94,18 +94,19 @@ LOCAL_INCLUDES += -I$(topsrcdir)/dom/bas
             -I$(topsrcdir)/dom/ipc \
             -I$(topsrcdir)/content/base/src \
             -I$(topsrcdir)/content/events/src \
             -I$(topsrcdir)/netwerk/base/src \
             -I$(topsrcdir)/netwerk/protocol/http
 endif
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),gtk2)
-OSHELPER	+= nsGNOMERegistry.cpp
+OSHELPER	+= nsCommonRegistry.cpp nsGNOMERegistry.cpp nsKDERegistry.cpp
 OSHELPER  += nsMIMEInfoUnix.cpp
+LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/xre
 endif
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),android)
 OSHELPER += nsMIMEInfoAndroid.cpp
 OSHELPER += nsAndroidHandlerApp.cpp
 OSHELPER += nsExternalSharingAppService.cpp
 EXPORTS += nsExternalSharingAppService.h
 endif
diff --git a/uriloader/exthandler/unix/nsCommonRegistry.cpp b/uriloader/exthandler/unix/nsCommonRegistry.cpp
new file mode 100644
--- /dev/null
+++ b/uriloader/exthandler/unix/nsCommonRegistry.cpp
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** 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 the GNOME helper app implementation.
+ *
+ * The Initial Developer of the Original Code is
+ * IBM Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2003
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Brian Ryner <bryner@brianryner.com>  (Original Author)
+ *
+ * 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 MPL, 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 MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsCommonRegistry.h"
+
+#include "nsGNOMERegistry.h"
+#include "nsKDERegistry.h"
+#include "nsString.h"
+#include "nsVoidArray.h"
+#include "nsKDEUtils.h"
+
+/* static */ PRBool
+nsCommonRegistry::HandlerExists(const char *aProtocolScheme)
+{
+    if( nsKDEUtils::kdeSupport())
+        return nsKDERegistry::HandlerExists( aProtocolScheme );
+    return nsGNOMERegistry::HandlerExists( aProtocolScheme );
+}
+
+/* static */ nsresult
+nsCommonRegistry::LoadURL(nsIURI *aURL)
+{
+    if( nsKDEUtils::kdeSupport())
+        return nsKDERegistry::LoadURL( aURL );
+    return nsGNOMERegistry::LoadURL( aURL );
+}
+
+/* static */ void
+nsCommonRegistry::GetAppDescForScheme(const nsACString& aScheme,
+                                     nsAString& aDesc)
+{
+    if( nsKDEUtils::kdeSupport())
+        return nsKDERegistry::GetAppDescForScheme( aScheme, aDesc );
+    return nsGNOMERegistry::GetAppDescForScheme( aScheme, aDesc );
+}
+
+
+/* static */ already_AddRefed<nsMIMEInfoBase>
+nsCommonRegistry::GetFromExtension(const nsACString& aFileExt)
+{
+    if( nsKDEUtils::kdeSupport())
+        return nsKDERegistry::GetFromExtension( aFileExt );
+    return nsGNOMERegistry::GetFromExtension( aFileExt );
+}
+
+/* static */ already_AddRefed<nsMIMEInfoBase>
+nsCommonRegistry::GetFromType(const nsACString& aMIMEType)
+{
+    if( nsKDEUtils::kdeSupport())
+        return nsKDERegistry::GetFromType( aMIMEType );
+    return nsGNOMERegistry::GetFromType( aMIMEType );
+}
diff --git a/uriloader/exthandler/unix/nsCommonRegistry.h b/uriloader/exthandler/unix/nsCommonRegistry.h
new file mode 100644
--- /dev/null
+++ b/uriloader/exthandler/unix/nsCommonRegistry.h
@@ -0,0 +1,56 @@
+/* ***** 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 the GNOME helper app implementation.
+ *
+ * The Initial Developer of the Original Code is
+ * IBM Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2003
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Brian Ryner <bryner@brianryner.com>  (Original Author)
+ *
+ * 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 MPL, 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 MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsIURI.h"
+#include "nsCOMPtr.h"
+
+class nsMIMEInfoBase;
+
+class nsCommonRegistry
+{
+ public:
+  static PRBool HandlerExists(const char *aProtocolScheme);
+
+  static nsresult LoadURL(nsIURI *aURL);
+
+  static void GetAppDescForScheme(const nsACString& aScheme,
+                                  nsAString& aDesc);
+
+  static already_AddRefed<nsMIMEInfoBase> GetFromExtension(const nsACString& aFileExt);
+
+  static already_AddRefed<nsMIMEInfoBase> GetFromType(const nsACString& aMIMEType);
+};
diff --git a/uriloader/exthandler/unix/nsKDERegistry.cpp b/uriloader/exthandler/unix/nsKDERegistry.cpp
new file mode 100644
--- /dev/null
+++ b/uriloader/exthandler/unix/nsKDERegistry.cpp
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** 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 the GNOME helper app implementation.
+ *
+ * The Initial Developer of the Original Code is
+ * IBM Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2003
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Brian Ryner <bryner@brianryner.com>  (Original Author)
+ *
+ * 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 MPL, 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 MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsKDERegistry.h"
+#include "prlink.h"
+#include "prmem.h"
+#include "nsString.h"
+#include "nsILocalFile.h"
+#include "nsMIMEInfoUnix.h"
+#include "nsAutoPtr.h"
+#include "nsKDEUtils.h"
+
+/* static */ PRBool
+nsKDERegistry::HandlerExists(const char *aProtocolScheme)
+{
+    nsCStringArray command;
+    command.AppendCString( NS_LITERAL_CSTRING( "HANDLEREXISTS" ));
+    command.AppendCString( nsCAutoString( aProtocolScheme ));
+    return nsKDEUtils::command( command );
+}
+
+/* static */ nsresult
+nsKDERegistry::LoadURL(nsIURI *aURL)
+{
+    nsCStringArray command;
+    command.AppendCString( NS_LITERAL_CSTRING( "OPEN" ));
+    nsCString url;
+    aURL->GetSpec( url );
+    command.AppendCString( url );
+    return nsKDEUtils::command( command );
+}
+
+/* static */ void
+nsKDERegistry::GetAppDescForScheme(const nsACString& aScheme,
+                                     nsAString& aDesc)
+{
+    nsCStringArray command;
+    command.AppendCString( NS_LITERAL_CSTRING( "GETAPPDESCFORSCHEME" ));
+    command.AppendCString( aScheme );
+    nsCStringArray output;
+    if( nsKDEUtils::command( command, &output ) && output.Count() == 1 )
+        CopyUTF8toUTF16( *output[ 0 ], aDesc );
+}
+
+
+/* static */ already_AddRefed<nsMIMEInfoBase>
+nsKDERegistry::GetFromExtension(const nsACString& aFileExt)
+{
+    NS_ASSERTION(aFileExt[0] != '.', "aFileExt shouldn't start with a dot");
+    nsCStringArray command;
+    command.AppendCString( NS_LITERAL_CSTRING( "GETFROMEXTENSION" ));
+    command.AppendCString( aFileExt );
+    return GetFromHelper( command );
+}
+
+/* static */ already_AddRefed<nsMIMEInfoBase>
+nsKDERegistry::GetFromType(const nsACString& aMIMEType)
+{
+    nsCStringArray command;
+    command.AppendCString( NS_LITERAL_CSTRING( "GETFROMTYPE" ));
+    command.AppendCString( aMIMEType );
+    return GetFromHelper( command );
+}
+
+/* static */ already_AddRefed<nsMIMEInfoBase>
+nsKDERegistry::GetFromHelper(const nsCStringArray& command)
+{
+    nsCStringArray output;
+    if( nsKDEUtils::command( command, &output ) && output.Count() == 3 )
+        {
+        nsCString mimetype = *output[ 0 ];
+        nsRefPtr<nsMIMEInfoUnix> mimeInfo = new nsMIMEInfoUnix( mimetype );
+        NS_ENSURE_TRUE(mimeInfo, nsnull);
+        nsCString description = *output[ 1 ];
+        mimeInfo->SetDescription(NS_ConvertUTF8toUTF16(description));
+        nsCString handlerAppName = *output[ 2 ];
+        mimeInfo->SetDefaultDescription(NS_ConvertUTF8toUTF16(handlerAppName));
+        mimeInfo->SetPreferredAction(nsIMIMEInfo::useSystemDefault);
+        nsMIMEInfoBase* retval;
+        NS_ADDREF((retval = mimeInfo));
+        return retval;
+        }
+    return nsnull;
+}
diff --git a/uriloader/exthandler/unix/nsKDERegistry.h b/uriloader/exthandler/unix/nsKDERegistry.h
new file mode 100644
--- /dev/null
+++ b/uriloader/exthandler/unix/nsKDERegistry.h
@@ -0,0 +1,61 @@
+/* ***** 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 the GNOME helper app implementation.
+ *
+ * The Initial Developer of the Original Code is
+ * IBM Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2003
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Brian Ryner <bryner@brianryner.com>  (Original Author)
+ *
+ * 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 MPL, 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 MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsIURI.h"
+#include "nsCOMPtr.h"
+
+class nsMIMEInfoBase;
+class nsCAutoString;
+class nsCStringArray;
+
+class nsKDERegistry
+{
+ public:
+  static PRBool HandlerExists(const char *aProtocolScheme);
+
+  static nsresult LoadURL(nsIURI *aURL);
+
+  static void GetAppDescForScheme(const nsACString& aScheme,
+                                  nsAString& aDesc);
+
+  static already_AddRefed<nsMIMEInfoBase> GetFromExtension(const nsACString& aFileExt);
+
+  static already_AddRefed<nsMIMEInfoBase> GetFromType(const nsACString& aMIMEType);
+ private:
+  static already_AddRefed<nsMIMEInfoBase> GetFromHelper(const nsCStringArray& command);
+
+};
diff --git a/uriloader/exthandler/unix/nsMIMEInfoUnix.cpp b/uriloader/exthandler/unix/nsMIMEInfoUnix.cpp
--- a/uriloader/exthandler/unix/nsMIMEInfoUnix.cpp
+++ b/uriloader/exthandler/unix/nsMIMEInfoUnix.cpp
@@ -48,28 +48,31 @@
 #include <QDesktopServices>
 #include <QUrl>
 #include <QString>
 #include <contentaction/contentaction.h>
 #include "nsContentHandlerApp.h"
 #endif
 
 #include "nsMIMEInfoUnix.h"
-#include "nsGNOMERegistry.h"
+#include "nsCommonRegistry.h"
 #include "nsIGIOService.h"
 #include "nsIGnomeVFSService.h"
 #include "nsAutoPtr.h"
 #ifdef MOZ_ENABLE_DBUS
 #include "nsDBusHandlerApp.h"
 #endif
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+#include "nsKDEUtils.h"
+#endif
 
 nsresult
 nsMIMEInfoUnix::LoadUriInternal(nsIURI * aURI)
 {
-  nsresult rv = nsGNOMERegistry::LoadURL(aURI);
+  nsresult rv = nsCommonRegistry::LoadURL(aURI);
 
 #if (MOZ_PLATFORM_MAEMO == 5) && defined (MOZ_ENABLE_GNOMEVFS)
   if (NS_FAILED(rv)){
     HildonURIAction *action = hildon_uri_get_default_action(mSchemeOrType.get(), nsnull);
     if (action) {
       nsCAutoString spec;
       aURI->GetAsciiSpec(spec);
       if (hildon_uri_open(spec.get(), action, nsnull))
@@ -91,22 +94,22 @@ nsMIMEInfoUnix::LoadUriInternal(nsIURI *
 
   return rv;
 }
 
 NS_IMETHODIMP
 nsMIMEInfoUnix::GetHasDefaultHandler(PRBool *_retval)
 {
   *_retval = PR_FALSE;
-  nsRefPtr<nsMIMEInfoBase> mimeInfo = nsGNOMERegistry::GetFromType(mSchemeOrType);
+  nsRefPtr<nsMIMEInfoBase> mimeInfo = nsCommonRegistry::GetFromType(mSchemeOrType);
   if (!mimeInfo) {
     nsCAutoString ext;
     nsresult rv = GetPrimaryExtension(ext);
     if (NS_SUCCEEDED(rv)) {
-      mimeInfo = nsGNOMERegistry::GetFromExtension(ext);
+      mimeInfo = nsCommonRegistry::GetFromExtension(ext);
     }
   }
   if (mimeInfo)
     *_retval = PR_TRUE;
 
   if (*_retval)
     return NS_OK;
 
@@ -149,32 +152,49 @@ nsMIMEInfoUnix::LaunchDefaultWithFile(ns
     ContentAction::Action::defaultActionForFile(uri, QString(mSchemeOrType.get()));
   if (action.isValid()) {
     action.trigger();
     return NS_OK;
   }
   return NS_ERROR_FAILURE;
 #endif
 
+  if( nsKDEUtils::kdeSupport()) {
+    PRBool supports;
+    if( NS_SUCCEEDED( GetHasDefaultHandler( &supports )) && supports ) {
+      nsCStringArray command;
+      command.AppendCString( NS_LITERAL_CSTRING( "OPEN" ));
+      command.AppendCString( nativePath );
+      command.AppendCString( NS_LITERAL_CSTRING( "MIMETYPE" ));
+      command.AppendCString( mSchemeOrType );
+      if( nsKDEUtils::command( command ))
+        return NS_OK;
+    }
+    if (!mDefaultApplication)
+      return NS_ERROR_FILE_NOT_FOUND;
+
+    return LaunchWithIProcess(mDefaultApplication, nativePath);
+  }
+
   nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
   nsCOMPtr<nsIGnomeVFSService> gnomevfs = do_GetService(NS_GNOMEVFSSERVICE_CONTRACTID);
   if (giovfs) {
     nsCOMPtr<nsIGIOMimeApp> app;
     if (NS_SUCCEEDED(giovfs->GetAppForMimeType(mSchemeOrType, getter_AddRefs(app))) && app)
       return app->Launch(nativePath);
   } else if (gnomevfs) {
     /* Fallback to GnomeVFS */
     nsCOMPtr<nsIGnomeVFSMimeApp> app;
     if (NS_SUCCEEDED(gnomevfs->GetAppForMimeType(mSchemeOrType, getter_AddRefs(app))) && app)
       return app->Launch(nativePath);
   }
 
   // If we haven't got an app we try to get a valid one by searching for the
   // extension mapped type
-  nsRefPtr<nsMIMEInfoBase> mimeInfo = nsGNOMERegistry::GetFromExtension(nativePath);
+  nsRefPtr<nsMIMEInfoBase> mimeInfo = nsCommonRegistry::GetFromExtension(nativePath);
   if (mimeInfo) {
     nsCAutoString type;
     mimeInfo->GetType(type);
     if (giovfs) {
       nsCOMPtr<nsIGIOMimeApp> app;
       if (NS_SUCCEEDED(giovfs->GetAppForMimeType(type, getter_AddRefs(app))) && app)
         return app->Launch(nativePath);
     } else if (gnomevfs) {
diff --git a/uriloader/exthandler/unix/nsOSHelperAppService.cpp b/uriloader/exthandler/unix/nsOSHelperAppService.cpp
--- a/uriloader/exthandler/unix/nsOSHelperAppService.cpp
+++ b/uriloader/exthandler/unix/nsOSHelperAppService.cpp
@@ -44,17 +44,17 @@
 #if (MOZ_PLATFORM_MAEMO == 6)
 #include <contentaction/contentaction.h>
 #include <QString>
 #endif
 
 #include "nsOSHelperAppService.h"
 #include "nsMIMEInfoUnix.h"
 #ifdef MOZ_WIDGET_GTK2
-#include "nsGNOMERegistry.h"
+#include "nsCommonRegistry.h"
 #endif
 #include "nsISupports.h"
 #include "nsString.h"
 #include "nsReadableUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsXPIDLString.h"
 #include "nsIURL.h"
 #include "nsIFileStreams.h"
@@ -1219,29 +1219,29 @@ nsresult nsOSHelperAppService::OSProtoco
     ContentAction::Action::defaultActionForScheme(QString(aProtocolScheme) + ':');
 
   if (action.isValid())
     *aHandlerExists = PR_TRUE;
 #endif
 
 #ifdef MOZ_WIDGET_GTK2
   // Check the GConf registry for a protocol handler
-  *aHandlerExists = nsGNOMERegistry::HandlerExists(aProtocolScheme);
+  *aHandlerExists = nsCommonRegistry::HandlerExists(aProtocolScheme);
 #if (MOZ_PLATFORM_MAEMO == 5) && defined (MOZ_ENABLE_GNOMEVFS)
   *aHandlerExists = nsMIMEInfoUnix::HandlerExists(aProtocolScheme);
 #endif
 #endif
 
   return NS_OK;
 }
 
 NS_IMETHODIMP nsOSHelperAppService::GetApplicationDescription(const nsACString& aScheme, nsAString& _retval)
 {
 #ifdef MOZ_WIDGET_GTK2
-  nsGNOMERegistry::GetAppDescForScheme(aScheme, _retval);
+  nsCommonRegistry::GetAppDescForScheme(aScheme, _retval);
   return _retval.IsEmpty() ? NS_ERROR_NOT_AVAILABLE : NS_OK;
 #else
   return NS_ERROR_NOT_AVAILABLE;
 #endif
 }
 
 nsresult nsOSHelperAppService::GetFileTokenForPath(const PRUnichar * platformAppPath, nsIFile ** aFile)
 {
@@ -1327,17 +1327,17 @@ nsOSHelperAppService::GetFromExtension(c
                                          minorType,
                                          mime_types_description,
                                          PR_TRUE);
 
   if (NS_FAILED(rv) || majorType.IsEmpty()) {
     
 #ifdef MOZ_WIDGET_GTK2
     LOG(("Looking in GNOME registry\n"));
-    nsMIMEInfoBase *gnomeInfo = nsGNOMERegistry::GetFromExtension(aFileExt).get();
+    nsMIMEInfoBase *gnomeInfo = nsCommonRegistry::GetFromExtension(aFileExt).get();
     if (gnomeInfo) {
       LOG(("Got MIMEInfo from GNOME registry\n"));
       return gnomeInfo;
     }
 #endif
 
     rv = LookUpTypeAndDescription(NS_ConvertUTF8toUTF16(aFileExt),
                                   majorType,
@@ -1453,17 +1453,17 @@ nsOSHelperAppService::GetFromType(const 
 #ifdef MOZ_WIDGET_GTK2
   nsMIMEInfoBase *gnomeInfo = nsnull;
   if (handler.IsEmpty()) {
     // No useful data yet.  Check the GNOME registry.  Unfortunately, newer
     // GNOME versions no longer have type-to-extension mappings, so we might
     // get back a MIMEInfo without any extensions set.  In that case we'll have
     // to look in our mime.types files for the extensions.    
     LOG(("Looking in GNOME registry\n"));
-    gnomeInfo = nsGNOMERegistry::GetFromType(aMIMEType).get();
+    gnomeInfo = nsCommonRegistry::GetFromType(aMIMEType).get();
     if (gnomeInfo && gnomeInfo->HasExtensions()) {
       LOG(("Got MIMEInfo from GNOME registry, and it has extensions set\n"));
       return gnomeInfo;
     }
   }
 #endif
 
   // Now look up our extensions
diff --git a/widget/src/gtk2/Makefile.in b/widget/src/gtk2/Makefile.in
--- a/widget/src/gtk2/Makefile.in
+++ b/widget/src/gtk2/Makefile.in
@@ -157,11 +157,14 @@ endif
 
 DEFINES		+= -DCAIRO_GFX
 
 INCLUDES	+= \
 		-I$(srcdir)/../xpwidgets \
 		-I$(srcdir)/../shared \
 		-I$(topsrcdir)/other-licenses/atk-1.0 \
 		$(NULL)
+
+LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/xre
+
 ifdef MOZ_X11
 INCLUDES   	+= -I$(srcdir)/../shared/x11
 endif
diff --git a/widget/src/gtk2/nsFilePicker.cpp b/widget/src/gtk2/nsFilePicker.cpp
--- a/widget/src/gtk2/nsFilePicker.cpp
+++ b/widget/src/gtk2/nsFilePicker.cpp
@@ -31,16 +31,17 @@
  * 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 MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include <gtk/gtk.h>
+#include <gdk/gdkx.h>
 
 #include "nsIFileURL.h"
 #include "nsIURI.h"
 #include "nsIWidget.h"
 #include "nsILocalFile.h"
 #include "nsIStringBundle.h"
 
 #include "nsArrayEnumerator.h"
@@ -50,16 +51,17 @@
 #include "nsReadableUtils.h"
 #include "mozcontainer.h"
 
 #include "prmem.h"
 #include "prlink.h"
 
 #include "nsFilePicker.h"
 #include "nsAccessibilityHelper.h"
+#include "nsKDEUtils.h"
 
 #ifdef MOZ_PLATFORM_MAEMO
 #include <hildon-fm-2/hildon/hildon-file-chooser-dialog.h>
 #endif
 
 #define MAX_PREVIEW_SIZE 180
 
 nsILocalFile *nsFilePicker::mPrevDisplayDirectory = nsnull;
@@ -270,17 +272,19 @@ nsFilePicker::AppendFilters(PRInt32 aFil
   return nsBaseFilePicker::AppendFilters(aFilterMask);
 }
 
 NS_IMETHODIMP
 nsFilePicker::AppendFilter(const nsAString& aTitle, const nsAString& aFilter)
 {
   if (aFilter.EqualsLiteral("..apps")) {
     // No platform specific thing we can do here, really....
-    return NS_OK;
+    // Unless it's KDE.
+    if( mMode != modeOpen || !nsKDEUtils::kdeSupport())
+        return NS_OK;
   }
 
   nsCAutoString filter, name;
   CopyUTF16toUTF8(aFilter, filter);
   CopyUTF16toUTF8(aTitle, name);
 
   mFilters.AppendElement(filter);
   mFilterNames.AppendElement(name);
@@ -418,16 +422,19 @@ confirm_overwrite_file(GtkWidget *parent
   gtk_widget_destroy(dialog);
 
   return result;
 }
 
 NS_IMETHODIMP
 nsFilePicker::Show(PRInt16 *aReturn)
 {
+  if( nsKDEUtils::kdeSupport())
+      return kdeFileDialog( aReturn );
+
   NS_ENSURE_ARG_POINTER(aReturn);
 
   nsXPIDLCString title;
   title.Adopt(ToNewUTF8String(mTitle));
 
   GtkWindow *parent_widget = get_gtk_window_for_nsiwidget(mParentWidget);
 
   GtkFileChooserAction action = GetGtkFileChooserAction(mMode);
@@ -568,8 +575,234 @@ nsFilePicker::Show(PRInt16 *aReturn)
     *aReturn = nsIFilePicker::returnCancel;
     break;
   }
 
   gtk_widget_destroy(file_chooser);
 
   return NS_OK;
 }
+
+nsCString nsFilePicker::kdeMakeFilter( int index )
+    {
+    nsCString buf = mFilters[ index ];
+    for( PRUint32 i = 0;
+         i < buf.Length();
+         ++i )
+        if( buf[ i ] == ';' ) // KDE separates just using spaces
+            buf.SetCharAt( ' ', i );
+    if (!mFilterNames[index].IsEmpty())
+        {
+        buf += "|";
+        buf += mFilterNames[index].get();
+        }
+    return buf;
+    }
+
+static PRInt32 windowToXid( nsIWidget* widget )
+    {
+    GtkWindow *parent_widget = get_gtk_window_for_nsiwidget( widget );
+    GdkWindow* gdk_window = gtk_widget_get_window( gtk_widget_get_toplevel( GTK_WIDGET( parent_widget )));
+    return GDK_WINDOW_XID( gdk_window );
+    }
+
+NS_IMETHODIMP nsFilePicker::kdeFileDialog(PRInt16 *aReturn)
+    {
+    NS_ENSURE_ARG_POINTER(aReturn);
+
+    if( mMode == modeOpen && mFilters.Length() == 1 && mFilters[ 0 ].EqualsLiteral( "..apps" ))
+        return kdeAppsDialog( aReturn );
+
+    nsXPIDLCString title;
+    title.Adopt(ToNewUTF8String(mTitle));
+
+    const char* arg = NULL;
+    if( mAllowURLs )
+        {
+        switch( mMode )
+            {
+            case nsIFilePicker::modeOpen:
+            case nsIFilePicker::modeOpenMultiple:
+                arg = "GETOPENURL";
+                break;
+            case nsIFilePicker::modeSave:
+                arg = "GETSAVEURL";
+                break;
+            case nsIFilePicker::modeGetFolder:
+                arg = "GETDIRECTORYURL";
+                break;
+            }
+        }
+    else
+        {
+        switch( mMode )
+            {
+            case nsIFilePicker::modeOpen:
+            case nsIFilePicker::modeOpenMultiple:
+                arg = "GETOPENFILENAME";
+                break;
+            case nsIFilePicker::modeSave:
+                arg = "GETSAVEFILENAME";
+                break;
+            case nsIFilePicker::modeGetFolder:
+                arg = "GETDIRECTORYFILENAME";
+                break;
+            }
+        }
+
+  nsCAutoString directory;
+  if (mDisplayDirectory) {
+    mDisplayDirectory->GetNativePath(directory);
+  } else if (mPrevDisplayDirectory) {
+    mPrevDisplayDirectory->GetNativePath(directory);
+  }
+
+    nsCAutoString startdir;
+  if (!directory.IsEmpty()) {
+    startdir = directory;
+  }
+  if (mMode == nsIFilePicker::modeSave) {
+    if( !startdir.IsEmpty())
+      {
+      startdir += "/";
+      startdir += ToNewUTF8String(mDefault);
+      }
+    else
+      startdir = ToNewUTF8String(mDefault);
+  }
+  if( startdir.IsEmpty())
+      startdir = ".";
+
+    nsCAutoString filters;
+    PRInt32 count = mFilters.Length();
+    if( count == 0 ) //just in case
+        filters = "*";
+    else
+        {
+        filters = kdeMakeFilter( 0 );
+        for (PRInt32 i = 1; i < count; ++i)
+            {
+            filters += "\n";
+            filters += kdeMakeFilter( i );
+            }
+        }
+
+    nsCStringArray command;
+    command.AppendCString( nsCAutoString( arg ));
+    command.AppendCString( startdir );
+    if( mMode != nsIFilePicker::modeGetFolder )
+        {
+        command.AppendCString( filters );
+        nsCAutoString selected;
+        selected.AppendInt( mSelectedType );
+        command.AppendCString( selected );
+        }
+    command.AppendCString( title );
+    if( mMode == nsIFilePicker::modeOpenMultiple )
+        command.AppendCString( NS_LITERAL_CSTRING( "MULTIPLE" ));
+    if( PRInt32 xid = windowToXid( mParentWidget ))
+        {
+        command.AppendCString( NS_LITERAL_CSTRING( "PARENT" ));
+        nsCAutoString parent;
+        parent.AppendInt( xid );
+        command.AppendCString( parent );
+        }
+
+    nsCStringArray output;
+    if( nsKDEUtils::commandBlockUi( command, get_gtk_window_for_nsiwidget( mParentWidget ), &output ))
+        {
+        *aReturn = nsIFilePicker::returnOK;
+        mFiles.Clear();
+        if( mMode != nsIFilePicker::modeGetFolder )
+            {
+            mSelectedType = atoi( output[ 0 ]->get());
+            output.RemoveCStringAt( 0 );
+            }
+        if (mMode == nsIFilePicker::modeOpenMultiple)
+            {
+            mFileURL.Truncate();
+            PRUint32 count = output.Count();
+            for( PRUint32 i = 0;
+                 i < count;
+                 ++i )
+                {
+                nsCOMPtr<nsILocalFile> localfile;
+                nsresult rv = NS_NewNativeLocalFile( *output[ i ],
+                                      PR_FALSE,
+                                      getter_AddRefs(localfile));
+                if (NS_SUCCEEDED(rv))
+                    mFiles.AppendObject(localfile);
+                }
+            }
+        else
+            {
+            if( output.Count() == 0 )
+                mFileURL = nsCString();
+            else if( mAllowURLs )
+                mFileURL = *output[ 0 ];
+            else // GetFile() actually requires it to be url even for local files :-/
+                {
+                mFileURL = nsCString( "file://" );
+                mFileURL.Append( *output[ 0 ] );
+                }
+            }
+  // Remember last used directory.
+  nsCOMPtr<nsILocalFile> file;
+  GetFile(getter_AddRefs(file));
+  if (file) {
+    nsCOMPtr<nsIFile> dir;
+    file->GetParent(getter_AddRefs(dir));
+    nsCOMPtr<nsILocalFile> localDir(do_QueryInterface(dir));
+    if (localDir) {
+      localDir.swap(mPrevDisplayDirectory);
+    }
+  }
+        if (mMode == nsIFilePicker::modeSave)
+            {
+            nsCOMPtr<nsILocalFile> file;
+            GetFile(getter_AddRefs(file));
+            if (file)
+                {
+                PRBool exists = PR_FALSE;
+                file->Exists(&exists);
+                if (exists) // TODO do overwrite check in the helper app
+                    *aReturn = nsIFilePicker::returnReplace;
+                }
+            }
+        }
+    else
+        {
+        *aReturn = nsIFilePicker::returnCancel;
+        }
+    return NS_OK;
+    }
+
+
+NS_IMETHODIMP nsFilePicker::kdeAppsDialog(PRInt16 *aReturn)
+    {
+    NS_ENSURE_ARG_POINTER(aReturn);
+
+    nsXPIDLCString title;
+    title.Adopt(ToNewUTF8String(mTitle));
+
+    nsCStringArray command;
+    command.AppendCString( NS_LITERAL_CSTRING( "APPSDIALOG" ));
+    command.AppendCString( title );
+    if( PRInt32 xid = windowToXid( mParentWidget ))
+        {
+        command.AppendCString( NS_LITERAL_CSTRING( "PARENT" ));
+        nsCAutoString parent;
+        parent.AppendInt( xid );
+        command.AppendCString( parent );
+        }
+
+    nsCStringArray output;
+    if( nsKDEUtils::commandBlockUi( command, get_gtk_window_for_nsiwidget( mParentWidget ), &output ))
+        {
+        *aReturn = nsIFilePicker::returnOK;
+        mFileURL = output.Count() > 0 ? *output[ 0 ] : nsCString();
+        }
+    else
+        {
+        *aReturn = nsIFilePicker::returnCancel;
+        }
+    return NS_OK;
+    }
diff --git a/widget/src/gtk2/nsFilePicker.h b/widget/src/gtk2/nsFilePicker.h
--- a/widget/src/gtk2/nsFilePicker.h
+++ b/widget/src/gtk2/nsFilePicker.h
@@ -89,11 +89,17 @@ protected:
   nsString  mDefault;
   nsString  mDefaultExtension;
 
   nsTArray<nsCString> mFilters;
   nsTArray<nsCString> mFilterNames;
 
 private:
   static nsILocalFile *mPrevDisplayDirectory;
+
+  bool kdeRunning();
+  bool getKdeRunning();
+  NS_IMETHODIMP kdeFileDialog(PRInt16 *aReturn);
+  NS_IMETHODIMP kdeAppsDialog(PRInt16 *aReturn);
+  nsCString kdeMakeFilter( int index );
 };
 
 #endif
diff --git a/xpcom/components/Makefile.in b/xpcom/components/Makefile.in
--- a/xpcom/components/Makefile.in
+++ b/xpcom/components/Makefile.in
@@ -94,10 +94,11 @@ FORCE_STATIC_LIB = 1
 
 # Force use of PIC
 FORCE_USE_PIC	= 1
 include $(topsrcdir)/config/rules.mk
 
 DEFINES	+= -D_IMPL_NS_COM
 
 ifneq (,$(filter gtk2,$(MOZ_WIDGET_TOOLKIT)))
+LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/xre
 CXXFLAGS += $(MOZ_GTK2_CFLAGS)
 endif
diff --git a/xpcom/components/ManifestParser.cpp b/xpcom/components/ManifestParser.cpp
--- a/xpcom/components/ManifestParser.cpp
+++ b/xpcom/components/ManifestParser.cpp
@@ -55,16 +55,17 @@
 #include "nsTextFormatter.h"
 #include "nsVersionComparator.h"
 #include "nsXPCOMCIDInternal.h"
 
 #include "nsIConsoleService.h"
 #include "nsIScriptError.h"
 #include "nsIXULAppInfo.h"
 #include "nsIXULRuntime.h"
+#include "nsKDEUtils.h"
 
 struct ManifestDirective
 {
   const char* directive;
   int argc;
 
   // Some directives should only be delivered for NS_COMPONENT_LOCATION
   // manifests.
@@ -422,16 +423,17 @@ ParseManifestCommon(NSLocationType aType
 
   NS_NAMED_LITERAL_STRING(kPlatform, "platform");
   NS_NAMED_LITERAL_STRING(kContentAccessible, "contentaccessible");
   NS_NAMED_LITERAL_STRING(kApplication, "application");
   NS_NAMED_LITERAL_STRING(kAppVersion, "appversion");
   NS_NAMED_LITERAL_STRING(kOs, "os");
   NS_NAMED_LITERAL_STRING(kOsVersion, "osversion");
   NS_NAMED_LITERAL_STRING(kABI, "abi");
+  NS_NAMED_LITERAL_STRING(kDesktop, "desktop");
 
   // Obsolete
   NS_NAMED_LITERAL_STRING(kXPCNativeWrappers, "xpcnativewrappers");
 
   nsAutoString appID;
   nsAutoString appVersion;
   nsAutoString osTarget;
   nsAutoString abi;
@@ -461,35 +463,39 @@ ParseManifestCommon(NSLocationType aType
         CopyUTF8toUTF16(s, abi);
         abi.Insert(PRUnichar('_'), 0);
         abi.Insert(osTarget, 0);
       }
     }
   }
 
   nsAutoString osVersion;
+  nsAutoString desktop;
 #if defined(XP_WIN)
   OSVERSIONINFO info = { sizeof(OSVERSIONINFO) };
   if (GetVersionEx(&info)) {
     nsTextFormatter::ssprintf(osVersion, NS_LITERAL_STRING("%ld.%ld").get(),
                                          info.dwMajorVersion,
                                          info.dwMinorVersion);
   }
+  desktop = NS_LITERAL_STRING("win");
 #elif defined(XP_MACOSX)
   SInt32 majorVersion, minorVersion;
   if ((Gestalt(gestaltSystemVersionMajor, &majorVersion) == noErr) &&
       (Gestalt(gestaltSystemVersionMinor, &minorVersion) == noErr)) {
     nsTextFormatter::ssprintf(osVersion, NS_LITERAL_STRING("%ld.%ld").get(),
                                          majorVersion,
                                          minorVersion);
   }
+  desktop = NS_LITERAL_STRING("macosx");
 #elif defined(MOZ_WIDGET_GTK2)
   nsTextFormatter::ssprintf(osVersion, NS_LITERAL_STRING("%ld.%ld").get(),
                                        gtk_major_version,
                                        gtk_minor_version);
+  desktop = nsKDEUtils::kdeSession() ? NS_LITERAL_STRING("kde") : NS_LITERAL_STRING("gnome");
 #endif
 
   // Because contracts must be registered after CIDs, we save and process them
   // at the end.
   nsTArray<CachedDirective> contracts;
 
   char *token;
   char *newline = buf;
@@ -558,24 +564,26 @@ ParseManifestCommon(NSLocationType aType
     bool ok = true;
     TriState stAppVersion = eUnspecified;
     TriState stApp = eUnspecified;
     TriState stOsVersion = eUnspecified;
     TriState stOs = eUnspecified;
     TriState stABI = eUnspecified;
     bool platform = false;
     bool contentAccessible = false;
+    TriState stDesktop = eUnspecified;
 
     while (NULL != (token = nsCRT::strtok(whitespace, kWhitespace, &whitespace)) && ok) {
       ToLowerCase(token);
       NS_ConvertASCIItoUTF16 wtoken(token);
 
       if (CheckStringFlag(kApplication, wtoken, appID, stApp) ||
           CheckStringFlag(kOs, wtoken, osTarget, stOs) ||
           CheckStringFlag(kABI, wtoken, abi, stABI) ||
+          CheckStringFlag(kDesktop, wtoken, desktop, stDesktop) ||
           CheckVersionFlag(kOsVersion, wtoken, osVersion, stOsVersion) ||
           CheckVersionFlag(kAppVersion, wtoken, appVersion, stAppVersion))
         continue;
 
       if (directive->contentflags &&
           (CheckFlag(kPlatform, wtoken, platform) ||
            CheckFlag(kContentAccessible, wtoken, contentAccessible)))
         continue;
@@ -594,16 +602,17 @@ ParseManifestCommon(NSLocationType aType
       ok = false;
     }
 
     if (!ok ||
         stApp == eBad ||
         stAppVersion == eBad ||
         stOs == eBad ||
         stOsVersion == eBad ||
+        stDesktop == eBad ||
         stABI == eBad)
       continue;
 
     if (directive->regfunc) {
 #ifdef MOZ_IPC
       if (GeckoProcessType_Default != XRE_GetProcessType())
         continue;
 #endif
diff --git a/xpcom/io/Makefile.in b/xpcom/io/Makefile.in
--- a/xpcom/io/Makefile.in
+++ b/xpcom/io/Makefile.in
@@ -192,17 +192,17 @@ include $(topsrcdir)/ipc/chromium/chromi
 DEFINES		+= -D_IMPL_NS_COM
 
 ifeq ($(OS_ARCH),Linux)
 ifneq (,$(findstring lib64,$(libdir)))
 DEFINES     += -DHAVE_USR_LIB64_DIR
 endif
 endif
 
-LOCAL_INCLUDES	+= -I..
+LOCAL_INCLUDES	+= -I.. -I$(topsrcdir)/toolkit/xre
 
 ifeq ($(MOZ_PLATFORM_MAEMO),5)
 CFLAGS          += $(MOZ_DBUS_CFLAGS)
 CXXFLAGS        += $(MOZ_DBUS_CFLAGS)
 endif
 
 ifdef MOZ_PLATFORM_MAEMO
 CFLAGS          += $(MOZ_PLATFORM_MAEMO_CFLAGS)
diff --git a/xpcom/io/nsLocalFileUnix.cpp b/xpcom/io/nsLocalFileUnix.cpp
--- a/xpcom/io/nsLocalFileUnix.cpp
+++ b/xpcom/io/nsLocalFileUnix.cpp
@@ -91,16 +91,17 @@
 #include "prproces.h"
 #include "nsIDirectoryEnumerator.h"
 #include "nsISimpleEnumerator.h"
 #include "nsITimelineService.h"
 
 #ifdef MOZ_WIDGET_GTK2
 #include "nsIGIOService.h"
 #include "nsIGnomeVFSService.h"
+#include "nsKDEUtils.h"
 #endif
 
 #ifdef XP_MACOSX
 #include <Carbon/Carbon.h>
 #include "CocoaFileUtils.h"
 #include "prmem.h"
 #include "plbase64.h"
 
@@ -1818,44 +1819,50 @@ nsLocalFile::Launch()
 
     return NS_OK;
 }
 #else
 NS_IMETHODIMP
 nsLocalFile::Reveal()
 {
 #ifdef MOZ_WIDGET_GTK2
-    nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
-    nsCOMPtr<nsIGnomeVFSService> gnomevfs = do_GetService(NS_GNOMEVFSSERVICE_CONTRACTID);
-    if (!giovfs && !gnomevfs)
-        return NS_ERROR_FAILURE;
-
+    nsCAutoString url;
     PRBool isDirectory;
     if (NS_FAILED(IsDirectory(&isDirectory)))
         return NS_ERROR_FAILURE;
 
     if (isDirectory) {
-        if (giovfs)
-            return giovfs->ShowURIForInput(mPath);
-        else 
-            /* Fallback to GnomeVFS */
-            return gnomevfs->ShowURIForInput(mPath);
+        url = mPath;
     } else {
         nsCOMPtr<nsIFile> parentDir;
         nsCAutoString dirPath;
         if (NS_FAILED(GetParent(getter_AddRefs(parentDir))))
             return NS_ERROR_FAILURE;
         if (NS_FAILED(parentDir->GetNativePath(dirPath)))
             return NS_ERROR_FAILURE;
 
-        if (giovfs)
-            return giovfs->ShowURIForInput(dirPath);
-        else 
-            return gnomevfs->ShowURIForInput(dirPath);        
+        url = dirPath;
     }
+
+    if(nsKDEUtils::kdeSupport()) {
+      nsCStringArray command;
+      command.AppendCString( NS_LITERAL_CSTRING( "OPEN" ));
+      command.AppendCString( url );
+      return nsKDEUtils::command( command ) ? NS_OK : NS_ERROR_FAILURE;
+    }
+
+    nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
+    nsCOMPtr<nsIGnomeVFSService> gnomevfs = do_GetService(NS_GNOMEVFSSERVICE_CONTRACTID);
+    if (!giovfs && !gnomevfs)
+      return NS_ERROR_FAILURE;
+
+    if (giovfs)
+      return giovfs->ShowURIForInput(url);
+    else
+      return gnomevfs->ShowURIForInput(url);
 #elif defined(XP_MACOSX)
     CFURLRef url;
     if (NS_SUCCEEDED(GetCFURL(&url))) {
       nsresult rv = CocoaFileUtils::RevealFileInFinder(url);
       ::CFRelease(url);
       return rv;
     }
     return NS_ERROR_FAILURE;
@@ -1881,16 +1888,23 @@ nsLocalFile::Launch()
 
     if (nsnull == connection)
       return NS_ERROR_FAILURE;
 
     if (hildon_mime_open_file(connection, mPath.get()) != kHILDON_SUCCESS)
       return NS_ERROR_FAILURE;
     return NS_OK;
 #else
+    if( nsKDEUtils::kdeSupport()) {
+      nsCStringArray command;
+      command.AppendCString( NS_LITERAL_CSTRING( "OPEN" ));
+      command.AppendCString( mPath );
+      return nsKDEUtils::command( command ) ? NS_OK : NS_ERROR_FAILURE;
+    }
+
     nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
     nsCOMPtr<nsIGnomeVFSService> gnomevfs = do_GetService(NS_GNOMEVFSSERVICE_CONTRACTID);
     if (giovfs) {
       return giovfs->ShowURIForInput(mPath);
     } else if (gnomevfs) {
       /* GnomeVFS fallback */
       return gnomevfs->ShowURIForInput(mPath);
     }
