diff --git a/uriloader/exthandler/nsMIMEInfoImpl.cpp b/uriloader/exthandler/nsMIMEInfoImpl.cpp index aa2e36a66355..800f55216983 100644 --- a/uriloader/exthandler/nsMIMEInfoImpl.cpp +++ b/uriloader/exthandler/nsMIMEInfoImpl.cpp @@ -370,4 +370,3 @@ nsMIMEInfoImpl::LaunchDefaultWithFile(nsIFile* aFile) return LaunchWithIProcess(mDefaultApplication, aFile); } - diff --git a/uriloader/exthandler/nsMIMEInfoImpl.h b/uriloader/exthandler/nsMIMEInfoImpl.h index b97af7837894..387c95eaa917 100644 --- a/uriloader/exthandler/nsMIMEInfoImpl.h +++ b/uriloader/exthandler/nsMIMEInfoImpl.h @@ -46,6 +46,17 @@ #include "nsCOMPtr.h" #include "nsIURI.h" +/** + * UTF8 moz-icon URI string for the default handler application's icon, if + * available. + */ +#define PROPERTY_DEFAULT_APP_ICON_URL "defaultApplicationIconURL" +/** + * UTF8 moz-icon URI string for the user's preferred handler application's + * icon, if available. + */ +#define PROPERTY_CUSTOM_APP_ICON_URL "customApplicationIconURL" + /** * Basic implementation of nsIMIMEInfo. Incomplete - it is meant to be * subclassed, and GetHasDefaultHandler as well as LaunchDefaultWithFile need to diff --git a/uriloader/exthandler/win/nsMIMEInfoWin.cpp b/uriloader/exthandler/win/nsMIMEInfoWin.cpp index 960208c392c0..197622d77ed8 100755 --- a/uriloader/exthandler/win/nsMIMEInfoWin.cpp +++ b/uriloader/exthandler/win/nsMIMEInfoWin.cpp @@ -38,8 +38,14 @@ * * ***** END LICENSE BLOCK ***** */ +#include "nsArrayEnumerator.h" +#include "nsCOMArray.h" #include "nsILocalFile.h" +#include "nsIVariant.h" #include "nsMIMEInfoWin.h" +#include "nsNetUtil.h" + +NS_IMPL_ISUPPORTS_INHERITED1(nsMIMEInfoWin, nsMIMEInfoBase, nsIPropertyBag) nsMIMEInfoWin::~nsMIMEInfoWin() { @@ -70,3 +76,46 @@ nsMIMEInfoWin::GetHasDefaultHandler(PRBool * _retval) *_retval = !mDefaultAppDescription.IsEmpty(); return NS_OK; } + +NS_IMETHODIMP +nsMIMEInfoWin::GetEnumerator(nsISimpleEnumerator* *_retval) +{ + nsCOMArray properties; + + nsCOMPtr variant; + GetProperty(NS_LITERAL_STRING("defaultApplicationIconURL"), getter_AddRefs(variant)); + if (variant) + properties.AppendObject(variant); + + GetProperty(NS_LITERAL_STRING("customApplicationIconURL"), getter_AddRefs(variant)); + if (variant) + properties.AppendObject(variant); + + return NS_NewArrayEnumerator(_retval, properties); +} + +static nsresult GetIconURLVariant(nsIFile* aApplication, nsIVariant* *_retval) +{ + nsresult rv = CallCreateInstance("@mozilla.org/variant;1", _retval); + if (NS_FAILED(rv)) + return rv; + nsCAutoString fileURLSpec; + NS_GetURLSpecFromFile(aApplication, fileURLSpec); + nsCAutoString iconURLSpec; iconURLSpec.AssignLiteral("moz-icon://"); + iconURLSpec += fileURLSpec; + nsCOMPtr writable(do_QueryInterface(*_retval)); + writable->SetAsAUTF8String(iconURLSpec); + return NS_OK; +} + +NS_IMETHODIMP +nsMIMEInfoWin::GetProperty(const nsAString& aName, nsIVariant* *_retval) +{ + nsresult rv = NS_ERROR_FAILURE; + if (mDefaultApplication && aName.EqualsLiteral(PROPERTY_DEFAULT_APP_ICON_URL)) + rv = GetIconURLVariant(mDefaultApplication, _retval); + else if (mPreferredApplication && aName.EqualsLiteral(PROPERTY_CUSTOM_APP_ICON_URL)) + rv = GetIconURLVariant(mPreferredApplication, _retval); + return rv; +} + diff --git a/uriloader/exthandler/win/nsMIMEInfoWin.h b/uriloader/exthandler/win/nsMIMEInfoWin.h index b27dc3ebce9f..e59bfbcd005c 100755 --- a/uriloader/exthandler/win/nsMIMEInfoWin.h +++ b/uriloader/exthandler/win/nsMIMEInfoWin.h @@ -38,16 +38,28 @@ #define nsMIMEInfoWin_h_ #include "nsMIMEInfoImpl.h" +#include "nsIPropertyBag.h" -class nsMIMEInfoWin : public nsMIMEInfoBase { +class nsMIMEInfoWin : public nsMIMEInfoBase, public nsIPropertyBag { public: nsMIMEInfoWin(const char* aType = "") : nsMIMEInfoBase(aType) {} nsMIMEInfoWin(const nsACString& aMIMEType) : nsMIMEInfoBase(aMIMEType) {} virtual ~nsMIMEInfoWin(); NS_IMETHOD GetHasDefaultHandler(PRBool * _retval); + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIPROPERTYBAG + + void SetDefaultApplicationHandler(nsIFile* aDefaultApplication) + { + mDefaultApplication = aDefaultApplication; + } protected: virtual nsresult LaunchDefaultWithFile(nsIFile* aFile); + + private: + nsCOMPtr mDefaultApplication; }; #endif diff --git a/uriloader/exthandler/win/nsOSHelperAppService.cpp b/uriloader/exthandler/win/nsOSHelperAppService.cpp index a129a3d2d57b..d525afa3fb4a 100644 --- a/uriloader/exthandler/win/nsOSHelperAppService.cpp +++ b/uriloader/exthandler/win/nsOSHelperAppService.cpp @@ -46,7 +46,7 @@ #include "nsIMIMEInfo.h" #include "nsMIMEInfoWin.h" #include "nsMimeTypes.h" -#include "nsILocalFile.h" +#include "nsILocalFileWin.h" #include "nsIProcess.h" #include "plstr.h" #include "nsAutoPtr.h" @@ -245,7 +245,6 @@ nsresult nsOSHelperAppService::LoadUriInternal(nsIURI * aURL) #ifdef WINCE // WinCE doesn't support helper applications yet. return NS_ERROR_NOT_IMPLEMENTED; #else - nsresult rv = NS_OK; // 1. Find the default app for this protocol @@ -347,7 +346,7 @@ nsresult nsOSHelperAppService::GetMIMEInfoFromRegistry( const nsAFlatString& fil nsOSHelperAppService::typeFromExtEquals(const PRUnichar* aExt, const char *aType) { #ifdef WINCE // WinCE doesn't support helper applications yet. - return FALSE; + return PR_FALSE; #else if (!aType) return PR_FALSE; @@ -385,12 +384,151 @@ nsOSHelperAppService::typeFromExtEquals(const PRUnichar* aExt, const char *aType #endif } +static void RemoveParameters(nsString& aPath) +{ + // Command Strings stored in the Windows registry with parameters look like + // this: + // + // 1) "C:\Program Files\Company Name\product.exe" -foo -bar (long version) + // -- OR -- + // 2) C:\PROGRA~1\COMPAN~2\product.exe -foo -bar (short version) + // + // For 1), the path is the first "" quoted string. (quotes are used to + // prevent parameter parsers from choking) + // For 2), the path is the string up until the first space (spaces are + // illegal in short DOS-style paths) + // + if (aPath.First() == PRUnichar('"')) { + aPath = Substring(aPath, 1, aPath.Length() - 1); + PRInt32 nextQuote = aPath.FindChar(PRUnichar('"')); + if (nextQuote != kNotFound) + aPath.Truncate(nextQuote); + } + else { + PRInt32 firstSpace = aPath.FindChar(PRUnichar(' ')); + if (firstSpace != kNotFound) + aPath.Truncate(firstSpace); + } +} + +// +// The "real" name of a given helper app (as specified by the path to the +// executable file held in various registry keys) is stored n the VERSIONINFO +// block in the file's resources. We need to find the path to the executable +// and then retrieve the "FileDescription" field value from the file. +// +// For a given extension, we find the file handler like so: +// +// HKCR +// \.ext\ <-- default value +// \\ +// \shell\open\command\ <-- default value +// +// We need to do some parsing on the to strip off params and +// deal with some Windows quirks (like the fact that many Shell "applications" +// are actually DLLs invoked via rundll32.exe) +// +nsresult +nsOSHelperAppService::GetDefaultAppInfo(nsAString& aTypeName, nsAString& aDefaultDescription, + nsIFile** aDefaultApplication) +{ + // If all else fails, use the file type key name, which will be something like "pngfile" for + // .pngs, "WMVFile" for .wmvs, etc. + aDefaultDescription = aTypeName; + *aDefaultApplication = nsnull; + + nsCAutoString handlerKeyName; + NS_CopyUnicodeToNative(aTypeName, handlerKeyName); + handlerKeyName += "\\shell\\open\\command"; + HKEY handlerKey; + LONG err = ::RegOpenKeyEx(HKEY_CLASSES_ROOT, handlerKeyName.get(), 0, KEY_QUERY_VALUE, &handlerKey); + if (err == ERROR_SUCCESS) { + nsAutoString handlerCommand; + PRBool found = GetValueString(handlerKey, NULL, handlerCommand); + if (found) { + 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(); + if (lastCommaPos != kNotFound) + handlerFilePath = Substring(handlerCommand, rundllSegmentLength, + lastCommaPos - rundllSegmentLength); + else + handlerFilePath = Substring(handlerCommand, rundllSegmentLength, + handlerCommand.Length() - rundllSegmentLength); + } + 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) + TCHAR* destination = nsnull; + nsCAutoString nativeHandlerFilePath; + NS_CopyUnicodeToNative(handlerFilePath, nativeHandlerFilePath); + DWORD required = ::ExpandEnvironmentStrings(nativeHandlerFilePath.get(), destination, 0); + destination = new TCHAR[required]; + if (!destination) { + ::RegCloseKey(handlerKey); + delete[] destination; + destination = nsnull; + return NS_ERROR_OUT_OF_MEMORY; + } + ::ExpandEnvironmentStrings(nativeHandlerFilePath.get(), destination, required); + NS_CopyNativeToUnicode(nsDependentCString(destination), handlerFilePath); + delete[] destination; + destination = nsnull; + + nsCOMPtr lf; + NS_NewLocalFile(handlerFilePath, PR_TRUE, getter_AddRefs(lf)); + if (!lf) { + ::RegCloseKey(handlerKey); + delete[] destination; + destination = nsnull; + return NS_ERROR_OUT_OF_MEMORY; + } + + nsILocalFileWin* lfw = nsnull; + nsresult rv = CallQueryInterface(lf, &lfw); + if (NS_SUCCEEDED(rv) && lfw) { + // The "FileDescription" field contains the actual name of the application. + lfw->GetVersionInfoField("FileDescription", aDefaultDescription); + // QI addref'ed for us. + *aDefaultApplication = lfw; + } + + } + ::RegCloseKey(handlerKey); + } + + return NS_OK; +} + already_AddRefed nsOSHelperAppService::GetByExtension(const nsAFlatString& aFileExt, const char *aTypeHint) { -#ifdef WINCE // WinCE doesn't support helper applications yet. +#ifdef WINCE // WinCE doesn't support helper applications yet return nsnull; #else - if (aFileExt.IsEmpty()) return nsnull; @@ -441,14 +579,12 @@ already_AddRefed nsOSHelperAppService::GetByExtension(const nsAFl mimeInfo->SetPreferredAction(nsIMIMEInfo::useSystemDefault); - nsAutoString visibleDesc(description); - PRInt32 pos = visibleDesc.FindChar('.'); - if (pos > 0) - visibleDesc.Truncate(pos); - // the format of the description usually looks like appname.version.something. - // for now, let's try to make it pretty and just show you the appname. - - mimeInfo->SetDefaultDescription(visibleDesc); + nsAutoString defaultDescription; + nsCOMPtr defaultApplication; + GetDefaultAppInfo(description, defaultDescription, getter_AddRefs(defaultApplication)); + + mimeInfo->SetDefaultDescription(defaultDescription); + mimeInfo->SetDefaultApplicationHandler(defaultApplication); // Get other nsIMIMEInfo fields from registry, if possible. if ( found ) diff --git a/uriloader/exthandler/win/nsOSHelperAppService.h b/uriloader/exthandler/win/nsOSHelperAppService.h index e56d806f7c05..bc1b1210229a 100644 --- a/uriloader/exthandler/win/nsOSHelperAppService.h +++ b/uriloader/exthandler/win/nsOSHelperAppService.h @@ -71,6 +71,7 @@ public: static PRBool GetValueString(HKEY hKey, const PRUnichar* pValueName, nsAString& result); protected: + nsresult GetDefaultAppInfo(nsAString& aTypeName, nsAString& aDefaultDescription, nsIFile** aDefaultApplication); // Lookup a mime info by extension, using an optional type hint already_AddRefed GetByExtension(const nsAFlatString& aFileExt, const char *aTypeHint = nsnull); nsresult FindOSMimeInfoForType(const char * aMimeContentType, nsIURI * aURI, char ** aFileExtension, nsIMIMEInfo ** aMIMEInfo);