forked from mirrors/gecko-dev
Backed out changeset f17c7565707b (bug 1772006) Backed out changeset c725fe1f5882 (bug 1772006) Backed out changeset d19663161261 (bug 1772006) Backed out changeset b6611ab002d9 (bug 1772006) Backed out changeset 790f42b64af9 (bug 1772006) Backed out changeset 79a734b4e4d9 (bug 1772006) Backed out changeset 42730aae16ea (bug 1772006) Backed out changeset b2542aef3054 (bug 1772006) Backed out changeset 962bfea4a309 (bug 1772006)
903 lines
32 KiB
C++
903 lines
32 KiB
C++
/* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "nsArrayEnumerator.h"
|
|
#include "nsCOMArray.h"
|
|
#include "nsLocalFile.h"
|
|
#include "nsMIMEInfoWin.h"
|
|
#include "nsNetUtil.h"
|
|
#include <windows.h>
|
|
#include <shellapi.h>
|
|
#include "nsIMutableArray.h"
|
|
#include "nsTArray.h"
|
|
#include <shlobj.h>
|
|
#include "nsIWindowsRegKey.h"
|
|
#include "nsUnicharUtils.h"
|
|
#include "nsITextToSubURI.h"
|
|
#include "nsVariant.h"
|
|
#include "mozilla/CmdLineAndEnvUtils.h"
|
|
#include "mozilla/ShellHeaderOnlyUtils.h"
|
|
#include "mozilla/StaticPrefs_browser.h"
|
|
#include "mozilla/UrlmonHeaderOnlyUtils.h"
|
|
#include "mozilla/UniquePtrExtensions.h"
|
|
|
|
#define RUNDLL32_EXE L"\\rundll32.exe"
|
|
|
|
NS_IMPL_ISUPPORTS_INHERITED(nsMIMEInfoWin, nsMIMEInfoBase, nsIPropertyBag)
|
|
|
|
nsMIMEInfoWin::~nsMIMEInfoWin() {}
|
|
|
|
nsresult nsMIMEInfoWin::LaunchDefaultWithFile(nsIFile* aFile) {
|
|
// Launch the file, unless it is an executable.
|
|
bool executable = true;
|
|
aFile->IsExecutable(&executable);
|
|
if (executable) return NS_ERROR_FAILURE;
|
|
|
|
return aFile->Launch();
|
|
}
|
|
|
|
nsresult nsMIMEInfoWin::ShellExecuteWithIFile(nsIFile* aExecutable, int aArgc,
|
|
const wchar_t** aArgv) {
|
|
nsresult rv;
|
|
|
|
NS_ASSERTION(aArgc >= 1, "aArgc must be at least 1");
|
|
|
|
nsAutoString execPath;
|
|
rv = aExecutable->GetTarget(execPath);
|
|
if (NS_FAILED(rv) || execPath.IsEmpty()) {
|
|
rv = aExecutable->GetPath(execPath);
|
|
}
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
auto assembledArgs = mozilla::MakeCommandLine(aArgc, aArgv);
|
|
if (!assembledArgs) {
|
|
return NS_ERROR_FILE_EXECUTION_FAILED;
|
|
}
|
|
|
|
_bstr_t execPathBStr(execPath.get());
|
|
// Pass VT_ERROR/DISP_E_PARAMNOTFOUND to omit an optional RPC parameter
|
|
// to execute a file with the default verb.
|
|
_variant_t verbDefault(DISP_E_PARAMNOTFOUND, VT_ERROR);
|
|
_variant_t workingDir;
|
|
_variant_t showCmd(SW_SHOWNORMAL);
|
|
|
|
// Ask Explorer to ShellExecute on our behalf, as some applications such as
|
|
// Skype for Business do not start correctly when inheriting our process's
|
|
// migitation policies.
|
|
// It does not work in a special environment such as Citrix. In such a case
|
|
// we fall back to launching an application as a child process. We need to
|
|
// find a way to handle the combination of these interop issues.
|
|
mozilla::LauncherVoidResult shellExecuteOk = mozilla::ShellExecuteByExplorer(
|
|
execPathBStr, assembledArgs.get(), verbDefault, workingDir, showCmd);
|
|
if (shellExecuteOk.isErr()) {
|
|
// No need to pass assembledArgs to LaunchWithIProcess. aArgv will be
|
|
// processed in nsProcess::RunProcess.
|
|
return LaunchWithIProcess(aExecutable, aArgc,
|
|
reinterpret_cast<const char16_t**>(aArgv));
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
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 (AutomationOnlyCheckIfLaunchStubbed(aFile)) {
|
|
return NS_OK;
|
|
}
|
|
|
|
if (mPreferredAction == useSystemDefault) {
|
|
if (mDefaultApplication &&
|
|
StaticPrefs::browser_pdf_launchDefaultEdgeAsApp()) {
|
|
// Since Edgium is the default handler for PDF and other kinds of files,
|
|
// if we're using the OS default and it's Edgium prefer its app mode so it
|
|
// operates as a viewer (without browser toolbars). Bug 1632277.
|
|
nsAutoCString defaultAppExecutable;
|
|
rv = mDefaultApplication->GetNativeLeafName(defaultAppExecutable);
|
|
if (NS_SUCCEEDED(rv) &&
|
|
defaultAppExecutable.LowerCaseEqualsLiteral("msedge.exe")) {
|
|
nsAutoString path;
|
|
rv = aFile->GetPath(path);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
// If the --app flag doesn't work we'll want to fallback to a
|
|
// regular path. Send two args so we call `msedge.exe --app={path}
|
|
// {path}`.
|
|
nsAutoString appArg;
|
|
appArg.AppendLiteral("--app=");
|
|
appArg.Append(path);
|
|
const wchar_t* argv[] = {appArg.get(), path.get()};
|
|
|
|
return ShellExecuteWithIFile(mDefaultApplication,
|
|
mozilla::ArrayLength(argv), argv);
|
|
}
|
|
}
|
|
}
|
|
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);
|
|
|
|
// 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.
|
|
if (!GetDllLaunchInfo(executable, aFile, args, false))
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
WCHAR rundll32Path[MAX_PATH + sizeof(RUNDLL32_EXE) / sizeof(WCHAR) +
|
|
1] = {L'\0'};
|
|
if (!GetSystemDirectoryW(rundll32Path, MAX_PATH)) {
|
|
return NS_ERROR_FILE_NOT_FOUND;
|
|
}
|
|
lstrcatW(rundll32Path, RUNDLL32_EXE);
|
|
|
|
SHELLEXECUTEINFOW seinfo;
|
|
memset(&seinfo, 0, sizeof(seinfo));
|
|
seinfo.cbSize = sizeof(SHELLEXECUTEINFOW);
|
|
seinfo.fMask = 0;
|
|
seinfo.hwnd = nullptr;
|
|
seinfo.lpVerb = nullptr;
|
|
seinfo.lpFile = rundll32Path;
|
|
seinfo.lpParameters = args.get();
|
|
seinfo.lpDirectory = nullptr;
|
|
seinfo.nShow = SW_SHOWNORMAL;
|
|
if (ShellExecuteExW(&seinfo)) return NS_OK;
|
|
|
|
switch ((LONG_PTR)seinfo.hInstApp) {
|
|
case 0:
|
|
case SE_ERR_OOM:
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
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;
|
|
default:
|
|
switch (GetLastError()) {
|
|
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;
|
|
}
|
|
}
|
|
return NS_ERROR_FILE_EXECUTION_FAILED;
|
|
}
|
|
}
|
|
nsAutoString path;
|
|
aFile->GetPath(path);
|
|
const wchar_t* argv[] = {path.get()};
|
|
return ShellExecuteWithIFile(executable, mozilla::ArrayLength(argv), argv);
|
|
}
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMIMEInfoWin::GetHasDefaultHandler(bool* _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;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMIMEInfoWin::GetEnumerator(nsISimpleEnumerator** _retval) {
|
|
nsCOMArray<nsIVariant> properties;
|
|
|
|
nsCOMPtr<nsIVariant> variant;
|
|
GetProperty(u"defaultApplicationIconURL"_ns, getter_AddRefs(variant));
|
|
if (variant) properties.AppendObject(variant);
|
|
|
|
GetProperty(u"customApplicationIconURL"_ns, getter_AddRefs(variant));
|
|
if (variant) properties.AppendObject(variant);
|
|
|
|
return NS_NewArrayEnumerator(_retval, properties, NS_GET_IID(nsIVariant));
|
|
}
|
|
|
|
static nsresult GetIconURLVariant(nsIFile* aApplication, nsIVariant** _retval) {
|
|
nsAutoCString fileURLSpec;
|
|
NS_GetURLSpecFromFile(aApplication, fileURLSpec);
|
|
nsAutoCString iconURLSpec;
|
|
iconURLSpec.AssignLiteral("moz-icon://");
|
|
iconURLSpec += fileURLSpec;
|
|
RefPtr<nsVariant> writable(new nsVariant());
|
|
writable->SetAsAUTF8String(iconURLSpec);
|
|
writable.forget(_retval);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMIMEInfoWin::GetProperty(const nsAString& aName, nsIVariant** _retval) {
|
|
nsresult rv;
|
|
if (mDefaultApplication &&
|
|
aName.EqualsLiteral(PROPERTY_DEFAULT_APP_ICON_URL)) {
|
|
rv = GetIconURLVariant(mDefaultApplication, _retval);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
} else if (mPreferredApplication &&
|
|
aName.EqualsLiteral(PROPERTY_CUSTOM_APP_ICON_URL)) {
|
|
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);
|
|
|
|
rv = GetIconURLVariant(executable, _retval);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// this implementation was pretty much copied verbatime from
|
|
// Tony Robinson's code in nsExternalProtocolWin.cpp
|
|
nsresult nsMIMEInfoWin::LoadUriInternal(nsIURI* aURL) {
|
|
nsresult rv = NS_OK;
|
|
|
|
// 1. Find the default app for this protocol
|
|
// 2. Set up the command line
|
|
// 3. Launch the app.
|
|
|
|
// For now, we'll just cheat essentially, check for the command line
|
|
// then just call ShellExecute()!
|
|
|
|
if (aURL) {
|
|
// extract the url spec from the url
|
|
nsAutoCString urlSpec;
|
|
aURL->GetAsciiSpec(urlSpec);
|
|
|
|
// Unescape non-ASCII characters in the URL
|
|
nsAutoString utf16Spec;
|
|
|
|
nsCOMPtr<nsITextToSubURI> textToSubURI =
|
|
do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (NS_FAILED(textToSubURI->UnEscapeNonAsciiURI("UTF-8"_ns, urlSpec,
|
|
utf16Spec))) {
|
|
CopyASCIItoUTF16(urlSpec, utf16Spec);
|
|
}
|
|
|
|
// Ask the shell/urlmon to parse |utf16Spec| to avoid malformed URLs.
|
|
// Failure is indicative of a potential security issue so we should
|
|
// bail out if so.
|
|
LauncherResult<_bstr_t> validatedUri = UrlmonValidateUri(utf16Spec.get());
|
|
if (validatedUri.isErr()) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
_variant_t args;
|
|
_variant_t verb(L"open");
|
|
_variant_t workingDir;
|
|
_variant_t showCmd(SW_SHOWNORMAL);
|
|
|
|
// To open a uri, we first try ShellExecuteByExplorer, which starts a new
|
|
// process as a child process of explorer.exe, because applications may not
|
|
// support the mitigation policies inherited from our process. If it fails,
|
|
// we fall back to ShellExecuteExW.
|
|
//
|
|
// For Thunderbird, however, there is a known issue that
|
|
// ShellExecuteByExplorer succeeds but explorer.exe shows an error popup
|
|
// if a uri to open includes credentials. This does not happen in Firefox
|
|
// because Firefox does not have to launch a process to open a uri.
|
|
//
|
|
// Since Thunderbird does not use mitigation policies which could cause
|
|
// compatibility issues, we get no benefit from using
|
|
// ShellExecuteByExplorer. Thus we skip it and go straight to
|
|
// ShellExecuteExW for Thunderbird.
|
|
#ifndef MOZ_THUNDERBIRD
|
|
mozilla::LauncherVoidResult shellExecuteOk =
|
|
mozilla::ShellExecuteByExplorer(validatedUri.inspect(), args, verb,
|
|
workingDir, showCmd);
|
|
if (shellExecuteOk.isOk()) {
|
|
return NS_OK;
|
|
}
|
|
#endif // MOZ_THUNDERBIRD
|
|
|
|
SHELLEXECUTEINFOW sinfo = {sizeof(sinfo)};
|
|
sinfo.fMask = SEE_MASK_NOASYNC;
|
|
sinfo.lpVerb = V_BSTR(&verb);
|
|
sinfo.nShow = showCmd;
|
|
sinfo.lpFile = validatedUri.inspect();
|
|
|
|
BOOL result = ShellExecuteExW(&sinfo);
|
|
if (!result || reinterpret_cast<LONG_PTR>(sinfo.hInstApp) < 32) {
|
|
rv = NS_ERROR_FAILURE;
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
// Given a path to a local file, return its nsILocalHandlerApp instance.
|
|
bool nsMIMEInfoWin::GetLocalHandlerApp(const nsAString& aCommandHandler,
|
|
nsCOMPtr<nsILocalHandlerApp>& aApp) {
|
|
nsCOMPtr<nsIFile> locfile;
|
|
nsresult rv = NS_NewLocalFile(aCommandHandler, true, getter_AddRefs(locfile));
|
|
if (NS_FAILED(rv)) return false;
|
|
|
|
aApp = do_CreateInstance("@mozilla.org/uriloader/local-handler-app;1");
|
|
if (!aApp) return false;
|
|
|
|
aApp->SetExecutable(locfile);
|
|
return true;
|
|
}
|
|
|
|
// Return the cleaned up file path associated with a command verb
|
|
// located in root/Applications.
|
|
bool nsMIMEInfoWin::GetAppsVerbCommandHandler(const nsAString& appExeName,
|
|
nsAString& applicationPath,
|
|
bool edit) {
|
|
nsCOMPtr<nsIWindowsRegKey> appKey =
|
|
do_CreateInstance("@mozilla.org/windows-registry-key;1");
|
|
if (!appKey) return 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 false;
|
|
|
|
// Check for the NoOpenWith flag, if it exists
|
|
uint32_t value;
|
|
if (NS_SUCCEEDED(appKey->ReadIntValue(u"NoOpenWith"_ns, &value)) &&
|
|
value == 1)
|
|
return false;
|
|
|
|
nsAutoString dummy;
|
|
if (NS_SUCCEEDED(appKey->ReadStringValue(u"NoOpenWith"_ns, dummy)))
|
|
return 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 false;
|
|
|
|
nsAutoString appFilesystemCommand;
|
|
if (NS_SUCCEEDED(appKey->ReadStringValue(u""_ns, appFilesystemCommand))) {
|
|
// Expand environment vars, clean up any misc.
|
|
if (!nsLocalFile::CleanupCmdHandlerPath(appFilesystemCommand)) return false;
|
|
|
|
applicationPath = appFilesystemCommand;
|
|
return true;
|
|
}
|
|
return 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)
|
|
bool nsMIMEInfoWin::GetDllLaunchInfo(nsIFile* aDll, nsIFile* aFile,
|
|
nsAString& args, bool edit) {
|
|
if (!aDll || !aFile) return false;
|
|
|
|
nsString appExeName;
|
|
aDll->GetLeafName(appExeName);
|
|
|
|
nsCOMPtr<nsIWindowsRegKey> appKey =
|
|
do_CreateInstance("@mozilla.org/windows-registry-key;1");
|
|
if (!appKey) return 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 false;
|
|
|
|
// Check for the NoOpenWith flag, if it exists
|
|
uint32_t value;
|
|
rv = appKey->ReadIntValue(u"NoOpenWith"_ns, &value);
|
|
if (NS_SUCCEEDED(rv) && value == 1) return false;
|
|
|
|
nsAutoString dummy;
|
|
if (NS_SUCCEEDED(appKey->ReadStringValue(u"NoOpenWith"_ns, dummy)))
|
|
return 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 false;
|
|
|
|
nsAutoString appFilesystemCommand;
|
|
if (NS_SUCCEEDED(appKey->ReadStringValue(u""_ns, appFilesystemCommand))) {
|
|
// Replace embedded environment variables.
|
|
uint32_t bufLength =
|
|
::ExpandEnvironmentStringsW(appFilesystemCommand.get(), nullptr, 0);
|
|
if (bufLength == 0) // Error
|
|
return false;
|
|
|
|
auto destination = mozilla::MakeUniqueFallible<wchar_t[]>(bufLength);
|
|
if (!destination) return false;
|
|
if (!::ExpandEnvironmentStringsW(appFilesystemCommand.get(),
|
|
destination.get(), bufLength))
|
|
return false;
|
|
|
|
appFilesystemCommand.Assign(destination.get());
|
|
|
|
// C:\Windows\System32\rundll32.exe "C:\Program Files\Windows
|
|
// Photo Gallery\PhotoViewer.dll", ImageView_Fullscreen %1
|
|
nsAutoString params;
|
|
constexpr auto rundllSegment = u"rundll32.exe "_ns;
|
|
int32_t 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
|
|
constexpr auto percentOneParam = u"%1"_ns;
|
|
index = params.Find(percentOneParam);
|
|
if (index == kNotFound) // no parameter
|
|
return false;
|
|
|
|
nsString target;
|
|
aFile->GetTarget(target);
|
|
params.Replace(index, 2, target);
|
|
|
|
args = params;
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Return the cleaned up file path associated with a progid command
|
|
// verb located in root.
|
|
bool nsMIMEInfoWin::GetProgIDVerbCommandHandler(const nsAString& appProgIDName,
|
|
nsAString& applicationPath,
|
|
bool edit) {
|
|
nsCOMPtr<nsIWindowsRegKey> appKey =
|
|
do_CreateInstance("@mozilla.org/windows-registry-key;1");
|
|
if (!appKey) return 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 false;
|
|
|
|
nsAutoString appFilesystemCommand;
|
|
if (NS_SUCCEEDED(appKey->ReadStringValue(u""_ns, appFilesystemCommand))) {
|
|
// Expand environment vars, clean up any misc.
|
|
if (!nsLocalFile::CleanupCmdHandlerPath(appFilesystemCommand)) return false;
|
|
|
|
applicationPath = appFilesystemCommand;
|
|
return true;
|
|
}
|
|
return 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<nsString>& trackList,
|
|
const nsAString& appFilesystemCommand) {
|
|
nsAutoString lower(appFilesystemCommand);
|
|
ToLowerCase(lower);
|
|
|
|
// Don't include firefox.exe in the list
|
|
WCHAR exe[MAX_PATH + 1];
|
|
uint32_t len = GetModuleFileNameW(nullptr, exe, MAX_PATH);
|
|
if (len < MAX_PATH && len != 0) {
|
|
int32_t 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);
|
|
trackList.AppendElement(lower);
|
|
}
|
|
|
|
// Helper routine that handles a compare between a path
|
|
// and an array of paths.
|
|
static bool IsPathInList(nsAString& appPath, nsTArray<nsString>& trackList) {
|
|
// trackList data is always lowercase, see ProcessPath
|
|
// above.
|
|
nsAutoString tmp(appPath);
|
|
ToLowerCase(tmp);
|
|
|
|
for (uint32_t i = 0; i < trackList.Length(); i++) {
|
|
if (tmp.Equals(trackList[i])) return true;
|
|
}
|
|
return 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 = nullptr;
|
|
|
|
nsCOMPtr<nsIMutableArray> appList = do_CreateInstance("@mozilla.org/array;1");
|
|
|
|
if (!appList) return NS_ERROR_FAILURE;
|
|
|
|
nsTArray<nsString> trackList;
|
|
|
|
nsAutoCString 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;
|
|
|
|
bool extKnown = false;
|
|
if (fileExt.IsEmpty()) {
|
|
extKnown = 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.
|
|
nsAutoCString 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(u""_ns, mimeFileExt))) {
|
|
CopyUTF16toUTF8(mimeFileExt, fileExt);
|
|
extKnown = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
nsAutoString fileExtToUse;
|
|
if (!fileExt.IsEmpty() && fileExt.First() != '.') {
|
|
fileExtToUse = char16_t('.');
|
|
}
|
|
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(u""_ns, appProgId))) {
|
|
// Bug 358297 - ignore the embedded internet explorer handler
|
|
if (appProgId != u"XPSViewer.Document"_ns) {
|
|
nsAutoString appFilesystemCommand;
|
|
if (GetProgIDVerbCommandHandler(appProgId, appFilesystemCommand,
|
|
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)) {
|
|
uint32_t count = 0;
|
|
if (NS_SUCCEEDED(regKey->GetValueCount(&count)) && count > 0) {
|
|
for (uint32_t 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,
|
|
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)) {
|
|
uint32_t count = 0;
|
|
if (NS_SUCCEEDED(regKey->GetValueCount(&count)) && count > 0) {
|
|
for (uint32_t 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,
|
|
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 = nsLiteralString(
|
|
u"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)) {
|
|
uint32_t count = 0;
|
|
if (NS_SUCCEEDED(regKey->GetValueCount(&count)) && count > 0) {
|
|
for (uint32_t 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,
|
|
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 = nsLiteralString(
|
|
u"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)) {
|
|
uint32_t count = 0;
|
|
if (NS_SUCCEEDED(regKey->GetValueCount(&count)) && count > 0) {
|
|
for (uint32_t index = 0; index < count; index++) {
|
|
nsAutoString appIndex, appProgId;
|
|
if (NS_FAILED(regKey->GetValueName(index, appProgId))) continue;
|
|
|
|
nsAutoString appFilesystemCommand;
|
|
if (!GetProgIDVerbCommandHandler(appProgId, appFilesystemCommand,
|
|
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(u"PerceivedType"_ns, perceivedType);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
nsAutoString openWithListPath(u"SystemFileAssociations\\"_ns);
|
|
openWithListPath.Append(perceivedType); // no period
|
|
openWithListPath.AppendLiteral("\\OpenWithList");
|
|
|
|
nsresult rv = appKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
|
|
openWithListPath,
|
|
nsIWindowsRegKey::ACCESS_QUERY_VALUE);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
uint32_t count = 0;
|
|
if (NS_SUCCEEDED(regKey->GetValueCount(&count)) && count > 0) {
|
|
for (uint32_t 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,
|
|
false) ||
|
|
IsPathInList(appFilesystemCommand, trackList))
|
|
continue;
|
|
ProcessPath(appList, trackList, appFilesystemCommand);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} // extKnown == false
|
|
|
|
// 7) list global HKEY_CLASSES_ROOT\*\OpenWithList
|
|
// Listing general purpose handlers, not specific to a mime type or file
|
|
// extension
|
|
|
|
workingRegistryPath = u"*\\OpenWithList"_ns;
|
|
|
|
rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
|
|
workingRegistryPath, nsIWindowsRegKey::ACCESS_QUERY_VALUE);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
uint32_t count = 0;
|
|
if (NS_SUCCEEDED(regKey->GetValueCount(&count)) && count > 0) {
|
|
for (uint32_t 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, false) ||
|
|
IsPathInList(appFilesystemCommand, trackList))
|
|
continue;
|
|
ProcessPath(appList, trackList, appFilesystemCommand);
|
|
}
|
|
}
|
|
regKey->Close();
|
|
}
|
|
|
|
// 8) General application's list - not file extension specific on windows
|
|
workingRegistryPath = u"Applications"_ns;
|
|
|
|
rv =
|
|
regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, workingRegistryPath,
|
|
nsIWindowsRegKey::ACCESS_ENUMERATE_SUB_KEYS |
|
|
nsIWindowsRegKey::ACCESS_QUERY_VALUE);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
uint32_t count = 0;
|
|
if (NS_SUCCEEDED(regKey->GetChildCount(&count)) && count > 0) {
|
|
for (uint32_t 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, false) ||
|
|
IsPathInList(appFilesystemCommand, trackList))
|
|
continue;
|
|
ProcessPath(appList, trackList, appFilesystemCommand);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return to the caller
|
|
*_retval = appList;
|
|
NS_ADDREF(*_retval);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMIMEInfoWin::IsCurrentAppOSDefault(bool* _retval) {
|
|
*_retval = false;
|
|
if (mDefaultApplication) {
|
|
// Determine if the default executable is our executable.
|
|
nsCOMPtr<nsIFile> ourBinary;
|
|
XRE_GetBinaryPath(getter_AddRefs(ourBinary));
|
|
bool isSame = false;
|
|
nsresult rv = mDefaultApplication->Equals(ourBinary, &isSame);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
*_retval = isSame;
|
|
}
|
|
return NS_OK;
|
|
}
|