зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
b623c0efa1
Коммит
2657cfb14b
|
@ -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.
|
||||
*
|
||||
|
|
Загрузка…
Ссылка в новой задаче