michael@0: /* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * vim:set ts=2 sts=2 sw=2 et cin: michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsOSHelperAppService.h" michael@0: #include "nsISupports.h" michael@0: #include "nsString.h" michael@0: #include "nsXPIDLString.h" michael@0: #include "nsIURL.h" michael@0: #include "nsIMIMEInfo.h" michael@0: #include "nsMIMEInfoWin.h" michael@0: #include "nsMimeTypes.h" michael@0: #include "nsILocalFileWin.h" michael@0: #include "nsIProcess.h" michael@0: #include "plstr.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsNativeCharsetUtils.h" michael@0: #include "nsIWindowsRegKey.h" michael@0: michael@0: // shellapi.h is needed to build with WIN32_LEAN_AND_MEAN michael@0: #include michael@0: michael@0: #define LOG(args) PR_LOG(mLog, PR_LOG_DEBUG, args) michael@0: michael@0: // helper methods: forward declarations... michael@0: static nsresult GetExtensionFrom4xRegistryInfo(const nsACString& aMimeType, michael@0: nsString& aFileExtension); michael@0: static nsresult GetExtensionFromWindowsMimeDatabase(const nsACString& aMimeType, michael@0: nsString& aFileExtension); michael@0: michael@0: nsOSHelperAppService::nsOSHelperAppService() : michael@0: nsExternalHelperAppService() michael@0: , mAppAssoc(nullptr) michael@0: { michael@0: CoInitialize(nullptr); michael@0: CoCreateInstance(CLSID_ApplicationAssociationRegistration, nullptr, michael@0: CLSCTX_INPROC, IID_IApplicationAssociationRegistration, michael@0: (void**)&mAppAssoc); michael@0: } michael@0: michael@0: nsOSHelperAppService::~nsOSHelperAppService() michael@0: { michael@0: if (mAppAssoc) michael@0: mAppAssoc->Release(); michael@0: mAppAssoc = nullptr; michael@0: CoUninitialize(); michael@0: } michael@0: michael@0: // The windows registry provides a mime database key which lists a set of mime types and corresponding "Extension" values. michael@0: // we can use this to look up our mime type to see if there is a preferred extension for the mime type. michael@0: static nsresult GetExtensionFromWindowsMimeDatabase(const nsACString& aMimeType, michael@0: nsString& aFileExtension) michael@0: { michael@0: nsAutoString mimeDatabaseKey; michael@0: mimeDatabaseKey.AssignLiteral("MIME\\Database\\Content Type\\"); michael@0: michael@0: AppendASCIItoUTF16(aMimeType, mimeDatabaseKey); michael@0: michael@0: nsCOMPtr regKey = michael@0: do_CreateInstance("@mozilla.org/windows-registry-key;1"); michael@0: if (!regKey) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, michael@0: mimeDatabaseKey, michael@0: nsIWindowsRegKey::ACCESS_QUERY_VALUE); michael@0: michael@0: if (NS_SUCCEEDED(rv)) michael@0: regKey->ReadStringValue(NS_LITERAL_STRING("Extension"), aFileExtension); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // We have a serious problem!! I have this content type and the windows registry only gives me michael@0: // helper apps based on extension. Right now, we really don't have a good place to go for michael@0: // trying to figure out the extension for a particular mime type....One short term hack is to look michael@0: // this information in 4.x (it's stored in the windows regsitry). michael@0: static nsresult GetExtensionFrom4xRegistryInfo(const nsACString& aMimeType, michael@0: nsString& aFileExtension) michael@0: { michael@0: nsCOMPtr regKey = michael@0: do_CreateInstance("@mozilla.org/windows-registry-key;1"); michael@0: if (!regKey) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: nsresult rv = regKey-> michael@0: Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, michael@0: NS_LITERAL_STRING("Software\\Netscape\\Netscape Navigator\\Suffixes"), michael@0: nsIWindowsRegKey::ACCESS_QUERY_VALUE); michael@0: if (NS_FAILED(rv)) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: rv = regKey->ReadStringValue(NS_ConvertASCIItoUTF16(aMimeType), michael@0: aFileExtension); michael@0: if (NS_FAILED(rv)) michael@0: return NS_OK; michael@0: michael@0: aFileExtension.Insert(char16_t('.'), 0); michael@0: michael@0: // this may be a comma separated list of extensions...just take the michael@0: // first one for now... michael@0: michael@0: int32_t pos = aFileExtension.FindChar(char16_t(',')); michael@0: if (pos > 0) { michael@0: // we have a comma separated list of types... michael@0: // truncate everything after the first comma (including the comma) michael@0: aFileExtension.Truncate(pos); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult nsOSHelperAppService::OSProtocolHandlerExists(const char * aProtocolScheme, bool * aHandlerExists) michael@0: { michael@0: // look up the protocol scheme in the windows registry....if we find a match then we have a handler for it... michael@0: *aHandlerExists = false; michael@0: if (aProtocolScheme && *aProtocolScheme) michael@0: { michael@0: // Vista: use new application association interface michael@0: if (mAppAssoc) { michael@0: wchar_t * pResult = nullptr; michael@0: NS_ConvertASCIItoUTF16 scheme(aProtocolScheme); michael@0: // We are responsible for freeing returned strings. michael@0: HRESULT hr = mAppAssoc->QueryCurrentDefault(scheme.get(), michael@0: AT_URLPROTOCOL, AL_EFFECTIVE, michael@0: &pResult); michael@0: if (SUCCEEDED(hr)) { michael@0: CoTaskMemFree(pResult); michael@0: *aHandlerExists = true; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: HKEY hKey; michael@0: LONG err = ::RegOpenKeyExW(HKEY_CLASSES_ROOT, michael@0: NS_ConvertASCIItoUTF16(aProtocolScheme).get(), michael@0: 0, michael@0: KEY_QUERY_VALUE, michael@0: &hKey); michael@0: if (err == ERROR_SUCCESS) michael@0: { michael@0: err = ::RegQueryValueExW(hKey, L"URL Protocol", michael@0: nullptr, nullptr, nullptr, nullptr); michael@0: *aHandlerExists = (err == ERROR_SUCCESS); michael@0: // close the key michael@0: ::RegCloseKey(hKey); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsOSHelperAppService::GetApplicationDescription(const nsACString& aScheme, nsAString& _retval) michael@0: { michael@0: nsCOMPtr regKey = michael@0: do_CreateInstance("@mozilla.org/windows-registry-key;1"); michael@0: if (!regKey) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: NS_ConvertASCIItoUTF16 buf(aScheme); michael@0: michael@0: // Vista: use new application association interface michael@0: if (mAppAssoc) { michael@0: wchar_t * pResult = nullptr; michael@0: // We are responsible for freeing returned strings. michael@0: HRESULT hr = mAppAssoc->QueryCurrentDefault(buf.get(), michael@0: AT_URLPROTOCOL, AL_EFFECTIVE, michael@0: &pResult); michael@0: if (SUCCEEDED(hr)) { michael@0: nsCOMPtr app; michael@0: nsAutoString appInfo(pResult); michael@0: CoTaskMemFree(pResult); michael@0: if (NS_SUCCEEDED(GetDefaultAppInfo(appInfo, _retval, getter_AddRefs(app)))) michael@0: return NS_OK; michael@0: } michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: nsCOMPtr app; michael@0: GetDefaultAppInfo(buf, _retval, getter_AddRefs(app)); michael@0: michael@0: if (!_retval.Equals(buf)) michael@0: return NS_OK; michael@0: michael@0: // Fall back to full path michael@0: buf.AppendLiteral("\\shell\\open\\command"); michael@0: nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, michael@0: buf, michael@0: nsIWindowsRegKey::ACCESS_QUERY_VALUE); michael@0: if (NS_FAILED(rv)) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: rv = regKey->ReadStringValue(EmptyString(), _retval); michael@0: michael@0: return NS_SUCCEEDED(rv) ? NS_OK : NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: // GetMIMEInfoFromRegistry: This function obtains the values of some of the nsIMIMEInfo michael@0: // attributes for the mimeType/extension associated with the input registry key. The default michael@0: // entry for that key is the name of a registry key under HKEY_CLASSES_ROOT. The default michael@0: // value for *that* key is the descriptive name of the type. The EditFlags value is a binary michael@0: // value; the low order bit of the third byte of which indicates that the user does not need michael@0: // to be prompted. michael@0: // michael@0: // This function sets only the Description attribute of the input nsIMIMEInfo. michael@0: /* static */ michael@0: nsresult nsOSHelperAppService::GetMIMEInfoFromRegistry(const nsAFlatString& fileType, nsIMIMEInfo *pInfo) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: michael@0: NS_ENSURE_ARG(pInfo); michael@0: nsCOMPtr regKey = michael@0: do_CreateInstance("@mozilla.org/windows-registry-key;1"); michael@0: if (!regKey) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, michael@0: fileType, nsIWindowsRegKey::ACCESS_QUERY_VALUE); michael@0: if (NS_FAILED(rv)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // OK, the default value here is the description of the type. michael@0: nsAutoString description; michael@0: rv = regKey->ReadStringValue(EmptyString(), description); michael@0: if (NS_SUCCEEDED(rv)) michael@0: pInfo->SetDescription(description); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: ///////////////////////////////////////////////////////////////////////////////////////////////// michael@0: // method overrides used to gather information from the windows registry for michael@0: // various mime types. michael@0: //////////////////////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: /// Looks up the type for the extension aExt and compares it to aType michael@0: /* static */ bool michael@0: nsOSHelperAppService::typeFromExtEquals(const char16_t* aExt, const char *aType) michael@0: { michael@0: if (!aType) michael@0: return false; michael@0: nsAutoString fileExtToUse; michael@0: if (aExt[0] != char16_t('.')) michael@0: fileExtToUse = char16_t('.'); michael@0: michael@0: fileExtToUse.Append(aExt); michael@0: michael@0: bool eq = false; michael@0: nsCOMPtr regKey = michael@0: do_CreateInstance("@mozilla.org/windows-registry-key;1"); michael@0: if (!regKey) michael@0: return eq; michael@0: michael@0: nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, michael@0: fileExtToUse, michael@0: nsIWindowsRegKey::ACCESS_QUERY_VALUE); michael@0: if (NS_FAILED(rv)) michael@0: return eq; michael@0: michael@0: nsAutoString type; michael@0: rv = regKey->ReadStringValue(NS_LITERAL_STRING("Content Type"), type); michael@0: if (NS_SUCCEEDED(rv)) michael@0: eq = type.EqualsASCII(aType); michael@0: michael@0: return eq; michael@0: } michael@0: michael@0: // Strip a handler command string of its quotes and parameters. michael@0: static void CleanupHandlerPath(nsString& aPath) michael@0: { michael@0: // Example command strings passed into this routine: michael@0: michael@0: // 1) C:\Program Files\Company\some.exe -foo -bar michael@0: // 2) C:\Program Files\Company\some.dll michael@0: // 3) C:\Windows\some.dll,-foo -bar michael@0: // 4) C:\Windows\some.cpl,-foo -bar michael@0: michael@0: int32_t lastCommaPos = aPath.RFindChar(','); michael@0: if (lastCommaPos != kNotFound) michael@0: aPath.Truncate(lastCommaPos); michael@0: michael@0: aPath.AppendLiteral(" "); michael@0: michael@0: // case insensitive michael@0: uint32_t index = aPath.Find(".exe ", true); michael@0: if (index == kNotFound) michael@0: index = aPath.Find(".dll ", true); michael@0: if (index == kNotFound) michael@0: index = aPath.Find(".cpl ", true); michael@0: michael@0: if (index != kNotFound) michael@0: aPath.Truncate(index + 4); michael@0: aPath.Trim(" ", true, true); michael@0: } michael@0: michael@0: // Strip the windows host process bootstrap executable rundll32.exe michael@0: // from a handler's command string if it exists. michael@0: static void StripRundll32(nsString& aCommandString) michael@0: { michael@0: // Example rundll formats: michael@0: // C:\Windows\System32\rundll32.exe "path to dll" michael@0: // rundll32.exe "path to dll" michael@0: // C:\Windows\System32\rundll32.exe "path to dll", var var michael@0: // rundll32.exe "path to dll", var var michael@0: michael@0: NS_NAMED_LITERAL_STRING(rundllSegment, "rundll32.exe "); michael@0: NS_NAMED_LITERAL_STRING(rundllSegmentShort, "rundll32 "); michael@0: michael@0: // case insensitive michael@0: int32_t strLen = rundllSegment.Length(); michael@0: int32_t index = aCommandString.Find(rundllSegment, true); michael@0: if (index == kNotFound) { michael@0: strLen = rundllSegmentShort.Length(); michael@0: index = aCommandString.Find(rundllSegmentShort, true); michael@0: } michael@0: michael@0: if (index != kNotFound) { michael@0: uint32_t rundllSegmentLength = index + strLen; michael@0: aCommandString.Cut(0, rundllSegmentLength); michael@0: } michael@0: } michael@0: michael@0: // Returns the fully qualified path to an application handler based on michael@0: // a parameterized command string. Note this routine should not be used michael@0: // to launch the associated application as it strips parameters and michael@0: // rundll.exe from the string. Designed for retrieving display information michael@0: // on a particular handler. michael@0: /* static */ bool nsOSHelperAppService::CleanupCmdHandlerPath(nsAString& aCommandHandler) michael@0: { michael@0: nsAutoString handlerCommand(aCommandHandler); michael@0: michael@0: // Straight command path: michael@0: // michael@0: // %SystemRoot%\system32\NOTEPAD.EXE var michael@0: // "C:\Program Files\iTunes\iTunes.exe" var var michael@0: // C:\Program Files\iTunes\iTunes.exe var var michael@0: // michael@0: // Example rundll handlers: michael@0: // michael@0: // rundll32.exe "%ProgramFiles%\Win...ery\PhotoViewer.dll", var var michael@0: // rundll32.exe "%ProgramFiles%\Windows Photo Gallery\PhotoViewer.dll" michael@0: // C:\Windows\System32\rundll32.exe "path to dll", var var michael@0: // %SystemRoot%\System32\rundll32.exe "%ProgramFiles%\Win...ery\Photo michael@0: // Viewer.dll", var var michael@0: michael@0: // Expand environment variables so we have full path strings. michael@0: uint32_t bufLength = ::ExpandEnvironmentStringsW(handlerCommand.get(), michael@0: L"", 0); michael@0: if (bufLength == 0) // Error michael@0: return false; michael@0: michael@0: nsAutoArrayPtr destination(new wchar_t[bufLength]); michael@0: if (!destination) michael@0: return false; michael@0: if (!::ExpandEnvironmentStringsW(handlerCommand.get(), destination, michael@0: bufLength)) michael@0: return false; michael@0: michael@0: handlerCommand = static_cast(destination); michael@0: michael@0: // Remove quotes around paths michael@0: handlerCommand.StripChars("\""); michael@0: michael@0: // Strip windows host process bootstrap so we can get to the actual michael@0: // handler. michael@0: StripRundll32(handlerCommand); michael@0: michael@0: // Trim any command parameters so that we have a native path we can michael@0: // initialize a local file with. michael@0: CleanupHandlerPath(handlerCommand); michael@0: michael@0: aCommandHandler.Assign(handlerCommand); michael@0: return true; michael@0: } michael@0: michael@0: // The "real" name of a given helper app (as specified by the path to the michael@0: // executable file held in various registry keys) is stored n the VERSIONINFO michael@0: // block in the file's resources. We need to find the path to the executable michael@0: // and then retrieve the "FileDescription" field value from the file. michael@0: nsresult michael@0: nsOSHelperAppService::GetDefaultAppInfo(const nsAString& aAppInfo, michael@0: nsAString& aDefaultDescription, michael@0: nsIFile** aDefaultApplication) michael@0: { michael@0: nsAutoString handlerCommand; michael@0: michael@0: // If all else fails, use the file type key name, which will be michael@0: // something like "pngfile" for .pngs, "WMVFile" for .wmvs, etc. michael@0: aDefaultDescription = aAppInfo; michael@0: *aDefaultApplication = nullptr; michael@0: michael@0: if (aAppInfo.IsEmpty()) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // aAppInfo may be a file, file path, program id, or michael@0: // Applications reference - michael@0: // c:\dir\app.exe michael@0: // Applications\appfile.exe/dll (shell\open...) michael@0: // ProgID.progid (shell\open...) michael@0: michael@0: nsAutoString handlerKeyName(aAppInfo); michael@0: michael@0: nsCOMPtr chkKey = michael@0: do_CreateInstance("@mozilla.org/windows-registry-key;1"); michael@0: if (!chkKey) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsresult rv = chkKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, michael@0: handlerKeyName, michael@0: nsIWindowsRegKey::ACCESS_QUERY_VALUE); michael@0: if (NS_FAILED(rv)) { michael@0: // It's a file system path to a handler michael@0: handlerCommand.Assign(aAppInfo); michael@0: } michael@0: else { michael@0: handlerKeyName.AppendLiteral("\\shell\\open\\command"); michael@0: nsCOMPtr regKey = michael@0: do_CreateInstance("@mozilla.org/windows-registry-key;1"); michael@0: if (!regKey) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, michael@0: handlerKeyName, michael@0: nsIWindowsRegKey::ACCESS_QUERY_VALUE); michael@0: if (NS_FAILED(rv)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // OK, the default value here is the description of the type. michael@0: rv = regKey->ReadStringValue(EmptyString(), handlerCommand); michael@0: if (NS_FAILED(rv)) { michael@0: michael@0: // Check if there is a DelegateExecute string michael@0: nsAutoString delegateExecute; michael@0: rv = regKey->ReadStringValue(NS_LITERAL_STRING("DelegateExecute"), delegateExecute); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Look for InProcServer32 michael@0: nsAutoString delegateExecuteRegPath; michael@0: delegateExecuteRegPath.AssignLiteral("CLSID\\"); michael@0: delegateExecuteRegPath.Append(delegateExecute); michael@0: delegateExecuteRegPath.AppendLiteral("\\InProcServer32"); michael@0: rv = chkKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, michael@0: delegateExecuteRegPath, michael@0: nsIWindowsRegKey::ACCESS_QUERY_VALUE); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: rv = chkKey->ReadStringValue(EmptyString(), handlerCommand); michael@0: } michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: // Look for LocalServer32 michael@0: delegateExecuteRegPath.AssignLiteral("CLSID\\"); michael@0: delegateExecuteRegPath.Append(delegateExecute); michael@0: delegateExecuteRegPath.AppendLiteral("\\LocalServer32"); michael@0: rv = chkKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, michael@0: delegateExecuteRegPath, michael@0: nsIWindowsRegKey::ACCESS_QUERY_VALUE); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = chkKey->ReadStringValue(EmptyString(), handlerCommand); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (!CleanupCmdHandlerPath(handlerCommand)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // XXX FIXME: If this fails, the UI will display the full command michael@0: // string. michael@0: // There are some rare cases this can happen - ["url.dll" -foo] michael@0: // for example won't resolve correctly to the system dir. The michael@0: // subsequent launch of the helper app will work though. michael@0: nsCOMPtr lf; michael@0: NS_NewLocalFile(handlerCommand, true, getter_AddRefs(lf)); michael@0: if (!lf) michael@0: return NS_ERROR_FILE_NOT_FOUND; michael@0: michael@0: nsILocalFileWin* lfw = nullptr; michael@0: CallQueryInterface(lf, &lfw); michael@0: michael@0: if (lfw) { michael@0: // The "FileDescription" field contains the actual name of the application. michael@0: lfw->GetVersionInfoField("FileDescription", aDefaultDescription); michael@0: // QI addref'ed for us. michael@0: *aDefaultApplication = lfw; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: already_AddRefed nsOSHelperAppService::GetByExtension(const nsAFlatString& aFileExt, const char *aTypeHint) michael@0: { michael@0: if (aFileExt.IsEmpty()) michael@0: return nullptr; michael@0: michael@0: // windows registry assumes your file extension is going to include the '.'. michael@0: // so make sure it's there... michael@0: nsAutoString fileExtToUse; michael@0: if (aFileExt.First() != char16_t('.')) michael@0: fileExtToUse = char16_t('.'); michael@0: michael@0: fileExtToUse.Append(aFileExt); michael@0: michael@0: // Try to get an entry from the windows registry. michael@0: nsCOMPtr regKey = michael@0: do_CreateInstance("@mozilla.org/windows-registry-key;1"); michael@0: if (!regKey) michael@0: return nullptr; michael@0: michael@0: nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, michael@0: fileExtToUse, michael@0: nsIWindowsRegKey::ACCESS_QUERY_VALUE); michael@0: if (NS_FAILED(rv)) michael@0: return nullptr; michael@0: michael@0: nsAutoCString typeToUse; michael@0: if (aTypeHint && *aTypeHint) { michael@0: typeToUse.Assign(aTypeHint); michael@0: } michael@0: else { michael@0: nsAutoString temp; michael@0: if (NS_FAILED(regKey->ReadStringValue(NS_LITERAL_STRING("Content Type"), michael@0: temp)) || temp.IsEmpty()) { michael@0: return nullptr; michael@0: } michael@0: // Content-Type is always in ASCII michael@0: LossyAppendUTF16toASCII(temp, typeToUse); michael@0: } michael@0: michael@0: nsRefPtr mimeInfo = new nsMIMEInfoWin(typeToUse); michael@0: michael@0: // don't append the '.' michael@0: mimeInfo->AppendExtension(NS_ConvertUTF16toUTF8(Substring(fileExtToUse, 1))); michael@0: mimeInfo->SetPreferredAction(nsIMIMEInfo::useSystemDefault); michael@0: michael@0: nsAutoString appInfo; michael@0: bool found; michael@0: michael@0: // Retrieve the default application for this extension michael@0: if (mAppAssoc) { michael@0: // Vista: use the new application association COM interfaces michael@0: // for resolving helpers. michael@0: nsString assocType(fileExtToUse); michael@0: wchar_t * pResult = nullptr; michael@0: HRESULT hr = mAppAssoc->QueryCurrentDefault(assocType.get(), michael@0: AT_FILEEXTENSION, AL_EFFECTIVE, michael@0: &pResult); michael@0: if (SUCCEEDED(hr)) { michael@0: found = true; michael@0: appInfo.Assign(pResult); michael@0: CoTaskMemFree(pResult); michael@0: } michael@0: else { michael@0: found = false; michael@0: } michael@0: } michael@0: else michael@0: { michael@0: found = NS_SUCCEEDED(regKey->ReadStringValue(EmptyString(), michael@0: appInfo)); michael@0: } michael@0: michael@0: // Bug 358297 - ignore the default handler, force the user to choose app michael@0: if (appInfo.EqualsLiteral("XPSViewer.Document")) michael@0: found = false; michael@0: michael@0: if (!found) { michael@0: return nullptr; michael@0: } michael@0: michael@0: // Get other nsIMIMEInfo fields from registry, if possible. michael@0: nsAutoString defaultDescription; michael@0: nsCOMPtr defaultApplication; michael@0: michael@0: if (NS_FAILED(GetDefaultAppInfo(appInfo, defaultDescription, michael@0: getter_AddRefs(defaultApplication)))) { michael@0: return nullptr; michael@0: } michael@0: michael@0: mimeInfo->SetDefaultDescription(defaultDescription); michael@0: mimeInfo->SetDefaultApplicationHandler(defaultApplication); michael@0: michael@0: // Grab the general description michael@0: GetMIMEInfoFromRegistry(appInfo, mimeInfo); michael@0: michael@0: return mimeInfo.forget(); michael@0: } michael@0: michael@0: already_AddRefed nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString& aMIMEType, const nsACString& aFileExt, bool *aFound) michael@0: { michael@0: *aFound = true; michael@0: michael@0: const nsCString& flatType = PromiseFlatCString(aMIMEType); michael@0: const nsCString& flatExt = PromiseFlatCString(aFileExt); michael@0: michael@0: nsAutoString fileExtension; michael@0: /* XXX The Equals is a gross hack to wallpaper over the most common Win32 michael@0: * extension issues caused by the fix for bug 116938. See bug michael@0: * 120327, comment 271 for why this is needed. Not even sure we michael@0: * want to remove this once we have fixed all this stuff to work michael@0: * right; any info we get from the OS on this type is pretty much michael@0: * useless.... michael@0: * We'll do extension-based lookup for this type later in this function. michael@0: */ michael@0: if (!aMIMEType.LowerCaseEqualsLiteral(APPLICATION_OCTET_STREAM)) { michael@0: // (1) try to use the windows mime database to see if there is a mapping to a file extension michael@0: // (2) try to see if we have some left over 4.x registry info we can peek at... michael@0: GetExtensionFromWindowsMimeDatabase(aMIMEType, fileExtension); michael@0: LOG(("Windows mime database: extension '%s'\n", fileExtension.get())); michael@0: if (fileExtension.IsEmpty()) { michael@0: GetExtensionFrom4xRegistryInfo(aMIMEType, fileExtension); michael@0: LOG(("4.x Registry: extension '%s'\n", fileExtension.get())); michael@0: } michael@0: } michael@0: // If we found an extension for the type, do the lookup michael@0: nsRefPtr mi; michael@0: if (!fileExtension.IsEmpty()) michael@0: mi = GetByExtension(fileExtension, flatType.get()); michael@0: LOG(("Extension lookup on '%s' found: 0x%p\n", fileExtension.get(), mi.get())); michael@0: michael@0: bool hasDefault = false; michael@0: if (mi) { michael@0: mi->GetHasDefaultHandler(&hasDefault); michael@0: // OK. We might have the case that |aFileExt| is a valid extension for the michael@0: // mimetype we were given. In that case, we do want to append aFileExt michael@0: // to the mimeinfo that we have. (E.g.: We are asked for video/mpeg and michael@0: // .mpg, but the primary extension for video/mpeg is .mpeg. But because michael@0: // .mpg is an extension for video/mpeg content, we want to append it) michael@0: if (!aFileExt.IsEmpty() && typeFromExtEquals(NS_ConvertUTF8toUTF16(flatExt).get(), flatType.get())) { michael@0: LOG(("Appending extension '%s' to mimeinfo, because its mimetype is '%s'\n", michael@0: flatExt.get(), flatType.get())); michael@0: bool extExist = false; michael@0: mi->ExtensionExists(aFileExt, &extExist); michael@0: if (!extExist) michael@0: mi->AppendExtension(aFileExt); michael@0: } michael@0: } michael@0: if (!mi || !hasDefault) { michael@0: nsRefPtr miByExt = michael@0: GetByExtension(NS_ConvertUTF8toUTF16(aFileExt), flatType.get()); michael@0: LOG(("Ext. lookup for '%s' found 0x%p\n", flatExt.get(), miByExt.get())); michael@0: if (!miByExt && mi) michael@0: return mi.forget(); michael@0: if (miByExt && !mi) { michael@0: return miByExt.forget(); michael@0: } michael@0: if (!miByExt && !mi) { michael@0: *aFound = false; michael@0: mi = new nsMIMEInfoWin(flatType); michael@0: if (!aFileExt.IsEmpty()) { michael@0: mi->AppendExtension(aFileExt); michael@0: } michael@0: michael@0: return mi.forget(); michael@0: } michael@0: michael@0: // if we get here, mi has no default app. copy from extension lookup. michael@0: nsCOMPtr defaultApp; michael@0: nsAutoString desc; michael@0: miByExt->GetDefaultDescription(desc); michael@0: michael@0: mi->SetDefaultDescription(desc); michael@0: } michael@0: return mi.forget(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsOSHelperAppService::GetProtocolHandlerInfoFromOS(const nsACString &aScheme, michael@0: bool *found, michael@0: nsIHandlerInfo **_retval) michael@0: { michael@0: NS_ASSERTION(!aScheme.IsEmpty(), "No scheme was specified!"); michael@0: michael@0: nsresult rv = OSProtocolHandlerExists(nsPromiseFlatCString(aScheme).get(), michael@0: found); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsMIMEInfoWin *handlerInfo = michael@0: new nsMIMEInfoWin(aScheme, nsMIMEInfoBase::eProtocolInfo); michael@0: NS_ENSURE_TRUE(handlerInfo, NS_ERROR_OUT_OF_MEMORY); michael@0: NS_ADDREF(*_retval = handlerInfo); michael@0: michael@0: if (!*found) { michael@0: // Code that calls this requires an object regardless if the OS has michael@0: // something for us, so we return the empty object. michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsAutoString desc; michael@0: GetApplicationDescription(aScheme, desc); michael@0: handlerInfo->SetDefaultDescription(desc); michael@0: michael@0: return NS_OK; michael@0: } michael@0: