michael@0: /* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*- 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 "nsArrayEnumerator.h" michael@0: #include "nsCOMArray.h" michael@0: #include "nsIFile.h" michael@0: #include "nsIVariant.h" michael@0: #include "nsMIMEInfoWin.h" michael@0: #include "nsNetUtil.h" michael@0: #include michael@0: #include michael@0: #include "nsAutoPtr.h" michael@0: #include "nsIMutableArray.h" michael@0: #include "nsTArray.h" michael@0: #include "shlobj.h" michael@0: #include "windows.h" michael@0: #include "nsIWindowsRegKey.h" michael@0: #include "nsIProcess.h" michael@0: #include "nsOSHelperAppService.h" michael@0: #include "nsUnicharUtils.h" michael@0: #include "nsITextToSubURI.h" michael@0: michael@0: #define RUNDLL32_EXE L"\\rundll32.exe" michael@0: michael@0: michael@0: NS_IMPL_ISUPPORTS_INHERITED(nsMIMEInfoWin, nsMIMEInfoBase, nsIPropertyBag) michael@0: michael@0: nsMIMEInfoWin::~nsMIMEInfoWin() michael@0: { michael@0: } michael@0: michael@0: nsresult michael@0: nsMIMEInfoWin::LaunchDefaultWithFile(nsIFile* aFile) michael@0: { michael@0: // Launch the file, unless it is an executable. michael@0: bool executable = true; michael@0: aFile->IsExecutable(&executable); michael@0: if (executable) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return aFile->Launch(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMIMEInfoWin::LaunchWithFile(nsIFile* aFile) michael@0: { michael@0: nsresult rv; michael@0: michael@0: // it doesn't make any sense to call this on protocol handlers michael@0: NS_ASSERTION(mClass == eMIMEInfo, michael@0: "nsMIMEInfoBase should have mClass == eMIMEInfo"); michael@0: michael@0: if (mPreferredAction == useSystemDefault) { michael@0: return LaunchDefaultWithFile(aFile); michael@0: } michael@0: michael@0: if (mPreferredAction == useHelperApp) { michael@0: if (!mPreferredApplication) michael@0: return NS_ERROR_FILE_NOT_FOUND; michael@0: michael@0: // at the moment, we only know how to hand files off to local handlers michael@0: nsCOMPtr localHandler = michael@0: do_QueryInterface(mPreferredApplication, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr executable; michael@0: rv = localHandler->GetExecutable(getter_AddRefs(executable)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsAutoString path; michael@0: aFile->GetPath(path); michael@0: michael@0: // Deal with local dll based handlers michael@0: nsCString filename; michael@0: executable->GetNativeLeafName(filename); michael@0: if (filename.Length() > 4) { michael@0: nsCString extension(Substring(filename, filename.Length() - 4, 4)); michael@0: michael@0: if (extension.LowerCaseEqualsLiteral(".dll")) { michael@0: nsAutoString args; michael@0: michael@0: // executable is rundll32, everything else is a list of parameters, michael@0: // including the dll handler. michael@0: if (!GetDllLaunchInfo(executable, aFile, args, false)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: WCHAR rundll32Path[MAX_PATH + sizeof(RUNDLL32_EXE) / sizeof(WCHAR) + 1] = {L'\0'}; michael@0: if (!GetSystemDirectoryW(rundll32Path, MAX_PATH)) { michael@0: return NS_ERROR_FILE_NOT_FOUND; michael@0: } michael@0: lstrcatW(rundll32Path, RUNDLL32_EXE); michael@0: michael@0: SHELLEXECUTEINFOW seinfo; michael@0: memset(&seinfo, 0, sizeof(seinfo)); michael@0: seinfo.cbSize = sizeof(SHELLEXECUTEINFOW); michael@0: seinfo.fMask = 0; michael@0: seinfo.hwnd = nullptr; michael@0: seinfo.lpVerb = nullptr; michael@0: seinfo.lpFile = rundll32Path; michael@0: seinfo.lpParameters = args.get(); michael@0: seinfo.lpDirectory = nullptr; michael@0: seinfo.nShow = SW_SHOWNORMAL; michael@0: if (ShellExecuteExW(&seinfo)) michael@0: return NS_OK; michael@0: michael@0: switch ((LONG_PTR)seinfo.hInstApp) { michael@0: case 0: michael@0: case SE_ERR_OOM: michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: case SE_ERR_ACCESSDENIED: michael@0: return NS_ERROR_FILE_ACCESS_DENIED; michael@0: case SE_ERR_ASSOCINCOMPLETE: michael@0: case SE_ERR_NOASSOC: michael@0: return NS_ERROR_UNEXPECTED; michael@0: case SE_ERR_DDEBUSY: michael@0: case SE_ERR_DDEFAIL: michael@0: case SE_ERR_DDETIMEOUT: michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: case SE_ERR_DLLNOTFOUND: michael@0: return NS_ERROR_FAILURE; michael@0: case SE_ERR_SHARE: michael@0: return NS_ERROR_FILE_IS_LOCKED; michael@0: default: michael@0: switch(GetLastError()) { michael@0: case ERROR_FILE_NOT_FOUND: michael@0: return NS_ERROR_FILE_NOT_FOUND; michael@0: case ERROR_PATH_NOT_FOUND: michael@0: return NS_ERROR_FILE_UNRECOGNIZED_PATH; michael@0: case ERROR_BAD_FORMAT: michael@0: return NS_ERROR_FILE_CORRUPTED; michael@0: } michael@0: michael@0: } michael@0: return NS_ERROR_FILE_EXECUTION_FAILED; michael@0: } michael@0: } michael@0: return LaunchWithIProcess(executable, path); michael@0: } michael@0: michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMIMEInfoWin::GetHasDefaultHandler(bool * _retval) michael@0: { michael@0: // We have a default application if we have a description michael@0: // We can ShellExecute anything; however, callers are probably interested if michael@0: // there is really an application associated with this type of file michael@0: *_retval = !mDefaultAppDescription.IsEmpty(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMIMEInfoWin::GetEnumerator(nsISimpleEnumerator* *_retval) michael@0: { michael@0: nsCOMArray properties; michael@0: michael@0: nsCOMPtr variant; michael@0: GetProperty(NS_LITERAL_STRING("defaultApplicationIconURL"), getter_AddRefs(variant)); michael@0: if (variant) michael@0: properties.AppendObject(variant); michael@0: michael@0: GetProperty(NS_LITERAL_STRING("customApplicationIconURL"), getter_AddRefs(variant)); michael@0: if (variant) michael@0: properties.AppendObject(variant); michael@0: michael@0: return NS_NewArrayEnumerator(_retval, properties); michael@0: } michael@0: michael@0: static nsresult GetIconURLVariant(nsIFile* aApplication, nsIVariant* *_retval) michael@0: { michael@0: nsresult rv = CallCreateInstance("@mozilla.org/variant;1", _retval); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: nsAutoCString fileURLSpec; michael@0: NS_GetURLSpecFromFile(aApplication, fileURLSpec); michael@0: nsAutoCString iconURLSpec; iconURLSpec.AssignLiteral("moz-icon://"); michael@0: iconURLSpec += fileURLSpec; michael@0: nsCOMPtr writable(do_QueryInterface(*_retval)); michael@0: writable->SetAsAUTF8String(iconURLSpec); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMIMEInfoWin::GetProperty(const nsAString& aName, nsIVariant* *_retval) michael@0: { michael@0: nsresult rv; michael@0: if (mDefaultApplication && aName.EqualsLiteral(PROPERTY_DEFAULT_APP_ICON_URL)) { michael@0: rv = GetIconURLVariant(mDefaultApplication, _retval); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } else if (mPreferredApplication && michael@0: aName.EqualsLiteral(PROPERTY_CUSTOM_APP_ICON_URL)) { michael@0: nsCOMPtr localHandler = michael@0: do_QueryInterface(mPreferredApplication, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr executable; michael@0: rv = localHandler->GetExecutable(getter_AddRefs(executable)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = GetIconURLVariant(executable, _retval); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // this implementation was pretty much copied verbatime from michael@0: // Tony Robinson's code in nsExternalProtocolWin.cpp michael@0: nsresult michael@0: nsMIMEInfoWin::LoadUriInternal(nsIURI * aURL) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: michael@0: // 1. Find the default app for this protocol michael@0: // 2. Set up the command line michael@0: // 3. Launch the app. michael@0: michael@0: // For now, we'll just cheat essentially, check for the command line michael@0: // then just call ShellExecute()! michael@0: michael@0: if (aURL) michael@0: { michael@0: // extract the url spec from the url michael@0: nsAutoCString urlSpec; michael@0: aURL->GetAsciiSpec(urlSpec); michael@0: michael@0: // Unescape non-ASCII characters in the URL michael@0: nsAutoCString urlCharset; michael@0: nsAutoString utf16Spec; michael@0: rv = aURL->GetOriginCharset(urlCharset); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr textToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = textToSubURI->UnEscapeNonAsciiURI(urlCharset, urlSpec, utf16Spec); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: static const wchar_t cmdVerb[] = L"open"; michael@0: SHELLEXECUTEINFOW sinfo; michael@0: memset(&sinfo, 0, sizeof(sinfo)); michael@0: sinfo.cbSize = sizeof(sinfo); michael@0: sinfo.fMask = SEE_MASK_FLAG_DDEWAIT | michael@0: SEE_MASK_FLAG_NO_UI; michael@0: sinfo.hwnd = nullptr; michael@0: sinfo.lpVerb = (LPWSTR)&cmdVerb; michael@0: sinfo.nShow = SW_SHOWNORMAL; michael@0: michael@0: LPITEMIDLIST pidl = nullptr; michael@0: SFGAOF sfgao; michael@0: michael@0: // Bug 394974 michael@0: if (SUCCEEDED(SHParseDisplayName(utf16Spec.get(), nullptr, michael@0: &pidl, 0, &sfgao))) { michael@0: sinfo.lpIDList = pidl; michael@0: sinfo.fMask |= SEE_MASK_INVOKEIDLIST; michael@0: } else { michael@0: // SHParseDisplayName failed. Bailing out as work around for michael@0: // Microsoft Security Bulletin MS07-061 michael@0: rv = NS_ERROR_FAILURE; michael@0: } michael@0: if (NS_SUCCEEDED(rv)) { michael@0: BOOL result = ShellExecuteExW(&sinfo); michael@0: if (!result || ((LONG_PTR)sinfo.hInstApp) < 32) michael@0: rv = NS_ERROR_FAILURE; michael@0: } michael@0: if (pidl) michael@0: CoTaskMemFree(pidl); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: // Given a path to a local file, return its nsILocalHandlerApp instance. michael@0: bool nsMIMEInfoWin::GetLocalHandlerApp(const nsAString& aCommandHandler, michael@0: nsCOMPtr& aApp) michael@0: { michael@0: nsCOMPtr locfile; michael@0: nsresult rv = michael@0: NS_NewLocalFile(aCommandHandler, true, getter_AddRefs(locfile)); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: michael@0: aApp = do_CreateInstance("@mozilla.org/uriloader/local-handler-app;1"); michael@0: if (!aApp) michael@0: return false; michael@0: michael@0: aApp->SetExecutable(locfile); michael@0: return true; michael@0: } michael@0: michael@0: // Return the cleaned up file path associated with a command verb michael@0: // located in root/Applications. michael@0: bool nsMIMEInfoWin::GetAppsVerbCommandHandler(const nsAString& appExeName, michael@0: nsAString& applicationPath, michael@0: bool edit) michael@0: { michael@0: nsCOMPtr appKey = michael@0: do_CreateInstance("@mozilla.org/windows-registry-key;1"); michael@0: if (!appKey) michael@0: return false; michael@0: michael@0: // HKEY_CLASSES_ROOT\Applications\iexplore.exe michael@0: nsAutoString applicationsPath; michael@0: applicationsPath.AppendLiteral("Applications\\"); michael@0: applicationsPath.Append(appExeName); michael@0: michael@0: nsresult rv = appKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, michael@0: applicationsPath, michael@0: nsIWindowsRegKey::ACCESS_QUERY_VALUE); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: michael@0: // Check for the NoOpenWith flag, if it exists michael@0: uint32_t value; michael@0: if (NS_SUCCEEDED(appKey->ReadIntValue( michael@0: NS_LITERAL_STRING("NoOpenWith"), &value)) && michael@0: value == 1) michael@0: return false; michael@0: michael@0: nsAutoString dummy; michael@0: if (NS_SUCCEEDED(appKey->ReadStringValue( michael@0: NS_LITERAL_STRING("NoOpenWith"), dummy))) michael@0: return false; michael@0: michael@0: appKey->Close(); michael@0: michael@0: // HKEY_CLASSES_ROOT\Applications\iexplore.exe\shell\open\command michael@0: applicationsPath.AssignLiteral("Applications\\"); michael@0: applicationsPath.Append(appExeName); michael@0: if (!edit) michael@0: applicationsPath.AppendLiteral("\\shell\\open\\command"); michael@0: else michael@0: applicationsPath.AppendLiteral("\\shell\\edit\\command"); michael@0: michael@0: michael@0: rv = appKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, michael@0: applicationsPath, michael@0: nsIWindowsRegKey::ACCESS_QUERY_VALUE); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: michael@0: nsAutoString appFilesystemCommand; michael@0: if (NS_SUCCEEDED(appKey->ReadStringValue(EmptyString(), michael@0: appFilesystemCommand))) { michael@0: michael@0: // Expand environment vars, clean up any misc. michael@0: if (!nsOSHelperAppService::CleanupCmdHandlerPath(appFilesystemCommand)) michael@0: return false; michael@0: michael@0: applicationPath = appFilesystemCommand; michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: // Return a fully populated command string based on michael@0: // passing information. Used in launchWithFile to trace michael@0: // back to the full handler path based on the dll. michael@0: // (dll, targetfile, return args, open/edit) michael@0: bool nsMIMEInfoWin::GetDllLaunchInfo(nsIFile * aDll, michael@0: nsIFile * aFile, michael@0: nsAString& args, michael@0: bool edit) michael@0: { michael@0: if (!aDll || !aFile) michael@0: return false; michael@0: michael@0: nsString appExeName; michael@0: aDll->GetLeafName(appExeName); michael@0: michael@0: nsCOMPtr appKey = michael@0: do_CreateInstance("@mozilla.org/windows-registry-key;1"); michael@0: if (!appKey) michael@0: return false; michael@0: michael@0: // HKEY_CLASSES_ROOT\Applications\iexplore.exe michael@0: nsAutoString applicationsPath; michael@0: applicationsPath.AppendLiteral("Applications\\"); michael@0: applicationsPath.Append(appExeName); michael@0: michael@0: nsresult rv = appKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, michael@0: applicationsPath, michael@0: nsIWindowsRegKey::ACCESS_QUERY_VALUE); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: michael@0: // Check for the NoOpenWith flag, if it exists michael@0: uint32_t value; michael@0: rv = appKey->ReadIntValue(NS_LITERAL_STRING("NoOpenWith"), &value); michael@0: if (NS_SUCCEEDED(rv) && value == 1) michael@0: return false; michael@0: michael@0: nsAutoString dummy; michael@0: if (NS_SUCCEEDED(appKey->ReadStringValue(NS_LITERAL_STRING("NoOpenWith"), michael@0: dummy))) michael@0: return false; michael@0: michael@0: appKey->Close(); michael@0: michael@0: // HKEY_CLASSES_ROOT\Applications\iexplore.exe\shell\open\command michael@0: applicationsPath.AssignLiteral("Applications\\"); michael@0: applicationsPath.Append(appExeName); michael@0: if (!edit) michael@0: applicationsPath.AppendLiteral("\\shell\\open\\command"); michael@0: else michael@0: applicationsPath.AppendLiteral("\\shell\\edit\\command"); michael@0: michael@0: rv = appKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, michael@0: applicationsPath, michael@0: nsIWindowsRegKey::ACCESS_QUERY_VALUE); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: michael@0: nsAutoString appFilesystemCommand; michael@0: if (NS_SUCCEEDED(appKey->ReadStringValue(EmptyString(), michael@0: appFilesystemCommand))) { michael@0: // Replace embedded environment variables. michael@0: uint32_t bufLength = michael@0: ::ExpandEnvironmentStringsW(appFilesystemCommand.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(appFilesystemCommand.get(), michael@0: destination, michael@0: bufLength)) michael@0: return false; michael@0: michael@0: appFilesystemCommand = static_cast(destination); michael@0: michael@0: // C:\Windows\System32\rundll32.exe "C:\Program Files\Windows michael@0: // Photo Gallery\PhotoViewer.dll", ImageView_Fullscreen %1 michael@0: nsAutoString params; michael@0: NS_NAMED_LITERAL_STRING(rundllSegment, "rundll32.exe "); michael@0: int32_t index = appFilesystemCommand.Find(rundllSegment); michael@0: if (index > kNotFound) { michael@0: params.Append(Substring(appFilesystemCommand, michael@0: index + rundllSegment.Length())); michael@0: } else { michael@0: params.Append(appFilesystemCommand); michael@0: } michael@0: michael@0: // check to make sure we have a %1 and fill it michael@0: NS_NAMED_LITERAL_STRING(percentOneParam, "%1"); michael@0: index = params.Find(percentOneParam); michael@0: if (index == kNotFound) // no parameter michael@0: return false; michael@0: michael@0: nsString target; michael@0: aFile->GetTarget(target); michael@0: params.Replace(index, 2, target); michael@0: michael@0: args = params; michael@0: michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: // Return the cleaned up file path associated with a progid command michael@0: // verb located in root. michael@0: bool nsMIMEInfoWin::GetProgIDVerbCommandHandler(const nsAString& appProgIDName, michael@0: nsAString& applicationPath, michael@0: bool edit) michael@0: { michael@0: nsCOMPtr appKey = michael@0: do_CreateInstance("@mozilla.org/windows-registry-key;1"); michael@0: if (!appKey) michael@0: return false; michael@0: michael@0: nsAutoString appProgId(appProgIDName); michael@0: michael@0: // HKEY_CLASSES_ROOT\Windows.XPSReachViewer\shell\open\command michael@0: if (!edit) michael@0: appProgId.AppendLiteral("\\shell\\open\\command"); michael@0: else michael@0: appProgId.AppendLiteral("\\shell\\edit\\command"); michael@0: michael@0: nsresult rv = appKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, michael@0: appProgId, michael@0: nsIWindowsRegKey::ACCESS_QUERY_VALUE); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: michael@0: nsAutoString appFilesystemCommand; michael@0: if (NS_SUCCEEDED(appKey->ReadStringValue(EmptyString(), appFilesystemCommand))) { michael@0: michael@0: // Expand environment vars, clean up any misc. michael@0: if (!nsOSHelperAppService::CleanupCmdHandlerPath(appFilesystemCommand)) michael@0: return false; michael@0: michael@0: applicationPath = appFilesystemCommand; michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: // Helper routine used in tracking app lists. Converts path michael@0: // entries to lower case and stores them in the trackList array. michael@0: void nsMIMEInfoWin::ProcessPath(nsCOMPtr& appList, michael@0: nsTArray& trackList, michael@0: const nsAString& appFilesystemCommand) michael@0: { michael@0: nsAutoString lower(appFilesystemCommand); michael@0: ToLowerCase(lower); michael@0: michael@0: // Don't include firefox.exe in the list michael@0: WCHAR exe[MAX_PATH+1]; michael@0: uint32_t len = GetModuleFileNameW(nullptr, exe, MAX_PATH); michael@0: if (len < MAX_PATH && len != 0) { michael@0: uint32_t index = lower.Find(exe); michael@0: if (index != -1) michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr aApp; michael@0: if (!GetLocalHandlerApp(appFilesystemCommand, aApp)) michael@0: return; michael@0: michael@0: // Save in our main tracking arrays michael@0: appList->AppendElement(aApp, false); michael@0: trackList.AppendElement(lower); michael@0: } michael@0: michael@0: // Helper routine that handles a compare between a path michael@0: // and an array of paths. michael@0: static bool IsPathInList(nsAString& appPath, michael@0: nsTArray& trackList) michael@0: { michael@0: // trackList data is always lowercase, see ProcessPath michael@0: // above. michael@0: nsAutoString tmp(appPath); michael@0: ToLowerCase(tmp); michael@0: michael@0: for (uint32_t i = 0; i < trackList.Length(); i++) { michael@0: if (tmp.Equals(trackList[i])) michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: /** michael@0: * Returns a list of nsILocalHandlerApp objects containing local michael@0: * handlers associated with this mimeinfo. Implemented per michael@0: * platform using information in this object to generate the michael@0: * best list. Typically used for an "open with" style user michael@0: * option. michael@0: * michael@0: * @return nsIArray of nsILocalHandlerApp michael@0: */ michael@0: NS_IMETHODIMP michael@0: nsMIMEInfoWin::GetPossibleLocalHandlers(nsIArray **_retval) michael@0: { michael@0: nsresult rv; michael@0: michael@0: *_retval = nullptr; michael@0: michael@0: nsCOMPtr appList = michael@0: do_CreateInstance("@mozilla.org/array;1"); michael@0: michael@0: if (!appList) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsTArray trackList; michael@0: michael@0: nsAutoCString fileExt; michael@0: GetPrimaryExtension(fileExt); 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_FAILURE; michael@0: nsCOMPtr appKey = michael@0: do_CreateInstance("@mozilla.org/windows-registry-key;1"); michael@0: if (!appKey) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsAutoString workingRegistryPath; michael@0: michael@0: bool extKnown = false; michael@0: if (fileExt.IsEmpty()) { michael@0: extKnown = true; michael@0: // Mime type discovery is possible in some cases, through michael@0: // HKEY_CLASSES_ROOT\MIME\Database\Content Type, however, a number michael@0: // of file extensions related to mime type are simply not defined, michael@0: // (application/rss+xml & application/atom+xml are good examples) michael@0: // in which case we can only provide a generic list. michael@0: nsAutoCString mimeType; michael@0: GetMIMEType(mimeType); michael@0: if (!mimeType.IsEmpty()) { michael@0: workingRegistryPath.AppendLiteral("MIME\\Database\\Content Type\\"); michael@0: workingRegistryPath.Append(NS_ConvertASCIItoUTF16(mimeType)); michael@0: michael@0: rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, michael@0: workingRegistryPath, michael@0: nsIWindowsRegKey::ACCESS_QUERY_VALUE); michael@0: if(NS_SUCCEEDED(rv)) { michael@0: nsAutoString mimeFileExt; michael@0: if (NS_SUCCEEDED(regKey->ReadStringValue(EmptyString(), mimeFileExt))) { michael@0: CopyUTF16toUTF8(mimeFileExt, fileExt); michael@0: extKnown = false; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsAutoString fileExtToUse; michael@0: if (fileExt.First() != '.') michael@0: fileExtToUse = char16_t('.'); michael@0: fileExtToUse.Append(NS_ConvertUTF8toUTF16(fileExt)); michael@0: michael@0: // Note, the order in which these occur has an effect on the michael@0: // validity of the resulting display list. michael@0: michael@0: if (!extKnown) { michael@0: // 1) Get the default handler if it exists michael@0: workingRegistryPath = fileExtToUse; michael@0: michael@0: rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, michael@0: workingRegistryPath, michael@0: nsIWindowsRegKey::ACCESS_QUERY_VALUE); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: nsAutoString appProgId; michael@0: if (NS_SUCCEEDED(regKey->ReadStringValue(EmptyString(), appProgId))) { michael@0: // Bug 358297 - ignore the embedded internet explorer handler michael@0: if (appProgId != NS_LITERAL_STRING("XPSViewer.Document")) { michael@0: nsAutoString appFilesystemCommand; michael@0: if (GetProgIDVerbCommandHandler(appProgId, michael@0: appFilesystemCommand, michael@0: false) && michael@0: !IsPathInList(appFilesystemCommand, trackList)) { michael@0: ProcessPath(appList, trackList, appFilesystemCommand); michael@0: } michael@0: } michael@0: } michael@0: regKey->Close(); michael@0: } michael@0: michael@0: michael@0: // 2) list HKEY_CLASSES_ROOT\.ext\OpenWithList michael@0: michael@0: workingRegistryPath = fileExtToUse; michael@0: workingRegistryPath.AppendLiteral("\\OpenWithList"); michael@0: michael@0: rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, michael@0: workingRegistryPath, michael@0: nsIWindowsRegKey::ACCESS_QUERY_VALUE); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: uint32_t count = 0; michael@0: if (NS_SUCCEEDED(regKey->GetValueCount(&count)) && count > 0) { michael@0: for (uint32_t index = 0; index < count; index++) { michael@0: nsAutoString appName; michael@0: if (NS_FAILED(regKey->GetValueName(index, appName))) michael@0: continue; michael@0: michael@0: // HKEY_CLASSES_ROOT\Applications\firefox.exe = "path params" michael@0: nsAutoString appFilesystemCommand; michael@0: if (!GetAppsVerbCommandHandler(appName, michael@0: appFilesystemCommand, michael@0: false) || michael@0: IsPathInList(appFilesystemCommand, trackList)) michael@0: continue; michael@0: ProcessPath(appList, trackList, appFilesystemCommand); michael@0: } michael@0: } michael@0: regKey->Close(); michael@0: } michael@0: michael@0: michael@0: // 3) List HKEY_CLASSES_ROOT\.ext\OpenWithProgids, with the michael@0: // different step of resolving the progids for the command handler. michael@0: michael@0: workingRegistryPath = fileExtToUse; michael@0: workingRegistryPath.AppendLiteral("\\OpenWithProgids"); michael@0: michael@0: rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, michael@0: workingRegistryPath, michael@0: nsIWindowsRegKey::ACCESS_QUERY_VALUE); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: uint32_t count = 0; michael@0: if (NS_SUCCEEDED(regKey->GetValueCount(&count)) && count > 0) { michael@0: for (uint32_t index = 0; index < count; index++) { michael@0: // HKEY_CLASSES_ROOT\.ext\OpenWithProgids\Windows.XPSReachViewer michael@0: nsAutoString appProgId; michael@0: if (NS_FAILED(regKey->GetValueName(index, appProgId))) michael@0: continue; michael@0: michael@0: nsAutoString appFilesystemCommand; michael@0: if (!GetProgIDVerbCommandHandler(appProgId, michael@0: appFilesystemCommand, michael@0: false) || michael@0: IsPathInList(appFilesystemCommand, trackList)) michael@0: continue; michael@0: ProcessPath(appList, trackList, appFilesystemCommand); michael@0: } michael@0: } michael@0: regKey->Close(); michael@0: } michael@0: michael@0: michael@0: // 4) Add any non configured applications located in the MRU list michael@0: michael@0: // HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion michael@0: // \Explorer\FileExts\.ext\OpenWithList michael@0: workingRegistryPath = michael@0: NS_LITERAL_STRING("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\"); michael@0: workingRegistryPath += fileExtToUse; michael@0: workingRegistryPath.AppendLiteral("\\OpenWithList"); michael@0: michael@0: rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, michael@0: workingRegistryPath, michael@0: nsIWindowsRegKey::ACCESS_QUERY_VALUE); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: uint32_t count = 0; michael@0: if (NS_SUCCEEDED(regKey->GetValueCount(&count)) && count > 0) { michael@0: for (uint32_t index = 0; index < count; index++) { michael@0: nsAutoString appName, appValue; michael@0: if (NS_FAILED(regKey->GetValueName(index, appName))) michael@0: continue; michael@0: if (appName.EqualsLiteral("MRUList")) michael@0: continue; michael@0: if (NS_FAILED(regKey->ReadStringValue(appName, appValue))) michael@0: continue; michael@0: michael@0: // HKEY_CLASSES_ROOT\Applications\firefox.exe = "path params" michael@0: nsAutoString appFilesystemCommand; michael@0: if (!GetAppsVerbCommandHandler(appValue, michael@0: appFilesystemCommand, michael@0: false) || michael@0: IsPathInList(appFilesystemCommand, trackList)) michael@0: continue; michael@0: ProcessPath(appList, trackList, appFilesystemCommand); michael@0: } michael@0: } michael@0: } michael@0: michael@0: michael@0: // 5) Add any non configured progids in the MRU list, with the michael@0: // different step of resolving the progids for the command handler. michael@0: michael@0: // HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion michael@0: // \Explorer\FileExts\.ext\OpenWithProgids michael@0: workingRegistryPath = michael@0: NS_LITERAL_STRING("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\"); michael@0: workingRegistryPath += fileExtToUse; michael@0: workingRegistryPath.AppendLiteral("\\OpenWithProgids"); michael@0: michael@0: regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, michael@0: workingRegistryPath, michael@0: nsIWindowsRegKey::ACCESS_QUERY_VALUE); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: uint32_t count = 0; michael@0: if (NS_SUCCEEDED(regKey->GetValueCount(&count)) && count > 0) { michael@0: for (uint32_t index = 0; index < count; index++) { michael@0: nsAutoString appIndex, appProgId; michael@0: if (NS_FAILED(regKey->GetValueName(index, appProgId))) michael@0: continue; michael@0: michael@0: nsAutoString appFilesystemCommand; michael@0: if (!GetProgIDVerbCommandHandler(appProgId, michael@0: appFilesystemCommand, michael@0: false) || michael@0: IsPathInList(appFilesystemCommand, trackList)) michael@0: continue; michael@0: ProcessPath(appList, trackList, appFilesystemCommand); michael@0: } michael@0: } michael@0: regKey->Close(); michael@0: } michael@0: michael@0: michael@0: // 6) Check the perceived type value, and use this to lookup the perceivedtype michael@0: // open with list. michael@0: // http://msdn2.microsoft.com/en-us/library/aa969373.aspx michael@0: michael@0: workingRegistryPath = fileExtToUse; michael@0: michael@0: regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, michael@0: workingRegistryPath, michael@0: nsIWindowsRegKey::ACCESS_QUERY_VALUE); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: nsAutoString perceivedType; michael@0: rv = regKey->ReadStringValue(NS_LITERAL_STRING("PerceivedType"), michael@0: perceivedType); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: nsAutoString openWithListPath(NS_LITERAL_STRING("SystemFileAssociations\\")); michael@0: openWithListPath.Append(perceivedType); // no period michael@0: openWithListPath.Append(NS_LITERAL_STRING("\\OpenWithList")); michael@0: michael@0: nsresult rv = appKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, michael@0: openWithListPath, michael@0: nsIWindowsRegKey::ACCESS_QUERY_VALUE); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: uint32_t count = 0; michael@0: if (NS_SUCCEEDED(regKey->GetValueCount(&count)) && count > 0) { michael@0: for (uint32_t index = 0; index < count; index++) { michael@0: nsAutoString appName; michael@0: if (NS_FAILED(regKey->GetValueName(index, appName))) michael@0: continue; michael@0: michael@0: // HKEY_CLASSES_ROOT\Applications\firefox.exe = "path params" michael@0: nsAutoString appFilesystemCommand; michael@0: if (!GetAppsVerbCommandHandler(appName, appFilesystemCommand, michael@0: false) || michael@0: IsPathInList(appFilesystemCommand, trackList)) michael@0: continue; michael@0: ProcessPath(appList, trackList, appFilesystemCommand); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } // extKnown == false michael@0: michael@0: michael@0: // 7) list global HKEY_CLASSES_ROOT\*\OpenWithList michael@0: // Listing general purpose handlers, not specific to a mime type or file extension michael@0: michael@0: workingRegistryPath = NS_LITERAL_STRING("*\\OpenWithList"); michael@0: michael@0: rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, michael@0: workingRegistryPath, michael@0: nsIWindowsRegKey::ACCESS_QUERY_VALUE); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: uint32_t count = 0; michael@0: if (NS_SUCCEEDED(regKey->GetValueCount(&count)) && count > 0) { michael@0: for (uint32_t index = 0; index < count; index++) { michael@0: nsAutoString appName; michael@0: if (NS_FAILED(regKey->GetValueName(index, appName))) michael@0: continue; michael@0: michael@0: // HKEY_CLASSES_ROOT\Applications\firefox.exe = "path params" michael@0: nsAutoString appFilesystemCommand; michael@0: if (!GetAppsVerbCommandHandler(appName, appFilesystemCommand, michael@0: false) || michael@0: IsPathInList(appFilesystemCommand, trackList)) michael@0: continue; michael@0: ProcessPath(appList, trackList, appFilesystemCommand); michael@0: } michael@0: } michael@0: regKey->Close(); michael@0: } michael@0: michael@0: michael@0: // 8) General application's list - not file extension specific on windows michael@0: workingRegistryPath = NS_LITERAL_STRING("Applications"); michael@0: michael@0: rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, michael@0: workingRegistryPath, michael@0: nsIWindowsRegKey::ACCESS_ENUMERATE_SUB_KEYS| michael@0: nsIWindowsRegKey::ACCESS_QUERY_VALUE); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: uint32_t count = 0; michael@0: if (NS_SUCCEEDED(regKey->GetChildCount(&count)) && count > 0) { michael@0: for (uint32_t index = 0; index < count; index++) { michael@0: nsAutoString appName; michael@0: if (NS_FAILED(regKey->GetChildName(index, appName))) michael@0: continue; michael@0: michael@0: // HKEY_CLASSES_ROOT\Applications\firefox.exe = "path params" michael@0: nsAutoString appFilesystemCommand; michael@0: if (!GetAppsVerbCommandHandler(appName, appFilesystemCommand, michael@0: false) || michael@0: IsPathInList(appFilesystemCommand, trackList)) michael@0: continue; michael@0: ProcessPath(appList, trackList, appFilesystemCommand); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Return to the caller michael@0: *_retval = appList; michael@0: NS_ADDREF(*_retval); michael@0: michael@0: return NS_OK; michael@0: }