Bug 1826375 - Part 1: Remove C++ `do-task` implementation for the Default Agent. r=mhughes,nalexander,sylvestre

Differential Revision: https://phabricator.services.mozilla.com/D203488
This commit is contained in:
Nicholas Rishel 2024-03-08 19:27:22 +00:00
Родитель b623c0efa1
Коммит 2657cfb14b
14 изменённых файлов: 12 добавлений и 751 удалений

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

@ -85,8 +85,4 @@ if CONFIG["MOZ_WIDGET_TOOLKIT"] in ("windows", "gtk", "cocoa"):
if CONFIG["MOZ_WIDGET_TOOLKIT"] in ("windows", "gtk"):
DEFINES["MENUBAR_CAN_AUTOHIDE"] = 1
if CONFIG["OS_ARCH"] == "WINNT" and CONFIG["MOZ_DEFAULT_BROWSER_AGENT"]:
# Impacts `/toolkit/content/license.html`.
DEFINES["MOZ_DEFAULT_BROWSER_AGENT"] = True
JAR_MANIFESTS += ["jar.mn"]

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

@ -32,7 +32,7 @@ ac_add_options --enable-disk-remnant-avoidance
ac_add_options --disable-webrtc # Bug 1393901
ac_add_options --disable-geckodriver # Bug 1489320
ac_add_options --disable-update-agent # Bug 1561797
ac_add_options --disable-default-browser-agent # WinToast does not build on mingw
ac_add_options --disable-default-browser-agent # Relies on toast notifications which don't build on mingw.
ac_add_options --disable-notification-server # Toast notifications don't build on mingw.
# Find our toolchain

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

@ -32,7 +32,7 @@ ac_add_options --enable-disk-remnant-avoidance
ac_add_options --disable-webrtc # Bug 1393901
ac_add_options --disable-geckodriver # Bug 1489320
ac_add_options --disable-update-agent # Bug 1561797
ac_add_options --disable-default-browser-agent # WinToast does not build on mingw
ac_add_options --disable-default-browser-agent # Relies on toast notifications which don't build on mingw.
ac_add_options --disable-notification-server # Toast notifications don't build on mingw.
# Find our toolchain

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

@ -61,10 +61,7 @@
@RESPATH@/update.locale
@RESPATH@/updater.ini
#endif
#ifdef MOZ_DEFAULT_BROWSER_AGENT
@RESPATH@/defaultagent.ini
#endif
#if defined(MOZ_UPDATE_AGENT) || defined(MOZ_DEFAULT_BROWSER_AGENT)
#if defined(MOZ_UPDATE_AGENT)
@RESPATH@/locale.ini
#endif

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

@ -153,9 +153,6 @@
<li><a href="about:license#validator">Validator License</a></li>
<li><a href="about:license#vtune">VTune License</a></li>
<li><a href="about:license#webrtc">WebRTC License</a></li>
#ifdef MOZ_DEFAULT_BROWSER_AGENT
<li><a href="about:license#wintoast">WinToast License</a></li>
#endif
<li><a href="about:license#x264">x264 License</a></li>
<li><a href="about:license#xiph">Xiph.org Foundation License</a></li>
</ul>
@ -3619,9 +3616,6 @@ SOFTWARE.
<li><code>third_party/rust/synstructure</code></li>
<li><code>third_party/rust/void</code></li>
<li><code>js/src/zydis</code> (unless otherwise specified)</li>
#ifdef MOZ_DEFAULT_BROWSER_AGENT
<li><code>third_party/WinToast</code> unless otherwise specified</li>
#endif
</ul>
See the individual LICENSE files or headers for copyright owners.</p>

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

@ -17,8 +17,6 @@ const EXIT_CODE = {
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
setTimeout: "resource://gre/modules/Timer.sys.mjs",
BackgroundTasksUtils: "resource://gre/modules/BackgroundTasksUtils.sys.mjs",
NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
// eslint-disable-next-line mozilla/no-browser-refs-in-toolkit
ShellService: "resource:///modules/ShellService.sys.mjs",
});
@ -157,25 +155,11 @@ export async function runBackgroundTask(commandLine) {
lazy.log.info(`Running do-task with AUMID "${aumid}"`);
let cppFallback = false;
try {
await lazy.BackgroundTasksUtils.enableNimbus(commandLine);
cppFallback =
lazy.NimbusFeatures.defaultAgent.getVariable("cppFallback");
} catch (e) {
lazy.log.error(`Error enabling nimbus: ${e}`);
}
try {
if (!cppFallback) {
lazy.log.info("Running JS do-task.");
await runWithRegistryLocked(async () => {
await doTask(defaultAgent, force);
});
} else {
lazy.log.info("Running C++ do-task.");
defaultAgent.doTask(aumid, force);
}
lazy.log.info("Running JS do-task.");
await runWithRegistryLocked(async () => {
await doTask(defaultAgent, force);
});
} catch (e) {
if (e.message) {
lazy.log.error(e.message);

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

@ -7,11 +7,8 @@
#include <windows.h>
#include <shlwapi.h>
#include <objbase.h>
#include <string.h>
#include <vector>
#include "nsAutoRef.h"
#include "nsDebug.h"
#include "nsProxyRelease.h"
#include "nsWindowsHelpers.h"
#include "nsString.h"
@ -303,62 +300,6 @@ DefaultAgent::Uninstall(const nsAString& aUniqueToken) {
return NS_OK;
}
NS_IMETHODIMP
DefaultAgent::DoTask(const nsAString& aUniqueToken, const bool aForce) {
// Acquire() has a short timeout. Since this runs in the background, we
// could use a longer timeout in this situation. However, if another
// installation's agent is already running, it will update CurrentDefault,
// possibly send a ping, and possibly show a notification.
// Once all that has happened, there is no real reason to do it again. We
// only send one ping per day, so we aren't going to do that again. And
// the only time we ever show a second notification is 7 days after the
// first one, so we aren't going to do that again either.
// If the other process didn't take those actions, there is no reason that
// this process would take them.
// If the other process fails, this one will most likely fail for the same
// reason.
// So we'll just bail if we can't get the mutex quickly.
RegistryMutex regMutex;
if (!regMutex.Acquire()) {
return NS_ERROR_NOT_AVAILABLE;
}
// Check that Firefox ran recently, if not then stop here.
// Also stop if no timestamp was found, which most likely indicates
// that Firefox was not yet run.
bool ranRecently = false;
if (!aForce && (!CheckIfAppRanRecently(&ranRecently) || !ranRecently)) {
return NS_ERROR_FAILURE;
}
DefaultBrowserResult defaultBrowserResult = GetDefaultBrowserInfo();
DefaultBrowserInfo browserInfo{};
if (defaultBrowserResult.isOk()) {
browserInfo = defaultBrowserResult.unwrap();
} else {
browserInfo.currentDefaultBrowser = Browser::Error;
browserInfo.previousDefaultBrowser = Browser::Error;
}
DefaultPdfResult defaultPdfResult = GetDefaultPdfInfo();
DefaultPdfInfo pdfInfo{};
if (defaultPdfResult.isOk()) {
pdfInfo = defaultPdfResult.unwrap();
} else {
pdfInfo.currentDefaultPdf = PDFHandler::Error;
}
NotificationActivities activitiesPerformed;
// We block while waiting for the notification which prevents STA thread
// callbacks from running as the event loop won't run. Moving notification
// handling to an MTA thread prevents this conflict.
activitiesPerformed = MaybeShowNotification(
browserInfo, PromiseFlatString(aUniqueToken).get(), aForce);
HRESULT hr = SendDefaultAgentPing(browserInfo, pdfInfo, activitiesPerformed);
return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
DefaultAgent::AppRanRecently(bool* aRanRecently) {
bool ranRecently = false;

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

@ -184,17 +184,6 @@ BrowserResult TryGetReplacePreviousDefaultBrowser(Browser currentDefault) {
return GetBrowserFromString(previousDefault);
}
DefaultBrowserResult GetDefaultBrowserInfo() {
DefaultBrowserInfo browserInfo;
MOZ_TRY_VAR(browserInfo.currentDefaultBrowser, TryGetDefaultBrowser());
MOZ_TRY_VAR(
browserInfo.previousDefaultBrowser,
TryGetReplacePreviousDefaultBrowser(browserInfo.currentDefaultBrowser));
return browserInfo;
}
// We used to prefix this key with the installation directory, but that causes
// problems with our new "only one ping per day across installs" restriction.
// To make sure all installations use consistent data, the value's name is

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

@ -10,7 +10,6 @@
#include <string>
#include "mozilla/DefineEnum.h"
#include "mozilla/WinHeaderOnlyUtils.h"
namespace mozilla::default_agent {
@ -24,9 +23,6 @@ struct DefaultBrowserInfo {
Browser previousDefaultBrowser;
};
using DefaultBrowserResult = mozilla::WindowsErrorResult<DefaultBrowserInfo>;
DefaultBrowserResult GetDefaultBrowserInfo();
Browser GetDefaultBrowser();
Browser GetReplacePreviousDefaultBrowser(Browser currentBrowser);

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

@ -7,36 +7,10 @@
#include "Notification.h"
#include <shlwapi.h>
#include <wchar.h>
#include <windows.h>
#include <winnt.h>
#include "mozilla/ArrayUtils.h"
#include "mozilla/CmdLineAndEnvUtils.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/mscom/EnsureMTA.h"
#include "mozilla/intl/FileSource.h"
#include "mozilla/intl/Localization.h"
#include "mozilla/ShellHeaderOnlyUtils.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Unused.h"
#include "mozilla/WinHeaderOnlyUtils.h"
#include "nsString.h"
#include "nsTArray.h"
#include "nsWindowsHelpers.h"
#include "readstrings.h"
#include "updatererrors.h"
#include "WindowsDefaultBrowser.h"
#include "common.h"
#include "DefaultBrowser.h"
#include "EventLog.h"
#include "Registry.h"
#include "SetDefaultBrowser.h"
#include "wintoastlib.h"
using mozilla::intl::Localization;
#include "nsLiteralString.h"
#define SEVEN_DAYS_IN_SECONDS (7 * 24 * 60 * 60)
@ -49,574 +23,6 @@ using mozilla::intl::Localization;
namespace mozilla::default_agent {
bool FirefoxInstallIsEnglish();
static bool SetInitialNotificationShown(bool wasShown) {
return !RegistrySetValueBool(IsPrefixed::Unprefixed,
L"InitialNotificationShown", wasShown)
.isErr();
}
static bool GetInitialNotificationShown() {
return RegistryGetValueBool(IsPrefixed::Unprefixed,
L"InitialNotificationShown")
.unwrapOr(mozilla::Some(false))
.valueOr(false);
}
static bool ResetInitialNotificationShown() {
return RegistryDeleteValue(IsPrefixed::Unprefixed,
L"InitialNotificationShown")
.isOk();
}
static bool SetFollowupNotificationShown(bool wasShown) {
return !RegistrySetValueBool(IsPrefixed::Unprefixed,
L"FollowupNotificationShown", wasShown)
.isErr();
}
static bool GetFollowupNotificationShown() {
return RegistryGetValueBool(IsPrefixed::Unprefixed,
L"FollowupNotificationShown")
.unwrapOr(mozilla::Some(false))
.valueOr(false);
}
static bool SetFollowupNotificationSuppressed(bool value) {
return !RegistrySetValueBool(IsPrefixed::Unprefixed,
L"FollowupNotificationSuppressed", value)
.isErr();
}
static bool GetFollowupNotificationSuppressed() {
return RegistryGetValueBool(IsPrefixed::Unprefixed,
L"FollowupNotificationSuppressed")
.unwrapOr(mozilla::Some(false))
.valueOr(false);
}
// Returns 0 if no value is set.
static ULONGLONG GetFollowupNotificationRequestTime() {
return RegistryGetValueQword(IsPrefixed::Unprefixed, L"FollowupRequestTime")
.unwrapOr(mozilla::Some(0))
.valueOr(0);
}
// Returns false if no value is set.
static bool GetPrefSetDefaultBrowserUserChoice() {
return RegistryGetValueBool(IsPrefixed::Prefixed,
L"SetDefaultBrowserUserChoice")
.unwrapOr(mozilla::Some(false))
.valueOr(false);
}
struct ToastStrings {
mozilla::UniquePtr<wchar_t[]> text1;
mozilla::UniquePtr<wchar_t[]> text2;
mozilla::UniquePtr<wchar_t[]> action1;
mozilla::UniquePtr<wchar_t[]> action2;
mozilla::UniquePtr<wchar_t[]> relImagePath;
};
struct Strings {
// Toast notification button text is hard to localize because it tends to
// overflow. Thus, we have 3 different toast notifications:
// - The initial notification, which includes a button with text like
// "Ask me later". Since we cannot easily localize this, we will display
// it only in English.
// - The followup notification, to be shown if the user clicked "Ask me
// later". Since we only have that button in English, we only need this
// notification in English.
// - The localized notification, which has much shorter button text to
// (hopefully) prevent overflow: just "Yes" and "No". Since we no longer
// have an "Ask me later" button, a followup localized notification is not
// needed.
ToastStrings initialToast;
ToastStrings followupToast;
ToastStrings localizedToast;
// Returned pointer points within this struct and should not be freed.
const ToastStrings* GetToastStrings(NotificationType whichToast,
bool englishStrings) const {
if (!englishStrings) {
return &localizedToast;
}
if (whichToast == NotificationType::Initial) {
return &initialToast;
}
return &followupToast;
}
};
// Gets all strings out of the relevant INI files.
// Returns true on success, false on failure
static bool GetStrings(Strings& strings) {
mozilla::UniquePtr<wchar_t[]> installPath;
bool success = GetInstallDirectory(installPath);
if (!success) {
LOG_ERROR_MESSAGE(L"Failed to get install directory when getting strings");
return false;
}
nsTArray<nsCString> resIds = {"branding/brand.ftl"_ns,
"browser/backgroundtasks/defaultagent.ftl"_ns};
RefPtr<Localization> l10n = Localization::Create(resIds, true);
nsAutoCString daHeaderText, daBodyText, daYesButton, daNoButton;
mozilla::ErrorResult daRv;
l10n->FormatValueSync("default-browser-notification-header-text"_ns, {},
daHeaderText, daRv);
ENSURE_SUCCESS(daRv, false);
l10n->FormatValueSync("default-browser-notification-body-text"_ns, {},
daBodyText, daRv);
ENSURE_SUCCESS(daRv, false);
l10n->FormatValueSync("default-browser-notification-yes-button-text"_ns, {},
daYesButton, daRv);
ENSURE_SUCCESS(daRv, false);
l10n->FormatValueSync("default-browser-notification-no-button-text"_ns, {},
daNoButton, daRv);
ENSURE_SUCCESS(daRv, false);
NS_ConvertUTF8toUTF16 daHeaderTextW(daHeaderText), daBodyTextW(daBodyText),
daYesButtonW(daYesButton), daNoButtonW(daNoButton);
strings.localizedToast.text1 =
mozilla::MakeUnique<wchar_t[]>(daHeaderTextW.Length() + 1);
wcsncpy(strings.localizedToast.text1.get(), daHeaderTextW.get(),
daHeaderTextW.Length() + 1);
strings.localizedToast.text2 =
mozilla::MakeUnique<wchar_t[]>(daBodyTextW.Length() + 1);
wcsncpy(strings.localizedToast.text2.get(), daBodyTextW.get(),
daBodyTextW.Length() + 1);
strings.localizedToast.action1 =
mozilla::MakeUnique<wchar_t[]>(daYesButtonW.Length() + 1);
wcsncpy(strings.localizedToast.action1.get(), daYesButtonW.get(),
daYesButtonW.Length() + 1);
strings.localizedToast.action2 =
mozilla::MakeUnique<wchar_t[]>(daNoButtonW.Length() + 1);
wcsncpy(strings.localizedToast.action2.get(), daNoButtonW.get(),
daNoButtonW.Length() + 1);
const wchar_t* iniFormat = L"%s\\defaultagent.ini";
int bufferSize = _scwprintf(iniFormat, installPath.get());
++bufferSize; // Extra character for terminating null
mozilla::UniquePtr<wchar_t[]> iniPath =
mozilla::MakeUnique<wchar_t[]>(bufferSize);
_snwprintf_s(iniPath.get(), bufferSize, _TRUNCATE, iniFormat,
installPath.get());
IniReader nonlocalizedReader(iniPath.get(), "Nonlocalized");
nonlocalizedReader.AddKey("InitialToastRelativeImagePath",
&strings.initialToast.relImagePath);
nonlocalizedReader.AddKey("FollowupToastRelativeImagePath",
&strings.followupToast.relImagePath);
nonlocalizedReader.AddKey("LocalizedToastRelativeImagePath",
&strings.localizedToast.relImagePath);
int result = nonlocalizedReader.Read();
if (result != OK) {
LOG_ERROR_MESSAGE(L"Unable to read non-localized strings: %d", result);
return false;
}
return true;
}
static mozilla::WindowsError LaunchFirefoxToHandleDefaultBrowserAgent() {
// Could also be `MOZ_APP_NAME.exe`, but there's no generality to be gained:
// the WDBA is Firefox-only.
FilePathResult firefoxPathResult = GetRelativeBinaryPath(L"firefox.exe");
if (firefoxPathResult.isErr()) {
return firefoxPathResult.unwrapErr();
}
std::wstring firefoxPath = firefoxPathResult.unwrap();
_bstr_t cmd = firefoxPath.c_str();
// Omit argv[0] because ShellExecute doesn't need it.
_variant_t args(L"-to-handle-default-browser-agent");
_variant_t operation(L"open");
_variant_t directory;
_variant_t showCmd(SW_SHOWNORMAL);
// To prevent inheriting environment variables from the background task, we
// run Firefox via Explorer instead of our own process. This mimics the
// implementation of the Windows Launcher Process.
auto result =
ShellExecuteByExplorer(cmd, args, operation, directory, showCmd);
NS_ENSURE_TRUE(result.isOk(), result.unwrapErr());
return mozilla::WindowsError::CreateSuccess();
}
/*
* Set the default browser.
*
* First check if we can directly write UserChoice, if so attempt that.
* If we can't write UserChoice, or if the attempt fails, fall back to
* showing the Default Apps page of Settings.
*
* @param aAumi The AUMI of the installation to set as default.
*/
static void SetDefaultBrowserFromNotification(const wchar_t* aumi) {
nsresult rv = NS_ERROR_FAILURE;
if (GetPrefSetDefaultBrowserUserChoice()) {
rv = SetDefaultBrowserUserChoice(aumi);
}
if (NS_SUCCEEDED(rv)) {
mozilla::Unused << LaunchFirefoxToHandleDefaultBrowserAgent();
} else {
LOG_ERROR_MESSAGE(L"Failed to SetDefaultBrowserUserChoice: %#X",
GetLastError());
LaunchModernSettingsDialogDefaultApps();
}
}
// This encapsulates the data that needs to be protected by a mutex because it
// will be shared by the main thread and the handler thread.
// To ensure the data is only written once, handlerDataHasBeenSet should be
// initialized to false, then set to true when the handler writes data into the
// structure.
struct HandlerData {
NotificationActivities activitiesPerformed;
bool handlerDataHasBeenSet;
};
// The value that ToastHandler writes into should be a global. We can't control
// when ToastHandler is called, and if this value isn't a global, ToastHandler
// may be called and attempt to access this after it has been deconstructed.
// Since this value is accessed by the handler thread and the main thread, it
// is protected by a mutex (gHandlerMutex).
// Since ShowNotification deconstructs the mutex, it might seem like once
// ShowNotification exits, we can just rely on the inability to wait on an
// invalid mutex to protect the deconstructed data, but it's possible that
// we could deconstruct the mutex while the handler is holding it and is
// already accessing the protected data.
static HandlerData gHandlerReturnData;
static HANDLE gHandlerMutex = INVALID_HANDLE_VALUE;
class ToastHandler : public WinToastLib::IWinToastHandler {
private:
NotificationType mWhichNotification;
HANDLE mEvent;
const std::wstring mAumiStr;
public:
ToastHandler(NotificationType whichNotification, HANDLE event,
const wchar_t* aumi)
: mWhichNotification(whichNotification), mEvent(event), mAumiStr(aumi) {}
void FinishHandler(NotificationActivities& returnData) const {
SetReturnData(returnData);
BOOL success = SetEvent(mEvent);
if (!success) {
LOG_ERROR_MESSAGE(L"Event could not be set: %#X", GetLastError());
}
}
void SetReturnData(NotificationActivities& toSet) const {
DWORD result = WaitForSingleObject(gHandlerMutex, MUTEX_TIMEOUT_MS);
if (result == WAIT_TIMEOUT) {
LOG_ERROR_MESSAGE(L"Unable to obtain mutex ownership");
return;
} else if (result == WAIT_FAILED) {
LOG_ERROR_MESSAGE(L"Failed to wait on mutex: %#X", GetLastError());
return;
} else if (result == WAIT_ABANDONED) {
LOG_ERROR_MESSAGE(L"Found abandoned mutex");
ReleaseMutex(gHandlerMutex);
return;
}
// Only set this data once
if (!gHandlerReturnData.handlerDataHasBeenSet) {
gHandlerReturnData.activitiesPerformed = toSet;
gHandlerReturnData.handlerDataHasBeenSet = true;
}
BOOL success = ReleaseMutex(gHandlerMutex);
if (!success) {
LOG_ERROR_MESSAGE(L"Unable to release mutex ownership: %#X",
GetLastError());
}
}
void toastActivated() const override {
NotificationActivities activitiesPerformed;
activitiesPerformed.type = mWhichNotification;
activitiesPerformed.shown = NotificationShown::Shown;
activitiesPerformed.action = NotificationAction::ToastClicked;
// Notification strings are written to indicate the default browser is
// restored to Firefox when the notification body is clicked to prevent
// ambiguity when buttons aren't pressed.
SetDefaultBrowserFromNotification(mAumiStr.c_str());
FinishHandler(activitiesPerformed);
}
void toastActivated(int actionIndex) const override {
NotificationActivities activitiesPerformed;
activitiesPerformed.type = mWhichNotification;
activitiesPerformed.shown = NotificationShown::Shown;
// Override this below
activitiesPerformed.action = NotificationAction::NoAction;
if (actionIndex == 0) {
// "Make Firefox the default" button, on both the initial and followup
// notifications. "Yes" button on the localized notification.
activitiesPerformed.action = NotificationAction::MakeFirefoxDefaultButton;
SetDefaultBrowserFromNotification(mAumiStr.c_str());
} else if (actionIndex == 1) {
// Do nothing. As long as we don't call
// SetFollowupNotificationRequestTime, there will be no followup
// notification.
activitiesPerformed.action = NotificationAction::DismissedByButton;
}
FinishHandler(activitiesPerformed);
}
void toastDismissed(WinToastDismissalReason state) const override {
NotificationActivities activitiesPerformed;
activitiesPerformed.type = mWhichNotification;
activitiesPerformed.shown = NotificationShown::Shown;
// Override this below
activitiesPerformed.action = NotificationAction::NoAction;
if (state == WinToastDismissalReason::TimedOut) {
activitiesPerformed.action = NotificationAction::DismissedByTimeout;
} else if (state == WinToastDismissalReason::ApplicationHidden) {
activitiesPerformed.action =
NotificationAction::DismissedByApplicationHidden;
} else if (state == WinToastDismissalReason::UserCanceled) {
activitiesPerformed.action = NotificationAction::DismissedToActionCenter;
}
FinishHandler(activitiesPerformed);
}
void toastFailed() const override {
NotificationActivities activitiesPerformed;
activitiesPerformed.type = mWhichNotification;
activitiesPerformed.shown = NotificationShown::Error;
activitiesPerformed.action = NotificationAction::NoAction;
LOG_ERROR_MESSAGE(L"Toast notification failed to display");
FinishHandler(activitiesPerformed);
}
};
// This function blocks until the shown notification is activated or dismissed.
static NotificationActivities ShowNotification(
NotificationType whichNotification, const wchar_t* aumi) {
// Initially set the value that will be returned to error. If the notification
// is shown successfully, we'll update it.
NotificationActivities activitiesPerformed = {whichNotification,
NotificationShown::Error,
NotificationAction::NoAction};
bool isEnglishInstall = FirefoxInstallIsEnglish();
Strings strings;
if (!GetStrings(strings)) {
return activitiesPerformed;
}
const ToastStrings* toastStrings =
strings.GetToastStrings(whichNotification, isEnglishInstall);
mozilla::mscom::EnsureMTA([&] {
using namespace WinToastLib;
if (!WinToast::isCompatible()) {
LOG_ERROR_MESSAGE(L"System is not compatible with WinToast");
return;
}
WinToast::instance()->setAppName(L"" MOZ_APP_DISPLAYNAME);
std::wstring aumiStr = aumi;
WinToast::instance()->setAppUserModelId(aumiStr);
WinToast::instance()->setShortcutPolicy(
WinToastLib::WinToast::SHORTCUT_POLICY_REQUIRE_NO_CREATE);
WinToast::WinToastError error;
if (!WinToast::instance()->initialize(&error)) {
LOG_ERROR_MESSAGE(WinToast::strerror(error).c_str());
return;
}
// This event object will let the handler notify us when it has handled the
// notification.
nsAutoHandle event(CreateEventW(nullptr, TRUE, FALSE, nullptr));
if (event.get() == nullptr) {
LOG_ERROR_MESSAGE(L"Unable to create event object: %#X", GetLastError());
return;
}
bool success = false;
if (whichNotification == NotificationType::Initial) {
success = SetInitialNotificationShown(true);
} else {
success = SetFollowupNotificationShown(true);
}
if (!success) {
// Return early in this case to prevent the notification from being shown
// on every run.
LOG_ERROR_MESSAGE(L"Unable to set notification as displayed");
return;
}
// We need the absolute image path, not the relative path.
mozilla::UniquePtr<wchar_t[]> installPath;
success = GetInstallDirectory(installPath);
if (!success) {
LOG_ERROR_MESSAGE(L"Failed to get install directory for the image path");
return;
}
const wchar_t* absPathFormat = L"%s\\%s";
int bufferSize = _scwprintf(absPathFormat, installPath.get(),
toastStrings->relImagePath.get());
++bufferSize; // Extra character for terminating null
mozilla::UniquePtr<wchar_t[]> absImagePath =
mozilla::MakeUnique<wchar_t[]>(bufferSize);
_snwprintf_s(absImagePath.get(), bufferSize, _TRUNCATE, absPathFormat,
installPath.get(), toastStrings->relImagePath.get());
// This is used to protect gHandlerReturnData.
gHandlerMutex = CreateMutexW(nullptr, TRUE, nullptr);
if (gHandlerMutex == nullptr) {
LOG_ERROR_MESSAGE(L"Unable to create mutex: %#X", GetLastError());
return;
}
// Automatically close this mutex when this function exits.
nsAutoHandle autoMutex(gHandlerMutex);
// No need to initialize gHandlerReturnData.activitiesPerformed, since it
// will be set by the handler. But we do need to initialize
// gHandlerReturnData.handlerDataHasBeenSet so the handler knows that no
// data has been set yet.
gHandlerReturnData.handlerDataHasBeenSet = false;
success = ReleaseMutex(gHandlerMutex);
if (!success) {
LOG_ERROR_MESSAGE(L"Unable to release mutex ownership: %#X",
GetLastError());
}
// Finally ready to assemble the notification and dispatch it.
WinToastTemplate toastTemplate =
WinToastTemplate(WinToastTemplate::ImageAndText02);
toastTemplate.setTextField(toastStrings->text1.get(),
WinToastTemplate::FirstLine);
toastTemplate.setTextField(toastStrings->text2.get(),
WinToastTemplate::SecondLine);
toastTemplate.addAction(toastStrings->action1.get());
toastTemplate.addAction(toastStrings->action2.get());
toastTemplate.setImagePath(absImagePath.get());
toastTemplate.setScenario(WinToastTemplate::Scenario::Reminder);
ToastHandler* handler =
new ToastHandler(whichNotification, event.get(), aumi);
INT64 id = WinToast::instance()->showToast(toastTemplate, handler, &error);
if (id < 0) {
LOG_ERROR_MESSAGE(WinToast::strerror(error).c_str());
return;
}
DWORD result =
WaitForSingleObject(event.get(), NOTIFICATION_WAIT_TIMEOUT_MS);
// Don't return after these errors. Attempt to hide the notification.
if (result == WAIT_FAILED) {
LOG_ERROR_MESSAGE(L"Unable to wait on event object: %#X", GetLastError());
} else if (result == WAIT_TIMEOUT) {
LOG_ERROR_MESSAGE(L"Timed out waiting for event object");
} else {
result = WaitForSingleObject(gHandlerMutex, MUTEX_TIMEOUT_MS);
if (result == WAIT_TIMEOUT) {
LOG_ERROR_MESSAGE(L"Unable to obtain mutex ownership");
// activitiesPerformed is already set to error. No change needed.
} else if (result == WAIT_FAILED) {
LOG_ERROR_MESSAGE(L"Failed to wait on mutex: %#X", GetLastError());
// activitiesPerformed is already set to error. No change needed.
} else if (result == WAIT_ABANDONED) {
LOG_ERROR_MESSAGE(L"Found abandoned mutex");
ReleaseMutex(gHandlerMutex);
// activitiesPerformed is already set to error. No change needed.
} else {
// Mutex is being held. It is safe to access gHandlerReturnData.
// If gHandlerReturnData.handlerDataHasBeenSet is false, the handler
// never ran. Use the error value activitiesPerformed already contains.
if (gHandlerReturnData.handlerDataHasBeenSet) {
activitiesPerformed = gHandlerReturnData.activitiesPerformed;
}
success = ReleaseMutex(gHandlerMutex);
if (!success) {
LOG_ERROR_MESSAGE(L"Unable to release mutex ownership: %#X",
GetLastError());
}
}
}
if (!WinToast::instance()->hideToast(id)) {
LOG_ERROR_MESSAGE(L"Failed to hide notification");
}
});
return activitiesPerformed;
}
// Previously this function checked that the Firefox build was using English.
// This was checked because of the peculiar way we were localizing toast
// notifications where we used a completely different set of strings in English.
//
// We've since unified the notification flows but need to clean up unused code
// and config files - Bug 1826375.
bool FirefoxInstallIsEnglish() { return false; }
// If a notification is shown, this function will block until the notification
// is activated or dismissed.
// aumi is the App User Model ID.
NotificationActivities MaybeShowNotification(
const DefaultBrowserInfo& browserInfo, const wchar_t* aumi, bool force) {
// Default to not showing a notification. Any other value will be returned
// directly from ShowNotification.
NotificationActivities activitiesPerformed = {NotificationType::Initial,
NotificationShown::NotShown,
NotificationAction::NoAction};
// Reset notification state machine, user setting default browser to Firefox
// is a strong signal that they intend to have it as the default browser.
if (browserInfo.currentDefaultBrowser == Browser::Firefox) {
ResetInitialNotificationShown();
}
bool initialNotificationShown = GetInitialNotificationShown();
if (!initialNotificationShown || force) {
if ((browserInfo.currentDefaultBrowser == Browser::EdgeWithBlink &&
browserInfo.previousDefaultBrowser == Browser::Firefox) ||
force) {
return ShowNotification(NotificationType::Initial, aumi);
}
return activitiesPerformed;
}
activitiesPerformed.type = NotificationType::Followup;
ULONGLONG followupNotificationRequestTime =
GetFollowupNotificationRequestTime();
bool followupNotificationRequested = followupNotificationRequestTime != 0;
bool followupNotificationShown = GetFollowupNotificationShown();
if (followupNotificationRequested && !followupNotificationShown &&
!GetFollowupNotificationSuppressed()) {
ULONGLONG secondsSinceRequestTime =
SecondsPassedSince(followupNotificationRequestTime);
if (secondsSinceRequestTime >= SEVEN_DAYS_IN_SECONDS) {
// If we go to show the followup notification and the user has already
// changed the default browser, permanently suppress the followup since
// it's no longer relevant.
if (browserInfo.currentDefaultBrowser == Browser::EdgeWithBlink) {
return ShowNotification(NotificationType::Followup, aumi);
} else {
SetFollowupNotificationSuppressed(true);
}
}
}
return activitiesPerformed;
}
std::string GetStringForNotificationType(NotificationType type) {
switch (type) {
case NotificationType::Initial:

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

@ -7,7 +7,9 @@
#ifndef __DEFAULT_BROWSER_NOTIFICATION_H__
#define __DEFAULT_BROWSER_NOTIFICATION_H__
#include "DefaultBrowser.h"
#include <string>
#include "nsStringFwd.h"
namespace mozilla::default_agent {
@ -39,9 +41,6 @@ struct NotificationActivities {
NotificationAction action;
};
NotificationActivities MaybeShowNotification(
const DefaultBrowserInfo& browserInfo, const wchar_t* aumi, bool force);
// These take enum values and get strings suitable for telemetry
std::string GetStringForNotificationType(NotificationType type);
std::string GetStringForNotificationShown(NotificationShown shown);

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

@ -1,9 +0,0 @@
; 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/.
; This file is in the UTF-8 encoding
[Nonlocalized]
InitialToastRelativeImagePath=browser/VisualElements/VisualElements_150.png
FollowupToastRelativeImagePath=browser/VisualElements/VisualElements_150.png
LocalizedToastRelativeImagePath=browser/VisualElements/VisualElements_150.png

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

@ -15,6 +15,7 @@ UNIFIED_SOURCES += [
"DefaultBrowser.cpp",
"DefaultPDF.cpp",
"EventLog.cpp",
"Notification.cpp",
"Policy.cpp",
"Registry.cpp",
"ScheduledTask.cpp",
@ -25,21 +26,6 @@ UNIFIED_SOURCES += [
"WindowsMutex.cpp",
]
SOURCES += [
"/third_party/WinToast/wintoastlib.cpp",
"/toolkit/mozapps/update/common/readstrings.cpp",
"Notification.cpp",
]
# Suppress warnings from third-party code.
SOURCES["/third_party/WinToast/wintoastlib.cpp"].flags += [
"-Wno-implicit-fallthrough",
"-Wno-nonportable-include-path", # Needed for wintoastlib.h including "Windows.h"
]
SOURCES["Notification.cpp"].flags += [
"-Wno-nonportable-include-path", # Needed for wintoastlib.h including "Windows.h"
]
EXPORTS.mozilla += [
"DefaultAgent.h",
"WindowsMutex.h",
@ -52,9 +38,7 @@ USE_LIBS += [
LOCAL_INCLUDES += [
"/browser/components/shell/",
"/other-licenses/nsis/Contrib/CityHash/cityhash",
"/third_party/WinToast",
"/toolkit/components/jsoncpp/include",
"/toolkit/mozapps/update/common",
]
OS_LIBS += [
@ -98,8 +82,6 @@ for var in ("MOZ_APP_BASENAME", "MOZ_APP_DISPLAYNAME", "MOZ_APP_VENDOR"):
DEFINES["UNICODE"] = True
DEFINES["_UNICODE"] = True
FINAL_TARGET_FILES += ["defaultagent.ini"]
FINAL_LIBRARY = "xul"
if CONFIG["ENABLE_TESTS"]:

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

@ -51,20 +51,6 @@ interface nsIDefaultAgent : nsISupports
*/
void uninstall(in AString aUniqueToken);
/**
* Actually performs the default agent task, which currently means generating
* and sending our telemetry ping and possibly showing a notification to the
* user if their browser has switched from Firefox to Edge with Blink.
*
* @param {AString} aUniqueToken
* A unique identifier for this installation; the same one provided when
* the task was registered.
* @param {boolean} aForce
* For debugging, forces the task to run even if it has run in the last
* 24 hours, and forces the notification to show.
*/
void doTask(in AString aUniqueToken, in boolean aForce);
/**
* Checks that the main app ran recently.
*