Bug 348808 - "use application selector instead of file selector dialog when picking helper apps" [p=jimm r=dolske r=rob_strong r=myk r=biesi a=blocking-firefox3+ for M9]
authorreed@reedloden.com
Thu, 18 Oct 2007 21:15:41 -0700
changeset 7018 7ecfc6d09d7fe46a77692bf7bdf557c006f1a16e
parent 7017 382e77b43fe21549afe7461da24dd6735abdbf7c
child 7019 6938ca5ddbeed129f3a1e1ee56faa743f9a9f00a
push id1
push userbsmedberg@mozilla.com
push dateThu, 20 Mar 2008 16:49:24 +0000
treeherdermozilla-central@61007906a1f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdolske, rob_strong, myk, biesi, blocking-firefox3
bugs348808
milestone1.9a9pre
Bug 348808 - "use application selector instead of file selector dialog when picking helper apps" [p=jimm r=dolske r=rob_strong r=myk r=biesi a=blocking-firefox3+ for M9]
browser/components/preferences/applications.js
netwerk/mime/public/nsIMIMEInfo.idl
toolkit/components/Makefile.in
toolkit/components/apppicker/Makefile.in
toolkit/components/apppicker/content/appPicker.css
toolkit/components/apppicker/content/appPicker.js
toolkit/components/apppicker/content/appPicker.xul
toolkit/components/apppicker/jar.mn
toolkit/locales/en-US/chrome/global/appPicker.dtd
toolkit/locales/jar.mn
toolkit/mozapps/downloads/src/nsHelperAppDlg.js.in
uriloader/exthandler/nsMIMEInfoImpl.cpp
uriloader/exthandler/nsMIMEInfoImpl.h
uriloader/exthandler/win/nsMIMEInfoWin.cpp
uriloader/exthandler/win/nsMIMEInfoWin.h
uriloader/exthandler/win/nsOSHelperAppService.cpp
uriloader/exthandler/win/nsOSHelperAppService.h
--- a/browser/components/preferences/applications.js
+++ b/browser/components/preferences/applications.js
@@ -1494,36 +1494,67 @@ var gApplicationsPane = {
                           this._getIconURLForPreferredAction(handlerInfo));
   },
 
   chooseApp: function(aEvent) {
     // Don't let the normal "on select action" handler get this event,
     // as we handle it specially ourselves.
     aEvent.stopPropagation();
 
+    var handlerApp;
+
+#ifdef XP_WIN
+    var params = {};
+    var handlerInfo = this._handledTypes[this._list.selectedItem.type];
+
+    if (handlerInfo.type == TYPE_MAYBE_FEED) {
+      // MIME info will be null, create a temp object.
+      params.mimeInfo = this._mimeSvc.getFromTypeAndExtension(handlerInfo.type, 
+                                                 handlerInfo.primaryExtension);
+    } else {
+      params.mimeInfo = handlerInfo.wrappedHandlerInfo;
+    }
+
+    params.title         = this._prefsBundle.getString("fpTitleChooseApp");
+    params.description   = handlerInfo.description;
+    params.filename      = null;
+    params.handlerApp    = null;
+
+    window.openDialog("chrome://global/content/appPicker.xul", null,
+                      "chrome,modal,centerscreen,titlebar,dialog=yes",
+                      params);
+
+    if (params.handlerApp && 
+        params.handlerApp.executable && 
+        params.handlerApp.executable.isFile()) {
+      handlerApp = params.handlerApp;
+
+      // Add the app to the type's list of possible handlers.
+      handlerInfo.possibleApplicationHandlers.appendElement(handlerApp, false);
+    }
+#else
     var fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
     var winTitle = this._prefsBundle.getString("fpTitleChooseApp");
     fp.init(window, winTitle, Ci.nsIFilePicker.modeOpen);
     fp.appendFilters(Ci.nsIFilePicker.filterApps);
 
-    var handlerApp;
-
     // Prompt the user to pick an app.  If they pick one, and it's a valid
     // selection, then add it to the list of possible handlers.
     if (fp.show() == Ci.nsIFilePicker.returnOK && fp.file &&
         this._isValidHandlerExecutable(fp.file)) {
       handlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"].
                    createInstance(Ci.nsILocalHandlerApp);
       handlerApp.name = getDisplayNameForFile(fp.file);
       handlerApp.executable = fp.file;
 
       // Add the app to the type's list of possible handlers.
       let handlerInfo = this._handledTypes[this._list.selectedItem.type];
       handlerInfo.possibleApplicationHandlers.appendElement(handlerApp, false);
     }
+#endif
 
     // Rebuild the actions menu whether the user picked an app or canceled.
     // If they picked an app, we want to add the app to the menu and select it.
     // If they canceled, we want to go back to their previous selection.
     this.rebuildActionsMenu();
 
     // If the user picked a new app from the menu, select it.
     if (handlerApp) {
--- a/netwerk/mime/public/nsIMIMEInfo.idl
+++ b/netwerk/mime/public/nsIMIMEInfo.idl
@@ -38,16 +38,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
 interface nsIURI;
 interface nsIFile;
 interface nsIUTF8StringEnumerator;
 interface nsIHandlerApp;
+interface nsIArray;
 interface nsIMutableArray;
 interface nsIInterfaceRequestor;
 
 typedef long nsHandlerInfoAction;
 
 /**
  * nsIHandlerInfo gives access to the information about how a given protocol
  * scheme or MIME-type is handled.
@@ -154,17 +155,17 @@ interface nsIHandlerInfo : nsISupports {
  * nsIMIMEInfo extends nsIHandlerInfo with a bunch of information specific to
  * MIME content-types. There is a one-to-many relationship between MIME types
  * and file extensions. This means that a MIMEInfo object may have multiple
  * file extensions associated with it.  However, the reverse is not true.
  *
  * MIMEInfo objects are generally retrieved from the MIME Service
  * @see nsIMIMEService
  */
-[scriptable, uuid(a4011016-81a1-47d9-b01e-19a2272ae89b)]
+[scriptable, uuid(cd7083f8-5fe9-4248-bb09-0b0e2982fde8)]
 interface nsIMIMEInfo : nsIHandlerInfo {
     /**
      * Gives you an array of file types associated with this type.
      *
      * @return Number of elements in the array.
      * @return Array of extensions.
      */
     nsIUTF8StringEnumerator getFileExtensions();
@@ -213,16 +214,27 @@ interface nsIMIMEInfo : nsIHandlerInfo {
     /**
      * Returns whether or not these two nsIMIMEInfos are logically
      * equivalent.
      *
      * @returns PR_TRUE if the two are considered equal
      */
     boolean equals(in nsIMIMEInfo aMIMEInfo);
 
+    /** 
+     * Returns a list of nsILocalHandlerApp objects containing
+     * handlers associated with this mimeinfo. Implemented per 
+     * platform using information in this object to generate the
+     * best list. Typically used for an "open with" style user 
+     * option.
+     * 
+     * @return nsIArray of nsILocalHandlerApp
+     */
+    readonly attribute nsIArray possibleLocalHandlers;
+
     /**
      * Launches the application with the specified file, in a way that
      * depends on the value of preferredAction. preferredAction must be
      * useHelperApp or useSystemDefault.
      *
      * @param aFile The file to launch this application with.
      *
      * @throw NS_ERROR_INVALID_ARG if action is not valid for this function.
--- a/toolkit/components/Makefile.in
+++ b/toolkit/components/Makefile.in
@@ -53,16 +53,17 @@ DIRS += \
         contentprefs \
         microformats \
         $(NULL)
 
 # These component dirs are built only for XUL apps
 
 ifdef MOZ_XUL_APP
 DIRS += \
+	apppicker \
 	filepicker \
 	console \
 	viewconfig \
 	typeaheadfind \
 	$(NULL)
 
 ifneq (,$(filter cocoa, $(MOZ_WIDGET_TOOLKIT)))
 TOOL_DIRS += alerts
new file mode 100644
--- /dev/null
+++ b/toolkit/components/apppicker/Makefile.in
@@ -0,0 +1,47 @@
+#
+# ***** 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
+# Netscape Communications Corporation.
+# Portions created by the Initial Developer are Copyright (C) 1998
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either 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
+
+include $(topsrcdir)/config/rules.mk
+
new file mode 100644
--- /dev/null
+++ b/toolkit/components/apppicker/content/appPicker.css
@@ -0,0 +1,39 @@
+
+#app-picker {
+    width:320px !important;
+    max-width:320px !important;
+}
+
+#content-description {
+    font-weight:bold;
+}
+
+#suggested-filename {
+    font-weight:normal;
+}
+
+#file-info {
+}
+
+#app-picker-list {
+    height:225px;
+    min-height:225px;
+}
+
+#app-picker-item {
+    padding-bottom:5px;
+    padding-top:5px;
+}
+
+#app-picker-item-image {
+}
+
+#app-picker-item-cell {
+    font-weight:normal; 
+    padding-right:10px;
+    padding-left:10px;
+}
+
+#browse-button {
+    margin-top:10px;
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/toolkit/components/apppicker/content/appPicker.js
@@ -0,0 +1,282 @@
+# -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+# Jim Mathies <jmathies@mozilla.com>
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the 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 *****
+
+function AppPicker() {};
+
+var g_dialog = null;
+
+AppPicker.prototype = 
+{
+    // Class members
+    _incomingParams:null,
+
+    /** 
+    * Init the dialog and populate the application list
+    */ 
+    appPickerLoad: function appPickerLoad() {
+        const nsILocalHandlerApp = Components.interfaces.nsILocalHandlerApp;
+
+        this._incomingParams = window.arguments[0];
+        this._incomingParams.handlerApp = null;
+
+        document.title = this._incomingParams.title;
+
+        // Header creation - at the very least, we must have 
+        // a mime type:
+        //        
+        // (icon) Zip File
+        // (icon) filename
+        //
+        // (icon) Web Feed
+        // (icon) mime/type
+        //
+        // (icon) mime/type
+        // (icon)
+
+        var mimeInfo = this._incomingParams.mimeInfo;
+        var filename = this._incomingParams.filename;
+        if (!filename) {
+          filename = mimeInfo.MIMEType;
+        }
+        var description = this._incomingParams.description;
+        if (!description) {
+          description = filename;
+          filename = "";
+        }
+        
+        // Setup the dialog header information
+        document.getElementById("content-description").setAttribute("value", 
+          description);
+        document.getElementById("suggested-filename").setAttribute("value",
+          filename);
+        document.getElementById("content-icon").setAttribute("src",
+          "moz-icon://" + filename + "?size=32&contentType=" +
+          mimeInfo.MIMEType);
+
+        // Grab a list of nsILocalHandlerApp application helpers to list
+        var fileList = mimeInfo.possibleLocalHandlers;
+
+        /*
+          <richlistitem id="app-picker-item" value=nsfileobject>
+            <hbox align="center">
+              <image id="app-picker-item-image" src=""/>
+              <vbox>
+                <hbox align="center">
+                  <listcell id="app-picker-item-cell" label="Outlook"/>
+                </hbox>
+              </vbox>
+            </hbox>
+          </richlistitem>
+        */
+
+        var list = document.getElementById("app-picker-list");
+
+        var primaryCount = 0;
+        
+        if (!fileList || fileList.length == 0) {
+          // display a message saying nothing is configured
+          document.getElementById("app-picker-notfound").removeAttribute("hidden");
+          return;
+        }
+        
+        for (var idx = 0; idx < fileList.length; idx++) {
+          var file = fileList.queryElementAt(idx, nsILocalHandlerApp);
+          try {
+              if (!file.executable || !file.executable.isFile())
+                continue;
+          } catch (err) {
+            continue;
+          }
+
+          var item = document.createElement("richlistitem");
+          item.setAttribute("id", "app-picker-item");
+          item.value = file;
+          item.setAttribute("ondblclick", "g_dialog.appDoubleClick();");
+
+          var hbox1 = document.createElement("hbox");
+          hbox1.setAttribute("align", "center");
+
+          var image = document.createElement("image");
+          image.setAttribute("id", "app-picker-item-image");
+          image.setAttribute("src", this.getFileIconURL(file.executable));
+
+          var vbox1 = document.createElement("vbox");
+          var hbox2 = document.createElement("hbox");
+          hbox2.setAttribute("align", "center");
+
+          var cell = document.createElement("listcell");
+          cell.setAttribute("id", "app-picker-item-cell");
+          cell.setAttribute("label", this.getFileDisplayName(file.executable));
+
+          hbox2.appendChild(cell);
+          vbox1.appendChild(hbox2);
+          hbox1.appendChild(image);
+          hbox1.appendChild(vbox1);
+          item.appendChild(hbox1);
+          list.appendChild(item);
+
+          primaryCount++;
+        }
+
+        if ( primaryCount == 0 ) {
+          // display a message saying nothing is configured
+          document.getElementById("app-picker-notfound").removeAttribute("hidden");
+        }
+    },
+
+    /** 
+    * Retrieve the moz-icon for the app
+    */ 
+    getFileIconURL: function getFileIconURL(file) {
+      var ios = Components.classes["@mozilla.org/network/io-service;1"].
+                getService(Components.interfaces.nsIIOService);
+
+      if (!ios) return "";
+      const nsIFileProtocolHandler =
+        Components.interfaces.nsIFileProtocolHandler;
+
+      var fph = ios.getProtocolHandler("file")
+                .QueryInterface(nsIFileProtocolHandler);
+      if (!fph) return "";
+
+      var urlSpec = fph.getURLSpecFromFile(file);
+      return "moz-icon://" + urlSpec + "?size=32";
+    },
+
+    /** 
+    * Retrieve the pretty description from the file
+    */ 
+    getFileDisplayName: function getFileDisplayName(file) {
+#ifdef XP_WIN
+      const nsILocalFileWin = Components.interfaces.nsILocalFileWin;
+      if (file instanceof nsILocalFileWin) {
+        try {
+          return file.getVersionInfoField("FileDescription");
+        } catch (e) {
+        }
+      }
+#endif
+#ifdef XP_MACOSX
+    const nsILocalFileMac = Components.interfaces.nsILocalFileMac;
+    if (file instanceof nsILocalFileMac) {
+      try {
+        return lfm.bundleDisplayName;
+      } catch (e) {
+      }
+    }
+#endif
+      return file.leafName;
+    },
+
+    /**
+    * Double click accepts an app
+    */
+    appDoubleClick: function appDoubleClick() {
+      var list = document.getElementById("app-picker-list");
+      var selItem = list.selectedItem;
+
+      if (!selItem) {
+          this._incomingParams.handlerApp = null;
+          return true;
+      }
+
+      this._incomingParams.handlerApp = selItem.value;
+      window.close();
+
+      return true;
+    },
+
+    appPickerOK: function appPickerOK() {
+      if (this._incomingParams.handlerApp) return true;
+
+      var list = document.getElementById("app-picker-list");
+      var selItem = list.selectedItem;
+
+      if (!selItem) {
+        this._incomingParams.handlerApp = null;
+        return true;
+      }
+      this._incomingParams.handlerApp = selItem.value;
+
+      return true;
+    },
+
+    appPickerCancel: function appPickerCancel() {
+      this._incomingParams.handlerApp = null;
+      return true;
+    },
+
+    /**
+    * User browse for an app.
+    */
+    appPickerBrowse: function appPickerBrowse() {
+      var nsIFilePicker = Components.interfaces.nsIFilePicker;
+      var fp = Components.classes["@mozilla.org/filepicker;1"].
+               createInstance(nsIFilePicker);
+
+      fp.init(window, this._incomingParams.title, nsIFilePicker.modeOpen);
+      fp.appendFilters(nsIFilePicker.filterApps);
+      
+      var fileLoc = Components.classes["@mozilla.org/file/directory_service;1"]
+                            .getService(Components.interfaces.nsIProperties);
+      var startLocation;
+#ifdef XP_WIN
+    startLocation = "ProgF"; // Program Files
+#else
+#ifdef XP_MACOSX
+    startLocation = "LocApp"; // Local Applications
+#else
+    startLocation = "Home";
+#endif
+#endif
+      fp.displayDirectory = 
+        fileLoc.get(startLocation, Components.interfaces.nsILocalFile);
+      
+      if (fp.show() == nsIFilePicker.returnOK && fp.file) {
+          var localHandlerApp = 
+            Components.classes["@mozilla.org/uriloader/local-handler-app;1"].
+            createInstance(Components.interfaces.nsILocalHandlerApp);
+          localHandlerApp.executable = fp.file;
+
+          this._incomingParams.handlerApp = localHandlerApp;
+          window.close();
+      }
+      return true;
+    }
+}
+
+// Global object
+var g_dialog = new AppPicker();
new file mode 100644
--- /dev/null
+++ b/toolkit/components/apppicker/content/appPicker.xul
@@ -0,0 +1,74 @@
+<?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 mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+# Jim Mathies <jmathies@mozilla.com>
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the 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 *****
+
+  <?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
+  <?xml-stylesheet href="chrome://global/content/appPicker.css" type="text/css"?>
+
+  <!DOCTYPE dialog SYSTEM "chrome://global/locale/appPicker.dtd" >
+
+  <dialog id="app-picker"
+    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+    onload="g_dialog.appPickerLoad();"
+    buttons="accept,cancel"
+    defaultButton="cancel"
+    ondialogaccept="return g_dialog.appPickerOK();"
+    ondialogcancel="return g_dialog.appPickerCancel();"
+    persist="screenX screenY">
+
+    <script type="application/javascript" src="chrome://global/content/appPicker.js"/>
+
+    <vbox>
+      <hbox id="file-info" align="left">
+        <image id="content-icon" src="" />
+        <vbox>
+          <label id="content-description" value=""/>
+          <label id="suggested-filename" value=""/>
+        </vbox>
+      </hbox>
+      <spacer height="10px"/>
+      <label id="sendto-message" value="&SendMsg.label;"/>
+
+      <richlistbox id="app-picker-list">
+      </richlistbox>
+
+      <label id="app-picker-notfound" value="&NoAppFound.label;" hidden="true"/>
+
+      <hbox>
+        <spacer flex="1"/>
+        <button id="browse-button" onclick="g_dialog.appPickerBrowse();" label="&BrowseButton.label;" align="center"/>
+      </hbox>
+    </vbox>
+    <separator id="groove" class="groove"/>
+  </dialog>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/toolkit/components/apppicker/jar.mn
@@ -0,0 +1,5 @@
+toolkit.jar:
+* content/global/appPicker.xul                     (content/appPicker.xul)
+* content/global/appPicker.js                      (content/appPicker.js)
+* content/global/appPicker.css                     (content/appPicker.css)
+
new file mode 100644
--- /dev/null
+++ b/toolkit/locales/en-US/chrome/global/appPicker.dtd
@@ -0,0 +1,3 @@
+<!ENTITY NoAppFound.label      "No applications were found for this file type.">
+<!ENTITY BrowseButton.label    "Browse...">
+<!ENTITY SendMsg.label         "Send this item to:">
--- a/toolkit/locales/jar.mn
+++ b/toolkit/locales/jar.mn
@@ -1,13 +1,14 @@
 #filter substitution
 
 @AB_CD@.jar:
 % locale global @AB_CD@ %locale/@AB_CD@/global/
   locale/@AB_CD@/global/about.dtd                       (%chrome/global/about.dtd)
+  locale/@AB_CD@/global/appPicker.dtd                   (%chrome/global/appPicker.dtd)
   locale/@AB_CD@/global/brand.dtd                       (generic/chrome/global/brand.dtd)
 + locale/@AB_CD@/global/browser.properties              (%chrome/global/browser.properties)
 + locale/@AB_CD@/global/charsetOverlay.dtd              (%chrome/global/charsetOverlay.dtd)
 + locale/@AB_CD@/global/commonDialog.dtd                (%chrome/global/commonDialog.dtd)
 + locale/@AB_CD@/global/commonDialogs.properties        (%chrome/global/commonDialogs.properties)
 + locale/@AB_CD@/global/config.dtd                      (%chrome/global/config.dtd)
 + locale/@AB_CD@/global/config.properties               (%chrome/global/config.properties)
 * locale/@AB_CD@/global/console.dtd                     (%chrome/global/console.dtd)
--- a/toolkit/mozapps/downloads/src/nsHelperAppDlg.js.in
+++ b/toolkit/mozapps/downloads/src/nsHelperAppDlg.js.in
@@ -21,16 +21,17 @@
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #   Bill Law <law@netscape.com>
 #   Scott MacGregor <mscott@netscape.com>
 #   Ben Goodger <ben@bengoodger.com> (2.0)
 #   Fredrik Holmqvist <thesuckiestemail@yahoo.se>
 #   Dan Mosedale <dmose@mozilla.org>
+#   Jim Mathies <jmathies@mozilla.com>
 #
 # Alternatively, the contents of this file may be used under the terms of
 # either the GNU General Public License Version 2 or later (the "GPL"), or
 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 # in which case the provisions of the GPL or the LGPL are applicable instead
 # of those above. If you wish to allow use of your version of this file only
 # under the terms of either the GPL or the LGPL, and not to allow others to
 # use your version of this file under the terms of the MPL, indicate your
@@ -926,20 +927,101 @@ nsUnknownContentTypeDialog.prototype = {
       return true;
     },
 
     // dialogElement:  Convenience. 
     dialogElement: function(id) {
       return this.mDialog.document.getElementById(id);
     },
 
+    // Retrieve the pretty description from the file
+    getFileDisplayName: function getFileDisplayName(file)
+    { 
+#ifdef XP_WIN
+        if (file instanceof Components.interfaces.nsILocalFileWin) {
+          try {
+            return file.getVersionInfoField("FileDescription");
+          } catch (ex) {
+          }
+        }
+#endif
+        return file.leafName;
+    },
+
     // chooseApp:  Open file picker and prompt user for application.
     chooseApp: function() {
+#ifdef XP_WIN
+    // Protect against the lack of an extension    
+    var fileExtension = "";
+    try {
+        fileExtension = this.mLauncher.MIMEInfo.primaryExtension;
+    } catch(ex) {
+    }
+
+    // Try to use the pretty description of the type, if one is available.
+    var typeString = this.mLauncher.MIMEInfo.description;
+
+    if (!typeString) {
+      // If there is none, use the extension to 
+      // identify the file, e.g. "ZIP file"
+      if (fileExtension) {
+        typeString =
+          this.dialogElement("strings").
+          getFormattedString("fileType", [fileExtension.toUpperCase()]);
+      } else {
+        // If we can't even do that, just give up and show the MIME type.
+        typeString = this.mLauncher.MIMEInfo.MIMEType;
+      }
+    }
+
+    var params = {};
+    params.title = 
+      this.dialogElement("strings").getString("chooseAppFilePickerTitle");
+    params.description = typeString;
+    params.filename    = this.mLauncher.suggestedFileName;
+    params.mimeInfo    = this.mLauncher.MIMEInfo;
+    params.handlerApp  = null;
+
+    this.mDialog.openDialog("chrome://global/content/appPicker.xul", null,
+                            "chrome,modal,centerscreen,titlebar,dialog=yes",
+                            params);
+
+    if (params.handlerApp &&
+        params.handlerApp.executable &&
+        params.handlerApp.executable.isFile()) {
+        // Show the "handler" menulist since we have a (user-specified) 
+        // application now.
+        this.dialogElement("modeDeck").setAttribute("selectedIndex", "0");
+
+        // Remember the file they chose to run.
+        this.chosenApp = params.handlerApp;
+
+        // Update dialog
+        var otherHandler = this.dialogElement("otherHandler");
+        otherHandler.removeAttribute("hidden");
+        otherHandler.setAttribute("path",
+          this.getPath(this.chosenApp.executable));
+        otherHandler.label = 
+          this.getFileDisplayName(this.chosenApp.executable);
+        this.dialogElement("openHandler").selectedIndex = 1;
+        this.dialogElement("openHandler").setAttribute("lastSelectedItemID",
+          "otherHandler");
+        this.dialogElement("mode").selectedItem = this.dialogElement("open");
+    } else {
+        var openHandler = this.dialogElement("openHandler");
+        var lastSelectedID = openHandler.getAttribute("lastSelectedItemID");
+        if (!lastSelectedID)
+            lastSelectedID = "defaultHandler";
+        openHandler.selectedItem = this.dialogElement(lastSelectedID);
+    }
+
+#else
       var nsIFilePicker = Components.interfaces.nsIFilePicker;
-      var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
+      var fp = Components.classes["@mozilla.org/filepicker;1"]
+                         .createInstance(nsIFilePicker);
       fp.init(this.mDialog,
               this.dialogElement("strings").getString("chooseAppFilePickerTitle"),
               nsIFilePicker.modeOpen);
 
       fp.appendFilters(nsIFilePicker.filterApps);
 
       if (fp.show() == nsIFilePicker.returnOK && fp.file) {
         // Show the "handler" menulist since we have a (user-specified) 
@@ -965,16 +1047,17 @@ nsUnknownContentTypeDialog.prototype = {
       }
       else {
         var openHandler = this.dialogElement("openHandler");
         var lastSelectedID = openHandler.getAttribute("lastSelectedItemID");
         if (!lastSelectedID)
           lastSelectedID = "defaultHandler";
         openHandler.selectedItem = this.dialogElement(lastSelectedID);
       }
+#endif
     },
 
     // Turn this on to get debugging messages.
     debug: false,
 
     // Dump text (if debug is on).
     dump: function( text ) {
         if ( this.debug ) {
--- a/uriloader/exthandler/nsMIMEInfoImpl.cpp
+++ b/uriloader/exthandler/nsMIMEInfoImpl.cpp
@@ -471,8 +471,13 @@ nsMIMEInfoImpl::LaunchDefaultWithFile(ns
     return NS_ERROR_FILE_NOT_FOUND;
 
   nsCAutoString nativePath;
   aFile->GetNativePath(nativePath);
   
   return LaunchWithIProcess(mDefaultApplication, nativePath);
 }
 
+NS_IMETHODIMP
+nsMIMEInfoBase::GetPossibleLocalHandlers(nsIArray **_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
--- a/uriloader/exthandler/nsMIMEInfoImpl.h
+++ b/uriloader/exthandler/nsMIMEInfoImpl.h
@@ -90,16 +90,17 @@ class nsMIMEInfoBase : public nsIMIMEInf
     NS_IMETHOD GetDefaultDescription(nsAString & aDefaultDescription);
     NS_IMETHOD LaunchWithFile(nsIFile *aFile);
     NS_IMETHOD LaunchWithURI(nsIURI *aURI,
                              nsIInterfaceRequestor *aWindowContext);
     NS_IMETHOD GetPreferredAction(nsHandlerInfoAction *aPreferredAction);
     NS_IMETHOD SetPreferredAction(nsHandlerInfoAction aPreferredAction);
     NS_IMETHOD GetAlwaysAskBeforeHandling(PRBool *aAlwaysAskBeforeHandling);
     NS_IMETHOD SetAlwaysAskBeforeHandling(PRBool aAlwaysAskBeforeHandling); 
+    NS_IMETHOD GetPossibleLocalHandlers(nsIArray **_retval); 
 
     enum HandlerClass {
       eMIMEInfo,
       eProtocolInfo
     };
 
     // nsMIMEInfoBase methods
     nsMIMEInfoBase(const char *aMIMEType = "") NS_HIDDEN;
--- a/uriloader/exthandler/win/nsMIMEInfoWin.cpp
+++ b/uriloader/exthandler/win/nsMIMEInfoWin.cpp
@@ -41,16 +41,24 @@
 #include "nsArrayEnumerator.h"
 #include "nsCOMArray.h"
 #include "nsILocalFile.h"
 #include "nsIVariant.h"
 #include "nsMIMEInfoWin.h"
 #include "nsNetUtil.h"
 #include <Windows.h>
 #include <shellapi.h>
+#include "nsAutoPtr.h"
+#include "nsIMutableArray.h"
+#include "nsTArray.h"
+#include "shlobj.h"
+#include "windows.h"
+#include "nsIWindowsRegKey.h"
+#include "nsIProcess.h"
+#include "nsOSHelperAppService.h"
 
 NS_IMPL_ISUPPORTS_INHERITED1(nsMIMEInfoWin, nsMIMEInfoBase, nsIPropertyBag)
 
 nsMIMEInfoWin::~nsMIMEInfoWin()
 {
 }
 
 nsresult
@@ -65,16 +73,101 @@ nsMIMEInfoWin::LaunchDefaultWithFile(nsI
   local->IsExecutable(&executable);
   if (executable)
     return NS_ERROR_FAILURE;
 
   return local->Launch();
 }
 
 NS_IMETHODIMP
+nsMIMEInfoWin::LaunchWithFile(nsIFile* aFile)
+{
+  nsresult rv;
+
+  // it doesn't make any sense to call this on protocol handlers
+  NS_ASSERTION(mClass == eMIMEInfo,
+               "nsMIMEInfoBase should have mClass == eMIMEInfo");
+
+  if (mPreferredAction == useSystemDefault) {
+    return LaunchDefaultWithFile(aFile);
+  }
+
+  if (mPreferredAction == useHelperApp) {
+    if (!mPreferredApplication)
+      return NS_ERROR_FILE_NOT_FOUND;
+
+    // at the moment, we only know how to hand files off to local handlers
+    nsCOMPtr<nsILocalHandlerApp> localHandler = 
+      do_QueryInterface(mPreferredApplication, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsIFile> executable;
+    rv = localHandler->GetExecutable(getter_AddRefs(executable));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCAutoString path;
+    aFile->GetNativePath(path);
+
+    // Deal with local dll based handlers
+    nsCString filename;
+    executable->GetNativeLeafName(filename);
+    if (filename.Length() > 4) {
+      nsCString extension(Substring(filename, filename.Length() - 4, 4));
+
+      if (extension.LowerCaseEqualsLiteral(".dll")) {
+        nsAutoString args;
+
+        // executable is rundll32, everything else is a list of parameters, 
+        // including the dll handler.
+        nsCOMPtr<nsILocalFile> locFile(do_QueryInterface(aFile));
+
+        if (!GetDllLaunchInfo(executable, locFile, args, PR_FALSE))
+          return NS_ERROR_INVALID_ARG;
+
+        PRUint32 result = (PRUint32)
+          ::ShellExecuteW(NULL, NULL, L"rundll32.exe", args.get(),
+                          NULL, SW_SHOWNORMAL);
+        // Returns a value greater than 32 if successful. See msdn.
+        if (result >= 32)
+          return NS_OK;
+
+        switch (result) {
+          case 0:
+          case SE_ERR_OOM:
+            return NS_ERROR_OUT_OF_MEMORY;
+          case ERROR_FILE_NOT_FOUND:
+            return NS_ERROR_FILE_NOT_FOUND;
+          case ERROR_PATH_NOT_FOUND:
+            return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+          case ERROR_BAD_FORMAT:
+            return NS_ERROR_FILE_CORRUPTED;
+          case SE_ERR_ACCESSDENIED:
+            return NS_ERROR_FILE_ACCESS_DENIED;
+          case SE_ERR_ASSOCINCOMPLETE:
+          case SE_ERR_NOASSOC:
+            return NS_ERROR_UNEXPECTED;
+          case SE_ERR_DDEBUSY:
+          case SE_ERR_DDEFAIL:
+          case SE_ERR_DDETIMEOUT:
+            return NS_ERROR_NOT_AVAILABLE;
+          case SE_ERR_DLLNOTFOUND:
+            return NS_ERROR_FAILURE;
+          case SE_ERR_SHARE:
+            return NS_ERROR_FILE_IS_LOCKED;
+        }
+        return NS_ERROR_FILE_EXECUTION_FAILED;
+      }
+    }
+    return LaunchWithIProcess(executable, path);
+  }
+
+  return NS_ERROR_INVALID_ARG;
+}
+
+NS_IMETHODIMP
 nsMIMEInfoWin::GetHasDefaultHandler(PRBool * _retval)
 {
   // We have a default application if we have a description
   // We can ShellExecute anything; however, callers are probably interested if
   // there is really an application associated with this type of file
   *_retval = !mDefaultAppDescription.IsEmpty();
   return NS_OK;
 }
@@ -165,8 +258,619 @@ nsMIMEInfoWin::LoadUriInternal(nsIURI * 
                                    SW_SHOWNORMAL);
     if (r < 32) 
       rv = NS_ERROR_FAILURE;
   }
 
   return rv;
 }
 
+// Given a path to a local file, return its nsILocalHandlerApp instance.
+PRBool nsMIMEInfoWin::GetLocalHandlerApp(const nsAString& aCommandHandler,
+                                         nsCOMPtr<nsILocalHandlerApp>& aApp)
+{
+  nsCOMPtr<nsILocalFile> locfile;
+  nsresult rv = 
+    NS_NewLocalFile(aCommandHandler, PR_TRUE, getter_AddRefs(locfile));
+  if (NS_FAILED(rv))
+    return PR_FALSE;
+
+  aApp = do_CreateInstance("@mozilla.org/uriloader/local-handler-app;1");
+  if (!aApp) 
+    return PR_FALSE;
+
+  aApp->SetExecutable(locfile);
+  return PR_TRUE;
+}
+
+// Return the cleaned up file path associated with a command verb 
+// located in root/Applications.
+PRBool nsMIMEInfoWin::GetAppsVerbCommandHandler(const nsAString& appExeName,
+                                                nsAString& applicationPath,
+                                                PRBool edit)
+{
+  nsCOMPtr<nsIWindowsRegKey> appKey = 
+    do_CreateInstance("@mozilla.org/windows-registry-key;1");
+  if (!appKey) 
+    return PR_FALSE; 
+
+  // HKEY_CLASSES_ROOT\Applications\iexplore.exe
+  nsAutoString applicationsPath;
+  applicationsPath.AppendLiteral("Applications\\");
+  applicationsPath.Append(appExeName);
+
+  nsresult rv = appKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
+                             applicationsPath,
+                             nsIWindowsRegKey::ACCESS_QUERY_VALUE);
+  if (NS_FAILED(rv)) 
+    return PR_FALSE;
+
+  // Check for the NoOpenWith flag, if it exists
+  PRUint32 value;
+  if (NS_SUCCEEDED(appKey->ReadIntValue(
+      NS_LITERAL_STRING("NoOpenWith"), &value)) &&
+      value == 1)
+    return PR_FALSE;
+
+  nsAutoString dummy;
+  if (NS_SUCCEEDED(appKey->ReadStringValue(
+        NS_LITERAL_STRING("NoOpenWith"), dummy)))
+    return PR_FALSE;
+
+  appKey->Close();
+
+  // HKEY_CLASSES_ROOT\Applications\iexplore.exe\shell\open\command
+  applicationsPath.AssignLiteral("Applications\\");
+  applicationsPath.Append(appExeName);
+  if (!edit)
+    applicationsPath.AppendLiteral("\\shell\\open\\command");
+  else
+    applicationsPath.AppendLiteral("\\shell\\edit\\command");
+
+
+  rv = appKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
+                    applicationsPath,
+                    nsIWindowsRegKey::ACCESS_QUERY_VALUE);
+  if (NS_FAILED(rv)) 
+    return PR_FALSE;
+
+  nsAutoString appFilesystemCommand;
+  if (NS_SUCCEEDED(appKey->ReadStringValue(EmptyString(), 
+                                           appFilesystemCommand))) {
+    
+    // Expand environment vars, clean up any misc.
+    if (!nsOSHelperAppService::CleanupCmdHandlerPath(appFilesystemCommand))
+      return PR_FALSE;
+    
+    applicationPath = appFilesystemCommand;
+    return PR_TRUE;
+  }
+  return PR_FALSE;
+}
+
+// Return a fully populated command string based on
+// passing information. Used in launchWithFile to trace
+// back to the full handler path based on the dll.
+// (dll, targetfile, return args, open/edit)
+PRBool nsMIMEInfoWin::GetDllLaunchInfo(nsIFile * aDll,
+                                       nsILocalFile * aFile,
+                                       nsAString& args,
+                                       PRBool edit)
+{
+  if (!aDll || !aFile) 
+    return PR_FALSE;
+
+  nsCOMPtr<nsILocalFile> localDll(do_QueryInterface(aDll));
+  if (!localDll)
+    return PR_FALSE;
+
+  nsString appExeName;
+  localDll->GetLeafName(appExeName);
+
+  nsCOMPtr<nsIWindowsRegKey> appKey = 
+    do_CreateInstance("@mozilla.org/windows-registry-key;1");
+  if (!appKey) 
+    return PR_FALSE; 
+
+  // HKEY_CLASSES_ROOT\Applications\iexplore.exe
+  nsAutoString applicationsPath;
+  applicationsPath.AppendLiteral("Applications\\");
+  applicationsPath.Append(appExeName);
+
+  nsresult rv = appKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
+                             applicationsPath,
+                             nsIWindowsRegKey::ACCESS_QUERY_VALUE);
+  if (NS_FAILED(rv))
+    return PR_FALSE;
+
+  // Check for the NoOpenWith flag, if it exists
+  PRUint32 value;
+  rv = appKey->ReadIntValue(NS_LITERAL_STRING("NoOpenWith"), &value);
+  if (NS_SUCCEEDED(rv) && value == 1)
+    return PR_FALSE;
+
+  nsAutoString dummy;
+  if (NS_SUCCEEDED(appKey->ReadStringValue(NS_LITERAL_STRING("NoOpenWith"), 
+                                           dummy)))
+    return PR_FALSE;
+
+  appKey->Close();
+
+  // HKEY_CLASSES_ROOT\Applications\iexplore.exe\shell\open\command
+  applicationsPath.AssignLiteral("Applications\\");
+  applicationsPath.Append(appExeName);
+  if (!edit)
+    applicationsPath.AppendLiteral("\\shell\\open\\command");
+  else
+    applicationsPath.AppendLiteral("\\shell\\edit\\command");
+
+  rv = appKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
+                    applicationsPath,
+                    nsIWindowsRegKey::ACCESS_QUERY_VALUE);
+  if (NS_FAILED(rv))
+    return PR_FALSE;
+
+  nsAutoString appFilesystemCommand;
+  if (NS_SUCCEEDED(appKey->ReadStringValue(EmptyString(),
+                                           appFilesystemCommand))) {
+    // Replace embedded environment variables.
+    PRUint32 bufLength = 
+      ::ExpandEnvironmentStringsW(appFilesystemCommand.get(),
+                                  L"", 0);
+    if (bufLength == 0) // Error
+      return PR_FALSE;
+
+    nsAutoArrayPtr<PRUnichar> destination(new PRUnichar[bufLength]);
+    if (!destination)
+      return PR_FALSE;
+    if (!::ExpandEnvironmentStringsW(appFilesystemCommand.get(),
+                                     destination,
+                                     bufLength))
+      return PR_FALSE;
+
+    appFilesystemCommand = destination;
+
+    // C:\Windows\System32\rundll32.exe "C:\Program Files\Windows 
+    // Photo Gallery\PhotoViewer.dll", ImageView_Fullscreen %1
+    nsAutoString params;
+    NS_NAMED_LITERAL_STRING(rundllSegment, "rundll32.exe ");
+    PRInt32 index = appFilesystemCommand.Find(rundllSegment);
+    if (index > kNotFound) {
+      params.Append(Substring(appFilesystemCommand,
+                    index + rundllSegment.Length()));
+    } else {
+      params.Append(appFilesystemCommand);
+    }
+
+    // check to make sure we have a %1 and fill it
+    NS_NAMED_LITERAL_STRING(percentOneParam, "%1");
+    index = params.Find(percentOneParam);
+    if (index == kNotFound) // no parameter
+      return PR_FALSE;
+
+    nsString target;
+    aFile->GetTarget(target);
+    params.Replace(index, 2, target);
+
+    args = params;
+
+    return PR_TRUE;
+  }
+  return PR_FALSE;
+}
+
+// Return the cleaned up file path associated with a progid command 
+// verb located in root.
+PRBool nsMIMEInfoWin::GetProgIDVerbCommandHandler(const nsAString& appProgIDName,
+                                                  nsAString& applicationPath,
+                                                  PRBool edit)
+{
+  nsCOMPtr<nsIWindowsRegKey> appKey =
+    do_CreateInstance("@mozilla.org/windows-registry-key;1");
+  if (!appKey) 
+    return PR_FALSE; 
+
+  nsAutoString appProgId(appProgIDName);
+
+  // HKEY_CLASSES_ROOT\Windows.XPSReachViewer\shell\open\command
+  if (!edit)
+    appProgId.AppendLiteral("\\shell\\open\\command");
+  else
+    appProgId.AppendLiteral("\\shell\\edit\\command");
+
+  nsresult rv = appKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
+                             appProgId,
+                             nsIWindowsRegKey::ACCESS_QUERY_VALUE);
+  if (NS_FAILED(rv))
+    return PR_FALSE;
+
+  nsAutoString appFilesystemCommand;
+  if (NS_SUCCEEDED(appKey->ReadStringValue(EmptyString(), appFilesystemCommand))) {
+    
+    // Expand environment vars, clean up any misc.
+    if (!nsOSHelperAppService::CleanupCmdHandlerPath(appFilesystemCommand))
+      return PR_FALSE;
+    
+    applicationPath = appFilesystemCommand;
+    return PR_TRUE;
+  }
+  return PR_FALSE;
+}
+
+// Helper routine used in tracking app lists. Converts path
+// entries to lower case and stores them in the trackList array.
+void nsMIMEInfoWin::ProcessPath(nsCOMPtr<nsIMutableArray>& appList,
+                                nsTArray<nsCAutoString>& trackList,
+                                const nsAString& appFilesystemCommand)
+{
+  NS_ConvertUTF16toUTF8 lower(appFilesystemCommand);
+  ToLowerCase(lower);
+
+  // Don't include firefox.exe in the list
+  char exe[MAX_PATH+1];
+  PRUint32 len = GetModuleFileName(NULL, exe, MAX_PATH);
+  if (len < MAX_PATH && len != 0) {
+    PRUint32 index = lower.Find(exe);
+    if (index != -1)
+      return;
+  }
+
+  nsCOMPtr<nsILocalHandlerApp> aApp;
+  if (!GetLocalHandlerApp(appFilesystemCommand, aApp))
+    return;
+
+  // Save in our main tracking arrays
+  appList->AppendElement(aApp, PR_FALSE);
+  trackList.AppendElement(lower);
+}
+
+// Helper routine that handles a compare between a path
+// and an array of paths.
+static PRBool IsPathInList(nsAString& appPath,
+                           nsTArray<nsCAutoString>& trackList)
+{
+  // trackList data is always lowercase, see ProcessPath
+  // above.
+  NS_ConvertUTF16toUTF8 tmp(appPath);
+  ToLowerCase(tmp);
+
+  for (PRUint32 i = 0; i < trackList.Length(); i++) {
+    if (tmp.Equals(trackList[i]))
+      return PR_TRUE;
+  }
+  return PR_FALSE;
+}
+
+/** 
+ * Returns a list of nsILocalHandlerApp objects containing local 
+ * handlers associated with this mimeinfo. Implemented per 
+ * platform using information in this object to generate the
+ * best list. Typically used for an "open with" style user 
+ * option.
+ * 
+ * @return nsIArray of nsILocalHandlerApp
+ */
+NS_IMETHODIMP
+nsMIMEInfoWin::GetPossibleLocalHandlers(nsIArray **_retval)
+{
+  nsresult rv;
+
+  *_retval = nsnull;
+
+  nsCOMPtr<nsIMutableArray> appList =
+    do_CreateInstance("@mozilla.org/array;1");
+
+  if (!appList)
+    return NS_ERROR_FAILURE;
+
+  nsTArray<nsCAutoString> trackList;
+
+  nsCAutoString fileExt;
+  GetPrimaryExtension(fileExt);
+
+  nsCOMPtr<nsIWindowsRegKey> regKey =
+    do_CreateInstance("@mozilla.org/windows-registry-key;1");
+  if (!regKey) 
+    return NS_ERROR_FAILURE; 
+  nsCOMPtr<nsIWindowsRegKey> appKey =
+    do_CreateInstance("@mozilla.org/windows-registry-key;1");
+  if (!appKey) 
+    return NS_ERROR_FAILURE; 
+
+  nsAutoString workingRegistryPath;
+
+  PRBool extKnown = PR_FALSE;
+  if (fileExt.IsEmpty()) {
+    extKnown = PR_TRUE;
+    // Mime type discovery is possible in some cases, through 
+    // HKEY_CLASSES_ROOT\MIME\Database\Content Type, however, a number
+    // of file extensions related to mime type are simply not defined,
+    // (application/rss+xml & application/atom+xml are good examples)
+    // in which case we can only provide a generic list.
+    nsCAutoString mimeType;
+    GetMIMEType(mimeType);
+    if (!mimeType.IsEmpty()) {
+      workingRegistryPath.AppendLiteral("MIME\\Database\\Content Type\\");
+      workingRegistryPath.Append(NS_ConvertASCIItoUTF16(mimeType));
+            
+      rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
+                        workingRegistryPath,
+                        nsIWindowsRegKey::ACCESS_QUERY_VALUE);
+      if(NS_SUCCEEDED(rv)) {
+        nsAutoString mimeFileExt;
+        if (NS_SUCCEEDED(regKey->ReadStringValue(EmptyString(), mimeFileExt))) {
+          CopyUTF16toUTF8(mimeFileExt, fileExt);
+          extKnown = PR_FALSE;
+        }
+      }
+    }
+  }
+
+  nsAutoString fileExtToUse;
+  if (fileExt.First() != '.')
+    fileExtToUse = PRUnichar('.');
+  fileExtToUse.Append(NS_ConvertUTF8toUTF16(fileExt));
+
+  // Note, the order in which these occur has an effect on the 
+  // validity of the resulting display list.
+
+  if (!extKnown) {
+    // 1) Get the default handler if it exists
+    workingRegistryPath = fileExtToUse;
+
+    rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
+                      workingRegistryPath,
+                      nsIWindowsRegKey::ACCESS_QUERY_VALUE);
+    if (NS_SUCCEEDED(rv)) {
+      nsAutoString appProgId;
+      if (NS_SUCCEEDED(regKey->ReadStringValue(EmptyString(), appProgId))) {
+        // Bug 358297 - ignore the embedded internet explorer handler
+        if (appProgId != NS_LITERAL_STRING("XPSViewer.Document")) {
+          nsAutoString appFilesystemCommand;
+          if (GetProgIDVerbCommandHandler(appProgId,
+                                          appFilesystemCommand,
+                                          PR_FALSE) &&
+              !IsPathInList(appFilesystemCommand, trackList)) {
+            ProcessPath(appList, trackList, appFilesystemCommand);
+          }
+        }
+      }
+      regKey->Close();
+    }
+
+
+    // 2) list HKEY_CLASSES_ROOT\.ext\OpenWithList\ 
+    
+    workingRegistryPath = fileExtToUse;
+    workingRegistryPath.AppendLiteral("\\OpenWithList");
+
+    rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
+                      workingRegistryPath,
+                      nsIWindowsRegKey::ACCESS_QUERY_VALUE);
+    if (NS_SUCCEEDED(rv)) {
+      PRUint32 count = 0;
+      if (NS_SUCCEEDED(regKey->GetValueCount(&count)) && count > 0) {
+        for (PRUint32 index = 0; index < count; index++) {
+          nsAutoString appName;
+          if (NS_FAILED(regKey->GetValueName(index, appName)))
+            continue;
+
+          // HKEY_CLASSES_ROOT\Applications\firefox.exe = "path params"
+          nsAutoString appFilesystemCommand;
+          if (!GetAppsVerbCommandHandler(appName,
+                                         appFilesystemCommand,
+                                         PR_FALSE) ||
+              IsPathInList(appFilesystemCommand, trackList))
+            continue;
+          ProcessPath(appList, trackList, appFilesystemCommand);
+        }
+      }
+      regKey->Close();
+    }
+
+
+    // 3) List HKEY_CLASSES_ROOT\.ext\OpenWithProgids, with the
+    // different step of resolving the progids for the command handler.
+
+    workingRegistryPath = fileExtToUse;
+    workingRegistryPath.AppendLiteral("\\OpenWithProgids");
+
+    rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
+                      workingRegistryPath,
+                      nsIWindowsRegKey::ACCESS_QUERY_VALUE);
+    if (NS_SUCCEEDED(rv)) {
+      PRUint32 count = 0;
+      if (NS_SUCCEEDED(regKey->GetValueCount(&count)) && count > 0) {
+        for (PRUint32 index = 0; index < count; index++) {
+          // HKEY_CLASSES_ROOT\.ext\OpenWithProgids\Windows.XPSReachViewer
+          nsAutoString appProgId;
+          if (NS_FAILED(regKey->GetValueName(index, appProgId)))
+            continue;
+
+          nsAutoString appFilesystemCommand;
+          if (!GetProgIDVerbCommandHandler(appProgId,
+                                           appFilesystemCommand,
+                                           PR_FALSE) ||
+              IsPathInList(appFilesystemCommand, trackList))
+            continue;
+          ProcessPath(appList, trackList, appFilesystemCommand);
+        }
+      }
+      regKey->Close();
+    }
+
+
+    // 4) Add any non configured applications located in the MRU list
+
+    // HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\
+    // Explorer\FileExts\.ext\OpenWithList
+    workingRegistryPath =
+      NS_LITERAL_STRING("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\");
+    workingRegistryPath += fileExtToUse;
+    workingRegistryPath.AppendLiteral("\\OpenWithList");
+
+    rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
+                      workingRegistryPath,
+                      nsIWindowsRegKey::ACCESS_QUERY_VALUE);
+    if (NS_SUCCEEDED(rv)) {
+      PRUint32 count = 0;
+      if (NS_SUCCEEDED(regKey->GetValueCount(&count)) && count > 0) {
+        for (PRUint32 index = 0; index < count; index++) {
+          nsAutoString appName, appValue;
+          if (NS_FAILED(regKey->GetValueName(index, appName)))
+            continue;
+          if (appName.EqualsLiteral("MRUList"))
+            continue;
+          if (NS_FAILED(regKey->ReadStringValue(appName, appValue)))
+            continue;
+          
+          // HKEY_CLASSES_ROOT\Applications\firefox.exe = "path params"
+          nsAutoString appFilesystemCommand;
+          if (!GetAppsVerbCommandHandler(appValue,
+                                         appFilesystemCommand,
+                                         PR_FALSE) ||
+              IsPathInList(appFilesystemCommand, trackList))
+            continue;
+          ProcessPath(appList, trackList, appFilesystemCommand);
+        }
+      }
+    }
+    
+
+    // 5) Add any non configured progids in the MRU list, with the
+    // different step of resolving the progids for the command handler.
+    
+    // HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\
+    // Explorer\FileExts\.ext\OpenWithProgids
+    workingRegistryPath =
+      NS_LITERAL_STRING("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\");
+    workingRegistryPath += fileExtToUse;
+    workingRegistryPath.AppendLiteral("\\OpenWithProgids");
+
+    regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
+                 workingRegistryPath,
+                 nsIWindowsRegKey::ACCESS_QUERY_VALUE);
+    if (NS_SUCCEEDED(rv)) {
+      PRUint32 count = 0;
+      if (NS_SUCCEEDED(regKey->GetValueCount(&count)) && count > 0) {
+        for (PRUint32 index = 0; index < count; index++) {
+          nsAutoString appIndex, appProgId;
+          if (NS_FAILED(regKey->GetValueName(index, appProgId)))
+            continue;
+
+          nsAutoString appFilesystemCommand;
+          if (!GetProgIDVerbCommandHandler(appProgId,
+                                           appFilesystemCommand,
+                                           PR_FALSE) ||
+              IsPathInList(appFilesystemCommand, trackList))
+            continue;
+          ProcessPath(appList, trackList, appFilesystemCommand);
+        }
+      }
+      regKey->Close();
+    }
+
+
+    // 6) Check the perceived type value, and use this to lookup the perceivedtype
+    // open with list.
+    // http://msdn2.microsoft.com/en-us/library/aa969373.aspx
+
+    workingRegistryPath = fileExtToUse;
+
+    regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
+                 workingRegistryPath,
+                 nsIWindowsRegKey::ACCESS_QUERY_VALUE);
+    if (NS_SUCCEEDED(rv)) {
+      nsAutoString perceivedType;
+      rv = regKey->ReadStringValue(NS_LITERAL_STRING("PerceivedType"),
+                                   perceivedType);
+      if (NS_SUCCEEDED(rv)) {
+        nsAutoString openWithListPath(NS_LITERAL_STRING("SystemFileAssociations\\"));
+        openWithListPath.Append(perceivedType); // no period
+        openWithListPath.Append(NS_LITERAL_STRING("\\OpenWithList"));
+
+        nsresult rv = appKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
+                                   openWithListPath,
+                                   nsIWindowsRegKey::ACCESS_QUERY_VALUE);
+        if (NS_SUCCEEDED(rv)) {
+          PRUint32 count = 0;
+          if (NS_SUCCEEDED(regKey->GetValueCount(&count)) && count > 0) {
+            for (PRUint32 index = 0; index < count; index++) {
+              nsAutoString appName;
+              if (NS_FAILED(regKey->GetValueName(index, appName)))
+                continue;
+              
+              // HKEY_CLASSES_ROOT\Applications\firefox.exe = "path params"
+              nsAutoString appFilesystemCommand;
+              if (!GetAppsVerbCommandHandler(appName, appFilesystemCommand, 
+                                             PR_FALSE) ||
+                  IsPathInList(appFilesystemCommand, trackList))
+                continue;
+              ProcessPath(appList, trackList, appFilesystemCommand);
+            }
+          }
+        }
+      }
+    }
+  } // extKnown == PR_FALSE
+
+
+  // 7) list global HKEY_CLASSES_ROOT\*\OpenWithList\
+  // Listing general purpose handlers, not specific to a mime type or file extension
+
+  workingRegistryPath = NS_LITERAL_STRING("*\\OpenWithList");
+
+  rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
+                    workingRegistryPath,
+                    nsIWindowsRegKey::ACCESS_QUERY_VALUE);
+  if (NS_SUCCEEDED(rv)) {
+    PRUint32 count = 0;
+    if (NS_SUCCEEDED(regKey->GetValueCount(&count)) && count > 0) {
+      for (PRUint32 index = 0; index < count; index++) {
+        nsAutoString appName;
+        if (NS_FAILED(regKey->GetValueName(index, appName)))
+          continue;
+
+        // HKEY_CLASSES_ROOT\Applications\firefox.exe = "path params"
+        nsAutoString appFilesystemCommand;
+        if (!GetAppsVerbCommandHandler(appName, appFilesystemCommand,
+                                       PR_FALSE) ||
+            IsPathInList(appFilesystemCommand, trackList))
+          continue;
+        ProcessPath(appList, trackList, appFilesystemCommand);
+      }
+    }
+    regKey->Close();
+  }
+
+
+  // 8) General application's list - not file extension specific on windows
+  workingRegistryPath = NS_LITERAL_STRING("Applications");
+
+  rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
+                    workingRegistryPath,
+                    nsIWindowsRegKey::ACCESS_ENUMERATE_SUB_KEYS|
+                    nsIWindowsRegKey::ACCESS_QUERY_VALUE);
+  if (NS_SUCCEEDED(rv)) {
+    PRUint32 count = 0;
+    if (NS_SUCCEEDED(regKey->GetChildCount(&count)) && count > 0) {
+      for (PRUint32 index = 0; index < count; index++) {
+        nsAutoString appName;
+        if (NS_FAILED(regKey->GetChildName(index, appName)))
+          continue;
+
+        // HKEY_CLASSES_ROOT\Applications\firefox.exe = "path params"
+        nsAutoString appFilesystemCommand;
+        if (!GetAppsVerbCommandHandler(appName, appFilesystemCommand,
+                                       PR_FALSE) ||
+            IsPathInList(appFilesystemCommand, trackList))
+          continue;
+        ProcessPath(appList, trackList, appFilesystemCommand);
+      }
+    }
+  }
+
+  // Return to the caller
+  *_retval = appList;
+  NS_ADDREF(*_retval);
+
+  return NS_OK;
+}
--- a/uriloader/exthandler/win/nsMIMEInfoWin.h
+++ b/uriloader/exthandler/win/nsMIMEInfoWin.h
@@ -34,36 +34,69 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsMIMEInfoWin_h_
 #define nsMIMEInfoWin_h_
 
 #include "nsMIMEInfoImpl.h"
 #include "nsIPropertyBag.h"
+#include "nsIMutableArray.h"
+#include "nsTArray.h"
 
 class nsMIMEInfoWin : public nsMIMEInfoBase, public nsIPropertyBag {
   public:
     nsMIMEInfoWin(const char* aType = "") : nsMIMEInfoBase(aType) {}
     nsMIMEInfoWin(const nsACString& aMIMEType) : nsMIMEInfoBase(aMIMEType) {}
     nsMIMEInfoWin(const nsACString& aType, HandlerClass aClass) :
       nsMIMEInfoBase(aType, aClass) {}
     virtual ~nsMIMEInfoWin();
 
+    NS_IMETHOD LaunchWithFile(nsIFile* aFile);
     NS_IMETHOD GetHasDefaultHandler(PRBool * _retval);
+    NS_IMETHOD GetPossibleLocalHandlers(nsIArray **_retval); 
 
     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;
+    
+    // Given a path to a local handler, return its 
+    // nsILocalHandlerApp instance.
+    PRBool GetLocalHandlerApp(const nsAString& aCommandHandler,
+                              nsCOMPtr<nsILocalHandlerApp>& aApp);
+
+    // Return the cleaned up file path associated 
+    // with a command verb located in root/Applications.
+    PRBool GetAppsVerbCommandHandler(const nsAString& appExeName,
+                                     nsAString& applicationPath,
+                                     PRBool bEdit);
+
+    // Return the cleaned up file path associated 
+    // with a progid command verb located in root.
+    PRBool GetProgIDVerbCommandHandler(const nsAString& appProgIDName,
+                                       nsAString& applicationPath,
+                                       PRBool bEdit);
+
+    // Lookup a rundll command handler and return
+    // a populated command template for use with rundll32.exe.
+    PRBool GetDllLaunchInfo(nsIFile * aDll,
+                            nsILocalFile * aFile,
+                            nsAString& args, PRBool bEdit);
+
+    // Helper routine used in tracking app lists
+    void ProcessPath(nsCOMPtr<nsIMutableArray>& appList,
+                     nsTArray<nsCAutoString>& trackList,
+                     const nsAString& appFilesystemCommand);
+
 };
 
 #endif
--- a/uriloader/exthandler/win/nsOSHelperAppService.cpp
+++ b/uriloader/exthandler/win/nsOSHelperAppService.cpp
@@ -324,86 +324,113 @@ nsOSHelperAppService::GetDefaultAppInfo(
     return NS_OK;
    
   // OK, the default value here is the description of the type.
   nsAutoString handlerCommand;
   rv = regKey->ReadStringValue(EmptyString(), handlerCommand);
   if (NS_FAILED(rv))
     return NS_OK;
 
-  nsAutoString handlerFilePath;
-  // First look to see if we're invoking a Windows shell service, such as 
-  // the Picture & Fax Viewer, which are invoked through rundll32.exe, and
-  // so we need to extract the DLL path because that's where the version
-  // info is held - not in rundll32.exe
-  //
-  // The format of rundll32.exe calls is:
-  //
-  // rundll32.exe c:\path\to.dll,Function %args
-  //
-  // What we want is the DLL - since that's where the real application name
-  // is stored, e.g. zipfldr.dll, shimgvw.dll, etc. 
-  // 
-  // Working from the end of the registry value, the path begins at the last
-  // comma in the string (stripping off Function and args) to the position
-  // just after the first space (the space after rundll32.exe).
-  // 
-  NS_NAMED_LITERAL_STRING(rundllSegment, "rundll32.exe ");
-  if (StringBeginsWith(handlerCommand, rundllSegment)) {
-    PRInt32 lastCommaPos = handlerCommand.RFindChar(',');
-    PRUint32 rundllSegmentLength = rundllSegment.Length();
-    PRUint32 len;
-    if (lastCommaPos != kNotFound)
-      len = lastCommaPos - rundllSegmentLength;
-    else
-      len = handlerCommand.Length() - rundllSegmentLength;
-    handlerFilePath = Substring(handlerCommand, rundllSegmentLength, len);
-  }
-  else
-    handlerFilePath = handlerCommand;
-
-  // Trim any command parameters so that we have a native path we can 
-  // initialize a local file with...
-  RemoveParameters(handlerFilePath);
-
-  // Similarly replace embedded environment variables... (this must be done
-  // AFTER |RemoveParameters| since it may introduce spaces into the path
-  // string)
-
-  DWORD required = ::ExpandEnvironmentStringsW(handlerFilePath.get(),
-                                               L"", 0);
-  if (!required) 
+  if (!CleanupCmdHandlerPath(handlerCommand))
     return NS_ERROR_FAILURE;
 
-  nsAutoArrayPtr<WCHAR> destination(new WCHAR[required]); 
-  if (!destination)
-    return NS_ERROR_OUT_OF_MEMORY;
-  if (!::ExpandEnvironmentStringsW(handlerFilePath.get(), destination,
-                                   required))
-    return NS_ERROR_FAILURE;
-
-  handlerFilePath = destination;
-
   nsCOMPtr<nsILocalFile> lf;
-  NS_NewLocalFile(handlerFilePath, PR_TRUE, getter_AddRefs(lf));
+  NS_NewLocalFile(handlerCommand, PR_TRUE, getter_AddRefs(lf));
   if (!lf)
     return NS_ERROR_OUT_OF_MEMORY;
 
   nsILocalFileWin* lfw = nsnull;
   CallQueryInterface(lf, &lfw);
   if (lfw) {
     // The "FileDescription" field contains the actual name of the application.
     lfw->GetVersionInfoField("FileDescription", aDefaultDescription);
     // QI addref'ed for us.
     *aDefaultApplication = lfw;
   }
 
   return NS_OK;
 }
 
+// Returns the dll path of a rundll command handler. 
+/* static */ PRBool nsOSHelperAppService::CleanupCmdHandlerPath(nsAString& aCommandHandler)
+{
+  nsAutoString handlerFilePath;
+  nsAutoString handlerCommand(aCommandHandler);
+
+  // %SystemRoot%\system32\NOTEPAD.EXE %1
+  //
+  // rundll32.exe "%ProgramFiles%\Windows Photo Gallery\
+  //   PhotoViewer.dll", ImageView_Fullscreen %1
+  //
+  // rundll32.exe "%ProgramFiles%\Windows Photo Gallery\PhotoViewer.dll"
+  //
+  // %SystemRoot%\System32\rundll32.exe "%ProgramFiles%\
+  //   Windows Photo Gallery\PhotoViewer.dll", ImageView_Fullscreen %1
+
+  // Replace embedded environment variables.
+  PRUint32 bufLength = ::ExpandEnvironmentStringsW(handlerCommand.get(),
+                                                   L"", 0);
+  if (bufLength == 0) // Error
+    return PR_FALSE;
+
+  nsAutoArrayPtr<PRUnichar> destination(new PRUnichar[bufLength]);
+  if (!destination)
+    return PR_FALSE;
+  if (!::ExpandEnvironmentStringsW(handlerCommand.get(), destination,
+                                   bufLength))
+    return PR_FALSE;
+
+  handlerCommand = destination;
+
+  // Look to see if we're invoking a Windows shell service, such as 
+  // the Picture & Fax Viewer, which are invoked through rundll32.exe.
+  //
+  // What we want is the DLL - since that's where the real application name
+  // is stored, e.g. zipfldr.dll, shimgvw.dll, etc. 
+  // 
+  // Working from the end of the registry value, the path begins at the last
+  // comma in the string (stripping off Function and args) to the position
+  // just after the first space (the space after rundll32.exe).
+
+  NS_NAMED_LITERAL_STRING(rundllSegment, "rundll32.exe ");
+
+  PRInt32 index = handlerCommand.Find(rundllSegment);
+  if (index != kNotFound) {
+    // Strip off the rundll command handler
+    PRInt32 lastCommaPos = handlerCommand.RFindChar(',');
+    PRUint32 rundllSegmentLength = index + rundllSegment.Length();
+    PRUint32 len;
+
+    if (lastCommaPos == kNotFound) {
+      // C:\Windows\System32\rundll32.exe "C:\Program Files\
+      //   Windows Photo Gallery\PhotoViewer.dll"
+      // rundll32.exe "C:\Program Files\Windows Photo Gallery\PhotoViewer.dll"
+      len = handlerCommand.Length() - rundllSegmentLength;
+    } else {
+      // C:\Windows\System32\rundll32.exe "C:\Program Files\
+      //   Windows Photo Gallery\PhotoViewer.dll", ImageView_Fullscreen %1
+      // rundll32.exe "C:\Program Files\Windows Photo Gallery\
+      //   PhotoViewer.dll", ImageView_Fullscreen %1
+      len = lastCommaPos - rundllSegmentLength;
+    }
+
+    // Clip off 'C:\Windows\System32\rundll32.exe ' or 'rundll32.exe '
+    handlerFilePath = Substring(handlerCommand, rundllSegmentLength, len);
+  } else {
+    handlerFilePath = handlerCommand;
+  }
+
+  // Trim any command parameters so that we have a native path we can
+  // initialize a local file with.
+  RemoveParameters(handlerFilePath);
+
+  aCommandHandler = handlerFilePath;
+  return PR_TRUE;
+}
+
 already_AddRefed<nsMIMEInfoWin> nsOSHelperAppService::GetByExtension(const nsAFlatString& aFileExt, const char *aTypeHint)
 {
   if (aFileExt.IsEmpty())
     return nsnull;
 
   // windows registry assumes your file extension is going to include the '.'.
   // so make sure it's there...
   nsAutoString fileExtToUse;
@@ -425,38 +452,45 @@ already_AddRefed<nsMIMEInfoWin> nsOSHelp
     return nsnull; 
 
   nsCAutoString typeToUse;
   if (aTypeHint && *aTypeHint) {
     typeToUse.Assign(aTypeHint);
   }
   else {
     nsAutoString temp;
-    if (NS_FAILED(regKey->ReadStringValue(NS_LITERAL_STRING("Content Type"), 
+    if (NS_FAILED(regKey->ReadStringValue(NS_LITERAL_STRING("Content Type"),
                   temp))) {
       return nsnull; 
     }
     // Content-Type is always in ASCII
     LossyAppendUTF16toASCII(temp, typeToUse);
   }
 
   nsMIMEInfoWin* mimeInfo = new nsMIMEInfoWin(typeToUse);
   if (!mimeInfo)
     return nsnull; // out of memory
 
   NS_ADDREF(mimeInfo);
+
   // don't append the '.'
   mimeInfo->AppendExtension(NS_ConvertUTF16toUTF8(Substring(fileExtToUse, 1)));
 
   mimeInfo->SetPreferredAction(nsIMIMEInfo::useSystemDefault);
 
   nsAutoString description;
-  PRBool found = NS_SUCCEEDED(regKey->ReadStringValue(EmptyString(), 
+  PRBool found = NS_SUCCEEDED(regKey->ReadStringValue(EmptyString(),
                                                       description));
 
+  // Bug 358297 - ignore the default handler, force the user to choose app
+  if (description.EqualsLiteral("XPSViewer.Document")) {
+    NS_IF_RELEASE(mimeInfo);
+    return nsnull;
+  }
+
   nsAutoString defaultDescription;
   nsCOMPtr<nsIFile> defaultApplication;
   GetDefaultAppInfo(description, defaultDescription,
                     getter_AddRefs(defaultApplication));
 
   mimeInfo->SetDefaultDescription(defaultDescription);
   mimeInfo->SetDefaultApplicationHandler(defaultApplication);
 
--- a/uriloader/exthandler/win/nsOSHelperAppService.h
+++ b/uriloader/exthandler/win/nsOSHelperAppService.h
@@ -66,16 +66,19 @@ public:
   already_AddRefed<nsIHandlerInfo> GetProtocolInfoFromOS(const nsACString &aScheme,
                                                          PRBool *found);
 
   /** Get the string value of a registry value and store it in result.
    * @return PR_TRUE on success, PR_FALSE on failure
    */
   static PRBool GetValueString(HKEY hKey, const PRUnichar* pValueName, nsAString& result);
 
+  // Removes registry command handler parameters, quotes, and expands environment strings.
+  static PRBool CleanupCmdHandlerPath(nsAString& aCommandHandler);
+
 protected:
   nsresult GetDefaultAppInfo(const nsAString& aTypeName, nsAString& aDefaultDescription, nsIFile** aDefaultApplication);
   // Lookup a mime info by extension, using an optional type hint
   already_AddRefed<nsMIMEInfoWin> GetByExtension(const nsAFlatString& aFileExt, const char *aTypeHint = nsnull);
   nsresult FindOSMimeInfoForType(const char * aMimeContentType, nsIURI * aURI, char ** aFileExtension, nsIMIMEInfo ** aMIMEInfo);
 
   static nsresult GetMIMEInfoFromRegistry(const nsAFlatString& fileType, nsIMIMEInfo *pInfo);
   /// Looks up the type for the extension aExt and compares it to aType