Bug 1698593 - Uninstall the Background Update Task from the Firefox uninstaller r=agashlin

Differential Revision: https://phabricator.services.mozilla.com/D114667
This commit is contained in:
Kirk Steuber 2021-05-10 19:47:14 +00:00
Родитель a2bfb965fd
Коммит 9d3986274c
5 изменённых файлов: 105 добавлений и 43 удалений

Просмотреть файл

@ -610,8 +610,12 @@ Section "Uninstall"
DeleteRegValue HKCU ${MOZ_LAUNCHER_SUBKEY} "$INSTDIR\${FileMainEXE}|Telemetry"
!endif
; Uninstall the default browser agent scheduled task.
; This also removes the registry entries it creates.
; Uninstall the default browser agent scheduled task and all other scheduled
; tasks registered by Firefox.
; This also removes the registry entries that the WDBA creates.
; One of the scheduled tasks that this will remove is the Background Update
; Task. Ideally, this will eventually be changed so that it doesn't rely on
; the WDBA. See Bug 1710143.
ExecWait '"$INSTDIR\default-browser-agent.exe" uninstall $AppUserModelID'
${RemoveDefaultBrowserAgentShortcut}

Просмотреть файл

@ -9,6 +9,7 @@
#include <string>
#include <time.h>
#include <comutil.h>
#include <taskschd.h>
#include "readstrings.h"
@ -72,14 +73,14 @@ HRESULT RegisterTask(const wchar_t* uniqueToken,
BSTR startTime /* = nullptr */) {
// Do data migration during the task installation. This might seem like it
// belongs in UpdateTask, but we want to be able to call
// RemoveTask();
// RemoveTasks();
// RegisterTask();
// and still have data migration happen. Also, UpdateTask calls this function,
// so migration will still get run in that case.
MaybeMigrateCurrentDefault();
// Make sure we don't try to register a task that already exists.
RemoveTask(uniqueToken);
RemoveTasks(uniqueToken, WhichTasks::WdbaTaskOnly);
// If we create a folder and then fail to create the task, we need to
// remember to delete the folder so that whatever set of permissions it ends
@ -301,7 +302,21 @@ HRESULT UpdateTask(const wchar_t* uniqueToken) {
return RegisterTask(uniqueToken, startTime.get());
}
HRESULT RemoveTask(const wchar_t* uniqueToken) {
bool EndsWith(const wchar_t* string, const wchar_t* suffix) {
size_t string_len = wcslen(string);
size_t suffix_len = wcslen(suffix);
if (suffix_len > string_len) {
return false;
}
const wchar_t* substring = string + string_len - suffix_len;
return wcscmp(substring, suffix) == 0;
}
HRESULT RemoveTasks(const wchar_t* uniqueToken, WhichTasks tasksToRemove) {
if (!uniqueToken || wcslen(uniqueToken) == 0) {
return E_INVALIDARG;
}
RefPtr<ITaskService> scheduler;
HRESULT hr = S_OK;
ENSURE(CoCreateInstance(CLSID_TaskScheduler, nullptr, CLSCTX_INPROC_SERVER,
@ -310,7 +325,7 @@ HRESULT RemoveTask(const wchar_t* uniqueToken) {
ENSURE(scheduler->Connect(VARIANT{}, VARIANT{}, VARIANT{}, VARIANT{}));
RefPtr<ITaskFolder> taskFolder;
BStrPtr folderBStr = BStrPtr(SysAllocString(kTaskVendor));
BStrPtr folderBStr(SysAllocString(kTaskVendor));
hr = scheduler->GetFolder(folderBStr.get(), getter_AddRefs(taskFolder));
if (FAILED(hr)) {
@ -323,28 +338,61 @@ HRESULT RemoveTask(const wchar_t* uniqueToken) {
}
}
std::wstring taskName(kTaskName);
taskName += uniqueToken;
BStrPtr taskNameBStr = BStrPtr(SysAllocString(taskName.c_str()));
hr = taskFolder->DeleteTask(taskNameBStr.get(), 0);
if (FAILED(hr)) {
if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) {
// Failing to delete the task because it didn't exist also isn't fatal.
return S_OK;
} else {
return hr;
}
}
// See if there are any tasks left in our folder, and delete it if not.
RefPtr<IRegisteredTaskCollection> tasksInFolder;
ENSURE(taskFolder->GetTasks(TASK_ENUM_HIDDEN, getter_AddRefs(tasksInFolder)));
LONG numTasks = 0;
ENSURE(tasksInFolder->get_Count(&numTasks));
if (numTasks <= 0) {
std::wstring WdbaTaskName(kTaskName);
WdbaTaskName += uniqueToken;
// This will be set to the last error that we encounter while deleting tasks.
// This allows us to keep attempting to remove the remaining tasks, even if
// we encounter an error, while still preserving what error we encountered so
// we can return it from this function.
HRESULT deleteResult = S_OK;
// Set to true if we intentionally skip any tasks.
bool tasksSkipped = false;
for (LONG i = 0; i < numTasks; ++i) {
RefPtr<IRegisteredTask> task;
// IRegisteredTaskCollection's are 1-indexed.
hr = tasksInFolder->get_Item(_variant_t(i + 1), getter_AddRefs(task));
if (FAILED(hr)) {
deleteResult = hr;
continue;
}
BSTR taskName;
hr = task->get_Name(&taskName);
if (FAILED(hr)) {
deleteResult = hr;
continue;
}
// Automatically free taskName when we are done with it.
BStrPtr uniqueTaskName(taskName);
if (tasksToRemove == WhichTasks::WdbaTaskOnly) {
if (WdbaTaskName.compare(taskName) != 0) {
tasksSkipped = true;
continue;
}
} else { // tasksToRemove == WhichTasks::AllTasksForInstallation
if (!EndsWith(taskName, uniqueToken)) {
tasksSkipped = true;
continue;
}
}
hr = taskFolder->DeleteTask(taskName, 0 /* flags */);
if (FAILED(hr)) {
deleteResult = hr;
}
}
// If we successfully removed all the tasks, delete the folder too.
if (!tasksSkipped && SUCCEEDED(deleteResult)) {
RefPtr<ITaskFolder> rootFolder;
BStrPtr rootFolderBStr = BStrPtr(SysAllocString(L"\\"));
ENSURE(
@ -352,5 +400,5 @@ HRESULT RemoveTask(const wchar_t* uniqueToken) {
ENSURE(rootFolder->DeleteFolder(folderBStr.get(), 0));
}
return hr;
return deleteResult;
}

Просмотреть файл

@ -14,6 +14,11 @@
// the install hash string.
HRESULT RegisterTask(const wchar_t* uniqueToken, BSTR startTime = nullptr);
HRESULT UpdateTask(const wchar_t* uniqueToken);
HRESULT RemoveTask(const wchar_t* uniqueToken);
enum class WhichTasks {
WdbaTaskOnly,
AllTasksForInstallation,
};
HRESULT RemoveTasks(const wchar_t* uniqueToken, WhichTasks tasksToRemove);
#endif // __DEFAULT_BROWSER_AGENT_SCHEDULED_TASK_H__

Просмотреть файл

@ -269,31 +269,35 @@ int wmain(int argc, wchar_t** argv) {
// The uninstall and unregister commands are allowed even if the policy
// disabling the task is set, so that uninstalls and updates always work.
// Similarly, debug commands are always allowed.
if (!wcscmp(argv[1], L"uninstall") || !wcscmp(argv[1], L"unregister-task")) {
if (!wcscmp(argv[1], L"uninstall")) {
if (argc < 3 || !argv[2]) {
return E_INVALIDARG;
}
if (!wcscmp(argv[1], L"uninstall")) {
// We aren't actually going to check whether we got the mutex here.
// Ideally we would acquire it since we are about to access the registry,
// so we would like to block simultaneous users of our registry key.
// But there are two reasons that it is preferable to ignore a mutex
// wait timeout here:
// 1. If we fail to uninstall our prefixed registry entries, the
// registry key containing them will never be removed, even when the
// last installation is uninstalled.
// 2. If we timed out waiting on the mutex, it implies that there are
// other installations. If there are other installations, there will
// be other prefixed registry entries. If there are other prefixed
// registry entries, we won't remove the whole key or touch the
// unprefixed entries during uninstallation. Therefore, we should
// be able to safely uninstall without stepping on anyone's toes.
regMutex.Acquire();
// We aren't actually going to check whether we got the mutex here.
// Ideally we would acquire it since we are about to access the registry,
// so we would like to block simultaneous users of our registry key.
// But there are two reasons that it is preferable to ignore a mutex
// wait timeout here:
// 1. If we fail to uninstall our prefixed registry entries, the
// registry key containing them will never be removed, even when the
// last installation is uninstalled.
// 2. If we timed out waiting on the mutex, it implies that there are
// other installations. If there are other installations, there will
// be other prefixed registry entries. If there are other prefixed
// registry entries, we won't remove the whole key or touch the
// unprefixed entries during uninstallation. Therefore, we should
// be able to safely uninstall without stepping on anyone's toes.
regMutex.Acquire();
RemoveAllRegistryEntries();
RemoveAllRegistryEntries();
return RemoveTasks(argv[2], WhichTasks::AllTasksForInstallation);
} else if (!wcscmp(argv[1], L"unregister-task")) {
if (argc < 3 || !argv[2]) {
return E_INVALIDARG;
}
return RemoveTask(argv[2]);
return RemoveTasks(argv[2], WhichTasks::WdbaTaskOnly);
} else if (!wcscmp(argv[1], L"debug-remote-disabled")) {
int disabled = IsAgentRemoteDisabled();
std::cerr << "default-browser-agent: IsAgentRemoteDisabled: " << disabled

Просмотреть файл

@ -50,6 +50,7 @@ LOCAL_INCLUDES += [
OS_LIBS += [
"advapi32",
"comsupp",
"kernel32",
"netapi32",
"ole32",