fune/toolkit/components/aboutthirdparty/AboutThirdParty.cpp
Toshihito Kikuchi 718f8615db Bug 1695817 - Part 6: Show name and publisher of an application owning a module. r=Gijs,fluent-reviewers,mhowell
This patch adds application info (Name and Publisher for now) in the
about:third-party page if a module is a part of an installed application,
which is registered in the registry and shown in Windows Control Panel.
To achieve this, we parse the registry to collect installed applications
in the background task.

Differential Revision: https://phabricator.services.mozilla.com/D109306
2021-05-28 22:35:59 +00:00

787 lines
27 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "AboutThirdParty.h"
#include "AboutThirdPartyUtils.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/NativeNt.h"
#include "mozilla/StaticPtr.h"
#include "MsiDatabase.h"
#include "nsComponentManagerUtils.h"
#include "nsIWindowsRegKey.h"
#include "nsThreadUtils.h"
#include <objbase.h>
using namespace mozilla;
namespace {
// A callback function passed to EnumSubkeys uses this type
// to control the enumeration loop.
enum class CallbackResult { Continue, Stop };
template <typename CallbackT>
void EnumSubkeys(nsIWindowsRegKey* aRegBase, const CallbackT& aCallback) {
uint32_t count = 0;
if (NS_FAILED(aRegBase->GetChildCount(&count))) {
return;
}
for (uint32_t i = 0; i < count; ++i) {
nsAutoString subkeyName;
if (NS_FAILED(aRegBase->GetChildName(i, subkeyName))) {
continue;
}
nsCOMPtr<nsIWindowsRegKey> subkey;
if (NS_FAILED(aRegBase->OpenChild(subkeyName, nsIWindowsRegKey::ACCESS_READ,
getter_AddRefs(subkey)))) {
continue;
}
CallbackResult result = aCallback(subkeyName, subkey);
if (result == CallbackResult::Continue) {
continue;
} else if (result == CallbackResult::Stop) {
break;
} else {
MOZ_ASSERT_UNREACHABLE("Unexpected CallbackResult.");
}
}
}
} // anonymous namespace
InstallLocationComparator::InstallLocationComparator(const nsAString& aFilePath)
: mFilePath(aFilePath) {}
int InstallLocationComparator::operator()(
const InstallLocationT& aLocation) const {
// Firstly we check whether mFilePath begins with aLocation.
// If yes, mFilePath is a part of the target installation,
// so we return 0 showing match.
const nsAString& location = aLocation.first();
size_t locationLen = location.Length();
if (locationLen <= mFilePath.Length() &&
nsCaseInsensitiveStringComparator(mFilePath.BeginReading(),
location.BeginReading(), locationLen,
locationLen) == 0) {
return 0;
}
return CompareIgnoreCase(mFilePath, location);
}
// The InstalledApplications class behaves like Chrome's InstalledApplications,
// which collects installed applications from two resources below.
//
// 1) Path strings in MSI package components
// An MSI package is consisting of multiple components. This class collects
// MSI components representing a file and stores them as a hash table.
//
// 2) Install location paths in the InstallLocation registry value
// If an application's installer is not MSI but sets the InstallLocation
// registry value, we can use it to search for an application by comparing
// a target module is located under that location path. This class stores
// location path strings as a sorted array so that we can binary-search it.
class InstalledApplications final {
// Limit the number of entries to avoid consuming too much memory
constexpr static uint32_t kMaxComponents = 1000000;
constexpr static uint32_t kMaxInstallLocations = 1000;
nsCOMPtr<nsIWindowsRegKey> mInstallerData;
nsCOMPtr<nsIInstalledApplication> mCurrentApp;
ComponentPathMapT mComponentPaths;
nsTArray<InstallLocationT> mLocations;
void AddInstallLocation(nsIWindowsRegKey* aProductSubKey) {
nsString location;
if (NS_FAILED(
aProductSubKey->ReadStringValue(u"InstallLocation"_ns, location)) ||
location.IsEmpty()) {
return;
}
if (location.Last() != u'\\') {
location.Append(u'\\');
}
mLocations.EmplaceBack(location, this->mCurrentApp);
}
void AddComponentGuid(const nsString& aPackedProductGuid,
const nsString& aPackedComponentGuid) {
nsAutoString componentSubkey(L"Components\\");
componentSubkey += aPackedComponentGuid;
// Pick a first value in the subkeys under |componentSubkey|.
nsString componentPath;
EnumSubkeys(mInstallerData, [&aPackedProductGuid, &componentSubkey,
&componentPath](const nsString& aSid,
nsIWindowsRegKey* aSidSubkey) {
// If we have a value in |componentPath|, the loop should
// have been stopped.
MOZ_ASSERT(componentPath.IsEmpty());
nsCOMPtr<nsIWindowsRegKey> compKey;
nsresult rv =
aSidSubkey->OpenChild(componentSubkey, nsIWindowsRegKey::ACCESS_READ,
getter_AddRefs(compKey));
if (NS_FAILED(rv)) {
return CallbackResult::Continue;
}
nsString compData;
if (NS_FAILED(compKey->ReadStringValue(aPackedProductGuid, compData))) {
return CallbackResult::Continue;
}
if (!CorrectMsiComponentPath(compData)) {
return CallbackResult::Continue;
}
componentPath = std::move(compData);
return CallbackResult::Stop;
});
if (componentPath.IsEmpty()) {
return;
}
// Use a full path as a key rather than a leaf name because
// the same name's module can be installed under system32
// and syswow64.
mComponentPaths.WithEntryHandle(componentPath, [this](auto&& addPtr) {
if (addPtr) {
// If the same file appeared in multiple installations, we set null
// for its value because there is no way to know which installation is
// the real owner.
addPtr.Data() = nullptr;
} else {
addPtr.Insert(this->mCurrentApp);
}
});
}
void AddProduct(const nsString& aProductId,
nsIWindowsRegKey* aProductSubKey) {
nsString displayName;
if (NS_FAILED(
aProductSubKey->ReadStringValue(u"DisplayName"_ns, displayName)) ||
displayName.IsEmpty()) {
// Skip if no name is found.
return;
}
nsString publisher;
if (NS_SUCCEEDED(
aProductSubKey->ReadStringValue(u"Publisher"_ns, publisher)) &&
publisher.EqualsIgnoreCase("Microsoft") &&
publisher.EqualsIgnoreCase("Microsoft Corporation")) {
// Skip if the publisher is Microsoft because it's not a third-party.
// We don't skip an application without the publisher name.
return;
}
mCurrentApp =
new InstalledApplication(std::move(displayName), std::move(publisher));
// Try an MSI database first because it's more accurate,
// then fall back to the InstallLocation key.
do {
if (!mInstallerData) {
break;
}
nsAutoString packedProdGuid;
if (!MsiPackGuid(aProductId, packedProdGuid)) {
break;
}
auto db = MsiDatabase::FromProductId(aProductId.get());
if (db.isNothing()) {
break;
}
db->ExecuteSingleColumnQuery(
L"SELECT DISTINCT ComponentId FROM Component",
[this, &packedProdGuid](const wchar_t* aComponentGuid) {
if (this->mComponentPaths.Count() >= kMaxComponents) {
return MsiDatabase::CallbackResult::Stop;
}
nsAutoString packedComponentGuid;
if (MsiPackGuid(nsDependentString(aComponentGuid),
packedComponentGuid)) {
this->AddComponentGuid(packedProdGuid, packedComponentGuid);
}
return MsiDatabase::CallbackResult::Continue;
});
// We've decided to collect data from an MSI database.
// Exiting the function.
return;
} while (false);
if (mLocations.Length() >= kMaxInstallLocations) {
return;
}
// If we cannot use an MSI database for any reason,
// try the InstallLocation key.
AddInstallLocation(aProductSubKey);
}
public:
InstalledApplications() {
nsresult rv;
nsCOMPtr<nsIWindowsRegKey> regKey =
do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
if (NS_SUCCEEDED(rv) &&
NS_SUCCEEDED(regKey->Open(
nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
u"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\"
u"Installer\\UserData"_ns,
nsIWindowsRegKey::ACCESS_READ | nsIWindowsRegKey::WOW64_64))) {
mInstallerData.swap(regKey);
}
}
~InstalledApplications() = default;
InstalledApplications(InstalledApplications&&) = delete;
InstalledApplications& operator=(InstalledApplications&&) = delete;
InstalledApplications(const InstalledApplications&) = delete;
InstalledApplications& operator=(const InstalledApplications&) = delete;
void Collect(ComponentPathMapT& aOutComponentPaths,
nsTArray<InstallLocationT>& aOutLocations) {
const nsLiteralString kUninstallKey(
u"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall");
static const uint16_t sProcessor = []() -> uint16_t {
SYSTEM_INFO si;
::GetSystemInfo(&si);
return si.wProcessorArchitecture;
}();
nsresult rv;
nsCOMPtr<nsIWindowsRegKey> regKey =
do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
if (NS_FAILED(rv)) {
return;
}
switch (sProcessor) {
case PROCESSOR_ARCHITECTURE_INTEL:
rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
kUninstallKey, nsIWindowsRegKey::ACCESS_READ);
if (NS_SUCCEEDED(rv)) {
EnumSubkeys(regKey, [this](const nsString& aProductId,
nsIWindowsRegKey* aProductSubKey) {
this->AddProduct(aProductId, aProductSubKey);
return CallbackResult::Continue;
});
}
break;
case PROCESSOR_ARCHITECTURE_AMD64:
// A 64-bit application may be installed by a 32-bit installer,
// or vice versa. So we enumerate both views regardless of
// the process's (not processor's) bitness.
rv = regKey->Open(
nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE, kUninstallKey,
nsIWindowsRegKey::ACCESS_READ | nsIWindowsRegKey::WOW64_64);
if (NS_SUCCEEDED(rv)) {
EnumSubkeys(regKey, [this](const nsString& aProductId,
nsIWindowsRegKey* aProductSubKey) {
this->AddProduct(aProductId, aProductSubKey);
return CallbackResult::Continue;
});
}
rv = regKey->Open(
nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE, kUninstallKey,
nsIWindowsRegKey::ACCESS_READ | nsIWindowsRegKey::WOW64_32);
if (NS_SUCCEEDED(rv)) {
EnumSubkeys(regKey, [this](const nsString& aProductId,
nsIWindowsRegKey* aProductSubKey) {
this->AddProduct(aProductId, aProductSubKey);
return CallbackResult::Continue;
});
}
break;
default:
MOZ_ASSERT(false, "Unsupported CPU architecture");
return;
}
// The "HKCU\SOFTWARE\" subtree is shared between the 32-bits and 64 bits
// views. No need to enumerate wow6432node for HKCU.
// https://docs.microsoft.com/en-us/windows/win32/winprog64/shared-registry-keys
rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, kUninstallKey,
nsIWindowsRegKey::ACCESS_READ);
if (NS_SUCCEEDED(rv)) {
EnumSubkeys(regKey, [this](const nsString& aProductId,
nsIWindowsRegKey* aProductSubKey) {
this->AddProduct(aProductId, aProductSubKey);
return CallbackResult::Continue;
});
}
aOutComponentPaths.SwapElements(mComponentPaths);
mLocations.Sort([](const InstallLocationT& aA, const InstallLocationT& aB) {
return CompareIgnoreCase(aA.first(), aB.first());
});
aOutLocations.SwapElements(mLocations);
}
};
class KnownModule final {
static KnownModule sKnownExtensions[static_cast<int>(KnownModuleType::Last)];
static bool GetInprocServerDllPathFromGuid(const GUID& aGuid,
nsAString& aResult) {
nsAutoStringN<60> subkey;
subkey.AppendPrintf(
"CLSID\\{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\\"
"InProcServer32",
aGuid.Data1, aGuid.Data2, aGuid.Data3, aGuid.Data4[0], aGuid.Data4[1],
aGuid.Data4[2], aGuid.Data4[3], aGuid.Data4[4], aGuid.Data4[5],
aGuid.Data4[6], aGuid.Data4[7]);
nsresult rv;
nsCOMPtr<nsIWindowsRegKey> regKey =
do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
if (NS_FAILED(rv)) {
return false;
}
rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, subkey,
nsIWindowsRegKey::ACCESS_READ);
if (NS_FAILED(rv)) {
return false;
}
rv = regKey->ReadStringValue(u""_ns, aResult);
return NS_SUCCEEDED(rv);
}
enum class HandlerType {
// For this type of handler, multiple extensions can be registered as
// subkeys under the handler subkey.
Multi,
// For this type of handler, a single extension can be registered as
// the default value of the handler subkey.
Single,
};
HandlerType mHandlerType;
nsLiteralString mSubkeyName;
using CallbackT = std::function<void(const nsString&, KnownModuleType)>;
void EnumInternal(nsIWindowsRegKey* aRegBase, KnownModuleType aType,
const CallbackT& aCallback) const {
nsCOMPtr<nsIWindowsRegKey> shexType;
if (NS_FAILED(aRegBase->OpenChild(mSubkeyName,
nsIWindowsRegKey::ACCESS_READ,
getter_AddRefs(shexType)))) {
return;
}
switch (mHandlerType) {
case HandlerType::Single: {
nsAutoString valData;
GUID guid;
if (NS_FAILED(shexType->ReadStringValue(u""_ns, valData)) ||
FAILED(::CLSIDFromString(valData.get(), &guid))) {
return;
}
nsAutoString dllPath;
if (!GetInprocServerDllPathFromGuid(guid, dllPath)) {
return;
}
aCallback(dllPath, aType);
break;
}
case HandlerType::Multi:
EnumSubkeys(shexType, [aType, &aCallback](const nsString& aSubKeyName,
nsIWindowsRegKey* aSubKey) {
GUID guid;
HRESULT hr = ::CLSIDFromString(aSubKeyName.get(), &guid);
if (hr == CO_E_CLASSSTRING) {
// If the key's name is not a GUID, the default value of the key
// may be a GUID.
nsAutoString valData;
if (NS_SUCCEEDED(aSubKey->ReadStringValue(u""_ns, valData))) {
hr = ::CLSIDFromString(valData.get(), &guid);
}
}
if (FAILED(hr)) {
return CallbackResult::Continue;
}
nsAutoString dllPath;
if (!GetInprocServerDllPathFromGuid(guid, dllPath)) {
return CallbackResult::Continue;
}
aCallback(dllPath, aType);
return CallbackResult::Continue;
});
break;
default:
MOZ_ASSERT_UNREACHABLE("Unexpected KnownModule::Type.");
break;
}
}
static void Enum(nsIWindowsRegKey* aRegBase, KnownModuleType aType,
const CallbackT& aCallback) {
sKnownExtensions[static_cast<int>(aType)].EnumInternal(aRegBase, aType,
aCallback);
}
KnownModule(HandlerType aHandlerType, nsLiteralString aSubkeyName)
: mHandlerType(aHandlerType), mSubkeyName(aSubkeyName) {}
public:
static void EnumAll(const CallbackT& aCallback) {
nsresult rv;
nsCOMPtr<nsIWindowsRegKey> regKey =
do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
if (NS_FAILED(rv)) {
return;
}
// Icon Overlay Handlers are registered under HKLM only.
// No need to look at HKCU.
rv = regKey->Open(
nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
u"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"_ns,
nsIWindowsRegKey::ACCESS_READ);
if (NS_SUCCEEDED(rv)) {
Enum(regKey, KnownModuleType::IconOverlay, aCallback);
}
// IMEs can be enumerated by
// ITfInputProcessorProfiles::EnumInputProcessorInfo, but enumerating
// the registry key is easier.
// The "HKLM\Software\Microsoft\CTF\TIP" subtree is shared between
// the 32-bits and 64 bits views.
// https://docs.microsoft.com/en-us/windows/win32/winprog64/shared-registry-keys
// This logic cannot detect legacy (TSF-unaware) IMEs.
rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
u"Software\\Microsoft\\CTF"_ns,
nsIWindowsRegKey::ACCESS_READ);
if (NS_SUCCEEDED(rv)) {
Enum(regKey, KnownModuleType::Ime, aCallback);
}
// Because HKCR is a merged view of HKLM\Software\Classes and
// HKCU\Software\Classes, looking at HKCR covers both per-machine
// and per-user extensions.
rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, u""_ns,
nsIWindowsRegKey::ACCESS_READ);
if (NS_FAILED(rv)) {
return;
}
EnumSubkeys(regKey, [&aCallback](const nsString& aSubKeyName,
nsIWindowsRegKey* aSubKey) {
if (aSubKeyName.EqualsIgnoreCase("DesktopBackground") ||
aSubKeyName.EqualsIgnoreCase("AudioCD")) {
return CallbackResult::Continue;
}
if (aSubKeyName.EqualsIgnoreCase("Directory")) {
nsCOMPtr<nsIWindowsRegKey> regBackground;
if (NS_SUCCEEDED(aSubKey->OpenChild(u"Background\\shellex"_ns,
nsIWindowsRegKey::ACCESS_READ,
getter_AddRefs(regBackground)))) {
Enum(regBackground, KnownModuleType::ContextMenuHandler, aCallback);
}
} else if (aSubKeyName.EqualsIgnoreCase("Network")) {
nsCOMPtr<nsIWindowsRegKey> regNetworkTypes;
if (NS_SUCCEEDED(aSubKey->OpenChild(u"Type"_ns,
nsIWindowsRegKey::ACCESS_READ,
getter_AddRefs(regNetworkTypes)))) {
EnumSubkeys(
regNetworkTypes,
[&aCallback](const nsString&, nsIWindowsRegKey* aRegNetworkType) {
nsCOMPtr<nsIWindowsRegKey> regNetworkTypeShex;
if (NS_FAILED(aRegNetworkType->OpenChild(
u"shellex"_ns, nsIWindowsRegKey::ACCESS_READ,
getter_AddRefs(regNetworkTypeShex)))) {
return CallbackResult::Continue;
}
Enum(regNetworkTypeShex, KnownModuleType::ContextMenuHandler,
aCallback);
Enum(regNetworkTypeShex, KnownModuleType::PropertySheetHandler,
aCallback);
return CallbackResult::Continue;
});
}
}
nsCOMPtr<nsIWindowsRegKey> regShex;
if (NS_FAILED(aSubKey->OpenChild(u"shellex"_ns,
nsIWindowsRegKey::ACCESS_READ,
getter_AddRefs(regShex)))) {
return CallbackResult::Continue;
}
Enum(regShex, KnownModuleType::ContextMenuHandler, aCallback);
Enum(regShex, KnownModuleType::PropertySheetHandler, aCallback);
if (aSubKeyName.EqualsIgnoreCase("AllFileSystemObjects") ||
aSubKeyName.EqualsIgnoreCase("Network") ||
aSubKeyName.EqualsIgnoreCase("NetShare") ||
aSubKeyName.EqualsIgnoreCase("NetServer") ||
aSubKeyName.EqualsIgnoreCase("DVD")) {
return CallbackResult::Continue;
}
if (aSubKeyName.EqualsIgnoreCase("Directory")) {
Enum(regShex, KnownModuleType::CopyHookHandler, aCallback);
Enum(regShex, KnownModuleType::DragDropHandler, aCallback);
return CallbackResult::Continue;
} else if (aSubKeyName.EqualsIgnoreCase("Drive")) {
Enum(regShex, KnownModuleType::DragDropHandler, aCallback);
return CallbackResult::Continue;
} else if (aSubKeyName.EqualsIgnoreCase("Folder")) {
Enum(regShex, KnownModuleType::DragDropHandler, aCallback);
return CallbackResult::Continue;
} else if (aSubKeyName.EqualsIgnoreCase("Printers")) {
Enum(regShex, KnownModuleType::CopyHookHandler, aCallback);
return CallbackResult::Continue;
}
Enum(regShex, KnownModuleType::DataHandler, aCallback);
Enum(regShex, KnownModuleType::DropHandler, aCallback);
Enum(regShex, KnownModuleType::IconHandler, aCallback);
Enum(regShex, KnownModuleType::PropertyHandler, aCallback);
Enum(regShex, KnownModuleType::InfotipHandler, aCallback);
return CallbackResult::Continue;
});
}
KnownModule() = delete;
KnownModule(KnownModule&&) = delete;
KnownModule& operator=(KnownModule&&) = delete;
KnownModule(const KnownModule&) = delete;
KnownModule& operator=(const KnownModule&) = delete;
};
KnownModule KnownModule::sKnownExtensions[] = {
{HandlerType::Multi, u"TIP"_ns},
{HandlerType::Multi, u"ShellIconOverlayIdentifiers"_ns},
{HandlerType::Multi, u"ContextMenuHandlers"_ns},
{HandlerType::Multi, u"CopyHookHandlers"_ns},
{HandlerType::Multi, u"DragDropHandlers"_ns},
{HandlerType::Multi, u"PropertySheetHandlers"_ns},
{HandlerType::Single, u"DataHandler"_ns},
{HandlerType::Single, u"DropHandler"_ns},
{HandlerType::Single, u"IconHandler"_ns},
{HandlerType::Single, u"{00021500-0000-0000-C000-000000000046}"_ns},
{HandlerType::Single, u"PropertyHandler"_ns},
};
namespace mozilla {
static StaticRefPtr<AboutThirdParty> sSingleton;
NS_IMPL_ISUPPORTS(InstalledApplication, nsIInstalledApplication);
NS_IMPL_ISUPPORTS(AboutThirdParty, nsIAboutThirdParty);
InstalledApplication::InstalledApplication(nsString&& aAppName,
nsString&& aPublisher)
: mName(std::move(aAppName)), mPublisher(std::move(aPublisher)) {}
NS_IMETHODIMP
InstalledApplication::GetName(nsAString& aResult) {
aResult = mName;
return NS_OK;
}
NS_IMETHODIMP
InstalledApplication::GetPublisher(nsAString& aResult) {
aResult = mPublisher;
return NS_OK;
}
/*static*/
already_AddRefed<AboutThirdParty> AboutThirdParty::GetSingleton() {
if (!sSingleton) {
sSingleton = new AboutThirdParty;
ClearOnShutdown(&sSingleton);
}
return do_AddRef(sSingleton);
}
AboutThirdParty::AboutThirdParty()
: mPromise(new BackgroundThreadPromise::Private(__func__)) {}
void AboutThirdParty::AddKnownModule(const nsString& aPath,
KnownModuleType aType) {
MOZ_ASSERT(!NS_IsMainThread());
const uint32_t flag = 1u << static_cast<uint32_t>(aType);
mKnownModules.WithEntryHandle(nt::GetLeafName(aPath), [flag](auto&& addPtr) {
if (addPtr) {
addPtr.Data() |= flag;
} else {
addPtr.Insert(flag);
}
});
}
void AboutThirdParty::BackgroundThread() {
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(mWorkerState == WorkerState::Running);
KnownModule::EnumAll(
[self = RefPtr{this}](const nsString& aDllPath, KnownModuleType aType) {
self->AddKnownModule(aDllPath, aType);
});
InstalledApplications apps;
apps.Collect(mComponentPaths, mLocations);
mWorkerState = WorkerState::Done;
}
NS_IMETHODIMP AboutThirdParty::LookupModuleType(const nsAString& aLeafName,
uint32_t* aResult) {
constexpr uint32_t kShellExtensions =
1u << static_cast<uint32_t>(KnownModuleType::IconOverlay) |
1u << static_cast<uint32_t>(KnownModuleType::ContextMenuHandler) |
1u << static_cast<uint32_t>(KnownModuleType::CopyHookHandler) |
1u << static_cast<uint32_t>(KnownModuleType::DragDropHandler) |
1u << static_cast<uint32_t>(KnownModuleType::PropertySheetHandler) |
1u << static_cast<uint32_t>(KnownModuleType::DataHandler) |
1u << static_cast<uint32_t>(KnownModuleType::DropHandler) |
1u << static_cast<uint32_t>(KnownModuleType::IconHandler) |
1u << static_cast<uint32_t>(KnownModuleType::InfotipHandler) |
1u << static_cast<uint32_t>(KnownModuleType::PropertyHandler);
MOZ_ASSERT(NS_IsMainThread());
*aResult = 0;
if (mWorkerState != WorkerState::Done) {
return NS_OK;
}
uint32_t flags;
if (!mKnownModules.Get(aLeafName, &flags)) {
*aResult = nsIAboutThirdParty::ModuleType_Unknown;
return NS_OK;
}
if (flags & (1u << static_cast<uint32_t>(KnownModuleType::Ime))) {
*aResult |= nsIAboutThirdParty::ModuleType_IME;
}
if (flags & kShellExtensions) {
*aResult |= nsIAboutThirdParty::ModuleType_ShellExtension;
}
return NS_OK;
}
NS_IMETHODIMP AboutThirdParty::LookupApplication(
const nsAString& aModulePath, nsIInstalledApplication** aResult) {
MOZ_ASSERT(NS_IsMainThread());
*aResult = nullptr;
if (mWorkerState != WorkerState::Done) {
return NS_OK;
}
const nsDependentSubstring leaf = nt::GetLeafName(aModulePath);
if (leaf.IsEmpty()) {
return NS_OK;
}
// Look up the component path's map first because it's more accurate
// than the location's array.
nsCOMPtr<nsIInstalledApplication> app = mComponentPaths.Get(aModulePath);
if (app) {
app.forget(aResult);
return NS_OK;
}
auto bounds = EqualRange(mLocations, 0, mLocations.Length(),
InstallLocationComparator(aModulePath));
// If more than one application includes the module, we return null
// because there is no way to know which is the real owner.
if (bounds.second() - bounds.first() != 1) {
return NS_OK;
}
app = mLocations[bounds.first()].second();
app.forget(aResult);
return NS_OK;
}
RefPtr<BackgroundThreadPromise> AboutThirdParty::CollectSystemInfoAsync() {
MOZ_ASSERT(NS_IsMainThread());
// Allow only the first call to start a background task.
if (mWorkerState.compareExchange(WorkerState::Init, WorkerState::Running)) {
nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction(
"AboutThirdParty::BackgroundThread", [self = RefPtr{this}]() mutable {
self->BackgroundThread();
NS_DispatchToMainThread(NS_NewRunnableFunction(
"AboutThirdParty::BackgroundThread Done",
[self]() { self->mPromise->Resolve(true, __func__); }));
});
nsresult rv =
NS_DispatchBackgroundTask(runnable.forget(), NS_DISPATCH_NORMAL);
if (NS_FAILED(rv)) {
mPromise->Reject(rv, __func__);
}
}
return mPromise;
}
NS_IMETHODIMP
AboutThirdParty::CollectSystemInfo(JSContext* aCx, dom::Promise** aResult) {
MOZ_ASSERT(NS_IsMainThread());
nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
MOZ_ASSERT(global);
ErrorResult result;
RefPtr<dom::Promise> promise(dom::Promise::Create(global, result));
if (NS_WARN_IF(result.Failed())) {
return result.StealNSResult();
}
CollectSystemInfoAsync()->Then(
GetMainThreadSerialEventTarget(), __func__,
[promise](bool) { promise->MaybeResolve(JS::NullHandleValue); },
[promise](nsresult aRv) { promise->MaybeReject(aRv); });
promise.forget(aResult);
return NS_OK;
}
} // namespace mozilla