Bug 1944416: Restore individual tabs from closed groups in closed windows r=dao,sessi...
[gecko.git] / browser / app / winlauncher / SameBinary.h
blobf7dd756ec2d63626a6c1b2594ec5f2ed91ade055
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_SameBinary_h
8 #define mozilla_SameBinary_h
10 #include "mozilla/WinHeaderOnlyUtils.h"
11 #include "mozilla/NativeNt.h"
12 #include "nsWindowsHelpers.h"
14 namespace mozilla {
16 class ProcessImagePath final {
17 PathType mType;
18 LauncherVoidResult mLastError;
20 // Using a larger buffer because an NT path may exceed MAX_PATH.
21 WCHAR mPathBuffer[(MAX_PATH * 2) + 1];
23 public:
24 // Initialize with an NT path string of a given process handle
25 explicit ProcessImagePath(const nsAutoHandle& aProcess)
26 : mType(PathType::eNtPath), mLastError(Ok()) {
27 DWORD len = std::size(mPathBuffer);
28 if (!::QueryFullProcessImageNameW(aProcess.get(), PROCESS_NAME_NATIVE,
29 mPathBuffer, &len)) {
30 mLastError = LAUNCHER_ERROR_FROM_LAST();
31 return;
35 // Initizlize with a DOS path string of a given imagebase address
36 explicit ProcessImagePath(HMODULE aImageBase)
37 : mType(PathType::eDosPath), mLastError(Ok()) {
38 DWORD len =
39 ::GetModuleFileNameW(aImageBase, mPathBuffer, std::size(mPathBuffer));
40 if (!len || len == std::size(mPathBuffer)) {
41 mLastError = LAUNCHER_ERROR_FROM_LAST();
42 return;
46 bool IsError() const { return mLastError.isErr(); }
48 const WindowsErrorType& GetError() const { return mLastError.inspectErr(); }
50 FileUniqueId GetId() const { return FileUniqueId(mPathBuffer, mType); }
52 bool CompareNtPaths(const ProcessImagePath& aOther) const {
53 if (mLastError.isErr() || aOther.mLastError.isErr() ||
54 mType != PathType::eNtPath || aOther.mType != PathType::eNtPath) {
55 return false;
58 UNICODE_STRING path1, path2;
59 ::RtlInitUnicodeString(&path1, mPathBuffer);
60 ::RtlInitUnicodeString(&path2, aOther.mPathBuffer);
61 return !!::RtlEqualUnicodeString(&path1, &path2, TRUE);
65 enum class ImageFileCompareOption {
66 Default,
67 CompareNtPathsOnly,
70 static inline mozilla::LauncherResult<bool> IsSameBinaryAsParentProcess(
71 ImageFileCompareOption aOption = ImageFileCompareOption::Default) {
72 mozilla::LauncherResult<DWORD> parentPid = mozilla::nt::GetParentProcessId();
73 if (parentPid.isErr()) {
74 return parentPid.propagateErr();
77 nsAutoHandle parentProcess(::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION,
78 FALSE, parentPid.unwrap()));
79 if (!parentProcess.get()) {
80 DWORD err = ::GetLastError();
81 if (err == ERROR_INVALID_PARAMETER || err == ERROR_ACCESS_DENIED) {
82 // In the ERROR_INVALID_PARAMETER case, the process identified by
83 // parentPid has already exited. This is a common case when the parent
84 // process is not Firefox, thus we should return false instead of erroring
85 // out.
86 // The ERROR_ACCESS_DENIED case can happen when the parent process is
87 // something that we don't have permission to query. For example, we may
88 // encounter this when Firefox is launched by the Windows Task Scheduler.
89 return false;
92 return LAUNCHER_ERROR_FROM_WIN32(err);
95 ProcessImagePath parentExe(parentProcess);
96 if (parentExe.IsError()) {
97 return ::mozilla::Err(parentExe.GetError());
100 if (aOption == ImageFileCompareOption::Default) {
101 bool skipFileIdComparison = false;
103 FileUniqueId id1 = parentExe.GetId();
104 if (id1.IsError()) {
105 // We saw a number of Win7 users failed to call NtOpenFile with
106 // STATUS_OBJECT_PATH_NOT_FOUND for an unknown reason. In this
107 // particular case, we fall back to the logic to compare NT path
108 // strings instead of a file id which will not fail because we don't
109 // need to open a file handle.
110 #if !defined(STATUS_OBJECT_PATH_NOT_FOUND)
111 constexpr NTSTATUS STATUS_OBJECT_PATH_NOT_FOUND = 0xc000003a;
112 #endif
113 const LauncherError& err = id1.GetError();
114 if (err.mError !=
115 WindowsError::FromNtStatus(STATUS_OBJECT_PATH_NOT_FOUND)) {
116 return ::mozilla::Err(err);
119 skipFileIdComparison = true;
122 if (!skipFileIdComparison) {
123 ProcessImagePath ourExe(nullptr);
124 if (ourExe.IsError()) {
125 return ::mozilla::Err(ourExe.GetError());
128 FileUniqueId id2 = ourExe.GetId();
129 if (id2.IsError()) {
130 return ::mozilla::Err(id2.GetError());
132 return id1 == id2;
136 nsAutoHandle ourProcess(::GetCurrentProcess());
137 ProcessImagePath ourExeNt(ourProcess);
138 if (ourExeNt.IsError()) {
139 return ::mozilla::Err(ourExeNt.GetError());
141 return parentExe.CompareNtPaths(ourExeNt);
144 } // namespace mozilla
146 #endif // mozilla_SameBinary_h