зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1695817 - Part 4: Label a module as ShellExtension or IME. r=Gijs,mhowell
This patch adds a feature to mark each module in the about:third-party page as a shell extension or an IME if it is so. To achieve this, when the page is loaded, it starts a background task to collect registered shell extensions from the registry and adds a tag next to a module's name. Differential Revision: https://phabricator.services.mozilla.com/D109305
This commit is contained in:
Родитель
62021812c8
Коммит
cb2d12abe0
|
@ -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 <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
|
||||
|
||||
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;
|
||||
|
@ -30,13 +332,70 @@ already_AddRefed<AboutThirdParty> 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<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);
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
RefPtr<BackgroundThreadPromise> AboutThirdParty::CollectSystemInfoAsync() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
|
|
|
@ -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<bool /* aIgnored */, nsresult, /* IsExclusive */ false>;
|
||||
|
||||
|
@ -24,9 +41,11 @@ class AboutThirdParty final : public nsIAboutThirdParty {
|
|||
};
|
||||
Atomic<WorkerState, SequentiallyConsistent> mWorkerState;
|
||||
RefPtr<BackgroundThreadPromise::Private> mPromise;
|
||||
nsTHashMap<nsStringCaseInsensitiveHashKey, uint32_t> mKnownModules;
|
||||
|
||||
~AboutThirdParty() = default;
|
||||
void BackgroundThread();
|
||||
void AddKnownModule(const nsString& aPath, KnownModuleType aType);
|
||||
|
||||
public:
|
||||
static already_AddRefed<AboutThirdParty> GetSingleton();
|
||||
|
|
|
@ -39,6 +39,10 @@ h1 {
|
|||
margin: 0;
|
||||
}
|
||||
|
||||
.module-tags {
|
||||
margin-inline: .5em;
|
||||
}
|
||||
|
||||
.module-tag {
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
<div>
|
||||
<button id="button-copy-to-clipboard"
|
||||
data-l10n-id="third-party-button-copy-to-clipboard"/>
|
||||
<button id="button-reload" hidden
|
||||
data-l10n-id="third-party-button-reload"/>
|
||||
</div>
|
||||
|
||||
<h2 data-l10n-id="third-party-section-title"></h2>
|
||||
|
@ -52,6 +54,12 @@
|
|||
<img src="chrome://global/skin/icons/security-broken.svg"
|
||||
class="svg-common image-unsigned" hidden
|
||||
data-l10n-id="third-party-unsigned-icon"/>
|
||||
<div class="module-tags">
|
||||
<span class="module-tag tag-ime" hidden
|
||||
data-l10n-id="third-party-tag-ime"></span>
|
||||
<span class="module-tag tag-shellex" hidden
|
||||
data-l10n-id="third-party-tag-shellex"></span>
|
||||
</div>
|
||||
<button class="svg-button button-open-dir"
|
||||
data-l10n-id="third-party-button-open">
|
||||
<img src="chrome://global/skin/icons/folder.svg"
|
||||
|
|
|
@ -55,6 +55,9 @@ async function fetchData() {
|
|||
for (const module of data.modules) {
|
||||
module.events = [];
|
||||
module.loadingOnMain = { count: 0, sum: 0 };
|
||||
module.typeFlags = AboutThirdParty.lookupModuleType(
|
||||
module.dllFile?.leafName
|
||||
);
|
||||
}
|
||||
|
||||
for (const [proc, perProc] of Object.entries(data.processes)) {
|
||||
|
@ -81,6 +84,14 @@ async function fetchData() {
|
|||
module.events.sort((a, b) => a.process.localeCompare(b.process));
|
||||
}
|
||||
|
||||
data.modules.sort((a, b) => {
|
||||
// First sort by |typeFlags| in ascending order to move up
|
||||
// unknown-type modules first, then sort by |loadingOnMain|
|
||||
// in descending order to move up slower modules.
|
||||
const diff = a.typeFlags - b.typeFlags;
|
||||
return diff == 0 ? b.loadingOnMain - a.loadingOnMain : diff;
|
||||
});
|
||||
|
||||
return data.modules;
|
||||
}
|
||||
|
||||
|
@ -144,6 +155,11 @@ function copyDataToClipboard(aData) {
|
|||
fileVersion: module.fileVersion,
|
||||
};
|
||||
|
||||
// We include the typeFlags field only when it's not 0 because
|
||||
// typeFlags == 0 means system info is not yet collected.
|
||||
if (module.typeFlags) {
|
||||
copied.typeFlags = module.typeFlags;
|
||||
}
|
||||
if (module.signedBy) {
|
||||
copied.signedBy = module.signedBy;
|
||||
}
|
||||
|
@ -191,6 +207,14 @@ function visualizeData(aData) {
|
|||
.querySelector(".button-expand")
|
||||
.addEventListener("click", onClickExpand);
|
||||
|
||||
const modTagsContainer = newCard.querySelector(".module-tags");
|
||||
if (module.typeFlags & Ci.nsIAboutThirdParty.ModuleType_IME) {
|
||||
modTagsContainer.querySelector(".tag-ime").hidden = false;
|
||||
}
|
||||
if (module.typeFlags & Ci.nsIAboutThirdParty.ModuleType_ShellExtension) {
|
||||
modTagsContainer.querySelector(".tag-shellex").hidden = false;
|
||||
}
|
||||
|
||||
const btnOpenDir = newCard.querySelector(".button-open-dir");
|
||||
btnOpenDir.fileObj = module.dllFile;
|
||||
btnOpenDir.addEventListener("click", onClickOpenDir);
|
||||
|
@ -265,8 +289,27 @@ async function onLoad() {
|
|||
e.target.disabled = false;
|
||||
});
|
||||
|
||||
let hasData = false;
|
||||
AboutThirdParty.collectSystemInfo()
|
||||
.then(() => {
|
||||
if (!hasData) {
|
||||
// If collectSystemInfo was completed before fetchData,
|
||||
// or there was no data available, visualizeData shows
|
||||
// full info and the reload button is not needed.
|
||||
return;
|
||||
}
|
||||
|
||||
const button = document.getElementById("button-reload");
|
||||
button.addEventListener("click", () => {
|
||||
location.reload();
|
||||
});
|
||||
button.hidden = false;
|
||||
})
|
||||
.catch(Cu.reportError);
|
||||
|
||||
const data = await fetchData();
|
||||
if (!data?.length) {
|
||||
hasData = data?.length;
|
||||
if (!hasData) {
|
||||
document.getElementById("no-data").hidden = false;
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,19 @@
|
|||
[scriptable, uuid(d33ff086-b328-4ae6-aaf5-52d41aa5df38)]
|
||||
interface nsIAboutThirdParty : nsISupports
|
||||
{
|
||||
/**
|
||||
* ModuleType flags used by lookupModuleType.
|
||||
*/
|
||||
const unsigned long ModuleType_Unknown = 1 << 0;
|
||||
const unsigned long ModuleType_IME = 1 << 1;
|
||||
const unsigned long ModuleType_ShellExtension = 1 << 2;
|
||||
|
||||
/**
|
||||
* Returns a bitwise combination of the ModuleType_* flags
|
||||
* for the given leaf name of a module.
|
||||
*/
|
||||
unsigned long lookupModuleType(in AString aLeafName);
|
||||
|
||||
/**
|
||||
* Posts a background task to collect system information and resolves
|
||||
* the returned promise when the task is finished.
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const kExtensionModuleName = "TestShellEx.dll";
|
||||
const kATP = Cc["@mozilla.org/about-thirdparty;1"].getService(
|
||||
Ci.nsIAboutThirdParty
|
||||
);
|
||||
|
|
|
@ -3,6 +3,12 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
add_task(async () => {
|
||||
Assert.equal(
|
||||
kATP.lookupModuleType(kExtensionModuleName),
|
||||
0,
|
||||
"lookupModuleType() returns 0 before system info is collected."
|
||||
);
|
||||
|
||||
// Make sure successive calls of collectSystemInfo() do not
|
||||
// cause anything bad.
|
||||
const kLoopCount = 100;
|
||||
|
@ -20,4 +26,28 @@ add_task(async () => {
|
|||
"All results from collectSystemInfo() are resolved."
|
||||
);
|
||||
}
|
||||
|
||||
Assert.equal(
|
||||
kATP.lookupModuleType("SHELL32.dll"),
|
||||
Ci.nsIAboutThirdParty.ModuleType_ShellExtension,
|
||||
"Shell32.dll is always registered as a shell extension."
|
||||
);
|
||||
|
||||
Assert.equal(
|
||||
kATP.lookupModuleType(""),
|
||||
Ci.nsIAboutThirdParty.ModuleType_Unknown,
|
||||
"Looking up an empty string succeeds and returns ModuleType_Unknown."
|
||||
);
|
||||
|
||||
Assert.equal(
|
||||
kATP.lookupModuleType(null),
|
||||
Ci.nsIAboutThirdParty.ModuleType_Unknown,
|
||||
"Looking up null succeeds and returns ModuleType_Unknown."
|
||||
);
|
||||
|
||||
Assert.equal(
|
||||
kATP.lookupModuleType("invalid name"),
|
||||
Ci.nsIAboutThirdParty.ModuleType_Unknown,
|
||||
"Looking up an invalid name succeeds and returns ModuleType_Unknown."
|
||||
);
|
||||
});
|
||||
|
|
|
@ -27,6 +27,12 @@ third-party-th-process = Process
|
|||
third-party-th-duration = Loading Duration (ms)
|
||||
third-party-th-status = Status
|
||||
|
||||
third-party-tag-ime = IME
|
||||
.title =
|
||||
This type of module is loaded when you use a third-party IME.
|
||||
third-party-tag-shellex = Shell Extension
|
||||
.title =
|
||||
This type of module is loaded when you open the system file dialog.
|
||||
third-party-tag-background = Background
|
||||
.title =
|
||||
This module did not block the application because it was loaded
|
||||
|
@ -39,6 +45,8 @@ third-party-status-blocked = Blocked
|
|||
third-party-status-redirected = Redirected
|
||||
|
||||
third-party-button-copy-to-clipboard = Copy raw data to clipboard
|
||||
third-party-button-reload = Reload with system info
|
||||
.title = Reload with system information
|
||||
third-party-button-open =
|
||||
.title = Open file location…
|
||||
third-party-button-expand =
|
||||
|
|
Загрузка…
Ссылка в новой задаче