summaryrefslogtreecommitdiffstats
path: root/browser/app/winlauncher/ErrorHandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'browser/app/winlauncher/ErrorHandler.cpp')
-rw-r--r--browser/app/winlauncher/ErrorHandler.cpp782
1 files changed, 782 insertions, 0 deletions
diff --git a/browser/app/winlauncher/ErrorHandler.cpp b/browser/app/winlauncher/ErrorHandler.cpp
new file mode 100644
index 0000000000..1286e0f90f
--- /dev/null
+++ b/browser/app/winlauncher/ErrorHandler.cpp
@@ -0,0 +1,782 @@
+/* -*- 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 https://mozilla.org/MPL/2.0/. */
+
+#include "ErrorHandler.h"
+
+#include <utility>
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/CmdLineAndEnvUtils.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/JSONWriter.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Unused.h"
+#include "mozilla/WinTokenUtils.h"
+#include "mozilla/WindowsVersion.h"
+#include "mozilla/XREAppData.h"
+#include "mozilla/glue/WindowsDllServices.h"
+#include "mozilla/mscom/ProcessRuntime.h"
+#include "nsWindowsHelpers.h"
+
+#if defined(MOZ_LAUNCHER_PROCESS)
+# include "mozilla/LauncherRegistryInfo.h"
+#endif // defined(MOZ_LAUNCHER_PROCESS)
+
+#include <algorithm>
+#include <process.h>
+#include <sstream>
+#include <string>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#include <objbase.h>
+#include <rpc.h>
+#if !defined(__MINGW32__)
+# include <comutil.h>
+# include <iwscapi.h>
+# include <wscapi.h>
+#endif // !defined(__MINGW32__)
+#include <tlhelp32.h>
+
+#if !defined(RRF_SUBKEY_WOW6464KEY)
+# define RRF_SUBKEY_WOW6464KEY 0x00010000
+#endif // !defined(RRF_SUBKEY_WOW6464KEY)
+
+#define QUOTE_ME2(x) #x
+#define QUOTE_ME(x) QUOTE_ME2(x)
+
+#define TELEMETRY_BASE_URL L"https://incoming.telemetry.mozilla.org/submit"
+#define TELEMETRY_NAMESPACE L"/firefox-launcher-process"
+#define TELEMETRY_LAUNCHER_PING_DOCTYPE L"/launcher-process-failure"
+#define TELEMETRY_LAUNCHER_PING_VERSION L"/1"
+
+static const wchar_t kUrl[] = TELEMETRY_BASE_URL TELEMETRY_NAMESPACE
+ TELEMETRY_LAUNCHER_PING_DOCTYPE TELEMETRY_LAUNCHER_PING_VERSION L"/";
+static const uint32_t kGuidCharLenWithNul = 39;
+static const uint32_t kGuidCharLenNoBracesNoNul = 36;
+static const mozilla::StaticXREAppData* gAppData;
+
+// Ordinarily, errors are only reported to the Windows Event Log when they are
+// not reported upstream via telemetry (usually due either to telemetry being
+// disabled or to network failure).
+//
+// If `--log-launcher-error` is given at the command line, launcher errors will
+// always be reported to the Windows Event Log, regardless of whether or not
+// they're sent upstream.
+static bool gForceEventLog = false;
+
+namespace {
+
+constexpr wchar_t kEventSourceName[] = L"" MOZ_APP_DISPLAYNAME " Launcher";
+
+struct EventSourceDeleter {
+ using pointer = HANDLE;
+
+ void operator()(pointer aEvtSrc) { ::DeregisterEventSource(aEvtSrc); }
+};
+
+using EventLog = mozilla::UniquePtr<HANDLE, EventSourceDeleter>;
+
+struct SerializedEventData {
+ HRESULT mHr;
+ uint32_t mLine;
+ char mFile[1];
+};
+
+} // anonymous namespace
+
+static void PostErrorToLog(const mozilla::LauncherError& aError) {
+ // This is very bare-bones; just enough to spit out an HRESULT to the
+ // Application event log.
+ EventLog log(::RegisterEventSourceW(nullptr, kEventSourceName));
+
+ if (!log) {
+ return;
+ }
+
+ size_t fileLen = strlen(aError.mFile);
+ size_t dataLen = sizeof(HRESULT) + sizeof(uint32_t) + fileLen;
+ auto evtDataBuf = mozilla::MakeUnique<char[]>(dataLen);
+ SerializedEventData& evtData =
+ *reinterpret_cast<SerializedEventData*>(evtDataBuf.get());
+ evtData.mHr = aError.mError.AsHResult();
+ evtData.mLine = aError.mLine;
+ // Since this is binary data, we're not concerning ourselves with null
+ // terminators.
+ memcpy(evtData.mFile, aError.mFile, fileLen);
+
+ ::ReportEventW(log.get(), EVENTLOG_ERROR_TYPE, 0, aError.mError.AsHResult(),
+ nullptr, 0, dataLen, nullptr, evtDataBuf.get());
+}
+
+#if defined(MOZ_TELEMETRY_REPORTING)
+
+namespace {
+
+// This JSONWriteFunc writes directly to a temp file. By creating this file
+// with the FILE_ATTRIBUTE_TEMPORARY attribute, we hint to the OS that this
+// file is short-lived. The OS will try to avoid flushing it to disk if at
+// all possible.
+class TempFileWriter final : public mozilla::JSONWriteFunc {
+ public:
+ TempFileWriter() : mFailed(false), mSuccessfulHandoff(false) {
+ wchar_t name[MAX_PATH + 1] = {};
+ if (_wtmpnam_s(name)) {
+ mFailed = true;
+ return;
+ }
+
+ mTempFileName = name;
+
+ mTempFile.own(::CreateFileW(name, GENERIC_WRITE, FILE_SHARE_READ, nullptr,
+ CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY, nullptr));
+ if (mTempFile.get() == INVALID_HANDLE_VALUE) {
+ mFailed = true;
+ }
+ }
+
+ ~TempFileWriter() {
+ if (mSuccessfulHandoff) {
+ // It is no longer our responsibility to delete the temp file if we have
+ // successfully handed it off to pingsender.
+ return;
+ }
+
+ mTempFile.reset();
+ ::DeleteFileW(mTempFileName.c_str());
+ }
+
+ explicit operator bool() const { return !mFailed; }
+
+ void Write(const mozilla::Span<const char>& aStr) final {
+ if (mFailed) {
+ return;
+ }
+
+ DWORD bytesWritten = 0;
+ if (!::WriteFile(mTempFile, aStr.data(), aStr.size(), &bytesWritten,
+ nullptr) ||
+ bytesWritten != aStr.size()) {
+ mFailed = true;
+ }
+ }
+
+ const std::wstring& GetFileName() const { return mTempFileName; }
+
+ void SetSuccessfulHandoff() { mSuccessfulHandoff = true; }
+
+ private:
+ bool mFailed;
+ bool mSuccessfulHandoff;
+ std::wstring mTempFileName;
+ nsAutoHandle mTempFile;
+};
+
+using SigMap = mozilla::Vector<std::wstring, 0, InfallibleAllocPolicy>;
+
+} // anonymous namespace
+
+// This is the guideline for maximum string length for telemetry intake
+static const size_t kMaxStrLen = 80;
+
+static mozilla::UniquePtr<char[]> WideToUTF8(const wchar_t* aStr,
+ const size_t aStrLenExclNul) {
+ // Yes, this might not handle surrogate pairs correctly. Let's just let
+ // WideCharToMultiByte fail in that unlikely case.
+ size_t cvtLen = std::min(aStrLenExclNul, kMaxStrLen);
+
+ int numConv = ::WideCharToMultiByte(CP_UTF8, 0, aStr, cvtLen, nullptr, 0,
+ nullptr, nullptr);
+ if (!numConv) {
+ return nullptr;
+ }
+
+ // Include room for the null terminator by adding one
+ auto buf = mozilla::MakeUnique<char[]>(numConv + 1);
+
+ numConv = ::WideCharToMultiByte(CP_UTF8, 0, aStr, cvtLen, buf.get(), numConv,
+ nullptr, nullptr);
+ if (!numConv) {
+ return nullptr;
+ }
+
+ // Add null termination. numConv does not include the terminator, so we don't
+ // subtract 1 when indexing into buf.
+ buf[numConv] = 0;
+
+ return buf;
+}
+
+static mozilla::UniquePtr<char[]> WideToUTF8(const wchar_t* aStr) {
+ return WideToUTF8(aStr, wcslen(aStr));
+}
+
+static mozilla::UniquePtr<char[]> WideToUTF8(const std::wstring& aStr) {
+ return WideToUTF8(aStr.c_str(), aStr.length());
+}
+
+// MinGW does not support the Windows Security Center APIs.
+# if !defined(__MINGW32__)
+
+static mozilla::UniquePtr<char[]> WideToUTF8(const _bstr_t& aStr) {
+ return WideToUTF8(static_cast<const wchar_t*>(aStr), aStr.length());
+}
+
+namespace {
+
+struct ProviderKey {
+ WSC_SECURITY_PROVIDER mProviderType;
+ const char* mKey;
+};
+
+} // anonymous namespace
+
+static bool EnumWSCProductList(RefPtr<IWSCProductList>& aProdList,
+ mozilla::JSONWriter& aJson) {
+ LONG count;
+ HRESULT hr = aProdList->get_Count(&count);
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ // Unlikely, but put a bound on the max length of the output array for the
+ // purposes of telemetry intake.
+ count = std::min(count, 1000L);
+
+ // Record the name(s) of each active registered product in this category
+ for (LONG index = 0; index < count; ++index) {
+ RefPtr<IWscProduct> product;
+ hr = aProdList->get_Item(index, getter_AddRefs(product));
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ WSC_SECURITY_PRODUCT_STATE state;
+ hr = product->get_ProductState(&state);
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ // We only care about products that are active
+ if (state == WSC_SECURITY_PRODUCT_STATE_OFF ||
+ state == WSC_SECURITY_PRODUCT_STATE_SNOOZED ||
+ state == WSC_SECURITY_PRODUCT_STATE_EXPIRED) {
+ continue;
+ }
+
+ _bstr_t bName;
+ hr = product->get_ProductName(bName.GetAddress());
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ auto buf = WideToUTF8(bName);
+ if (!buf) {
+ return false;
+ }
+
+ aJson.StringElement(mozilla::MakeStringSpan(buf.get()));
+ }
+
+ return true;
+}
+
+static const ProviderKey gProvKeys[] = {
+ {WSC_SECURITY_PROVIDER_ANTIVIRUS, "av"},
+ {WSC_SECURITY_PROVIDER_ANTISPYWARE, "antispyware"},
+ {WSC_SECURITY_PROVIDER_FIREWALL, "firewall"}};
+
+static bool AddWscInfo(mozilla::JSONWriter& aJson) {
+ if (!mozilla::IsWin8OrLater()) {
+ // We haven't written anything yet, so we can return true here and continue
+ // capturing data.
+ return true;
+ }
+
+ // We need COM for this. Using ProcessRuntime so that process-global COM
+ // configuration is done correctly
+ mozilla::mscom::ProcessRuntime mscom(
+ mozilla::mscom::ProcessRuntime::ProcessCategory::Launcher);
+ if (!mscom) {
+ // We haven't written anything yet, so we can return true here and continue
+ // capturing data.
+ return true;
+ }
+
+ aJson.StartObjectProperty("security");
+
+ const CLSID clsid = __uuidof(WSCProductList);
+ const IID iid = __uuidof(IWSCProductList);
+
+ for (uint32_t index = 0; index < mozilla::ArrayLength(gProvKeys); ++index) {
+ // NB: A separate instance of IWSCProductList is needed for each distinct
+ // security provider type; MSDN says that we cannot reuse the same object
+ // and call Initialize() to pave over the previous data.
+ RefPtr<IWSCProductList> prodList;
+ HRESULT hr = ::CoCreateInstance(clsid, nullptr, CLSCTX_INPROC_SERVER, iid,
+ getter_AddRefs(prodList));
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ hr = prodList->Initialize(gProvKeys[index].mProviderType);
+ if (FAILED(hr)) {
+ return false;
+ }
+
+ aJson.StartArrayProperty(mozilla::MakeStringSpan(gProvKeys[index].mKey));
+
+ if (!EnumWSCProductList(prodList, aJson)) {
+ return false;
+ }
+
+ aJson.EndArray();
+ }
+
+ aJson.EndObject();
+
+ return true;
+}
+# endif // !defined(__MINGW32__)
+
+// Max array length for telemetry intake.
+static const size_t kMaxArrayLen = 1000;
+
+static bool AddModuleInfo(const nsAutoHandle& aSnapshot,
+ mozilla::JSONWriter& aJson) {
+ if (aSnapshot.get() == INVALID_HANDLE_VALUE) {
+ // We haven't written anything yet, so we can return true here and continue
+ // capturing data.
+ return true;
+ }
+
+ SigMap signatures;
+ size_t moduleCount = 0;
+
+ MODULEENTRY32W module = {sizeof(module)};
+ if (!::Module32FirstW(aSnapshot, &module)) {
+ // We haven't written anything yet, so we can return true here and continue
+ // capturing data.
+ return true;
+ }
+
+ mozilla::glue::BasicDllServices dllServices;
+
+ aJson.StartObjectProperty("modules");
+
+ // For each module, add its version number (or empty string if not present),
+ // followed by an optional index into the signatures array
+ do {
+ ++moduleCount;
+
+ wchar_t leaf[_MAX_FNAME] = {};
+ if (::_wsplitpath_s(module.szExePath, nullptr, 0, nullptr, 0, leaf,
+ mozilla::ArrayLength(leaf), nullptr, 0)) {
+ return false;
+ }
+
+ if (_wcslwr_s(leaf, mozilla::ArrayLength(leaf))) {
+ return false;
+ }
+
+ auto leafUtf8 = WideToUTF8(leaf);
+ if (!leafUtf8) {
+ return false;
+ }
+
+ aJson.StartArrayProperty(mozilla::MakeStringSpan(leafUtf8.get()));
+
+ std::string version;
+ DWORD verInfoSize = ::GetFileVersionInfoSizeW(module.szExePath, nullptr);
+ if (verInfoSize) {
+ auto verInfoBuf = mozilla::MakeUnique<BYTE[]>(verInfoSize);
+
+ if (::GetFileVersionInfoW(module.szExePath, 0, verInfoSize,
+ verInfoBuf.get())) {
+ VS_FIXEDFILEINFO* fixedInfo = nullptr;
+ UINT fixedInfoLen = 0;
+
+ if (::VerQueryValueW(verInfoBuf.get(), L"\\",
+ reinterpret_cast<LPVOID*>(&fixedInfo),
+ &fixedInfoLen)) {
+ std::ostringstream oss;
+ oss << HIWORD(fixedInfo->dwFileVersionMS) << '.'
+ << LOWORD(fixedInfo->dwFileVersionMS) << '.'
+ << HIWORD(fixedInfo->dwFileVersionLS) << '.'
+ << LOWORD(fixedInfo->dwFileVersionLS);
+ version = oss.str();
+ }
+ }
+ }
+
+ aJson.StringElement(version);
+
+ mozilla::Maybe<ptrdiff_t> sigIndex;
+ auto signedBy = dllServices.GetBinaryOrgName(module.szExePath);
+ if (signedBy) {
+ std::wstring strSignedBy(signedBy.get());
+ auto entry = std::find(signatures.begin(), signatures.end(), strSignedBy);
+ if (entry == signatures.end()) {
+ mozilla::Unused << signatures.append(std::move(strSignedBy));
+ entry = &signatures.back();
+ }
+
+ sigIndex = mozilla::Some(entry - signatures.begin());
+ }
+
+ if (sigIndex) {
+ aJson.IntElement(sigIndex.value());
+ }
+
+ aJson.EndArray();
+ } while (moduleCount < kMaxArrayLen && ::Module32NextW(aSnapshot, &module));
+
+ aJson.EndObject();
+
+ aJson.StartArrayProperty("signatures");
+
+ // Serialize each entry in the signatures array
+ for (auto&& itr : signatures) {
+ auto sigUtf8 = WideToUTF8(itr);
+ if (!sigUtf8) {
+ continue;
+ }
+
+ aJson.StringElement(mozilla::MakeStringSpan(sigUtf8.get()));
+ }
+
+ aJson.EndArray();
+
+ return true;
+}
+
+namespace {
+
+struct PingThreadContext {
+ explicit PingThreadContext(const mozilla::LauncherError& aError,
+ const char* aProcessType)
+ : mLauncherError(aError),
+ mModulesSnapshot(::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0)),
+ mProcessType(aProcessType ? aProcessType : "") {}
+ mozilla::LauncherError mLauncherError;
+ nsAutoHandle mModulesSnapshot;
+ std::string mProcessType;
+};
+
+} // anonymous namespace
+
+static bool PrepPing(const PingThreadContext& aContext, const std::wstring& aId,
+ mozilla::JSONWriter& aJson) {
+# if defined(DEBUG)
+ const mozilla::JSONWriter::CollectionStyle style =
+ mozilla::JSONWriter::MultiLineStyle;
+# else
+ const mozilla::JSONWriter::CollectionStyle style =
+ mozilla::JSONWriter::SingleLineStyle;
+# endif // defined(DEBUG)
+
+ aJson.Start(style);
+
+ aJson.StringProperty("type", "launcher-process-failure");
+ aJson.IntProperty("version", 1);
+
+ auto idUtf8 = WideToUTF8(aId);
+ if (idUtf8) {
+ aJson.StringProperty("id", mozilla::MakeStringSpan(idUtf8.get()));
+ }
+
+ time_t now;
+ time(&now);
+ tm gmTm;
+ if (!gmtime_s(&gmTm, &now)) {
+ char isoTimeBuf[32] = {};
+ if (strftime(isoTimeBuf, mozilla::ArrayLength(isoTimeBuf), "%FT%T.000Z",
+ &gmTm)) {
+ aJson.StringProperty("creationDate", isoTimeBuf);
+ }
+ }
+
+ aJson.StringProperty("update_channel", QUOTE_ME(MOZ_UPDATE_CHANNEL));
+
+ if (gAppData) {
+ aJson.StringProperty("build_id",
+ mozilla::MakeStringSpan(gAppData->buildID));
+ aJson.StringProperty("build_version",
+ mozilla::MakeStringSpan(gAppData->version));
+ }
+
+ OSVERSIONINFOEXW osv = {sizeof(osv)};
+ if (::GetVersionExW(reinterpret_cast<OSVERSIONINFOW*>(&osv))) {
+ std::ostringstream oss;
+ oss << osv.dwMajorVersion << "." << osv.dwMinorVersion << "."
+ << osv.dwBuildNumber;
+
+ if (osv.dwMajorVersion == 10 && osv.dwMinorVersion == 0) {
+ // Get the "Update Build Revision" (UBR) value
+ DWORD ubrValue;
+ DWORD ubrValueLen = sizeof(ubrValue);
+ LSTATUS ubrOk =
+ ::RegGetValueW(HKEY_LOCAL_MACHINE,
+ L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
+ L"UBR", RRF_RT_DWORD | RRF_SUBKEY_WOW6464KEY, nullptr,
+ &ubrValue, &ubrValueLen);
+ if (ubrOk == ERROR_SUCCESS) {
+ oss << "." << ubrValue;
+ }
+ }
+
+ if (oss) {
+ aJson.StringProperty("os_version", oss.str());
+ }
+
+ bool isServer = osv.wProductType == VER_NT_DOMAIN_CONTROLLER ||
+ osv.wProductType == VER_NT_SERVER;
+ aJson.BoolProperty("server_os", isServer);
+ }
+
+ WCHAR localeName[LOCALE_NAME_MAX_LENGTH] = {};
+ int localeNameLen =
+ ::GetUserDefaultLocaleName(localeName, mozilla::ArrayLength(localeName));
+ if (localeNameLen) {
+ auto localeNameUtf8 = WideToUTF8(localeName, localeNameLen - 1);
+ if (localeNameUtf8) {
+ aJson.StringProperty("os_locale",
+ mozilla::MakeStringSpan(localeNameUtf8.get()));
+ }
+ }
+
+ SYSTEM_INFO sysInfo;
+ ::GetNativeSystemInfo(&sysInfo);
+ aJson.IntProperty("cpu_arch", sysInfo.wProcessorArchitecture);
+ aJson.IntProperty("num_logical_cpus", sysInfo.dwNumberOfProcessors);
+
+ mozilla::LauncherResult<bool> isAdminWithoutUac =
+ mozilla::IsAdminWithoutUac();
+ if (isAdminWithoutUac.isOk()) {
+ aJson.BoolProperty("is_admin_without_uac", isAdminWithoutUac.unwrap());
+ }
+
+ if (!aContext.mProcessType.empty()) {
+ aJson.StringProperty("process_type", aContext.mProcessType);
+ }
+
+ MEMORYSTATUSEX memStatus = {sizeof(memStatus)};
+ if (::GlobalMemoryStatusEx(&memStatus)) {
+ aJson.StartObjectProperty("memory");
+ aJson.IntProperty("total_phys", memStatus.ullTotalPhys);
+ aJson.IntProperty("avail_phys", memStatus.ullAvailPhys);
+ aJson.IntProperty("avail_page_file", memStatus.ullAvailPageFile);
+ aJson.IntProperty("avail_virt", memStatus.ullAvailVirtual);
+ aJson.EndObject();
+ }
+
+ aJson.StringProperty("xpcom_abi", TARGET_XPCOM_ABI);
+
+ aJson.StartObjectProperty("launcher_error", style);
+
+ std::string srcFileLeaf(aContext.mLauncherError.mFile);
+ // Obtain the leaf name of the file for privacy reasons
+ // (In case this is somebody's local build)
+ auto pos = srcFileLeaf.find_last_of("/\\");
+ if (pos != std::string::npos) {
+ srcFileLeaf = srcFileLeaf.substr(pos + 1);
+ }
+
+ aJson.StringProperty("source_file", srcFileLeaf);
+
+ aJson.IntProperty("source_line", aContext.mLauncherError.mLine);
+ aJson.IntProperty("hresult", aContext.mLauncherError.mError.AsHResult());
+
+# if defined(NIGHTLY_BUILD)
+ if (aContext.mLauncherError.mDetourError.isSome()) {
+ static const char* kHexMap = "0123456789abcdef";
+ char hexStr[sizeof(mozilla::DetourError::mOrigBytes) * 2 + 1];
+ int cnt = 0;
+ for (uint8_t byte : aContext.mLauncherError.mDetourError->mOrigBytes) {
+ hexStr[cnt++] = kHexMap[(byte >> 4) & 0x0f];
+ hexStr[cnt++] = kHexMap[byte & 0x0f];
+ }
+ hexStr[cnt] = 0;
+ aJson.StringProperty("detour_orig_bytes", hexStr);
+ }
+# endif // defined(NIGHTLY_BUILD)
+
+ aJson.EndObject();
+
+# if !defined(__MINGW32__)
+ if (!AddWscInfo(aJson)) {
+ return false;
+ }
+# endif // !defined(__MINGW32__)
+
+ if (!AddModuleInfo(aContext.mModulesSnapshot, aJson)) {
+ return false;
+ }
+
+ aJson.End();
+
+ return true;
+}
+
+static bool DoSendPing(const PingThreadContext& aContext) {
+ TempFileWriter tempFile;
+ mozilla::JSONWriter json(tempFile);
+
+ UUID uuid;
+ if (::UuidCreate(&uuid) != RPC_S_OK) {
+ return false;
+ }
+
+ wchar_t guidBuf[kGuidCharLenWithNul] = {};
+ if (::StringFromGUID2(uuid, guidBuf, kGuidCharLenWithNul) !=
+ kGuidCharLenWithNul) {
+ return false;
+ }
+
+ // Strip the curly braces off of the guid
+ std::wstring guidNoBraces(guidBuf + 1, kGuidCharLenNoBracesNoNul);
+
+ // Populate json with the ping information
+ if (!PrepPing(aContext, guidNoBraces, json)) {
+ return false;
+ }
+
+ // Obtain the name of the temp file that we have written
+ const std::wstring& fileName = tempFile.GetFileName();
+
+ // Using the path to our executable binary, construct the path to
+ // pingsender.exe
+ mozilla::UniquePtr<wchar_t[]> exePath(mozilla::GetFullBinaryPath());
+
+ wchar_t drive[_MAX_DRIVE] = {};
+ wchar_t dir[_MAX_DIR] = {};
+ if (_wsplitpath_s(exePath.get(), drive, mozilla::ArrayLength(drive), dir,
+ mozilla::ArrayLength(dir), nullptr, 0, nullptr, 0)) {
+ return false;
+ }
+
+ wchar_t pingSenderPath[MAX_PATH + 1] = {};
+ if (_wmakepath_s(pingSenderPath, mozilla::ArrayLength(pingSenderPath), drive,
+ dir, L"pingsender", L"exe")) {
+ return false;
+ }
+
+ // Construct the telemetry URL
+ wchar_t urlBuf[mozilla::ArrayLength(kUrl) + kGuidCharLenNoBracesNoNul] = {};
+ if (wcscpy_s(urlBuf, kUrl)) {
+ return false;
+ }
+
+ if (wcscat_s(urlBuf, guidNoBraces.c_str())) {
+ return false;
+ }
+
+ // Now build the command line arguments to pingsender
+ wchar_t* pingSenderArgv[] = {pingSenderPath, urlBuf,
+ const_cast<wchar_t*>(fileName.c_str())};
+
+ mozilla::UniquePtr<wchar_t[]> pingSenderCmdLine(mozilla::MakeCommandLine(
+ mozilla::ArrayLength(pingSenderArgv), pingSenderArgv));
+
+ // Now start pingsender to handle the rest
+ PROCESS_INFORMATION pi;
+
+ STARTUPINFOW si = {sizeof(si)};
+ si.dwFlags = STARTF_USESHOWWINDOW;
+ si.wShowWindow = SW_HIDE;
+
+ if (!::CreateProcessW(pingSenderPath, pingSenderCmdLine.get(), nullptr,
+ nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) {
+ return false;
+ }
+
+ tempFile.SetSuccessfulHandoff();
+
+ nsAutoHandle proc(pi.hProcess);
+ nsAutoHandle thread(pi.hThread);
+
+ return true;
+}
+
+static unsigned __stdcall SendPingThread(void* aContext) {
+ mozilla::UniquePtr<PingThreadContext> context(
+ reinterpret_cast<PingThreadContext*>(aContext));
+
+ if (!DoSendPing(*context) || gForceEventLog) {
+ PostErrorToLog(context->mLauncherError);
+ }
+
+ return 0;
+}
+
+#endif // defined(MOZ_TELEMETRY_REPORTING)
+
+static bool SendPing(const mozilla::LauncherError& aError,
+ const char* aProcessType) {
+#if defined(MOZ_TELEMETRY_REPORTING)
+# if defined(MOZ_LAUNCHER_PROCESS)
+ mozilla::LauncherRegistryInfo regInfo;
+ mozilla::LauncherResult<bool> telemetryEnabled = regInfo.IsTelemetryEnabled();
+ if (telemetryEnabled.isErr() || !telemetryEnabled.unwrap()) {
+ // Do not send anything if telemetry has been opted out
+ return false;
+ }
+# endif // defined(MOZ_LAUNCHER_PROCESS)
+
+ // We send this ping when the launcher process fails. After we start the
+ // SendPingThread, this thread falls back from running as the launcher process
+ // to running as the browser main thread. Once this happens, it will be unsafe
+ // to set up PoisonIOInterposer (since we have already spun up a background
+ // thread).
+ mozilla::SaveToEnv("MOZ_DISABLE_POISON_IO_INTERPOSER=1");
+
+ // Capture aError and our module list into context for processing on another
+ // thread.
+ auto thdParam = mozilla::MakeUnique<PingThreadContext>(aError, aProcessType);
+
+ // The ping does a lot of file I/O. Since we want this thread to continue
+ // executing browser startup, we should gather that information on a
+ // background thread.
+ uintptr_t thdHandle =
+ _beginthreadex(nullptr, 0, &SendPingThread, thdParam.get(),
+ STACK_SIZE_PARAM_IS_A_RESERVATION, nullptr);
+ if (!thdHandle) {
+ return false;
+ }
+
+ // We have handed off thdParam to the background thread
+ mozilla::Unused << thdParam.release();
+
+ ::CloseHandle(reinterpret_cast<HANDLE>(thdHandle));
+ return true;
+#else
+ return false;
+#endif
+}
+
+namespace mozilla {
+
+void HandleLauncherError(const LauncherError& aError,
+ const char* aProcessType) {
+#if defined(MOZ_LAUNCHER_PROCESS)
+ LauncherRegistryInfo regInfo;
+ Unused << regInfo.DisableDueToFailure();
+#endif // defined(MOZ_LAUNCHER_PROCESS)
+
+ if (!SendPing(aError, aProcessType)) {
+ // couldn't (or shouldn't) send telemetry; fall back to event log
+ PostErrorToLog(aError);
+ }
+}
+
+void SetLauncherErrorAppData(const StaticXREAppData& aAppData) {
+ gAppData = &aAppData;
+}
+
+void SetLauncherErrorForceEventLog() { gForceEventLog = true; }
+
+} // namespace mozilla