diff --git a/toolkit/components/aboutthirdparty/AboutThirdParty.cpp b/toolkit/components/aboutthirdparty/AboutThirdParty.cpp index 5794ec4e8936..0549cc268086 100644 --- a/toolkit/components/aboutthirdparty/AboutThirdParty.cpp +++ b/toolkit/components/aboutthirdparty/AboutThirdParty.cpp @@ -8,9 +8,311 @@ #include "mozilla/ClearOnShutdown.h" #include "mozilla/dom/Promise.h" +#include "mozilla/NativeNt.h" #include "mozilla/StaticPtr.h" +#include "nsComponentManagerUtils.h" +#include "nsIWindowsRegKey.h" #include "nsThreadUtils.h" +#include + +using namespace mozilla; + +namespace { + +// A callback function passed to EnumSubkeys uses this type +// to control the enumeration loop. +enum class CallbackResult { Continue, Stop }; + +template +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 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 + +class KnownModule final { + static KnownModule sKnownExtensions[static_cast(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 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 EnumInternal(nsIWindowsRegKey* aRegBase, KnownModuleType aType, + const CallbackT& aCallback) const { + nsCOMPtr 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(aType)].EnumInternal(aRegBase, aType, + aCallback); + } + + KnownModule(HandlerType aHandlerType, nsLiteralString aSubkeyName) + : mHandlerType(aHandlerType), mSubkeyName(aSubkeyName) {} + + public: + static void EnumAll(const CallbackT& aCallback) { + nsresult rv; + nsCOMPtr 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 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 regNetworkTypes; + if (NS_SUCCEEDED(aSubKey->OpenChild(u"Type"_ns, + nsIWindowsRegKey::ACCESS_READ, + getter_AddRefs(regNetworkTypes)))) { + EnumSubkeys( + regNetworkTypes, + [&aCallback](const nsString&, nsIWindowsRegKey* aRegNetworkType) { + nsCOMPtr 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 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 sSingleton; @@ -30,13 +332,70 @@ already_AddRefed AboutThirdParty::GetSingleton() { 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(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); + }); + mWorkerState = WorkerState::Done; } +NS_IMETHODIMP AboutThirdParty::LookupModuleType(const nsAString& aLeafName, + uint32_t* aResult) { + constexpr uint32_t kShellExtensions = + 1u << static_cast(KnownModuleType::IconOverlay) | + 1u << static_cast(KnownModuleType::ContextMenuHandler) | + 1u << static_cast(KnownModuleType::CopyHookHandler) | + 1u << static_cast(KnownModuleType::DragDropHandler) | + 1u << static_cast(KnownModuleType::PropertySheetHandler) | + 1u << static_cast(KnownModuleType::DataHandler) | + 1u << static_cast(KnownModuleType::DropHandler) | + 1u << static_cast(KnownModuleType::IconHandler) | + 1u << static_cast(KnownModuleType::InfotipHandler) | + 1u << static_cast(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(KnownModuleType::Ime))) { + *aResult |= nsIAboutThirdParty::ModuleType_IME; + } + + if (flags & kShellExtensions) { + *aResult |= nsIAboutThirdParty::ModuleType_ShellExtension; + } + + return NS_OK; +} + RefPtr AboutThirdParty::CollectSystemInfoAsync() { MOZ_ASSERT(NS_IsMainThread()); diff --git a/toolkit/components/aboutthirdparty/AboutThirdParty.h b/toolkit/components/aboutthirdparty/AboutThirdParty.h index 81f052fbe69d..b55ea4e75c5f 100644 --- a/toolkit/components/aboutthirdparty/AboutThirdParty.h +++ b/toolkit/components/aboutthirdparty/AboutThirdParty.h @@ -9,9 +9,26 @@ #include "mozilla/MozPromise.h" #include "nsIAboutThirdParty.h" +#include "nsTHashMap.h" namespace mozilla { +enum class KnownModuleType : uint32_t { + Ime = 0, + IconOverlay, + ContextMenuHandler, + CopyHookHandler, + DragDropHandler, + PropertySheetHandler, + DataHandler, + DropHandler, + IconHandler, + InfotipHandler, + PropertyHandler, + + Last, +}; + using BackgroundThreadPromise = MozPromise; @@ -24,9 +41,11 @@ class AboutThirdParty final : public nsIAboutThirdParty { }; Atomic mWorkerState; RefPtr mPromise; + nsTHashMap mKnownModules; ~AboutThirdParty() = default; void BackgroundThread(); + void AddKnownModule(const nsString& aPath, KnownModuleType aType); public: static already_AddRefed GetSingleton(); diff --git a/toolkit/components/aboutthirdparty/content/aboutThirdParty.css b/toolkit/components/aboutthirdparty/content/aboutThirdParty.css index 046a89da1359..fb4ffb209cf5 100644 --- a/toolkit/components/aboutthirdparty/content/aboutThirdParty.css +++ b/toolkit/components/aboutthirdparty/content/aboutThirdParty.css @@ -39,6 +39,10 @@ h1 { margin: 0; } +.module-tags { + margin-inline: .5em; +} + .module-tag { font-size: 12px; white-space: nowrap; diff --git a/toolkit/components/aboutthirdparty/content/aboutThirdParty.html b/toolkit/components/aboutthirdparty/content/aboutThirdParty.html index b8e0fd487d1b..dfca2c5c6b78 100644 --- a/toolkit/components/aboutthirdparty/content/aboutThirdParty.html +++ b/toolkit/components/aboutthirdparty/content/aboutThirdParty.html @@ -22,6 +22,8 @@

@@ -52,6 +54,12 @@ +
+ + +