summaryrefslogtreecommitdiffstats
path: root/toolkit/components/aboutthirdparty/tests/TestShellEx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /toolkit/components/aboutthirdparty/tests/TestShellEx
parentInitial commit. (diff)
downloadfirefox-esr-upstream.tar.xz
firefox-esr-upstream.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/aboutthirdparty/tests/TestShellEx')
-rw-r--r--toolkit/components/aboutthirdparty/tests/TestShellEx/Factory.cpp74
-rw-r--r--toolkit/components/aboutthirdparty/tests/TestShellEx/Icon.cpp109
-rw-r--r--toolkit/components/aboutthirdparty/tests/TestShellEx/RegUtils.cpp148
-rw-r--r--toolkit/components/aboutthirdparty/tests/TestShellEx/RegUtils.h52
-rw-r--r--toolkit/components/aboutthirdparty/tests/TestShellEx/Resource.h12
-rw-r--r--toolkit/components/aboutthirdparty/tests/TestShellEx/TestShellEx.cpp68
-rw-r--r--toolkit/components/aboutthirdparty/tests/TestShellEx/TestShellEx.def10
-rw-r--r--toolkit/components/aboutthirdparty/tests/TestShellEx/TestShellEx.rc39
-rw-r--r--toolkit/components/aboutthirdparty/tests/TestShellEx/dinosaur.icobin0 -> 1406 bytes
-rw-r--r--toolkit/components/aboutthirdparty/tests/TestShellEx/moz.build33
10 files changed, 545 insertions, 0 deletions
diff --git a/toolkit/components/aboutthirdparty/tests/TestShellEx/Factory.cpp b/toolkit/components/aboutthirdparty/tests/TestShellEx/Factory.cpp
new file mode 100644
index 0000000000..a341c27b73
--- /dev/null
+++ b/toolkit/components/aboutthirdparty/tests/TestShellEx/Factory.cpp
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
+ * 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 "mozilla/Atomics.h"
+#include "mozilla/RefPtr.h"
+
+#include <windows.h>
+#include <shlobj.h>
+
+already_AddRefed<IExtractIconW> CreateIconExtension();
+
+class ClassFactory final : public IClassFactory {
+ mozilla::Atomic<uint32_t> mRefCnt;
+
+ ~ClassFactory() = default;
+
+ public:
+ ClassFactory() : mRefCnt(0) {}
+
+ // IUnknown
+
+ STDMETHODIMP QueryInterface(REFIID aRefIID, void** aResult) {
+ if (!aResult) {
+ return E_INVALIDARG;
+ }
+
+ if (aRefIID == IID_IClassFactory) {
+ RefPtr ref(static_cast<IClassFactory*>(this));
+ ref.forget(aResult);
+ return S_OK;
+ }
+
+ return E_NOINTERFACE;
+ }
+
+ STDMETHODIMP_(ULONG) AddRef() { return ++mRefCnt; }
+
+ STDMETHODIMP_(ULONG) Release() {
+ ULONG result = --mRefCnt;
+ if (!result) {
+ delete this;
+ }
+ return result;
+ }
+
+ // IClassFactory
+
+ STDMETHODIMP CreateInstance(IUnknown* aOuter, REFIID aRefIID,
+ void** aResult) {
+ if (aOuter) {
+ return CLASS_E_NOAGGREGATION;
+ }
+
+ RefPtr<IUnknown> instance;
+ if (IsEqualCLSID(aRefIID, IID_IExtractIconA) ||
+ IsEqualCLSID(aRefIID, IID_IExtractIconW)) {
+ instance = CreateIconExtension();
+ } else {
+ return E_NOINTERFACE;
+ }
+
+ return instance ? instance->QueryInterface(aRefIID, aResult)
+ : E_OUTOFMEMORY;
+ }
+
+ STDMETHODIMP LockServer(BOOL) { return S_OK; }
+};
+
+already_AddRefed<IClassFactory> CreateFactory() {
+ return mozilla::MakeAndAddRef<ClassFactory>();
+}
diff --git a/toolkit/components/aboutthirdparty/tests/TestShellEx/Icon.cpp b/toolkit/components/aboutthirdparty/tests/TestShellEx/Icon.cpp
new file mode 100644
index 0000000000..116c0e03a1
--- /dev/null
+++ b/toolkit/components/aboutthirdparty/tests/TestShellEx/Icon.cpp
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
+ * 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 "mozilla/Atomics.h"
+#include "mozilla/RefPtr.h"
+#include "Resource.h"
+
+#include <windows.h>
+#include <shlobj.h>
+
+#include <string>
+
+extern std::wstring gDllPath;
+extern GUID CLSID_TestShellEx;
+
+class IconExtension final : public IPersistFile,
+ public IExtractIconA,
+ public IExtractIconW {
+ mozilla::Atomic<uint32_t> mRefCnt;
+
+ ~IconExtension() = default;
+
+ public:
+ IconExtension() : mRefCnt(0) {}
+
+ // IUnknown
+
+ STDMETHODIMP QueryInterface(REFIID aRefIID, void** aResult) {
+ if (!aResult) {
+ return E_INVALIDARG;
+ }
+
+ if (aRefIID == IID_IPersist) {
+ RefPtr ref(static_cast<IPersist*>(this));
+ ref.forget(aResult);
+ return S_OK;
+ } else if (aRefIID == IID_IPersistFile) {
+ RefPtr ref(static_cast<IPersistFile*>(this));
+ ref.forget(aResult);
+ return S_OK;
+ } else if (aRefIID == IID_IExtractIconA) {
+ RefPtr ref(static_cast<IExtractIconA*>(this));
+ ref.forget(aResult);
+ return S_OK;
+ } else if (aRefIID == IID_IExtractIconW) {
+ RefPtr ref(static_cast<IExtractIconW*>(this));
+ ref.forget(aResult);
+ return S_OK;
+ }
+
+ return E_NOINTERFACE;
+ }
+
+ STDMETHODIMP_(ULONG) AddRef() { return ++mRefCnt; }
+
+ STDMETHODIMP_(ULONG) Release() {
+ ULONG result = --mRefCnt;
+ if (!result) {
+ delete this;
+ }
+ return result;
+ }
+
+ // IPersist
+
+ STDMETHODIMP GetClassID(CLSID* aClassID) {
+ *aClassID = CLSID_TestShellEx;
+ return S_OK;
+ }
+
+ // IPersistFile
+
+ STDMETHODIMP GetCurFile(LPOLESTR*) { return E_NOTIMPL; }
+ STDMETHODIMP IsDirty() { return S_FALSE; }
+ STDMETHODIMP Load(LPCOLESTR, DWORD) { return S_OK; }
+ STDMETHODIMP Save(LPCOLESTR, BOOL) { return E_NOTIMPL; }
+ STDMETHODIMP SaveCompleted(LPCOLESTR) { return E_NOTIMPL; }
+
+ // IExtractIconA
+
+ STDMETHODIMP Extract(PCSTR, UINT, HICON*, HICON*, UINT) { return E_NOTIMPL; }
+ STDMETHODIMP GetIconLocation(UINT, PSTR, UINT, int*, UINT*) {
+ return E_NOTIMPL;
+ }
+
+ // IExtractIconW
+
+ STDMETHODIMP Extract(PCWSTR, UINT, HICON*, HICON*, UINT) { return S_FALSE; }
+
+ STDMETHODIMP GetIconLocation(UINT, PWSTR aIconFile, UINT aCchMax, int* aIndex,
+ UINT* aOutFlags) {
+ if (aCchMax <= gDllPath.size()) {
+ return E_NOT_SUFFICIENT_BUFFER;
+ }
+
+ gDllPath.copy(aIconFile, gDllPath.size());
+ aIconFile[gDllPath.size()] = 0;
+ *aOutFlags = GIL_DONTCACHE;
+ *aIndex = -IDI_ICON1;
+ return S_OK;
+ }
+};
+
+already_AddRefed<IExtractIconW> CreateIconExtension() {
+ return mozilla::MakeAndAddRef<IconExtension>();
+}
diff --git a/toolkit/components/aboutthirdparty/tests/TestShellEx/RegUtils.cpp b/toolkit/components/aboutthirdparty/tests/TestShellEx/RegUtils.cpp
new file mode 100644
index 0000000000..33b2feb12a
--- /dev/null
+++ b/toolkit/components/aboutthirdparty/tests/TestShellEx/RegUtils.cpp
@@ -0,0 +1,148 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
+ * 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 "mozilla/UniquePtr.h"
+#include "RegUtils.h"
+
+#include <windows.h>
+#include <strsafe.h>
+
+extern std::wstring gDllPath;
+
+const wchar_t kClsIdPrefix[] = L"CLSID\\";
+const wchar_t* kExtensionSubkeys[] = {
+ L".zzz\\shellex\\IconHandler",
+};
+
+bool RegKey::SetStringInternal(const wchar_t* aValueName,
+ const wchar_t* aValueData,
+ DWORD aValueDataLength) {
+ if (!mKey) {
+ return false;
+ }
+
+ return ::RegSetValueExW(mKey, aValueName, 0, REG_SZ,
+ reinterpret_cast<const BYTE*>(aValueData),
+ aValueDataLength) == ERROR_SUCCESS;
+}
+
+RegKey::RegKey(HKEY root, const wchar_t* aSubkey) : mKey(nullptr) {
+ ::RegCreateKeyExW(root, aSubkey, 0, nullptr, 0, KEY_ALL_ACCESS, nullptr,
+ &mKey, nullptr);
+}
+
+RegKey::~RegKey() {
+ if (mKey) {
+ ::RegCloseKey(mKey);
+ }
+}
+
+bool RegKey::SetString(const wchar_t* aValueName, const wchar_t* aValueData) {
+ return SetStringInternal(
+ aValueName, aValueData,
+ aValueData
+ ? static_cast<DWORD>((wcslen(aValueData) + 1) * sizeof(wchar_t))
+ : 0);
+}
+
+bool RegKey::SetString(const wchar_t* aValueName,
+ const std::wstring& aValueData) {
+ return SetStringInternal(
+ aValueName, aValueData.c_str(),
+ static_cast<DWORD>((aValueData.size() + 1) * sizeof(wchar_t)));
+}
+
+std::wstring RegKey::GetString(const wchar_t* aValueName) {
+ DWORD len = 0;
+ LSTATUS status = ::RegGetValueW(mKey, aValueName, nullptr, RRF_RT_REG_SZ,
+ nullptr, nullptr, &len);
+
+ mozilla::UniquePtr<uint8_t[]> buf = mozilla::MakeUnique<uint8_t[]>(len);
+ status = ::RegGetValueW(mKey, aValueName, nullptr, RRF_RT_REG_SZ, nullptr,
+ buf.get(), &len);
+ if (status != ERROR_SUCCESS) {
+ return L"";
+ }
+
+ return reinterpret_cast<wchar_t*>(buf.get());
+}
+
+ComRegisterer::ComRegisterer(const GUID& aClsId, const wchar_t* aFriendlyName)
+ : mClassRoot(HKEY_CURRENT_USER, L"Software\\Classes"),
+ mFriendlyName(aFriendlyName) {
+ wchar_t guidStr[64];
+ HRESULT hr = ::StringCbPrintfW(
+ guidStr, sizeof(guidStr),
+ L"{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}", aClsId.Data1,
+ aClsId.Data2, aClsId.Data3, aClsId.Data4[0], aClsId.Data4[1],
+ aClsId.Data4[2], aClsId.Data4[3], aClsId.Data4[4], aClsId.Data4[5],
+ aClsId.Data4[6], aClsId.Data4[7]);
+ if (FAILED(hr)) {
+ return;
+ }
+
+ mClsId = guidStr;
+}
+
+bool ComRegisterer::UnregisterAll() {
+ bool isOk = true;
+ LSTATUS ls;
+
+ for (const wchar_t* subkey : kExtensionSubkeys) {
+ RegKey root(mClassRoot, subkey);
+
+ std::wstring currentHandler = root.GetString(nullptr);
+ if (currentHandler != mClsId) {
+ // If another extension is registered, don't overwrite it.
+ continue;
+ }
+
+ // Set an empty string instead of deleting the key.
+ if (!root.SetString(nullptr)) {
+ isOk = false;
+ }
+ }
+
+ std::wstring subkey(kClsIdPrefix);
+ subkey += mClsId;
+ ls = ::RegDeleteTreeW(mClassRoot, subkey.c_str());
+ if (ls != ERROR_SUCCESS && ls != ERROR_FILE_NOT_FOUND) {
+ isOk = false;
+ }
+
+ return isOk;
+}
+
+bool ComRegisterer::RegisterObject(const wchar_t* aThreadModel) {
+ std::wstring subkey(kClsIdPrefix);
+ subkey += mClsId;
+
+ RegKey root(mClassRoot, subkey.c_str());
+ if (!root || !root.SetString(nullptr, mFriendlyName)) {
+ return false;
+ }
+
+ RegKey inproc(root, L"InprocServer32");
+ return inproc && inproc.SetString(nullptr, gDllPath) &&
+ inproc.SetString(L"ThreadingModel", aThreadModel);
+}
+
+bool ComRegisterer::RegisterExtensions() {
+ for (const wchar_t* subkey : kExtensionSubkeys) {
+ RegKey root(mClassRoot, subkey);
+ std::wstring currentHandler = root.GetString(nullptr);
+ if (!currentHandler.empty()) {
+ // If another extension is registered, don't overwrite it.
+ continue;
+ }
+
+ if (!root.SetString(nullptr, mClsId)) {
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/toolkit/components/aboutthirdparty/tests/TestShellEx/RegUtils.h b/toolkit/components/aboutthirdparty/tests/TestShellEx/RegUtils.h
new file mode 100644
index 0000000000..21a23fdf16
--- /dev/null
+++ b/toolkit/components/aboutthirdparty/tests/TestShellEx/RegUtils.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
+ * 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/. */
+
+#ifndef mozilla_TestShellEx_RegUtils_h
+#define mozilla_TestShellEx_RegUtils_h
+
+#include <windows.h>
+#include <string>
+
+class RegKey final {
+ HKEY mKey;
+
+ bool SetStringInternal(const wchar_t* aValueName, const wchar_t* aValueData,
+ DWORD aValueDataLength);
+
+ public:
+ RegKey() : mKey(nullptr) {}
+ RegKey(HKEY root, const wchar_t* aSubkey);
+ ~RegKey();
+
+ RegKey(RegKey&& aOther) = delete;
+ RegKey& operator=(RegKey&& aOther) = delete;
+ RegKey(const RegKey&) = delete;
+ RegKey& operator=(const RegKey&) = delete;
+
+ explicit operator bool() const { return !!mKey; }
+ operator HKEY() const { return mKey; }
+
+ bool SetString(const wchar_t* aValueName,
+ const wchar_t* aValueData = nullptr);
+ bool SetString(const wchar_t* aValueName, const std::wstring& aValueData);
+ std::wstring GetString(const wchar_t* aValueName);
+};
+
+class ComRegisterer final {
+ RegKey mClassRoot;
+ std::wstring mClsId;
+ std::wstring mFriendlyName;
+
+ public:
+ ComRegisterer(const GUID& aClsId, const wchar_t* aFriendlyName);
+ ~ComRegisterer() = default;
+
+ bool UnregisterAll();
+ bool RegisterObject(const wchar_t* aThreadModel);
+ bool RegisterExtensions();
+};
+
+#endif // mozilla_TestShellEx_RegUtils_h
diff --git a/toolkit/components/aboutthirdparty/tests/TestShellEx/Resource.h b/toolkit/components/aboutthirdparty/tests/TestShellEx/Resource.h
new file mode 100644
index 0000000000..fa037194d7
--- /dev/null
+++ b/toolkit/components/aboutthirdparty/tests/TestShellEx/Resource.h
@@ -0,0 +1,12 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
+ * 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/. */
+
+#ifndef mozilla_TestShellEx_Resource_h
+#define mozilla_TestShellEx_Resource_h
+
+#define IDI_ICON1 100
+
+#endif // mozilla_TestShellEx_Resource_h
diff --git a/toolkit/components/aboutthirdparty/tests/TestShellEx/TestShellEx.cpp b/toolkit/components/aboutthirdparty/tests/TestShellEx/TestShellEx.cpp
new file mode 100644
index 0000000000..dc0588483f
--- /dev/null
+++ b/toolkit/components/aboutthirdparty/tests/TestShellEx/TestShellEx.cpp
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
+ * 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 "mozilla/RefPtr.h"
+#include "RegUtils.h"
+
+#include <windows.h>
+#include <objbase.h>
+
+already_AddRefed<IClassFactory> CreateFactory();
+
+// {10A9521E-0205-4CC7-93A1-62F30A9A54B3}
+GUID CLSID_TestShellEx = {
+ 0x10a9521e, 0x205, 0x4cc7, {0x93, 0xa1, 0x62, 0xf3, 0xa, 0x9a, 0x54, 0xb3}};
+wchar_t kFriendlyName[] = L"Minimum Shell Extension for Firefox testing";
+
+std::wstring gDllPath;
+
+BOOL APIENTRY DllMain(HMODULE aModule, DWORD aReason, LPVOID) {
+ wchar_t buf[MAX_PATH];
+ switch (aReason) {
+ case DLL_PROCESS_ATTACH:
+ if (!::GetModuleFileNameW(aModule, buf, ARRAYSIZE(buf))) {
+ return FALSE;
+ }
+ gDllPath = buf;
+ break;
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ case DLL_PROCESS_DETACH:
+ break;
+ }
+ return TRUE;
+}
+
+STDAPI DllGetClassObject(REFCLSID aClsid, REFIID aIid, void** aResult) {
+ if (!IsEqualCLSID(aClsid, CLSID_TestShellEx) ||
+ !IsEqualCLSID(aIid, IID_IClassFactory)) {
+ return CLASS_E_CLASSNOTAVAILABLE;
+ }
+
+ RefPtr<IClassFactory> factory = CreateFactory();
+ return factory ? factory->QueryInterface(aIid, aResult) : E_OUTOFMEMORY;
+}
+
+STDAPI DllCanUnloadNow() { return S_OK; }
+
+// These functions enable us to run "regsvr32 [/u] TestShellEx.dll" manually.
+// (No admin privilege is needed because all access is under HKCU.)
+// We don't use these functions in the mochitest, but having these makes easier
+// to test the module manually.
+STDAPI DllRegisterServer() {
+ ComRegisterer reg(CLSID_TestShellEx, kFriendlyName);
+ if (!reg.RegisterObject(L"Apartment") || !reg.RegisterExtensions()) {
+ return E_ACCESSDENIED;
+ }
+ return S_OK;
+}
+STDAPI DllUnregisterServer() {
+ ComRegisterer reg(CLSID_TestShellEx, kFriendlyName);
+ if (!reg.UnregisterAll()) {
+ return E_ACCESSDENIED;
+ }
+ return S_OK;
+}
diff --git a/toolkit/components/aboutthirdparty/tests/TestShellEx/TestShellEx.def b/toolkit/components/aboutthirdparty/tests/TestShellEx/TestShellEx.def
new file mode 100644
index 0000000000..e83041771d
--- /dev/null
+++ b/toolkit/components/aboutthirdparty/tests/TestShellEx/TestShellEx.def
@@ -0,0 +1,10 @@
+;+# 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/.
+
+LIBRARY TestShellEx
+EXPORTS
+ DllGetClassObject PRIVATE
+ DllCanUnloadNow PRIVATE
+ DllRegisterServer PRIVATE
+ DllUnregisterServer PRIVATE
diff --git a/toolkit/components/aboutthirdparty/tests/TestShellEx/TestShellEx.rc b/toolkit/components/aboutthirdparty/tests/TestShellEx/TestShellEx.rc
new file mode 100644
index 0000000000..7500551f02
--- /dev/null
+++ b/toolkit/components/aboutthirdparty/tests/TestShellEx/TestShellEx.rc
@@ -0,0 +1,39 @@
+/* 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 <winres.h>
+
+#include "Resource.h"
+
+IDI_ICON1 ICON DISCARDABLE "dinosaur.ico"
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,2,3,4 // This field will be collected
+ PRODUCTVERSION 5,6,7,8
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE 0x0L
+
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "CompanyName", "Mozilla Corporation"
+ VALUE "OriginalFilename", "TestShellEx.dll"
+ VALUE "ProductName", "Sample Shell Extension"
+ END
+ END
+
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0409, 1252
+ END
+END
diff --git a/toolkit/components/aboutthirdparty/tests/TestShellEx/dinosaur.ico b/toolkit/components/aboutthirdparty/tests/TestShellEx/dinosaur.ico
new file mode 100644
index 0000000000..d44438903b
--- /dev/null
+++ b/toolkit/components/aboutthirdparty/tests/TestShellEx/dinosaur.ico
Binary files differ
diff --git a/toolkit/components/aboutthirdparty/tests/TestShellEx/moz.build b/toolkit/components/aboutthirdparty/tests/TestShellEx/moz.build
new file mode 100644
index 0000000000..44344a0e2e
--- /dev/null
+++ b/toolkit/components/aboutthirdparty/tests/TestShellEx/moz.build
@@ -0,0 +1,33 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# 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/.
+
+DIST_INSTALL = False
+
+SharedLibrary("TestShellEx")
+
+UNIFIED_SOURCES = [
+ "Factory.cpp",
+ "Icon.cpp",
+ "RegUtils.cpp",
+ "TestShellEx.cpp",
+]
+
+RCFILE = "TestShellEx.rc"
+DEFFILE = "TestShellEx.def"
+USE_LIBS += [
+ "mozglue",
+]
+
+if CONFIG["OS_ARCH"] == "WINNT":
+ OS_LIBS += [
+ "advapi32",
+ "uuid",
+ ]
+
+if CONFIG["COMPILE_ENVIRONMENT"]:
+ shared_library = "!%sTestShellEx%s" % (CONFIG["DLL_PREFIX"], CONFIG["DLL_SUFFIX"])
+ TEST_HARNESS_FILES.testing.mochitest.browser.toolkit.components.aboutthirdparty.tests.browser += [
+ shared_library
+ ]