--- 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