Bug 385065 - protocol handling dialog. toolkit: r=mano; uriloader: r=cbiesinger, sr=dmose general: ui-r=beltzner
authorsdwilsh@shawnwilsher.com
Wed, 25 Jul 2007 21:24:25 -0700
changeset 4013 5dc87a373560b1ba9ca90565e0a40c2afec29bc1
parent 4012 648152e8058e91e30e7f4f5f419fa40a61d29aec
child 4014 59a540e220140633fad8eca5f3ff326c3890c7f7
push idunknown
push userunknown
push dateunknown
reviewersmano, uriloader, cbiesinger, dmose, beltzner
bugs385065
milestone1.9a7pre
Bug 385065 - protocol handling dialog. toolkit: r=mano; uriloader: r=cbiesinger, sr=dmose general: ui-r=beltzner
browser/installer/unix/packages-static
browser/installer/windows/packages-static
toolkit/mozapps/Makefile.in
toolkit/mozapps/handling/Makefile.in
toolkit/mozapps/handling/content/dialog.js
toolkit/mozapps/handling/content/dialog.xul
toolkit/mozapps/handling/content/handler.css
toolkit/mozapps/handling/content/handler.xml
toolkit/mozapps/handling/content/handling.dtd
toolkit/mozapps/handling/content/handling.properties
toolkit/mozapps/handling/src/Makefile.in
toolkit/mozapps/handling/src/nsContentDispatchChooser.js
toolkit/mozapps/jar.mn
toolkit/themes/pinstripe/mozapps/handling/handling.css
toolkit/themes/pinstripe/mozapps/jar.mn
toolkit/themes/winstripe/mozapps/handling/handling.css
toolkit/themes/winstripe/mozapps/jar.mn
uriloader/exthandler/Makefile.in
uriloader/exthandler/beos/nsMIMEInfoBeOS.cpp
uriloader/exthandler/beos/nsMIMEInfoBeOS.h
uriloader/exthandler/beos/nsOSHelperAppService.cpp
uriloader/exthandler/mac/nsMIMEInfoMac.cpp
uriloader/exthandler/mac/nsMIMEInfoMac.h
uriloader/exthandler/mac/nsOSHelperAppService.cpp
uriloader/exthandler/nsExternalHelperAppService.cpp
uriloader/exthandler/nsExternalHelperAppService.h
uriloader/exthandler/nsExternalProtocolHandler.cpp
uriloader/exthandler/nsIContentDispatchChooser.idl
uriloader/exthandler/nsIExternalProtocolService.idl
uriloader/exthandler/nsMIMEInfoImpl.cpp
uriloader/exthandler/nsMIMEInfoImpl.h
uriloader/exthandler/os2/nsMIMEInfoOS2.cpp
uriloader/exthandler/os2/nsMIMEInfoOS2.h
uriloader/exthandler/os2/nsOSHelperAppService.cpp
uriloader/exthandler/os2/nsOSHelperAppService.h
uriloader/exthandler/unix/nsMIMEInfoUnix.cpp
uriloader/exthandler/unix/nsMIMEInfoUnix.h
uriloader/exthandler/unix/nsOSHelperAppService.cpp
uriloader/exthandler/win/nsMIMEInfoWin.cpp
uriloader/exthandler/win/nsMIMEInfoWin.h
uriloader/exthandler/win/nsOSHelperAppService.cpp
--- a/browser/installer/unix/packages-static
+++ b/browser/installer/unix/packages-static
@@ -229,16 +229,17 @@ bin/components/nsURLFormatter.js
 bin/components/urlformatter.xpt
 bin/components/libbrowserdirprovider.so
 bin/components/libbrowsercomps.so
 bin/components/txEXSLTRegExFunctions.js
 bin/components/nsLivemarkService.js
 bin/components/nsTaggingService.js
 bin/components/nsDefaultCLH.js
 bin/components/nsContentPrefService.js
+bin/components/nsContentDispatchChooser.js
 
 ; Modules
 bin/modules/*
 
 ; Safe Browsing
 bin/components/nsSafebrowsingApplication.js
 bin/components/safebrowsing.xpt
 bin/components/nsUrlClassifierListManager.js
--- a/browser/installer/windows/packages-static
+++ b/browser/installer/windows/packages-static
@@ -229,16 +229,17 @@ bin\components\nsURLFormatter.js
 bin\components\urlformatter.xpt
 bin\components\browserdirprovider.dll
 bin\components\brwsrcmp.dll
 bin\components\txEXSLTRegExFunctions.js
 bin\components\nsLivemarkService.js
 bin\components\nsTaggingService.js
 bin\components\nsDefaultCLH.js
 bin\components\nsContentPrefService.js
+bin\components\nsContentDispatchChooser.js
 
 ; Modules
 bin\modules\*
 
 ; Safe Browsing
 bin\components\nsSafebrowsingApplication.js
 bin\components\safebrowsing.xpt
 bin\components\nsUrlClassifierListManager.js
--- a/toolkit/mozapps/Makefile.in
+++ b/toolkit/mozapps/Makefile.in
@@ -15,16 +15,17 @@
 #
 # The Initial Developer of the Original Code is
 # Netscape Communications Corporation.
 # Portions created by the Initial Developer are Copyright (C) 2002
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #  Ben Goodger <ben@bengoodger.com>
+#  Shawn Wilsher <me@shawnwilsher.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
@@ -37,11 +38,11 @@
 
 DEPTH   = ../..
 topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH   = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-DIRS = downloads extensions update xpinstall plugins
+DIRS = downloads extensions update xpinstall plugins handling
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/handling/Makefile.in
@@ -0,0 +1,48 @@
+# ***** 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 protocol handling code.
+#
+# 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):
+#   Shawn Wilsher <me@shawnwilsher.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 *****
+
+DEPTH   = ../../..
+topsrcdir = @top_srcdir@
+srcdir    = @srcdir@
+VPATH   = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+DIRS = src
+
+include $(topsrcdir)/config/rules.mk
+
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/handling/content/dialog.js
@@ -0,0 +1,230 @@
+/* ***** 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 Protocol Handler Dialog.
+ *
+ * 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):
+ *   Shawn Wilsher <me@shawnwilsher.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 ***** */
+
+/**
+ * This dialog builds its content based on arguments passed into it.
+ * window.arguments[0]:
+ *   The title of the dialog.
+ * window.arguments[1]:
+ *   The url of the image that appears to the left of the description text
+ * window.arguments[2]:
+ *   The text of the description that will appear above the choices the user
+ *   can choose from.
+ * window.arguments[3]:
+ *   The text of the label directly above the choices the user can choose from.
+ * window.arguments[4]:
+ *   This is the text to be placed in the label for the checkbox.  If no text is
+ *   passed (ie, it's an empty string), the checkbox will be hidden.
+ * window.arguments[5]:
+ *   This is the text that is displayed below the checkbox when it is checked.
+ * window.arguments[6]:
+ *   This is the nsIHandlerInfo that gives us all our precious information.
+ * window.arguments[7]:
+ *   This is the nsIURI that we are being brought up for in the first place.
+ */
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+
+var dialog = {
+  //////////////////////////////////////////////////////////////////////////////
+  //// Member Variables
+
+  _handlerInfo: null,
+  _URI: null,
+  _itemChoose: null,
+  _okButton: null,
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// Methods
+
+ /**
+  * This function initializes the content of the dialog.
+  */
+  initialize: function initialize()
+  {
+    this._handlerInfo = window.arguments[6].QueryInterface(Ci.nsIHandlerInfo);
+    this._URI         = window.arguments[7].QueryInterface(Ci.nsIURI);
+    this._itemChoose  = document.getElementById("item-choose");
+    this._okButton    = document.documentElement.getButton("accept");
+
+    this.updateOKButton();
+
+    let description = {
+      image: document.getElementById("description-image"),
+      text:  document.getElementById("description-text")
+    };
+    let options = document.getElementById("item-action-text");
+    let checkbox = {
+      desc: document.getElementById("remember"),
+      text:  document.getElementById("remember-text")
+    };
+
+    // Setting values
+    document.title               = window.arguments[0];
+    description.image.src        = window.arguments[1];
+    description.text.textContent = window.arguments[2];
+    options.value                = window.arguments[3];
+    checkbox.desc.label          = window.arguments[4];
+    checkbox.text.textContent    = window.arguments[5];
+
+    // Hide stuff that needs to be hidden
+    if (!checkbox.desc.label)
+      checkbox.desc.hidden = true;
+
+    // UI is ready, lets populate our list
+    this.populateList();
+  },
+
+ /**
+  * Populates the list that a user can choose from.
+  */
+  populateList: function populateList()
+  {
+    // TODO: make this work with more than one (depends on Bug 385740)
+    let app = this._handlerInfo.preferredApplicationHandler;
+
+    if (app) {
+      let elm = document.createElement("richlistitem");
+      elm.setAttribute("type", "handler");
+      elm.setAttribute("name", app.name);
+      elm.object = app;
+
+      document.getElementById("items").insertBefore(elm, this._itemChoose);
+    }
+
+    if (this._handlerInfo.hasDefaultHandler) {
+      let elm = document.createElement("richlistitem");
+      elm.setAttribute("type", "handler");
+      elm.id = "os-default-handler";
+      elm.setAttribute("name", this._handlerInfo.defaultDescription);
+    
+      document.getElementById("items").insertBefore(elm, this._itemChoose);
+    }
+  },
+  
+ /**
+  * Brings up a filepicker and allows a user to choose an application.
+  */
+  chooseApplication: function chooseApplication()
+  {
+    let bundle = document.getElementById("base-strings");
+    let title = bundle.getString("choose.application.title");
+
+    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
+    fp.init(window, title, Ci.nsIFilePicker.modeOpen);
+    fp.appendFilters(Ci.nsIFilePicker.filterApps);
+
+    if (fp.show() == Ci.nsIFilePicker.returnOK && fp.file) {
+      let uri = Cc["@mozilla.org/network/util;1"].
+                getService(Ci.nsIIOService).
+                newFileURI(fp.file);
+      let elm = document.createElement("richlistitem");
+      elm.setAttribute("type", "handler");
+      elm.setAttribute("name", fp.file.leafName);
+      elm.setAttribute("image", "moz-icon://" + uri.spec + "?size=32");
+
+      let handlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"].
+                       createInstance(Ci.nsILocalHandlerApp);
+      handlerApp.executable = fp.file;
+      elm.obj = handlerApp;
+
+      let parent = document.getElementById("items");
+      parent.selectedItem = parent.insertBefore(elm, parent.firstChild);
+      parent.ensureSelectedElementIsVisible();
+    }
+  },
+
+ /**
+  * Function called when the OK button is pressed.
+  */
+  onAccept: function onAccept()
+  {
+    let checkbox = document.getElementById("remember");
+    if (!checkbox.hidden) {
+      // We need to make sure that the default is properly set now
+      if (this.selectedItem.obj) {
+        // default OS handler doesn't have this property
+        this._handlerInfo.preferredAction = Ci.nsIHandlerInfo.useHelperApp;
+        this._handlerInfo.preferredApplicationHandler = this.selectedItem.obj;
+      }
+      else
+        this._handlerInfo.preferredAction = Ci.nsIHandlerInfo.useSystemDefault;
+    }
+    this._handlerInfo.alwaysAskBeforeHandling = !checkbox.checked;
+
+    let hs = Cc["@mozilla.org/uriloader/handler-service;1"].
+             getService(Ci.nsIHandlerService);
+    hs.store(this._handlerInfo);
+
+    this._handlerInfo.launchWithURI(this._URI);
+
+    return true;
+  },
+
+ /**
+  * Determines if the OK button should be disabled or not
+  */
+  updateOKButton: function updateOKButton()
+  {
+    this._okButton.disabled = this._itemChoose.selected;
+  },
+
+ /**
+  * Updates the UI based on the checkbox being checked or not.
+  */
+  onCheck: function onCheck()
+  {
+    document.getElementById("remember-text").hidden =
+      !document.getElementById("remember").checked;
+
+    window.sizeToContent();
+  },
+
+  /////////////////////////////////////////////////////////////////////////////
+  //// Getters / Setters
+
+ /**
+  * Returns the selected element in the richlistbox
+  */
+  get selectedItem()
+  {
+    return document.getElementById("items").selectedItem;
+  }
+};
+
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/handling/content/dialog.xul
@@ -0,0 +1,78 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin/global.css"?>
+<?xml-stylesheet href="chrome://mozapps/content/handling/handler.css"?>
+<?xml-stylesheet href="chrome://mozapps/skin/handling/handling.css"?>
+<!-- ***** 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 Protocol Handler Dialog.
+   -
+   - 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):
+   -   Shawn Wilsher <me@shawnwilsher.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 LGPL or the GPL. 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 ***** -->
+
+<!-- TODO this needs to move to locale/ once we are certain of the strings -->
+<!DOCTYPE dialog SYSTEM "chrome://mozapps/content/handling/handling.dtd">
+
+<dialog id="handling"
+        ondialogaccept="return dialog.onAccept();"
+        onload="dialog.initialize();"
+        style="width: 320px;"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+  <script src="chrome://mozapps/content/handling/dialog.js" type="application/javascript"/>
+
+  <stringbundleset id="strings">
+    <stringbundle id="base-strings"
+                  src="chrome://mozapps/content/handling/handling.properties"/>
+  </stringbundleset>
+
+  <hbox>
+    <image id="description-image"/>
+    <description id="description-text"/>
+  </hbox>
+
+  <vbox flex="1">
+    <description id="item-action-text"/>
+    <richlistbox id="items" flex="1"
+                 onselect="dialog.updateOKButton();">
+      <richlistitem id="item-choose" orient="horizontal" selected="true">
+        <label value="&ChooseApp.description;" flex="1"/>
+        <button oncommand="dialog.chooseApplication();"
+                label="&ChooseApp.label;" accesskey="&ChooseApp.accessKey;"/>
+      </richlistitem>
+    </richlistbox>
+  </vbox>
+
+  <checkbox id="remember" oncommand="dialog.onCheck();"/>
+  <description id="remember-text" hidden="true"/>
+
+</dialog>
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/handling/content/handler.css
@@ -0,0 +1,3 @@
+richlistitem[type="handler"] {
+  -moz-binding: url('chrome://mozapps/content/handling/handler.xml#handler');
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/handling/content/handler.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0"?>
+<!-- ***** 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 Protocol Handler Dialog.
+   -
+   - 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):
+   -   Shawn Wilsher <me@shawnwilsher.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 LGPL or the GPL. 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 ***** -->
+
+<bindings id="hanlder-bindings"
+          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="handler"
+           extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
+
+    <content>
+      <xul:vbox pack="center">
+        <xul:image xbl:inherits="src=image" height="32" width="32"/>
+      </xul:vbox>
+      <xul:vbox flex="1">
+        <xul:label xbl:inherits="value=name"/>
+        <xul:label xbl:inherits="value=description"/>
+      </xul:vbox>
+    </content>
+  </binding>
+
+</bindings>
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/handling/content/handling.dtd
@@ -0,0 +1,3 @@
+<!ENTITY ChooseApp.description "Choose an Application">
+<!ENTITY ChooseApp.label "Choose...">
+<!ENTITY ChooseApp.accessKey "C">
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/handling/content/handling.properties
@@ -0,0 +1,7 @@
+protocol.title=Launch Application
+protocol.description=This link needs to be opened with an application.
+protocol.choices.label=Send to:
+protocol.checkbox.label=Remember my choice for %S links.
+protocol.checkbox.extra=This can be changed in %S's preferences. 
+
+choose.application.title=Another Application...
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/handling/src/Makefile.in
@@ -0,0 +1,51 @@
+# ***** 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 protocol handling code.
+#
+# 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):
+#   Shawn Wilsher <me@shawnwilsher.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 *****
+
+DEPTH   = ../../../..
+topsrcdir = @top_srcdir@
+srcdir    = @srcdir@
+VPATH   = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = contentDispatchChooser
+
+EXTRA_COMPONENTS = nsContentDispatchChooser.js
+GARBAGE += nsContentDispatchChooser.js
+
+include $(topsrcdir)/config/rules.mk
+
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/handling/src/nsContentDispatchChooser.js
@@ -0,0 +1,127 @@
+/* ***** 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 nsContentDispatchChooser.
+ *
+ * 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):
+ *   Shawn Wilsher <me@shawnwilsher.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 ***** */
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+////////////////////////////////////////////////////////////////////////////////
+//// Constants
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+
+const CONTENT_HANDLING_URL = "chrome://mozapps/content/handling/dialog.xul";
+// TODO this needs to move to locale, but l10n folks want to wait until we
+//      finalize these for sure before doing so
+const STRINGBUNDLE_URL = "chrome://mozapps/content/handling/handling.properties";
+
+////////////////////////////////////////////////////////////////////////////////
+//// nsContentDispatchChooser class
+
+function nsContentDispatchChooser()
+{
+}
+
+nsContentDispatchChooser.prototype =
+{
+  classDescription: "Used to handle different types of content",
+  classID: Components.ID("e35d5067-95bc-4029-8432-e8f1e431148d"),
+  contractID: "@mozilla.org/content-dispatch-chooser;1",
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// nsIContentDispatchChooser
+
+  ask: function ask(aHandler, aWindowContext, aURI, aReason)
+  {
+    let window = null;
+    try {
+      if (aWindowContext)
+        window = aWindowContext.getInterface(Ci.nsIDOMWindow);
+    } catch (e) { /* it's OK to not have a window */ }
+
+    let sbs = Cc["@mozilla.org/intl/stringbundle;1"].
+              getService(Ci.nsIStringBundleService);
+    let bundle = sbs.createBundle(STRINGBUNDLE_URL);
+
+    let xai = Cc["@mozilla.org/xre/app-info;1"].
+              getService(Ci.nsIXULAppInfo);
+    // TODO when this is hooked up for content, we will need different strings
+    //      for most of these
+    let arr = [bundle.GetStringFromName("protocol.title"),
+               "",
+               bundle.GetStringFromName("protocol.description"),
+               bundle.GetStringFromName("protocol.choices.label"),
+               bundle.formatStringFromName("protocol.checkbox.label",
+                                           [aURI.scheme], 1),
+               bundle.formatStringFromName("protocol.checkbox.extra",
+                                           [xai.name], 1)];
+
+    let params = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
+    for each (let text in arr) {
+      let string = Cc["@mozilla.org/supports-string;1"].
+                   createInstance(Ci.nsISupportsString);
+      string.data = text;
+      params.appendElement(string, false);
+    }
+    params.appendElement(aHandler, false);
+    params.appendElement(aURI, false);
+
+    let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
+             getService(Ci.nsIWindowWatcher);
+    ww.openWindow(window,
+                  CONTENT_HANDLING_URL,
+                  null,
+                  "chrome,dialog=yes,resizable,centerscreen",
+                  params);
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// nsISupports
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentDispatchChooser])
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//// Module
+
+let components = [nsContentDispatchChooser];
+
+function NSGetModule(compMgr, fileSpec)
+{
+  return XPCOMUtils.generateModule(components);
+}
+
--- a/toolkit/mozapps/jar.mn
+++ b/toolkit/mozapps/jar.mn
@@ -23,8 +23,14 @@ toolkit.jar:
 * content/mozapps/shared/richview.xml                           (shared/content/richview.xml)
   content/mozapps/plugins/pluginInstallerWizard.xul             (plugins/content/pluginInstallerWizard.xul)
   content/mozapps/plugins/pluginInstallerWizard.js              (plugins/content/pluginInstallerWizard.js)
   content/mozapps/plugins/pluginInstallerWizard.css             (plugins/content/pluginInstallerWizard.css)
   content/mozapps/plugins/pluginInstallerDatasource.js          (plugins/content/pluginInstallerDatasource.js)
   content/mozapps/plugins/pluginInstallerService.js             (plugins/content/pluginInstallerService.js)
   content/mozapps/plugins/missingPlugin.xml                     (plugins/content/missingPlugin.xml)
   content/mozapps/plugins/missingPluginBinding.css              (plugins/content/missingPluginBinding.css)
+  content/mozapps/handling/handler.css                          (handling/content/handler.css)
+  content/mozapps/handling/handler.xml                          (handling/content/handler.xml)
+  content/mozapps/handling/handling.properties                  (handling/content/handling.properties)
+  content/mozapps/handling/handling.dtd                         (handling/content/handling.dtd)
+  content/mozapps/handling/dialog.xul                           (handling/content/dialog.xul)
+  content/mozapps/handling/dialog.js                            (handling/content/dialog.js)
new file mode 100644
--- /dev/null
+++ b/toolkit/themes/pinstripe/mozapps/handling/handling.css
@@ -0,0 +1,13 @@
+#description-image:not([src]) {
+  height: 32px;
+  width: 32px;
+}
+
+richlistitem {
+  min-height: 36px; /* Don't forget to update the richlistbox height! */
+}
+
+richlistbox {
+  /* 3 items high, plus 4px for top and bottom margins, less 2px for border */
+  min-height: 110px; 
+}
--- a/toolkit/themes/pinstripe/mozapps/jar.mn
+++ b/toolkit/themes/pinstripe/mozapps/jar.mn
@@ -1,16 +1,20 @@
 classic.jar:
 % skin mozapps classic/1.0 %skin/classic/mozapps/
 #ifndef MINIMO
-  skin/classic/mozapps/downloads/progress-bar-paused.gif          (downloads/progress-bar-paused.gif)
-  skin/classic/mozapps/downloads/progress-bar.gif                 (downloads/progress-bar.gif)
-  skin/classic/mozapps/downloads/progress-remainder.gif           (downloads/progress-remainder.gif)
-  skin/classic/mozapps/downloads/background-gradient.gif          (downloads/background-gradient.gif)
-  skin/classic/mozapps/downloads/background-stripe.gif            (downloads/background-stripe.gif)
+  skin/classic/mozapps/downloads/pause.png                        (downloads/pause.png)
+  skin/classic/mozapps/downloads/remove.png                       (downloads/remove.png)
+  skin/classic/mozapps/downloads/reload.png                       (downloads/reload.png)
+  skin/classic/mozapps/downloads/cancel.png                       (downloads/cancel.png)
+  skin/classic/mozapps/downloads/open.png                         (downloads/open.png)
+  skin/classic/mozapps/downloads/start.png                        (downloads/start.png)
+  skin/classic/mozapps/downloads/info.png                         (downloads/info.png)
+  skin/classic/mozapps/downloads/link.png                         (downloads/link.png)
+  skin/classic/mozapps/downloads/location.png                     (downloads/location.png)
   skin/classic/mozapps/downloads/download-inprogress.png          (downloads/download-inprogress.png)
   skin/classic/mozapps/downloads/downloadIcon.png                 (downloads/downloadIcon.png)
   skin/classic/mozapps/downloads/downloads.css                    (downloads/downloads.css)
   skin/classic/mozapps/downloads/unknownContentType.css           (downloads/unknownContentType.css)
   skin/classic/mozapps/downloads/downloads.xml                    (downloads/downloads.xml)
   skin/classic/mozapps/extensions/extensionItem.png               (extensions/extensionItem.png)
   skin/classic/mozapps/extensions/itemDisabledFader.png           (extensions/itemDisabledFader.png)
   skin/classic/mozapps/extensions/itemEnabledFader.png            (extensions/itemEnabledFader.png)
@@ -33,9 +37,10 @@ classic.jar:
   skin/classic/mozapps/update/updates.css                         (update/updates.css)
   skin/classic/mozapps/update/update.png                          (update/update.png)
   skin/classic/mozapps/xpinstall/xpinstallItemGeneric.png         (xpinstall/xpinstallItemGeneric.png)
   skin/classic/mozapps/xpinstall/xpinstallConfirm.css             (xpinstall/xpinstallConfirm.css)
 #ifdef MOZ_PLACES
   skin/classic/mozapps/places/defaultFavicon.png                  (places/defaultFavicon.png)
   skin/classic/mozapps/places/tagContainerIcon.png                (places/tagContainerIcon.png)
 #endif
+  skin/classic/mozapps/handling/handling.css                      (handling/handling.css)
 #endif
new file mode 100644
--- /dev/null
+++ b/toolkit/themes/winstripe/mozapps/handling/handling.css
@@ -0,0 +1,13 @@
+#description-image:not([src]) {
+  height: 32px;
+  width: 32px;
+}
+
+richlistitem {
+  min-height: 36px; /* Don't forget to update the richlistbox height! */
+}
+
+richlistbox {
+  /* 3 items high, plus 4px for top and bottom margins, less 2px for border */
+  min-height: 110px; 
+}
--- a/toolkit/themes/winstripe/mozapps/jar.mn
+++ b/toolkit/themes/winstripe/mozapps/jar.mn
@@ -34,9 +34,10 @@ classic.jar:
         skin/classic/mozapps/update/warning.gif                       (update/warning.gif)
         skin/classic/mozapps/update/updates.css                       (update/updates.css)
         skin/classic/mozapps/xpinstall/xpinstallConfirm.css           (xpinstall/xpinstallConfirm.css)
         skin/classic/mozapps/xpinstall/xpinstallItemGeneric.png       (xpinstall/xpinstallItemGeneric.png)
 #ifdef MOZ_PLACES
         skin/classic/mozapps/places/defaultFavicon.png                (places/defaultFavicon.png)
         skin/classic/mozapps/places/tagContainerIcon.png                (places/tagContainerIcon.png)
 #endif
+        skin/classic/mozapps/handling/handling.css                    (handling/handling.css)
 #endif
--- a/uriloader/exthandler/Makefile.in
+++ b/uriloader/exthandler/Makefile.in
@@ -106,16 +106,17 @@ OSHELPER		+= nsInternetConfig.cpp \
 			nsMIMEInfoMac.cpp \
 			$(NULL)
 endif
 
 LOCAL_INCLUDES = -I$(srcdir)
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),gtk2)
 OSHELPER	+= nsGNOMERegistry.cpp
+OSHELPER  += nsMIMEInfoUnix.cpp
 endif
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),beos)
 OSHELPER  += nsMIMEInfoBeOS.cpp
 endif
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
 OSHELPER  += nsMIMEInfoWin.cpp
@@ -130,16 +131,17 @@ EXPORTS = \
 		$(OSDIR)/nsOSHelperAppService.h \
 			$(NULL)
 
 XPIDLSRCS = \
 	nsCExternalHandlerService.idl	\
 	nsIExternalProtocolService.idl \
 	nsIExternalHelperAppService.idl	\
 	nsIHelperAppLauncherDialog.idl \
+	nsIContentDispatchChooser.idl \
 	nsIHandlerService.idl	\
 	$(NULL)
 
 ifneq (,$(filter mac cocoa,$(MOZ_WIDGET_TOOLKIT)))
 XPIDLSRCS		+= nsIInternetConfigService.idl
 endif
 
 CPPSRCS	= \
--- a/uriloader/exthandler/beos/nsMIMEInfoBeOS.cpp
+++ b/uriloader/exthandler/beos/nsMIMEInfoBeOS.cpp
@@ -53,8 +53,41 @@ nsMIMEInfoBeOS::LaunchDefaultWithFile(ns
 
   PRBool executable = PR_TRUE;
   local->IsExecutable(&executable);
   if (executable)
     return NS_ERROR_FAILURE;
 
   return local->Launch();
 }
+
+nsresult
+nsMIMEInfoBeOS::LoadUriInternal(nsIURI * aURL)
+{
+	nsresult rv = NS_OK;
+
+	if (aURL) {
+		// Get the Protocol
+		nsCAutoString scheme;
+		aURL->GetScheme(scheme);
+		BString protoStr(scheme.get());
+		protoStr.Prepend("application/x-vnd.Be.URL.");
+		// Get the Spec
+		nsCAutoString spec;
+		aURL->GetSpec(spec);
+		const char* args[] = { spec.get() };
+		
+		//Launch the app		
+		BMimeType protocol;
+		bool isInstalled = false;
+		if (protocol.SetTo(protoStr.String()) == B_OK)
+		{
+			if(protocol.IsInstalled())
+			{
+				isInstalled = true;	
+				be_roster->Launch(protoStr.String(), NS_ARRAY_LENGTH(args), (char **)args);
+			}
+		}
+		if ((!isInstalled) && (!strcmp("mailto", scheme.get())))
+			be_roster->Launch("text/x-email", NS_ARRAY_LENGTH(args), (char **)args);
+	}
+	return rv;
+}
--- a/uriloader/exthandler/beos/nsMIMEInfoBeOS.h
+++ b/uriloader/exthandler/beos/nsMIMEInfoBeOS.h
@@ -44,11 +44,12 @@ class nsMIMEInfoBeOS : public nsMIMEInfo
     nsMIMEInfoBeOS(const char* aType = "") : nsMIMEInfoImpl(aType) {}
     nsMIMEInfoBeOS(const nsACString& aMIMEType) : nsMIMEInfoImpl(aMIMEType) {}
     nsMIMEInfoBeOS(const nsACString& aType, HandlerClass aClass) :
       nsMIMEInfoImpl(aType, aClass) {}
     virtual ~nsMIMEInfoBeOS();
 
   protected:
     virtual NS_HIDDEN_(nsresult) LaunchDefaultWithFile(nsIFile* aFile);
+    virtual NS_HIDDEN_(nsresult) LoadUriInternal(nsIURI *aURI);
 };
 
 #endif
--- a/uriloader/exthandler/beos/nsOSHelperAppService.cpp
+++ b/uriloader/exthandler/beos/nsOSHelperAppService.cpp
@@ -87,49 +87,16 @@ nsresult nsOSHelperAppService::OSProtoco
 					*aHandlerExists = PR_TRUE;
 			}
 		}
 	}
 
 	return NS_OK;
 }
 
-nsresult nsOSHelperAppService::LoadUriInternal(nsIURI * aURL)
-{
-	LOG(("-- nsOSHelperAppService::LoadUrl\n"));
-	nsresult rv = NS_OK;
-
-	if (aURL) {
-		// Get the Protocol
-		nsCAutoString scheme;
-		aURL->GetScheme(scheme);
-		BString protoStr(scheme.get());
-		protoStr.Prepend("application/x-vnd.Be.URL.");
-		// Get the Spec
-		nsCAutoString spec;
-		aURL->GetSpec(spec);
-		const char* args[] = { spec.get() };
-		
-		//Launch the app		
-		BMimeType protocol;
-		bool isInstalled = false;
-		if (protocol.SetTo(protoStr.String()) == B_OK)
-		{
-			if(protocol.IsInstalled())
-			{
-				isInstalled = true;	
-				be_roster->Launch(protoStr.String(), NS_ARRAY_LENGTH(args), (char **)args);
-			}
-		}
-		if ((!isInstalled) && (!strcmp("mailto", scheme.get())))
-			be_roster->Launch("text/x-email", NS_ARRAY_LENGTH(args), (char **)args);
-	}
-	return rv;
-}
-
 
 nsresult nsOSHelperAppService::SetMIMEInfoForType(const char *aMIMEType, nsMIMEInfoBeOS**_retval) {
 
 	LOG(("-- nsOSHelperAppService::SetMIMEInfoForType: %s\n",aMIMEType));
 
 	nsresult rv = NS_ERROR_FAILURE;
 
 	nsMIMEInfoBeOS* mimeInfo = new nsMIMEInfoBeOS(aMIMEType);
--- a/uriloader/exthandler/mac/nsMIMEInfoMac.cpp
+++ b/uriloader/exthandler/mac/nsMIMEInfoMac.cpp
@@ -39,16 +39,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include <LaunchServices.h>
 
 #include "nsMIMEInfoMac.h"
 #include "nsILocalFileMac.h"
 #include "nsIFileURL.h"
+#include "nsIInternetConfigService.h"
 
 NS_IMETHODIMP
 nsMIMEInfoMac::LaunchWithURI(nsIURI* aURI)
 {
   nsCOMPtr<nsIFile> application;
   nsresult rv;
   
   if (mPreferredAction == useHelperApp) {
@@ -68,20 +69,23 @@ nsMIMEInfoMac::LaunchWithURI(nsIURI* aUR
     rv = localHandlerApp->GetExecutable(getter_AddRefs(application));
     NS_ENSURE_SUCCESS(rv, rv);
     
   } else if (mPreferredAction == useSystemDefault)
     application = mDefaultApplication;
   else
     return NS_ERROR_INVALID_ARG;
 
+  if (mClass == eProtocolInfo)
+    return LoadUriInternal(aURI);
+
   // get the nsILocalFile version of the doc to launch with
   nsCOMPtr<nsILocalFile> docToLoad;
   rv = GetLocalFileFromURI(aURI, getter_AddRefs(docToLoad));
-  if (NS_FAILED(rv)) return rv;
+  NS_ENSURE_SUCCESS(rv, rv);
 
   // if we've already got an app, just QI so we have the launchWithDoc method
   nsCOMPtr<nsILocalFileMac> app;
   if (application) {
     app = do_QueryInterface(application, &rv);
     if (NS_FAILED(rv)) return rv;
   } else {
     // otherwise ask LaunchServices for an app directly
@@ -99,8 +103,34 @@ nsMIMEInfoMac::LaunchWithURI(nsIURI* aUR
       app->InitWithFSRef(&appFSRef);
     } else {
       return NS_ERROR_FAILURE;
     }
   }
   
   return app->LaunchWithDoc(docToLoad, PR_FALSE); 
 }
+
+nsresult 
+nsMIMEInfoMac::LoadUriInternal(nsIURI *aURI)
+{
+  NS_ENSURE_ARG_POINTER(aURI);
+  nsresult rv = NS_ERROR_FAILURE;
+  
+  nsCAutoString uri;
+  aURI->GetSpec(uri);
+  if (!uri.IsEmpty()) {
+    nsCOMPtr<nsIInternetConfigService> icService = 
+      do_GetService(NS_INTERNETCONFIGSERVICE_CONTRACTID);
+    if (icService)
+      rv = icService->LaunchURL(uri.get());
+  }
+  return rv;
+}
+
+NS_IMETHODIMP
+nsMIMEInfoMac::GetHasDefaultHandler(PRBool *_retval)
+{
+  // We have a default application if we have a description
+  *_retval = !mDefaultAppDescription.IsEmpty();
+  return NS_OK;
+}
+
--- a/uriloader/exthandler/mac/nsMIMEInfoMac.h
+++ b/uriloader/exthandler/mac/nsMIMEInfoMac.h
@@ -42,19 +42,20 @@
 class nsMIMEInfoMac : public nsMIMEInfoImpl {
   public:
     nsMIMEInfoMac(const char* aMIMEType = "") : nsMIMEInfoImpl(aMIMEType) {}
     nsMIMEInfoMac(const nsACString& aMIMEType) : nsMIMEInfoImpl(aMIMEType) {}
     nsMIMEInfoMac(const nsACString& aType, HandlerClass aClass) :
       nsMIMEInfoImpl(aType, aClass) {}
 
     NS_IMETHOD LaunchWithURI(nsIURI* aURI);
-
+    NS_IMETHOD GetHasDefaultHandler(PRBool *_retval);
+  protected:
+    virtual NS_HIDDEN_(nsresult) LoadUriInternal(nsIURI *aURI);
 #ifdef DEBUG
-  protected:
     virtual NS_HIDDEN_(nsresult) LaunchDefaultWithFile(nsIFile* aFile) {
       NS_NOTREACHED("do not call this method, use LaunchWithFile");
       return NS_ERROR_UNEXPECTED;
     }
 #endif
 };
 
 
--- a/uriloader/exthandler/mac/nsOSHelperAppService.cpp
+++ b/uriloader/exthandler/mac/nsOSHelperAppService.cpp
@@ -164,34 +164,16 @@ NS_IMETHODIMP nsOSHelperAppService::GetA
     }
 
     ::CFRelease(schemeCFString);
   }
 
   return rv;
 }
 
-nsresult nsOSHelperAppService::LoadUriInternal(nsIURI * aURL)
-{
-  nsCAutoString url;
-  nsresult rv = NS_ERROR_FAILURE;
-  
-  if (aURL)
-  {
-    aURL->GetSpec(url);
-    if (!url.IsEmpty())
-    {
-      nsCOMPtr<nsIInternetConfigService> icService (do_GetService(NS_INTERNETCONFIGSERVICE_CONTRACTID));
-      if (icService)
-        rv = icService->LaunchURL(url.get());
-    }
-  }
-  return rv;
-}
-
 nsresult nsOSHelperAppService::GetFileTokenForPath(const PRUnichar * aPlatformAppPath, nsIFile ** aFile)
 {
   nsresult rv;
   nsCOMPtr<nsILocalFileMac> localFile (do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
   NS_ENSURE_SUCCESS(rv,rv);
 
   CFURLRef pathAsCFURL;
   CFStringRef pathAsCFString = ::CFStringCreateWithCharacters(NULL,
--- a/uriloader/exthandler/nsExternalHelperAppService.cpp
+++ b/uriloader/exthandler/nsExternalHelperAppService.cpp
@@ -72,16 +72,17 @@
 #include "nsIRDFService.h"
 #include "nsIRDFRemoteDataSource.h"
 #include "nsHelperAppRDF.h"
 #endif //MOZ_RDF
 #include "nsIMIMEInfo.h"
 #include "nsIRefreshURI.h" // XXX needed to redirect according to Refresh: URI
 #include "nsIDocumentLoader.h" // XXX needed to get orig. channel and assoc. refresh uri
 #include "nsIHelperAppLauncherDialog.h"
+#include "nsIContentDispatchChooser.h"
 #include "nsNetUtil.h"
 #include "nsIIOService.h"
 #include "nsNetCID.h"
 #include "nsChannelProperties.h"
 
 #include "nsMimeTypes.h"
 // used for header disposition information.
 #include "nsIHttpChannel.h"
@@ -994,22 +995,22 @@ nsresult nsExternalHelperAppService::Fil
 
   return FillHandlerInfoForTypeFromDS(typeNodeResource.get(), type, rdf,
                                       NC_SCHEME_NODE_PREFIX, aHandlerInfo);
 #else
   return NS_ERROR_NOT_AVAILABLE;
 #endif
 }
 
-#ifdef MOZ_RDF
 nsresult nsExternalHelperAppService::FillHandlerInfoForTypeFromDS(
   nsIRDFResource *aTypeNodeResource, const nsCAutoString &aType, 
   nsIRDFService *rdf, const char *aTypeNodePrefix,
   nsIHandlerInfo * aHandlerInfo)
 {
+#ifdef MOZ_RDF
 
   // we need a way to determine if this type resource is really in the graph
   // or not... Test that there's a #value arc from the type resource to the
   // type literal string.
   nsCOMPtr<nsIRDFLiteral> typeLiteral;
   NS_ConvertUTF8toUTF16 UTF16Type(aType);
   nsresult rv = rdf->GetLiteral( UTF16Type.get(), 
                                  getter_AddRefs( typeLiteral ) );
@@ -1034,18 +1035,20 @@ nsresult nsExternalHelperAppService::Fil
 
   } // if we have a node in the graph for this content type
   // If we had success, but entry doesn't exist, we don't want to return success
   else if (NS_SUCCEEDED(rv)) {
     rv = NS_ERROR_NOT_AVAILABLE;
   }
 
   return rv;
+#else
+  return NS_ERROR_NOT_AVAILABLE;
+#endif /* MOZ_RDF */
 }
-#endif /* MOZ_RDF */
 
 nsresult nsExternalHelperAppService::FillMIMEInfoForExtensionFromDS(
   const nsACString& aFileExtension, nsIMIMEInfo * aMIMEInfo)
 {
   nsCAutoString type;
   PRBool found = GetTypeFromDS(aFileExtension, type);
   if (!found)
     return NS_ERROR_NOT_AVAILABLE;
@@ -1183,251 +1186,86 @@ NS_IMETHODIMP nsExternalHelperAppService
   return NS_OK;
 }
 
 NS_IMETHODIMP nsExternalHelperAppService::LoadUrl(nsIURI * aURL)
 {
   return LoadURI(aURL, nsnull);
 }
 
-
-//  nsExternalHelperAppService::LoadURI() may now pose a confirm dialog
-//  that existing callers aren't expecting. We must do it on an event
-//  callback to make sure we don't hang someone up.
-
-class nsExternalLoadRequest : public nsRunnable {
-  public:
-    nsExternalLoadRequest(nsIURI *uri, nsIPrompt *prompt)
-      : mURI(uri), mPrompt(prompt) {}
-
-    NS_IMETHOD Run() {
-      if (gExtProtSvc && gExtProtSvc->isExternalLoadOK(mURI, mPrompt))
-        gExtProtSvc->LoadUriInternal(mURI);
-      return NS_OK;
-    }
-
-  private:
-    nsCOMPtr<nsIURI>    mURI;
-    nsCOMPtr<nsIPrompt> mPrompt;
-};
-
-NS_IMETHODIMP nsExternalHelperAppService::LoadURI(nsIURI * aURL, nsIPrompt * aPrompt)
-{
-  nsCAutoString spec;
-  aURL->GetSpec(spec);
-
-  if (spec.Find("%00") != -1)
-    return NS_ERROR_MALFORMED_URI;
-
-  spec.ReplaceSubstring("\"", "%22");
-  spec.ReplaceSubstring("`", "%60");
-
-  nsCOMPtr<nsIIOService> ios(do_GetIOService());
-  nsCOMPtr<nsIURI> uri;
-  nsresult rv = ios->NewURI(spec, nsnull, nsnull, getter_AddRefs(uri));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIRunnable> event = new nsExternalLoadRequest(uri, aPrompt);
-  return NS_DispatchToCurrentThread(event);
-}
-
 // helper routines used by LoadURI to check whether we're allowed
 // to load external schemes and whether or not to warn the user
 
 static const char kExternalProtocolPrefPrefix[]  = "network.protocol-handler.external.";
 static const char kExternalProtocolDefaultPref[] = "network.protocol-handler.external-default";
 static const char kExternalWarningPrefPrefix[]   = "network.protocol-handler.warn-external.";
 static const char kExternalWarningDefaultPref[]  = "network.protocol-handler.warn-external-default";
 
-
-PRBool nsExternalHelperAppService::isExternalLoadOK(nsIURI* aURL, nsIPrompt* aPrompt)
+NS_IMETHODIMP 
+nsExternalHelperAppService::LoadURI(nsIURI *aURI,
+                                    nsIInterfaceRequestor *aWindowContext)
 {
-  if (!aURL)
-    return PR_FALSE;
+  NS_ENSURE_ARG_POINTER(aURI);
 
   nsCAutoString scheme;
-  aURL->GetScheme(scheme);
+  (void)aURI->GetScheme(scheme);
   if (scheme.IsEmpty())
-    return PR_FALSE; // must have a scheme
+    return NS_OK; // must have a scheme
 
   nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
   if (!prefs)
-    return PR_FALSE; // deny if we can't check prefs
-
+    return NS_OK; // deny if we can't check prefs
 
   // Deny load if the prefs say to do so
   nsCAutoString externalPref(kExternalProtocolPrefPrefix);
   externalPref += scheme;
   PRBool allowLoad  = PR_FALSE;
   nsresult rv = prefs->GetBoolPref(externalPref.get(), &allowLoad);
   if (NS_FAILED(rv))
   {
     // no scheme-specific value, check the default
     rv = prefs->GetBoolPref(kExternalProtocolDefaultPref, &allowLoad);
   }
   if (NS_FAILED(rv) || !allowLoad)
-    return PR_FALSE; // explicitly denied or missing default pref
-
+    return NS_OK; // explicitly denied or missing default pref
 
   // allowLoad is now true. See whether we have to ask the user
   nsCAutoString warningPref(kExternalWarningPrefPrefix);
   warningPref += scheme;
   PRBool warn = PR_TRUE;
   rv = prefs->GetBoolPref(warningPref.get(), &warn);
   if (NS_FAILED(rv))
   {
     // no scheme-specific value, check the default
-    rv = prefs->GetBoolPref(kExternalWarningDefaultPref, &warn);
-  }
-
-
-  if (NS_FAILED(rv) || warn)
-  {
-    // explicit "warn" setting or missing default pref:
-    // we must ask the user before loading this type externally
-    PRBool remember = PR_FALSE;
-    allowLoad = promptForScheme(aURL, aPrompt, &remember);
-
-    if (remember)
-    {
-      if (allowLoad)
-        // suppress future warnings for this scheme
-        prefs->SetBoolPref(warningPref.get(), PR_FALSE);
-      else
-        // prevent externally loading this scheme in the future
-        prefs->SetBoolPref(externalPref.get(), PR_FALSE);
-    }
-  }
-
-  return allowLoad;
-}
-
-PRBool nsExternalHelperAppService::promptForScheme(nsIURI* aURI,
-                                                   nsIPrompt* aPrompt,
-                                                   PRBool *aRemember)
-{
-  // if no prompt passed in get one from the windowwatcher
-  nsCOMPtr<nsIPrompt> prompt(aPrompt);
-  if (!prompt)
-  {
-    nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
-    if (wwatch)
-      wwatch->GetNewPrompter(0, getter_AddRefs(prompt));
-  }
-  if (!prompt) {
-    NS_ERROR("No prompt to warn user about external load, denying");
-    return PR_FALSE; // told to warn but no prompt: deny
-  }
-
-  // load the strings we need
-  nsCOMPtr<nsIStringBundleService> sbSvc(do_GetService(NS_STRINGBUNDLE_CONTRACTID));
-  if (!sbSvc) {
-    NS_ERROR("Couldn't load StringBundleService");
-    return PR_FALSE;
-  }
-
-  nsCOMPtr<nsIStringBundle> appstrings;
-  nsresult rv = sbSvc->CreateBundle("chrome://global/locale/appstrings.properties",
-                                    getter_AddRefs(appstrings));
-  if (NS_FAILED(rv) || !appstrings) {
-    NS_ERROR("Failed to create appstrings.properties bundle");
-    return PR_FALSE;
+    prefs->GetBoolPref(kExternalWarningDefaultPref, &warn);
   }
-
-  nsCAutoString spec;
-  aURI->GetSpec(spec);
-  NS_ConvertUTF8toUTF16 uri(spec);
-
-  // The maximum amount of space the URI should take up in the dialog.
-  const PRUint32 maxWidth = 75;
-  const PRUint32 maxLines = 12; // (not counting ellipsis line)
-  const PRUint32 maxLength = maxWidth * maxLines;
-
-  // If the URI seems too long, insert zero-width spaces to make it wrappable
-  // and crop it in the middle (replacing the cropped portion with an ellipsis),
-  // so the dialog doesn't grow so wide or tall it renders buttons off-screen.
-  if (uri.Length() > maxWidth) {
-    PRUint32 charIdx = maxWidth;
-    PRUint32 lineIdx = 1;
-    
-    PRInt32 numCharsToCrop = uri.Length() - maxLength;
-
-    while (charIdx < uri.Length()) {
-      // Don't insert characters in the middle of a surrogate pair.
-      if (NS_IS_LOW_SURROGATE(uri[charIdx]))
-        --charIdx;
-
-      if (numCharsToCrop > 0 && lineIdx == maxLines / 2) {
-        NS_NAMED_LITERAL_STRING(ellipsis, "\n...\n");
-
-        // Don't end the crop in the middle of a surrogate pair.
-        if (NS_IS_HIGH_SURROGATE(uri[charIdx + numCharsToCrop - 1]))
-          ++numCharsToCrop;
-
-        uri.Replace(charIdx, numCharsToCrop, ellipsis);
-        charIdx += ellipsis.Length();
-      }
-      else {
-        // 0x200B is the zero-width breakable space character.
-        uri.Insert(PRUnichar(0x200B), charIdx);
-        charIdx += 1;
-      }
+ 
+  nsCOMPtr<nsIHandlerInfo> handler;
+  rv = GetProtocolHandlerInfo(scheme, getter_AddRefs(handler));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsHandlerInfoAction preferredAction;
+  handler->GetPreferredAction(&preferredAction);
+  PRBool alwaysAsk = PR_TRUE;
+  handler->GetAlwaysAskBeforeHandling(&alwaysAsk);
+
+  // if we are not supposed to warn, or we are not supposed to always ask and
+  // the preferred action is to use a helper app or the system default, we just
+  // launch the URI.
+  if (!warn ||
+      !alwaysAsk && (preferredAction == nsIHandlerInfo::useHelperApp ||
+                     preferredAction == nsIHandlerInfo::useSystemDefault))
+    return handler->LaunchWithURI(aURI);
   
-      charIdx += maxWidth;
-      ++lineIdx;
-    }
-  }
-
-  nsCAutoString asciischeme;
-  aURI->GetScheme(asciischeme);
-  NS_ConvertUTF8toUTF16 scheme(asciischeme);
-
-  nsXPIDLString desc;
-  GetApplicationDescription(asciischeme, desc);
-
-  nsXPIDLString title;
-  appstrings->GetStringFromName(NS_LITERAL_STRING("externalProtocolTitle").get(),
-                                getter_Copies(title));
-  nsXPIDLString checkMsg;
-  appstrings->GetStringFromName(NS_LITERAL_STRING("externalProtocolChkMsg").get(),
-                                getter_Copies(checkMsg));
-  nsXPIDLString launchBtn;
-  appstrings->GetStringFromName(NS_LITERAL_STRING("externalProtocolLaunchBtn").get(),
-                                getter_Copies(launchBtn));
-
-  if (desc.IsEmpty())
-    appstrings->GetStringFromName(NS_LITERAL_STRING("externalProtocolUnknown").get(),
-                                  getter_Copies(desc));
-
-  nsXPIDLString message;
-  const PRUnichar* msgArgs[] = { scheme.get(), uri.get(), desc.get() };
-  appstrings->FormatStringFromName(NS_LITERAL_STRING("externalProtocolPrompt").get(),
-                                   msgArgs,
-                                   NS_ARRAY_LENGTH(msgArgs),
-                                   getter_Copies(message));
-
-  if (scheme.IsEmpty() || uri.IsEmpty() || title.IsEmpty() ||
-      checkMsg.IsEmpty() || launchBtn.IsEmpty() || message.IsEmpty() ||
-      desc.IsEmpty())
-    return PR_FALSE;
-
-  // all pieces assembled, now we can pose the dialog
-  PRInt32 choice = 1; // assume "cancel" in case of failure
-  rv = prompt->ConfirmEx(title.get(), message.get(),
-                         nsIPrompt::BUTTON_DELAY_ENABLE +
-                         nsIPrompt::BUTTON_POS_1_DEFAULT +
-                         (nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_0) +
-                         (nsIPrompt::BUTTON_TITLE_CANCEL * nsIPrompt::BUTTON_POS_1),
-                         launchBtn.get(), 0, 0, checkMsg.get(),
-                         aRemember, &choice);
-
-  if (NS_SUCCEEDED(rv) && choice == 0)
-    return PR_TRUE;
-
-  return PR_FALSE;
+  nsCOMPtr<nsIContentDispatchChooser> chooser =
+    do_CreateInstance("@mozilla.org/content-dispatch-chooser;1", &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+  
+  return chooser->Ask(handler, aWindowContext, aURI,
+                      nsIContentDispatchChooser::REASON_CANNOT_HANDLE);
 }
 
 NS_IMETHODIMP nsExternalHelperAppService::GetApplicationDescription(const nsACString& aScheme, nsAString& _retval)
 {
   // this method should only be implemented by each OS specific implementation of this service.
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
--- a/uriloader/exthandler/nsExternalHelperAppService.h
+++ b/uriloader/exthandler/nsExternalHelperAppService.h
@@ -346,23 +346,16 @@ protected:
    * Functions related to the tempory file cleanup service provided by
    * nsExternalHelperAppService
    */
   NS_HIDDEN_(nsresult) ExpungeTemporaryFiles();
   /**
    * Array for the files that should be deleted
    */
   nsCOMArray<nsILocalFile> mTemporaryFilesList;
-
-  /**
-   * OS-specific loading of external URLs
-   */
-  virtual NS_HIDDEN_(nsresult) LoadUriInternal(nsIURI * aURL) = 0;
-  NS_HIDDEN_(PRBool) isExternalLoadOK(nsIURI* aURI, nsIPrompt* aPrompt);
-  NS_HIDDEN_(PRBool) promptForScheme(nsIURI* aURI, nsIPrompt* aPrompt, PRBool *aRemember);
 };
 
 /**
  * We need to read the data out of the incoming stream into a buffer which we
  * can then use to write the data into the output stream representing the
  * temp file.
  */
 #define DATA_BUFFER_SIZE (4096*2) 
--- a/uriloader/exthandler/nsExternalProtocolHandler.cpp
+++ b/uriloader/exthandler/nsExternalProtocolHandler.cpp
@@ -183,20 +183,17 @@ nsresult nsExtProtocolChannel::OpenURL()
 #ifdef DEBUG
     nsCAutoString urlScheme;
     mUrl->GetScheme(urlScheme);
     PRBool haveHandler = PR_FALSE;
     extProtService->ExternalProtocolHandlerExists(urlScheme.get(), &haveHandler);
     NS_ASSERTION(haveHandler, "Why do we have a channel for this url if we don't support the protocol?");
 #endif
 
-    // get an nsIPrompt from the channel if we can
-    nsCOMPtr<nsIPrompt> prompt;
-    NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, prompt);
-    rv = extProtService->LoadURI(mUrl, prompt);
+    rv = extProtService->LoadURI(mUrl, nsnull);
   }
 
   // Drop notification callbacks to prevent cycles.
   mCallbacks = 0;
 
   return rv;
 }
 
new file mode 100644
--- /dev/null
+++ b/uriloader/exthandler/nsIContentDispatchChooser.idl
@@ -0,0 +1,78 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Shawn Wilsher <me@shawnwilsher.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 "nsISupports.idl"
+
+interface nsIHandlerInfo;
+interface nsIHelperAppLauncher;
+interface nsIURI;
+interface nsIInterfaceRequestor;
+
+/**
+ * This is used to ask a user what they would like to do with a given piece of
+ * content.
+ */
+[scriptable, uuid(456ca3b2-02be-4f97-89a2-08c08d3ad88f)]
+interface nsIContentDispatchChooser : nsISupports {
+ /**
+  * This request is passed to the helper app dialog because Gecko can not
+  * handle content of this type.
+  */
+  const unsigned long REASON_CANNOT_HANDLE = 0;
+
+ /**
+  * Asks the user what to do with the content.
+  *
+  * @param aHander
+  *        The interface describing the details of how this content should or
+  *        can be handled.
+  * @param aWindowContext
+  *        The parent window context to show this chooser.  This can be null,
+  *        and some implementations may not care about it.  Generally, you'll
+  *        want to pass an nsIDOMWindow in so the chooser can be properly
+  *        parented when opened.
+  * @param aURI
+  *        The URI of the resource that we are asking about.
+  * @param aReason
+  *        The reason why we are asking (see above).
+  */
+  void ask(in nsIHandlerInfo aHandler,
+           in nsIInterfaceRequestor aWindowContext,
+           in nsIURI aURI,
+           in unsigned long aReason);
+};
+
--- a/uriloader/exthandler/nsIExternalProtocolService.idl
+++ b/uriloader/exthandler/nsIExternalProtocolService.idl
@@ -36,28 +36,28 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
 interface nsIURI;
 interface nsIFile;
-interface nsIPrompt;
+interface nsIInterfaceRequestor;
 
 /**
  * The external protocol service is used for finding and launching
  * web handlers (a la registerProtocolHandler in the HTML5 draft) or 
  * platform-specific applications for handling particular protocols.
  *
  * You can ask the external protocol service if it has an external
  * handler for a given protocol scheme. And you can ask it to load 
  * the url using the default handler.
  */
-[scriptable, uuid(a49813a4-98b7-4bdb-998c-8bd9704af0c0)]
+[scriptable, uuid(01e9ce6e-1c81-4f1a-b128-2f2526ec40e3)]
 interface nsIExternalProtocolService : nsISupports
 {
   /**
    * Check whether a handler for a specific protocol exists.
    * @param aProtocolScheme The scheme from a url: http, ftp, mailto, etc.
    * @return true if we have a handler and false otherwise.
    */
   boolean externalProtocolHandlerExists(in string aProtocolScheme);
@@ -71,28 +71,31 @@ interface nsIExternalProtocolService : n
    * application.  For example, a non-exposed protocol would not be loaded by the
    * application in response to a link click or a X-remote openURL command.
    * Instead, it would be deferred to the system's external protocol handler.
    */
   boolean isExposedProtocol(in string aProtocolScheme);
 
   /**
    * Used to load a url via an external protocol handler (if one exists)
+   *
    * @param aURL The url to load
+   * @deprecated Use LoadURI instead (See Bug 389565 for removal)
    */
-  void loadUrl (in nsIURI aURL);
+   void loadUrl (in nsIURI aURL);
 
   /**
    * Used to load a URI via an external application. Might prompt the user for
-   * permission to load the external application. Replaces loadUrl()
+   * permission to load the external application.
    *
-   * @param aURI    The URI to load
-   * @param aPrompt If null we grab one from windowwatcher if we need it
+   * @param aURI           The URI to load
+   * @param aWindowContext The parent window to open the dialog with.
    */
-  void loadURI(in nsIURI aURI, in nsIPrompt aPrompt);
+  void loadURI(in nsIURI aURI,
+               [optional] in nsIInterfaceRequestor aWindowContext);
 
   /**
    * Gets a human-readable description for the application responsible for
    * handling a specific protocol.
    *
    * @param aScheme The scheme to look up. For example, "mms".
    *
    * @throw NS_ERROR_NOT_IMPLEMENTED
--- a/uriloader/exthandler/nsMIMEInfoImpl.cpp
+++ b/uriloader/exthandler/nsMIMEInfoImpl.cpp
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=4 sw=4 sts=4 et: */
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
 /* ***** 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/
  *
@@ -359,16 +359,19 @@ nsMIMEInfoBase::LaunchWithURI(nsIURI* aU
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = GetLocalFileFromURI(aURI, getter_AddRefs(docToLoad));
     NS_ENSURE_SUCCESS(rv, rv);
 
     return LaunchWithIProcess(executable, docToLoad);
   }
   else if (mPreferredAction == useSystemDefault) {
+    if (mClass == eProtocolInfo)
+      return LoadUriInternal(aURI);
+
     rv = GetLocalFileFromURI(aURI, getter_AddRefs(docToLoad));
     NS_ENSURE_SUCCESS(rv, rv);
 
     return LaunchDefaultWithFile(docToLoad);
   }
 
   return NS_ERROR_INVALID_ARG;
 }
--- a/uriloader/exthandler/nsMIMEInfoImpl.h
+++ b/uriloader/exthandler/nsMIMEInfoImpl.h
@@ -128,16 +128,23 @@ class nsMIMEInfoBase : public nsIMIMEInf
      * For even more control over the launching, override launchWithFile.
      * Also see the comment about nsIMIMEInfo in general, above.
      *
      * @param aFile The file that should be opened
      */
     virtual NS_HIDDEN_(nsresult) LaunchDefaultWithFile(nsIFile* aFile) = 0;
 
     /**
+     * Loads the URI with the OS default app.
+     *
+     * @param aURI The URI to pass off to the OS.
+     */
+    virtual NS_HIDDEN_(nsresult) LoadUriInternal(nsIURI *aURI) = 0;
+
+    /**
      * This method can be used to launch the file using nsIProcess, with the
      * path of the file being the first parameter to the executable. This is
      * meant as a helper method for implementations of
      * LaunchWithFile/LaunchDefaultWithFile.
      * Neither aApp nor aFile may be null.
      *
      * @param aApp The application to launch
      * @param aFile The file to open in the application
@@ -196,21 +203,27 @@ class nsMIMEInfoImpl : public nsMIMEInfo
     NS_IMETHOD GetDefaultDescription(nsAString& aDefaultDescription);
 
     // additional methods
     /**
      * Sets the default application. Supposed to be only called by the OS Helper
      * App Services; the default application is immutable after it is first set.
      */
     void SetDefaultApplication(nsIFile* aApp) { if (!mDefaultApplication) mDefaultApplication = aApp; }
+
   protected:
     // nsMIMEInfoBase methods
     /**
      * The base class implementation is to use LaunchWithIProcess in combination
      * with mDefaultApplication. Subclasses can override that behaviour.
      */
     virtual NS_HIDDEN_(nsresult) LaunchDefaultWithFile(nsIFile* aFile);
 
+    /**
+     * Loads the URI with the OS default app.  This should be overridden by each
+     * OS's implementation.
+     */
+    virtual NS_HIDDEN_(nsresult) LoadUriInternal(nsIURI *aURI) = 0;
 
     nsCOMPtr<nsIFile>      mDefaultApplication; ///< default application associated with this type.
 };
 
 #endif //__nsmimeinfoimpl_h___
--- a/uriloader/exthandler/os2/nsMIMEInfoOS2.cpp
+++ b/uriloader/exthandler/os2/nsMIMEInfoOS2.cpp
@@ -149,8 +149,260 @@ NS_IMETHODIMP nsMIMEInfoOS2::LaunchWithU
   // make the registry call to launch the app
   nsCOMPtr<nsIProcess> process = do_CreateInstance(NS_PROCESS_CONTRACTID);
   if (NS_FAILED(rv = process->Init(application)))
     return rv;
   PRUint32 pid;
   return process->Run(PR_FALSE, &strPath, 1, &pid);
 }
 
+nsresult nsMIMEInfoOS2::LoadUriInternal(nsIURI * aURL)
+{
+  LOG(("-- nsOSHelperAppService::LoadUriInternal\n"));
+  nsCOMPtr<nsIPref> thePrefsService(do_GetService(NS_PREF_CONTRACTID));
+  if (!thePrefsService) {
+    return NS_ERROR_FAILURE;
+  }
+
+  /* Convert SimpleURI to StandardURL */
+  nsresult rv;
+  nsCOMPtr<nsIURI> uri = do_CreateInstance(kStandardURLCID, &rv);
+  if (NS_FAILED(rv)) {
+    return NS_ERROR_FAILURE;
+  }
+  nsCAutoString urlSpec;
+  aURL->GetSpec(urlSpec);
+  uri->SetSpec(urlSpec);
+
+  /* Get the protocol so we can look up the preferences */
+  nsCAutoString uProtocol;
+  uri->GetScheme(uProtocol);
+
+  nsCAutoString prefName;
+  prefName = NS_LITERAL_CSTRING("applications.") + uProtocol;
+  nsXPIDLCString prefString;
+
+  nsCAutoString applicationName;
+  nsCAutoString parameters;
+
+  rv = thePrefsService->CopyCharPref(prefName.get(), getter_Copies(prefString));
+  if (NS_FAILED(rv) || prefString.IsEmpty()) {
+    char szAppFromINI[CCHMAXPATH];
+    char szParamsFromINI[MAXINIPARAMLENGTH];
+    /* did OS2.INI contain application? */
+    rv = GetApplicationAndParametersFromINI(uProtocol,
+                                            szAppFromINI, sizeof(szAppFromINI),
+                                            szParamsFromINI, sizeof(szParamsFromINI));
+    if (NS_SUCCEEDED(rv)) {
+      applicationName = szAppFromINI;
+      parameters = szParamsFromINI;
+    } else {
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  // Dissect the URI
+  nsCAutoString uURL, uUsername, uPassword, uHost, uPort, uPath;
+  nsCAutoString uEmail, uGroup;
+  PRInt32 iPort;
+
+  // when passing to OS/2 apps later, we need ASCII URLs,
+  // UTF-8 would probably not get handled correctly
+  aURL->GetAsciiSpec(uURL);
+  uri->GetAsciiHost(uHost);
+  uri->GetUsername(uUsername);
+  NS_UnescapeURL(uUsername);
+  uri->GetPassword(uPassword);
+  NS_UnescapeURL(uPassword);
+  uri->GetPort(&iPort);
+  /* GetPort returns -1 if there is no port in the URI */
+  if (iPort != -1)
+    uPort.AppendInt(iPort);
+  uri->GetPath(uPath);
+  NS_UnescapeURL(uPath);
+
+  // One could use nsIMailtoUrl to get email and newsgroup,
+  // but it is probably easier to do that quickly by hand here
+  // uEmail is both email address and message id  for news
+  uEmail = uUsername + NS_LITERAL_CSTRING("@") + uHost;
+  // uPath can almost be used as newsgroup and as channel for IRC
+  // but strip leading "/"
+  uGroup = Substring(uPath, 1, uPath.Length());
+
+  NS_NAMED_LITERAL_CSTRING(url, "%url%");
+  NS_NAMED_LITERAL_CSTRING(username, "%username%");
+  NS_NAMED_LITERAL_CSTRING(password, "%password%");
+  NS_NAMED_LITERAL_CSTRING(host, "%host%");
+  NS_NAMED_LITERAL_CSTRING(port, "%port%");
+  NS_NAMED_LITERAL_CSTRING(email, "%email%");
+  NS_NAMED_LITERAL_CSTRING(group, "%group%");
+  NS_NAMED_LITERAL_CSTRING(msgid, "%msgid%");
+  NS_NAMED_LITERAL_CSTRING(channel, "%channel%");
+  
+  if (applicationName.IsEmpty() && parameters.IsEmpty()) {
+    /* Put application name in parameters */
+    applicationName.Append(prefString);
+  
+    prefName.Append(".");
+    nsCOMPtr<nsIPrefBranch> prefBranch;
+    rv = thePrefsService->GetBranch(prefName.get(), getter_AddRefs(prefBranch));
+    if (NS_SUCCEEDED(rv) && prefBranch) {
+      rv = prefBranch->GetCharPref("parameters", getter_Copies(prefString));
+      /* If parameters have been specified, use them instead of the separate entities */
+      if (NS_SUCCEEDED(rv) && !prefString.IsEmpty()) {
+        parameters.Append(" ");
+        parameters.Append(prefString);
+  
+        PRInt32 pos = parameters.Find(url.get());
+        if (pos != kNotFound) {
+          nsCAutoString uURL;
+          aURL->GetSpec(uURL);
+          NS_UnescapeURL(uURL);
+          uURL.Cut(0, uProtocol.Length()+1);
+          parameters.Replace(pos, url.Length(), uURL);
+        }
+      } else {
+        /* port */
+        if (!uPort.IsEmpty()) {
+          rv = prefBranch->GetCharPref("port", getter_Copies(prefString));
+          if (NS_SUCCEEDED(rv) && !prefString.IsEmpty()) {
+            parameters.Append(" ");
+            parameters.Append(prefString);
+          }
+        }
+        /* username */
+        if (!uUsername.IsEmpty()) {
+          rv = prefBranch->GetCharPref("username", getter_Copies(prefString));
+          if (NS_SUCCEEDED(rv) && !prefString.IsEmpty()) {
+            parameters.Append(" ");
+            parameters.Append(prefString);
+          }
+        }
+        /* password */
+        if (!uPassword.IsEmpty()) {
+          rv = prefBranch->GetCharPref("password", getter_Copies(prefString));
+          if (NS_SUCCEEDED(rv) && !prefString.IsEmpty()) {
+            parameters.Append(" ");
+            parameters.Append(prefString);
+          }
+        }
+        /* host */
+        if (!uHost.IsEmpty()) {
+          rv = prefBranch->GetCharPref("host", getter_Copies(prefString));
+          if (NS_SUCCEEDED(rv) && !prefString.IsEmpty()) {
+            parameters.Append(" ");
+            parameters.Append(prefString);
+          }
+        }
+      }
+    }
+  }
+
+#ifdef DEBUG_peter
+  printf("uURL=%s\n", uURL.get());
+  printf("uUsername=%s\n", uUsername.get());
+  printf("uPassword=%s\n", uPassword.get());
+  printf("uHost=%s\n", uHost.get());
+  printf("uPort=%s\n", uPort.get());
+  printf("uPath=%s\n", uPath.get());
+  printf("uEmail=%s\n", uEmail.get());
+  printf("uGroup=%s\n", uGroup.get());
+#endif
+  
+  PRInt32 pos;
+  PRBool replaced = PR_FALSE;
+  pos = parameters.Find(url.get());
+  if (pos != kNotFound) {
+    replaced = PR_TRUE;
+    parameters.Replace(pos, url.Length(), uURL);
+  }
+  pos = parameters.Find(username.get());
+  if (pos != kNotFound) {
+    replaced = PR_TRUE;
+    parameters.Replace(pos, username.Length(), uUsername);
+  }
+  pos = parameters.Find(password.get());
+  if (pos != kNotFound) {
+    replaced = PR_TRUE;
+    parameters.Replace(pos, password.Length(), uPassword);
+  }
+  pos = parameters.Find(host.get());
+  if (pos != kNotFound) {
+    replaced = PR_TRUE;
+    parameters.Replace(pos, host.Length(), uHost);
+  }
+  pos = parameters.Find(port.get());
+  if (pos != kNotFound) {
+    replaced = PR_TRUE;
+    parameters.Replace(pos, port.Length(), uPort);
+  }
+  pos = parameters.Find(email.get());
+  if (pos != kNotFound) {
+    replaced = PR_TRUE;
+    parameters.Replace(pos, email.Length(), uEmail);
+  }
+  pos = parameters.Find(group.get());
+  if (pos != kNotFound) {
+    replaced = PR_TRUE;
+    parameters.Replace(pos, group.Length(), uGroup);
+  }
+  pos = parameters.Find(msgid.get());
+  if (pos != kNotFound) {
+    replaced = PR_TRUE;
+    parameters.Replace(pos, msgid.Length(), uEmail);
+  }
+  pos = parameters.Find(channel.get());
+  if (pos != kNotFound) {
+    replaced = PR_TRUE;
+    parameters.Replace(pos, channel.Length(), uGroup);
+  }
+  // If no replacement variable was used, the user most likely uses the WPS URL
+  // object and does not know about the replacement variables.
+  // Just append the full URL.
+  if (!replaced) {
+    parameters.Append(" ");
+    parameters.Append(uURL);
+  }
+
+  const char *params[3];
+  params[0] = parameters.get();
+#ifdef DEBUG_peter
+  printf("params[0]=%s\n", params[0]);
+#endif
+  PRInt32 numParams = 1;
+
+  nsCOMPtr<nsILocalFile> application;
+  rv = NS_NewNativeLocalFile(nsDependentCString(applicationName.get()), PR_FALSE, getter_AddRefs(application));
+  if (NS_FAILED(rv)) {
+     /* Maybe they didn't qualify the name - search path */
+     char szAppPath[CCHMAXPATH];
+     APIRET rc = DosSearchPath(SEARCH_IGNORENETERRS | SEARCH_ENVIRONMENT,
+                               "PATH", applicationName.get(),
+                               szAppPath, sizeof(szAppPath));
+     if (rc == NO_ERROR) {
+       rv = NS_NewNativeLocalFile(nsDependentCString(szAppPath), PR_FALSE, getter_AddRefs(application));
+     }
+     if (NS_FAILED(rv) || (rc != NO_ERROR)) {
+       /* Try just launching it with COMSPEC */
+       rv = NS_NewNativeLocalFile(nsDependentCString(getenv("COMSPEC")), PR_FALSE, getter_AddRefs(application));
+       if (NS_FAILED(rv)) {
+         return rv;
+       }
+  
+       params[0] = "/c";
+       params[1] = applicationName.get();
+       params[2] = parameters.get();
+       numParams = 3;
+     }
+  }
+
+  nsCOMPtr<nsIProcess> process = do_CreateInstance(NS_PROCESS_CONTRACTID);
+
+  if (NS_FAILED(rv = process->Init(application)))
+     return rv;
+
+  PRUint32 pid;
+  if (NS_FAILED(rv = process->Run(PR_FALSE, params, numParams, &pid)))
+    return rv;
+
+  return NS_OK;
+}
+
--- a/uriloader/exthandler/os2/nsMIMEInfoOS2.h
+++ b/uriloader/exthandler/os2/nsMIMEInfoOS2.h
@@ -44,19 +44,19 @@ class nsMIMEInfoOS2 : public nsMIMEInfoI
   public:
     nsMIMEInfoOS2(const char* aType = "") : nsMIMEInfoImpl(aType) {}
     nsMIMEInfoOS2(const nsACString& aMIMEType) : nsMIMEInfoImpl(aMIMEType) {}
     nsMIMEInfoOS2(const nsACString& aType, HandlerClass aClass) :
       nsMIMEInfoImpl(aType, aClass) {}
     virtual ~nsMIMEInfoOS2();
 
     NS_IMETHOD LaunchWithURI(nsIURI* aURI);
-
+  protected:
+    virtual NS_HIDDEN_(nsresult) LoadUriInternal(nsIURI *aURI);
 #ifdef DEBUG
-  protected:
     virtual NS_HIDDEN_(nsresult) LaunchDefaultWithFile(nsIFile* aFile) {
       NS_NOTREACHED("Do not call this, use LaunchWithFile");
       return NS_ERROR_UNEXPECTED;
     }
 #endif
 };
 
 #endif
--- a/uriloader/exthandler/os2/nsOSHelperAppService.cpp
+++ b/uriloader/exthandler/os2/nsOSHelperAppService.cpp
@@ -1269,268 +1269,16 @@ nsresult nsOSHelperAppService::OSProtoco
   if (NS_SUCCEEDED(rv)) {
     *aHandlerExists = PR_TRUE;
     return NS_OK;
   }
 
   return NS_ERROR_FAILURE;
 }
 
-nsresult nsOSHelperAppService::LoadUriInternal(nsIURI * aURL)
-{
-  LOG(("-- nsOSHelperAppService::LoadUriInternal\n"));
-  nsCOMPtr<nsIPref> thePrefsService(do_GetService(NS_PREF_CONTRACTID));
-  if (!thePrefsService) {
-    return NS_ERROR_FAILURE;
-  }
-
-  /* Convert SimpleURI to StandardURL */
-  nsresult rv;
-  nsCOMPtr<nsIURI> uri = do_CreateInstance(kStandardURLCID, &rv);
-  if (NS_FAILED(rv)) {
-    return NS_ERROR_FAILURE;
-  }
-  nsCAutoString urlSpec;
-  aURL->GetSpec(urlSpec);
-  uri->SetSpec(urlSpec);
-
-  /* Get the protocol so we can look up the preferences */
-  nsCAutoString uProtocol;
-  uri->GetScheme(uProtocol);
-
-  nsCAutoString prefName;
-  prefName = NS_LITERAL_CSTRING("applications.") + uProtocol;
-  nsXPIDLCString prefString;
-
-  nsCAutoString applicationName;
-  nsCAutoString parameters;
-
-  rv = thePrefsService->CopyCharPref(prefName.get(), getter_Copies(prefString));
-  if (NS_FAILED(rv) || prefString.IsEmpty()) {
-    char szAppFromINI[CCHMAXPATH];
-    char szParamsFromINI[MAXINIPARAMLENGTH];
-    /* did OS2.INI contain application? */
-    rv = GetApplicationAndParametersFromINI(uProtocol,
-                                            szAppFromINI, sizeof(szAppFromINI),
-                                            szParamsFromINI, sizeof(szParamsFromINI));
-    if (NS_SUCCEEDED(rv)) {
-      applicationName = szAppFromINI;
-      parameters = szParamsFromINI;
-    } else {
-      return NS_ERROR_FAILURE;
-    }
-  }
-
-  // Dissect the URI
-  nsCAutoString uURL, uUsername, uPassword, uHost, uPort, uPath;
-  nsCAutoString uEmail, uGroup;
-  PRInt32 iPort;
-
-  // when passing to OS/2 apps later, we need ASCII URLs,
-  // UTF-8 would probably not get handled correctly
-  aURL->GetAsciiSpec(uURL);
-  uri->GetAsciiHost(uHost);
-  uri->GetUsername(uUsername);
-  NS_UnescapeURL(uUsername);
-  uri->GetPassword(uPassword);
-  NS_UnescapeURL(uPassword);
-  uri->GetPort(&iPort);
-  /* GetPort returns -1 if there is no port in the URI */
-  if (iPort != -1)
-    uPort.AppendInt(iPort);
-  uri->GetPath(uPath);
-  NS_UnescapeURL(uPath);
-
-  // One could use nsIMailtoUrl to get email and newsgroup,
-  // but it is probably easier to do that quickly by hand here
-  // uEmail is both email address and message id  for news
-  uEmail = uUsername + NS_LITERAL_CSTRING("@") + uHost;
-  // uPath can almost be used as newsgroup and as channel for IRC
-  // but strip leading "/"
-  uGroup = Substring(uPath, 1, uPath.Length());
-
-  NS_NAMED_LITERAL_CSTRING(url, "%url%");
-  NS_NAMED_LITERAL_CSTRING(username, "%username%");
-  NS_NAMED_LITERAL_CSTRING(password, "%password%");
-  NS_NAMED_LITERAL_CSTRING(host, "%host%");
-  NS_NAMED_LITERAL_CSTRING(port, "%port%");
-  NS_NAMED_LITERAL_CSTRING(email, "%email%");
-  NS_NAMED_LITERAL_CSTRING(group, "%group%");
-  NS_NAMED_LITERAL_CSTRING(msgid, "%msgid%");
-  NS_NAMED_LITERAL_CSTRING(channel, "%channel%");
-  
-  if (applicationName.IsEmpty() && parameters.IsEmpty()) {
-    /* Put application name in parameters */
-    applicationName.Append(prefString);
-  
-    prefName.Append(".");
-    nsCOMPtr<nsIPrefBranch> prefBranch;
-    rv = thePrefsService->GetBranch(prefName.get(), getter_AddRefs(prefBranch));
-    if (NS_SUCCEEDED(rv) && prefBranch) {
-      rv = prefBranch->GetCharPref("parameters", getter_Copies(prefString));
-      /* If parameters have been specified, use them instead of the separate entities */
-      if (NS_SUCCEEDED(rv) && !prefString.IsEmpty()) {
-        parameters.Append(" ");
-        parameters.Append(prefString);
-  
-        PRInt32 pos = parameters.Find(url.get());
-        if (pos != kNotFound) {
-          nsCAutoString uURL;
-          aURL->GetSpec(uURL);
-          NS_UnescapeURL(uURL);
-          uURL.Cut(0, uProtocol.Length()+1);
-          parameters.Replace(pos, url.Length(), uURL);
-        }
-      } else {
-        /* port */
-        if (!uPort.IsEmpty()) {
-          rv = prefBranch->GetCharPref("port", getter_Copies(prefString));
-          if (NS_SUCCEEDED(rv) && !prefString.IsEmpty()) {
-            parameters.Append(" ");
-            parameters.Append(prefString);
-          }
-        }
-        /* username */
-        if (!uUsername.IsEmpty()) {
-          rv = prefBranch->GetCharPref("username", getter_Copies(prefString));
-          if (NS_SUCCEEDED(rv) && !prefString.IsEmpty()) {
-            parameters.Append(" ");
-            parameters.Append(prefString);
-          }
-        }
-        /* password */
-        if (!uPassword.IsEmpty()) {
-          rv = prefBranch->GetCharPref("password", getter_Copies(prefString));
-          if (NS_SUCCEEDED(rv) && !prefString.IsEmpty()) {
-            parameters.Append(" ");
-            parameters.Append(prefString);
-          }
-        }
-        /* host */
-        if (!uHost.IsEmpty()) {
-          rv = prefBranch->GetCharPref("host", getter_Copies(prefString));
-          if (NS_SUCCEEDED(rv) && !prefString.IsEmpty()) {
-            parameters.Append(" ");
-            parameters.Append(prefString);
-          }
-        }
-      }
-    }
-  }
-
-#ifdef DEBUG_peter
-  printf("uURL=%s\n", uURL.get());
-  printf("uUsername=%s\n", uUsername.get());
-  printf("uPassword=%s\n", uPassword.get());
-  printf("uHost=%s\n", uHost.get());
-  printf("uPort=%s\n", uPort.get());
-  printf("uPath=%s\n", uPath.get());
-  printf("uEmail=%s\n", uEmail.get());
-  printf("uGroup=%s\n", uGroup.get());
-#endif
-  
-  PRInt32 pos;
-  PRBool replaced = PR_FALSE;
-  pos = parameters.Find(url.get());
-  if (pos != kNotFound) {
-    replaced = PR_TRUE;
-    parameters.Replace(pos, url.Length(), uURL);
-  }
-  pos = parameters.Find(username.get());
-  if (pos != kNotFound) {
-    replaced = PR_TRUE;
-    parameters.Replace(pos, username.Length(), uUsername);
-  }
-  pos = parameters.Find(password.get());
-  if (pos != kNotFound) {
-    replaced = PR_TRUE;
-    parameters.Replace(pos, password.Length(), uPassword);
-  }
-  pos = parameters.Find(host.get());
-  if (pos != kNotFound) {
-    replaced = PR_TRUE;
-    parameters.Replace(pos, host.Length(), uHost);
-  }
-  pos = parameters.Find(port.get());
-  if (pos != kNotFound) {
-    replaced = PR_TRUE;
-    parameters.Replace(pos, port.Length(), uPort);
-  }
-  pos = parameters.Find(email.get());
-  if (pos != kNotFound) {
-    replaced = PR_TRUE;
-    parameters.Replace(pos, email.Length(), uEmail);
-  }
-  pos = parameters.Find(group.get());
-  if (pos != kNotFound) {
-    replaced = PR_TRUE;
-    parameters.Replace(pos, group.Length(), uGroup);
-  }
-  pos = parameters.Find(msgid.get());
-  if (pos != kNotFound) {
-    replaced = PR_TRUE;
-    parameters.Replace(pos, msgid.Length(), uEmail);
-  }
-  pos = parameters.Find(channel.get());
-  if (pos != kNotFound) {
-    replaced = PR_TRUE;
-    parameters.Replace(pos, channel.Length(), uGroup);
-  }
-  // If no replacement variable was used, the user most likely uses the WPS URL
-  // object and does not know about the replacement variables.
-  // Just append the full URL.
-  if (!replaced) {
-    parameters.Append(" ");
-    parameters.Append(uURL);
-  }
-
-  const char *params[3];
-  params[0] = parameters.get();
-#ifdef DEBUG_peter
-  printf("params[0]=%s\n", params[0]);
-#endif
-  PRInt32 numParams = 1;
-
-  nsCOMPtr<nsILocalFile> application;
-  rv = NS_NewNativeLocalFile(nsDependentCString(applicationName.get()), PR_FALSE, getter_AddRefs(application));
-  if (NS_FAILED(rv)) {
-     /* Maybe they didn't qualify the name - search path */
-     char szAppPath[CCHMAXPATH];
-     APIRET rc = DosSearchPath(SEARCH_IGNORENETERRS | SEARCH_ENVIRONMENT,
-                               "PATH", applicationName.get(),
-                               szAppPath, sizeof(szAppPath));
-     if (rc == NO_ERROR) {
-       rv = NS_NewNativeLocalFile(nsDependentCString(szAppPath), PR_FALSE, getter_AddRefs(application));
-     }
-     if (NS_FAILED(rv) || (rc != NO_ERROR)) {
-       /* Try just launching it with COMSPEC */
-       rv = NS_NewNativeLocalFile(nsDependentCString(getenv("COMSPEC")), PR_FALSE, getter_AddRefs(application));
-       if (NS_FAILED(rv)) {
-         return rv;
-       }
-  
-       params[0] = "/c";
-       params[1] = applicationName.get();
-       params[2] = parameters.get();
-       numParams = 3;
-     }
-  }
-
-  nsCOMPtr<nsIProcess> process = do_CreateInstance(NS_PROCESS_CONTRACTID);
-
-  if (NS_FAILED(rv = process->Init(application)))
-     return rv;
-
-  PRUint32 pid;
-  if (NS_FAILED(rv = process->Run(PR_FALSE, params, numParams, &pid)))
-    return rv;
-
-  return NS_OK;
-}
-
 already_AddRefed<nsMIMEInfoOS2>
 nsOSHelperAppService::GetFromExtension(const nsCString& aFileExt) {
   // if the extension is empty, return immediately
   if (aFileExt.IsEmpty())
     return nsnull;
   
   LOG(("Here we do an extension lookup for '%s'\n", aFileExt.get()));
 
--- a/uriloader/exthandler/os2/nsOSHelperAppService.h
+++ b/uriloader/exthandler/os2/nsOSHelperAppService.h
@@ -59,17 +59,16 @@ public:
   virtual ~nsOSHelperAppService();
 
   // method overrides for mime.types and mime.info look up steps
   already_AddRefed<nsIMIMEInfo> GetMIMEInfoFromOS(const nsACString& aMimeType,
                                                   const nsACString& aFileExt,
                                                   PRBool     *aFound);
 
   // override nsIExternalProtocolService methods
-  nsresult LoadUriInternal(nsIURI * aURL);
   NS_IMETHODIMP GetApplicationDescription(const nsACString& aScheme, nsAString& _retval);
 
   nsresult OSProtocolHandlerExists(const char * aProtocolScheme, PRBool * aHandlerExists);
 protected:
   already_AddRefed<nsMIMEInfoOS2> GetFromType(const nsCString& aMimeType);
   already_AddRefed<nsMIMEInfoOS2> GetFromExtension(const nsCString& aFileExt);
 
 private:
new file mode 100644
--- /dev/null
+++ b/uriloader/exthandler/unix/nsMIMEInfoUnix.cpp
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 3; 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 Mozilla browser.
+ *
+ * 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):
+ *   Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * 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 ***** */
+
+#include "nsMIMEInfoWin.h"
+
+nsresult
+nsMIMEInfoUnix::LoadUriInternal(nsIURI * aURI)
+{
+  return nsGNOMERegistry::LoadURL(aURI);
+}
+
new file mode 100644
--- /dev/null
+++ b/uriloader/exthandler/unix/nsMIMEInfoUnix.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 3; 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 Mozilla browser.
+ *
+ * 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):
+ *   Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *
+ * 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 nsMIMEInfoUnix_h_
+#define nsMIMEInfoUnix_h_
+
+#include "nsMIMEInfoImpl.h"
+
+class nsMIMEInfoUnix : public nsMIMEInfoBase
+{
+protected:
+  virtual NS_HIDDEN_(nsresult) LoadUriInternal(nsIURI *aURI);
+};
+
+#endif // nsMIMEInfoUnix_h_
--- a/uriloader/exthandler/unix/nsOSHelperAppService.cpp
+++ b/uriloader/exthandler/unix/nsOSHelperAppService.cpp
@@ -1253,56 +1253,16 @@ nsresult nsOSHelperAppService::OSProtoco
   // Check the GConf registry for a protocol handler
   if (!*aHandlerExists)
     *aHandlerExists = nsGNOMERegistry::HandlerExists(aProtocolScheme);
 #endif
 
   return NS_OK;
 }
 
-nsresult nsOSHelperAppService::LoadUriInternal(nsIURI * aURI)
-{
-  // Gets a string pref network.protocol-handler.app.<scheme>
-  // and executes it
-  LOG(("-- nsOSHelperAppService::LoadUrl\n"));
-
-  nsCAutoString scheme;
-  nsresult rv = aURI->GetScheme(scheme);
-  if (NS_FAILED(rv)) // need a scheme
-    return rv;
-
-  nsCOMPtr<nsIFile> appFile;
-  rv = GetHandlerAppFromPrefs(scheme.get(), getter_AddRefs(appFile));
-  if (NS_SUCCEEDED(rv)) {
-    // Let's not support passing arguments for now
-    nsCOMPtr<nsIProcess> proc(do_CreateInstance("@mozilla.org/process/util;1", &rv));
-    if (NS_FAILED(rv))
-      return rv;
-
-    rv = proc->Init(appFile);
-    if (NS_FAILED(rv))
-      return rv;
-
-    nsCAutoString spec;
-    rv = aURI->GetAsciiSpec(spec);
-    if (NS_FAILED(rv))
-      return rv;
-
-    const char* args[] = { spec.get() };
-    PRUint32 tmp;
-    return proc->Run(/*blocking*/PR_FALSE, args, NS_ARRAY_LENGTH(args), &tmp);
-  }
-
-#ifdef MOZ_WIDGET_GTK2
-  return nsGNOMERegistry::LoadURL(aURI);
-#else
-  return rv;
-#endif
-}
-
 NS_IMETHODIMP nsOSHelperAppService::GetApplicationDescription(const nsACString& aScheme, nsAString& _retval)
 {
   nsCOMPtr<nsIFile> appFile;
   nsresult rv = GetHandlerAppFromPrefs(PromiseFlatCString(aScheme).get(),
                                        getter_AddRefs(appFile));
   if (NS_SUCCEEDED(rv))
     return appFile->GetLeafName(_retval);
 
--- a/uriloader/exthandler/win/nsMIMEInfoWin.cpp
+++ b/uriloader/exthandler/win/nsMIMEInfoWin.cpp
@@ -39,16 +39,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsArrayEnumerator.h"
 #include "nsCOMArray.h"
 #include "nsILocalFile.h"
 #include "nsIVariant.h"
 #include "nsMIMEInfoWin.h"
 #include "nsNetUtil.h"
+#include <shellapi.h>
 
 NS_IMPL_ISUPPORTS_INHERITED1(nsMIMEInfoWin, nsMIMEInfoBase, nsIPropertyBag)
 
 nsMIMEInfoWin::~nsMIMEInfoWin()
 {
 }
 
 nsresult
@@ -127,8 +128,44 @@ nsMIMEInfoWin::GetProperty(const nsAStri
 
     rv = GetIconURLVariant(executable, _retval);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
+// this implementation was pretty much copied verbatime from 
+// Tony Robinson's code in nsExternalProtocolWin.cpp
+nsresult
+nsMIMEInfoWin::LoadUriInternal(nsIURI * aURL)
+{
+  nsresult rv = NS_OK;
+
+  // 1. Find the default app for this protocol
+  // 2. Set up the command line
+  // 3. Launch the app.
+
+  // For now, we'll just cheat essentially, check for the command line
+  // then just call ShellExecute()!
+
+  if (aURL)
+  {
+    // extract the url spec from the url
+    nsCAutoString urlSpec;
+    aURL->GetAsciiSpec(urlSpec);
+
+    // Some versions of windows (Win2k before SP3, Win XP before SP1)
+    // crash in ShellExecute on long URLs (bug 161357).
+    // IE 5 and 6 support URLS of 2083 chars in length, 2K is safe
+    const PRUint32 maxSafeURL(2048);
+    if (urlSpec.Length() > maxSafeURL)
+      return NS_ERROR_FAILURE;
+
+    LONG r = (LONG) ::ShellExecute(NULL, "open", urlSpec.get(), NULL, NULL, 
+                                   SW_SHOWNORMAL);
+    if (r < 32) 
+      rv = NS_ERROR_FAILURE;
+  }
+
+  return rv;
+}
+
--- a/uriloader/exthandler/win/nsMIMEInfoWin.h
+++ b/uriloader/exthandler/win/nsMIMEInfoWin.h
@@ -52,16 +52,18 @@ class nsMIMEInfoWin : public nsMIMEInfoB
 
     NS_DECL_ISUPPORTS_INHERITED
     NS_DECL_NSIPROPERTYBAG
 
     void SetDefaultApplicationHandler(nsIFile* aDefaultApplication) 
     { 
       mDefaultApplication = aDefaultApplication; 
     }
+
   protected:
+    virtual NS_HIDDEN_(nsresult) LoadUriInternal(nsIURI *aURI);
     virtual nsresult LaunchDefaultWithFile(nsIFile* aFile);
   
   private:
     nsCOMPtr<nsIFile>      mDefaultApplication;
 };
 
 #endif
--- a/uriloader/exthandler/win/nsOSHelperAppService.cpp
+++ b/uriloader/exthandler/win/nsOSHelperAppService.cpp
@@ -152,52 +152,16 @@ nsresult nsOSHelperAppService::OSProtoco
        // close the key
        ::RegCloseKey(hKey);
      }
   }
 
   return NS_OK;
 }
 
-// this implementation was pretty much copied verbatime from 
-// Tony Robinson's code in nsExternalProtocolWin.cpp
-
-nsresult nsOSHelperAppService::LoadUriInternal(nsIURI * aURL)
-{
-  nsresult rv = NS_OK;
-
-  // 1. Find the default app for this protocol
-  // 2. Set up the command line
-  // 3. Launch the app.
-
-  // For now, we'll just cheat essentially, check for the command line
-  // then just call ShellExecute()!
-
-  if (aURL)
-  {
-    // extract the url spec from the url
-    nsCAutoString urlSpec;
-    aURL->GetAsciiSpec(urlSpec);
-
-    // Some versions of windows (Win2k before SP3, Win XP before SP1)
-    // crash in ShellExecute on long URLs (bug 161357).
-    // IE 5 and 6 support URLS of 2083 chars in length, 2K is safe
-    const PRUint32 maxSafeURL(2048);
-    if (urlSpec.Length() > maxSafeURL)
-      return NS_ERROR_FAILURE;
-
-    LONG r = (LONG) ::ShellExecute(NULL, "open", urlSpec.get(), NULL, NULL, 
-                                   SW_SHOWNORMAL);
-    if (r < 32) 
-      rv = NS_ERROR_FAILURE;
-  }
-
-  return rv;
-}
-
 NS_IMETHODIMP nsOSHelperAppService::GetApplicationDescription(const nsACString& aScheme, nsAString& _retval)
 {
   nsCOMPtr<nsIWindowsRegKey> regKey = 
     do_CreateInstance("@mozilla.org/windows-registry-key;1");
   if (!regKey) 
     return NS_ERROR_NOT_AVAILABLE;
 
   NS_ConvertASCIItoUTF16 buf(aScheme);