diff options
Diffstat (limited to 'toolkit/mozapps')
56 files changed, 695 insertions, 919 deletions
diff --git a/toolkit/mozapps/defaultagent/BackgroundTask_defaultagent.sys.mjs b/toolkit/mozapps/defaultagent/BackgroundTask_defaultagent.sys.mjs index c727a55997..691f76c319 100644 --- a/toolkit/mozapps/defaultagent/BackgroundTask_defaultagent.sys.mjs +++ b/toolkit/mozapps/defaultagent/BackgroundTask_defaultagent.sys.mjs @@ -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", }); @@ -37,11 +35,20 @@ ChromeUtils.defineLazyGetter(lazy, "log", () => { return new ConsoleAPI(consoleOptions); }); -// Should be slightly longer than NOTIFICATION_WAIT_TIMEOUT_MS in -// Notification.cpp (divided by 1000 to convert millseconds to seconds) to not -// cause race between timeouts. Currently 12 hours + 5 additional minutes. -export const backgroundTaskTimeoutSec = 12 * 60 * 60 + 60 * 5; -const kNotificationTimeoutMs = 12 * 60 * 60 * 1000; +// Should be slightly longer than kNotificationTimeoutMs and kGleanSendWait below +// (divided by 1000 to convert millseconds to seconds) to not cause race +// between timeouts. +// +// Additionally, should be less than the Windows Scheduled Task timeout +// execTimeLimitBStr in ScheduledTask.cpp. +// +// Current bounds are 11 hours 55 minutes 10 seconds and 12 hours 5 minutes. +export const backgroundTaskTimeoutSec = 12 * 60 * 60; + +// 11 hours 55 minutes in milliseconds. +const kNotificationTimeoutMs = 11 * 60 * 60 * 1000 + 55 * 60 * 1000; +// 10 seconds in milliseconds. +const kGleanSendWait = 10000; const kNotificationShown = Object.freeze({ notShown: "not-shown", @@ -148,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); @@ -181,7 +174,7 @@ export async function runBackgroundTask(commandLine) { // Bug 1857333: We wait for arbitrary time for Glean to submit telemetry. lazy.log.info("Pinged glean, waiting for submission."); - await new Promise(resolve => lazy.setTimeout(resolve, 5000)); + await new Promise(resolve => lazy.setTimeout(resolve, kGleanSendWait)); return EXIT_CODE.SUCCESS; } diff --git a/toolkit/mozapps/defaultagent/DefaultAgent.cpp b/toolkit/mozapps/defaultagent/DefaultAgent.cpp index 2ebb5e466e..3bff6e3243 100644 --- a/toolkit/mozapps/defaultagent/DefaultAgent.cpp +++ b/toolkit/mozapps/defaultagent/DefaultAgent.cpp @@ -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" @@ -304,62 +301,6 @@ DefaultAgent::Uninstall(const nsAString& aUniqueToken) { } 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; *aRanRecently = CheckIfAppRanRecently(&ranRecently) && ranRecently; diff --git a/toolkit/mozapps/defaultagent/DefaultBrowser.cpp b/toolkit/mozapps/defaultagent/DefaultBrowser.cpp index 87d3f62632..d9543665b7 100644 --- a/toolkit/mozapps/defaultagent/DefaultBrowser.cpp +++ b/toolkit/mozapps/defaultagent/DefaultBrowser.cpp @@ -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 diff --git a/toolkit/mozapps/defaultagent/DefaultBrowser.h b/toolkit/mozapps/defaultagent/DefaultBrowser.h index f1b940959f..081f3b9ccf 100644 --- a/toolkit/mozapps/defaultagent/DefaultBrowser.h +++ b/toolkit/mozapps/defaultagent/DefaultBrowser.h @@ -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); diff --git a/toolkit/mozapps/defaultagent/Notification.cpp b/toolkit/mozapps/defaultagent/Notification.cpp index 961e57c9b3..39236eac15 100644 --- a/toolkit/mozapps/defaultagent/Notification.cpp +++ b/toolkit/mozapps/defaultagent/Notification.cpp @@ -7,616 +7,22 @@ #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) -// If the notification hasn't been activated or dismissed within 12 hours, -// stop waiting for it. -#define NOTIFICATION_WAIT_TIMEOUT_MS (12 * 60 * 60 * 1000) +// If the notification hasn't been activated or dismissed within 11 hours 55 +// minutes, stop waiting for it. +#define NOTIFICATION_WAIT_TIMEOUT_MS (11 * 60 * 60 * 1000 + 55 * 60 * 1000) // If the mutex hasn't been released within a few minutes, something is wrong // and we should give up on it #define MUTEX_TIMEOUT_MS (10 * 60 * 1000) 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: diff --git a/toolkit/mozapps/defaultagent/Notification.h b/toolkit/mozapps/defaultagent/Notification.h index 210c55f559..e4e007088d 100644 --- a/toolkit/mozapps/defaultagent/Notification.h +++ b/toolkit/mozapps/defaultagent/Notification.h @@ -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); diff --git a/toolkit/mozapps/defaultagent/ScheduledTask.cpp b/toolkit/mozapps/defaultagent/ScheduledTask.cpp index a9cd647c03..c867e59b76 100644 --- a/toolkit/mozapps/defaultagent/ScheduledTask.cpp +++ b/toolkit/mozapps/defaultagent/ScheduledTask.cpp @@ -13,14 +13,11 @@ #include <comutil.h> #include <taskschd.h> -#include "readstrings.h" -#include "updatererrors.h" #include "EventLog.h" #include "mozilla/RefPtr.h" #include "mozilla/ScopeExit.h" #include "mozilla/UniquePtr.h" #include "mozilla/WinHeaderOnlyUtils.h" -#include "WindowsDefaultBrowser.h" #include "DefaultBrowser.h" diff --git a/toolkit/mozapps/defaultagent/SetDefaultBrowser.cpp b/toolkit/mozapps/defaultagent/SetDefaultBrowser.cpp index 8bc0889e67..6d829ff045 100644 --- a/toolkit/mozapps/defaultagent/SetDefaultBrowser.cpp +++ b/toolkit/mozapps/defaultagent/SetDefaultBrowser.cpp @@ -6,17 +6,16 @@ #include <windows.h> #include <appmodel.h> #include <shlobj.h> // for SHChangeNotify and IApplicationAssociationRegistration -#include <functional> #include <timeapi.h> #include "mozilla/ArrayUtils.h" #include "mozilla/CmdLineAndEnvUtils.h" #include "mozilla/RefPtr.h" +#include "mozilla/Result.h" #include "mozilla/UniquePtr.h" #include "mozilla/WindowsVersion.h" #include "mozilla/WinHeaderOnlyUtils.h" #include "WindowsUserChoice.h" -#include "nsThreadUtils.h" #include "EventLog.h" #include "SetDefaultBrowser.h" @@ -70,6 +69,77 @@ static bool AddMillisecondsToSystemTime(SYSTEMTIME& aSystemTime, return true; } +/* + * Takes two times: the start time and the current time, and returns the number + * of seconds left before the current time hits the next minute from the start + * time. Used to check if we are within the same minute as the start time and + * how much time we have left to perform an operation in the same minute. + * + * Used with user choice hashes, which have to be written to the registry + * in the same minute as they are generated. + * + * Example 1: + * operationStartTime - 10m 20s 800ms + * currentTime - 10m 22s 0ms + * The next minute is 11, so the return value is 11m - 10m 22s 0ms, converted to + * milliseconds. + * + * Example 2: + * operationStartTime - 10m 59s 800ms + * currentTime - 11m 0s 0ms + * The next minute is 11, but the minute the operation started on was 10, so the + * time to the next minute is 0 (because the current time is already at the next + * minute). + * + * @param operationStartTime + * @param currentTime + * + * @returns the number of milliseconds left from the current time to the + * next minute from operationStartTime, or zero if the currentTime is already at + * the next minute or greater + */ +static WORD GetMillisecondsToNextMinute(SYSTEMTIME operationStartTime, + SYSTEMTIME currentTime) { + SYSTEMTIME operationStartTimeMinute = operationStartTime; + SYSTEMTIME currentTimeMinute = currentTime; + + // Zero out the seconds and milliseconds so that we can confirm they are the + // same minutes + operationStartTimeMinute.wSecond = 0; + operationStartTimeMinute.wMilliseconds = 0; + + currentTimeMinute.wSecond = 0; + currentTimeMinute.wMilliseconds = 0; + + // Convert to a 64 bit value so we can compare them directly + FILETIME fileTime1; + FILETIME fileTime2; + if (!::SystemTimeToFileTime(&operationStartTimeMinute, &fileTime1) || + !::SystemTimeToFileTime(¤tTimeMinute, &fileTime2)) { + // Error: report that there is 0 milliseconds till the next minute + return 0; + } + + // The minutes for both times have to be the same, so confirm that they are, + // and if they aren't, return 0 milliseconds to indicate that we're already + // not on the minute that operationStartTime was on + if ((fileTime1.dwLowDateTime != fileTime2.dwLowDateTime) || + (fileTime1.dwHighDateTime != fileTime2.dwHighDateTime)) { + return 0; + } + + // The minutes are the same; determine the number of milliseconds left until + // the next minute + const WORD secondsToMilliseconds = 1000; + const WORD minutesToSeconds = 60; + + // 1 minute converted to milliseconds - (the current second converted to + // milliseconds + the current milliseconds) + return (1 * minutesToSeconds * secondsToMilliseconds) - + ((currentTime.wSecond * secondsToMilliseconds) + + currentTime.wMilliseconds); +} + // Compare two SYSTEMTIMEs as FILETIME after clearing everything // below minutes. static bool CheckEqualMinutes(SYSTEMTIME aSystemTime1, @@ -149,6 +219,75 @@ static bool SetUserChoiceRegistry(const wchar_t* aExt, const wchar_t* aProgID, return true; } +struct LaunchExeErr {}; +using LaunchExeResult = + mozilla::Result<std::tuple<DWORD, mozilla::UniquePtr<wchar_t[]>>, + LaunchExeErr>; + +static LaunchExeResult LaunchExecutable(const wchar_t* exePath, int aArgsLength, + const wchar_t* const* aArgs) { + const wchar_t* args[] = {exePath}; + mozilla::UniquePtr<wchar_t[]> cmdLine(mozilla::MakeCommandLine( + mozilla::ArrayLength(args), const_cast<wchar_t**>(args), aArgsLength, + const_cast<wchar_t**>(aArgs))); + + PROCESS_INFORMATION pi; + STARTUPINFOW si = {sizeof(si)}; + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + + if (!::CreateProcessW(exePath, cmdLine.get(), nullptr, nullptr, FALSE, 0, + nullptr, nullptr, &si, &pi)) { + HRESULT hr = HRESULT_FROM_WIN32(GetLastError()); + LOG_ERROR(hr); + return Err(LaunchExeErr()); + } + + nsAutoHandle process(pi.hProcess); + nsAutoHandle mainThread(pi.hThread); + + DWORD exitCode; + if (::WaitForSingleObject(process.get(), INFINITE) == WAIT_OBJECT_0 && + ::GetExitCodeProcess(process.get(), &exitCode)) { + return std::make_tuple(exitCode, std::move(cmdLine)); + } + + return Err(LaunchExeErr()); +} + +static bool LaunchPowershell(const wchar_t* command, + const wchar_t* powershellPath) { + const wchar_t* args[] = { + L"-NoProfile", // ensure nothing is monkeying with powershell + L"-c", + command, + }; + + return LaunchExecutable(powershellPath, mozilla::ArrayLength(args), args) + .map([](auto result) { + auto& [exitCode, regCmdLine] = result; + bool success = (exitCode == 0); + if (!success) { + LOG_ERROR_MESSAGE(L"%s returned failure exitCode %d", + regCmdLine.get(), exitCode); + } + return success; + }) + .unwrapOr(false); +} + +static bool FindPowershell(mozilla::UniquePtr<wchar_t[]>& powershellPath) { + auto exePath = mozilla::MakeUnique<wchar_t[]>(MAX_PATH + 1); + if (!ConstructSystem32Path(L"WindowsPowershell\\v1.0\\powershell.exe", + exePath.get(), MAX_PATH + 1)) { + LOG_ERROR_MESSAGE(L"Failed to construct path to powershell.exe"); + return false; + } + + powershellPath.swap(exePath); + return true; +} + /* * Set an association with a UserChoice key * @@ -165,7 +304,9 @@ static bool SetUserChoiceRegistry(const wchar_t* aExt, const wchar_t* aProgID, static bool SetUserChoice(const wchar_t* aExt, const wchar_t* aSid, const wchar_t* aProgID, bool inMsix) { if (inMsix) { - LOG_ERROR_MESSAGE(L"SetUserChoice does not work on MSIX builds."); + LOG_ERROR_MESSAGE( + L"SetUserChoice should not be called on MSIX builds. Call " + L"SetDefaultExtensionHandlersUserChoiceImplMsix instead."); return false; } @@ -298,6 +439,247 @@ nsresult SetDefaultExtensionHandlersUserChoice( return rv; } +/* + * Takes the list of file extension pairs and fills a list of program ids for + * each pair. + * + * @param aFileExtensions array of file association pairs to + * set as default, like `[ ".pdf", "FirefoxPDF" ]`. + */ +static nsresult GenerateProgramIDs(const nsTArray<nsString>& aFileExtensions, + nsTArray<nsString>& progIDs) { + for (size_t i = 0; i + 1 < aFileExtensions.Length(); i += 2) { + const wchar_t* fileExtension = aFileExtensions[i].get(); + + // Formatting the ProgID here prevents using this helper to target arbitrary + // ProgIDs. + mozilla::UniquePtr<wchar_t[]> progID; + nsresult rv = GetMsixProgId(fileExtension, progID); + if (NS_FAILED(rv)) { + LOG_ERROR_MESSAGE(L"Failed to retrieve MSIX progID for %s", + fileExtension); + return rv; + } + + progIDs.AppendElement(nsString(progID.get())); + } + + return NS_OK; +} + +/* + * Takes the list of file extension pairs and a matching list of program ids for + * each of those pairs and verifies that the system is successfully to match + * each. + * + * @param aFileExtensions array of file association pairs to set as default, + * like `[ ".pdf", "FirefoxPDF" ]`. + * @param aProgIDs array of program ids. The order of this array matches the + * file extensions parameter. + */ +static nsresult VerifyUserDefaults(const nsTArray<nsString>& aFileExtensions, + const nsTArray<nsString>& aProgIDs) { + for (size_t i = 0; i + 1 < aFileExtensions.Length(); i += 2) { + const wchar_t* fileExtension = aFileExtensions[i].get(); + + if (!VerifyUserDefault(fileExtension, aProgIDs[i / 2].get())) { + return NS_ERROR_WDBA_REJECTED; + } + } + + return NS_OK; +} + +/* + * Queries the system for the minimum resolution (or tick time) in milliseconds + * that can be used with ::Sleep. If a Sleep is not triggered with a time + * divisible by the tick time, control can return to the thread before the time + * specified to Sleep. + * + * @param defaultValue what to return if the system query fails. + */ +static UINT GetSystemSleepIntervalInMilliseconds(UINT defaultValue) { + TIMECAPS timeCapabilities; + bool timeCapsFetchSuccessful = + (MMSYSERR_NOERROR == + timeGetDevCaps(&timeCapabilities, sizeof(timeCapabilities))); + if (!timeCapsFetchSuccessful) { + return defaultValue; + } + + return timeCapabilities.wPeriodMin > 0 ? timeCapabilities.wPeriodMin + : defaultValue; +} + +/* + * MSIX implementation for SetDefaultExtensionHandlersUserChoice. + * + * Due to the fact that MSIX builds run in a virtual, walled off environment, + * calling into the Win32 registry APIs doesn't work to set registry keys. + * MSIX builds access a virtual registry. + * + * Sends a "script" via command line to Powershell to modify the registry + * in an executable that is outside of the walled garden MSIX FX package + * environment, which is the only way to do that so far. Only works + * on slightly older versions of Windows 11 (older than 23H2). + * + * This was originally done using calls to Reg.exe, but permissions can't + * be changed that way, requiring using Powershell to call into .Net functions + * directly. Launching Powershell is slow (on the order of a second on some + * systems) so this method jams the calls to do everything into one launch of + * Powershell, to make it as quick as possible. + * + */ +static nsresult SetDefaultExtensionHandlersUserChoiceImplMsix( + const wchar_t* aAumi, const wchar_t* const aSid, + const nsTArray<nsString>& aFileExtensions) { + mozilla::UniquePtr<wchar_t[]> exePath; + if (!FindPowershell(exePath)) { + LOG_ERROR_MESSAGE(L"Could not locate Powershell"); + return NS_ERROR_FAILURE; + } + + // The following is the start of the script that will be sent to Powershell. + // In includes a function; calls to the function get appended per file + // extension, done below. + nsString startScript( + uR"( +# Force exceptions to stop execution +$ErrorActionPreference = 'Stop' + +function Set-DefaultHandlerRegistry($Path, $ProgID, $Hash) { + $Path = "$Path\UserChoice" + $CurrentUser = [Microsoft.Win32.Registry]::CurrentUser + + # DeleteSubKey throws if we don't have sufficient permissions to delete key, + # signaling failure to launching process. + # + # Note: DeleteSubKeyTree fails when DENY permissions are set on key, whereas + # DeleteSubKey succeeds. + $CurrentUser.DeleteSubKey($Path, $false) + $key = $CurrentUser.CreateSubKey($Path) + + $StringType = [Microsoft.Win32.RegistryValueKind]::String + $key.SetValue('ProgID', $ProgID, $StringType) + $key.SetValue('Hash', $Hash, $StringType) +} + +)"); // Newlines in the above powershell script at the end are important!!! + + // NOTE!!!! CreateProcess / calling things on the command line has a character + // count limit. For CMD.exe, it's 8K. For CreateProcessW, it's technically + // 32K. I can't find documentation about Powershell, but think 8K is safe to + // assume as a good maximum. The above script is about 1000 characters, and we + // will append another 100 characters at most per extension, so we'd need to + // be setting about 70 handlers at once to worry about the theoretical limit. + // Which we won't do. So the length shouldn't be a problem. + if (aFileExtensions.Length() >= 70) { + LOG_ERROR_MESSAGE( + L"SetDefaultExtensionHandlersUserChoiceImplMsix can't cope with 70 or " + L"more file extensions at once. Please break it up into multiple calls " + L"with fewer extensions per call."); + return NS_ERROR_FAILURE; + } + + // NOTE!!!! + // User choice hashes have to be generated and written to the registry in the + // same minute. So we do everything we can upfront before we get to the hash + // generation, to ensure that hash generation and the call to Powershell to + // write to the registry has as much time as possible to run. + + // Program ID fetch / generation might be slow, so do that ahead of time. + nsTArray<nsString> progIDs; + nsresult rv = GenerateProgramIDs(aFileExtensions, progIDs); + NS_ENSURE_SUCCESS(rv, rv); + + nsString scriptBuffer; + + // Everyting in the loop below should succeed or fail reasonably fast (within + // 20 milliseconds or something well under a second) besides the Powershell + // call. The Powershell call itself will either fail or succeed and break out + // of the loop, so repeating 10 times should be fine and is mostly to allow + // for getting the timing right with the user choice hash generation happening + // in the same minute - meaning this should likely only happen twice through + // the loop at most. + for (int i = 0; i < 10; i++) { + // Pre-allocate the memory for the scriptBuffer upfront so that we don't + // have to keep allocating every time Append is called. + const int scriptBufferCapacity = 16 * 1024; + scriptBuffer = startScript; + scriptBuffer.SetCapacity(scriptBufferCapacity); + + SYSTEMTIME hashTimestamp; + ::GetSystemTime(&hashTimestamp); + + // Time critical stuff starts here: + + for (size_t i = 0; i + 1 < aFileExtensions.Length(); i += 2) { + const wchar_t* fileExtension = aFileExtensions[i].get(); + + nsAutoString keyPath; + AppendAssociationKeyPath(fileExtension, keyPath); + + auto hashWchar = GenerateUserChoiceHash( + fileExtension, aSid, progIDs[i / 2].get(), hashTimestamp); + if (!hashWchar) { + return NS_ERROR_FAILURE; + } + auto hash = nsDependentString(hashWchar.get()); + + // Append a line to the script buffer in the form: + // Set-DefaultHandlerRegistry $RegistryKeyPath $ProgID $UserChoiceHash + scriptBuffer += u"Set-DefaultHandlerRegistry "_ns + keyPath + u" "_ns + + progIDs[i / 2] + u" "_ns + hash + u"\n"_ns; + } + + // The hash changes at the end of each minute, so check that the hash should + // be the same by the time we're done writing. + const ULONGLONG kWriteTimingThresholdMilliseconds = 2000; + + // Generating the hash could have taken some time, so figure out what time + // we are at right now. + SYSTEMTIME writeEndTimestamp; + ::GetSystemTime(&writeEndTimestamp); + + // Check if we have enough time to launch Powershell or if we should sleep + // to the next minute and try again + auto millisecondsLeftUntilNextMinute = + GetMillisecondsToNextMinute(hashTimestamp, writeEndTimestamp); + if (millisecondsLeftUntilNextMinute >= kWriteTimingThresholdMilliseconds) { + break; + } + + LOG_ERROR_MESSAGE( + L"Hash is too close to next minute, sleeping until next minute to " + L"ensure that hash generation matches write to registry."); + + UINT sleepUntilNextMinuteBufferMilliseconds = + GetSystemSleepIntervalInMilliseconds( + 50); // Default to 50ms if we can't figure out the right interval + // to sleep for + ::Sleep(millisecondsLeftUntilNextMinute + + (sleepUntilNextMinuteBufferMilliseconds * 2)); + + // Try again, if we have any attempts left + } + + // Call Powershell to set the registry keys now - this is the really time + // consuming thing 250ms to launch on a VM in a reasonably fast case, possibly + // a lot slower on other systems + bool powershellSuccessful = + LaunchPowershell(scriptBuffer.get(), exePath.get()); + if (!powershellSuccessful) { + // If powershell failed, it likely means that something got mucked with + // the registry and that Windows is popping up notifications to the user, + // so don't try again right now, so as to not overwhelm the user and annoy + // them. + return NS_ERROR_FAILURE; + } + + // Validate now + return VerifyUserDefaults(aFileExtensions, progIDs); +} + nsresult SetDefaultExtensionHandlersUserChoiceImpl( const wchar_t* aAumi, const wchar_t* const aSid, const nsTArray<nsString>& aFileExtensions) { @@ -306,9 +688,8 @@ nsresult SetDefaultExtensionHandlersUserChoiceImpl( GetCurrentPackageFullName(&pfnLen, nullptr) != APPMODEL_ERROR_NO_PACKAGE; if (inMsix) { - // MSIX packages can not meaningfully modify the registry keys related to - // default handlers - return NS_ERROR_FAILURE; + return SetDefaultExtensionHandlersUserChoiceImplMsix(aAumi, aSid, + aFileExtensions); } for (size_t i = 0; i + 1 < aFileExtensions.Length(); i += 2) { diff --git a/toolkit/mozapps/defaultagent/defaultagent.ini b/toolkit/mozapps/defaultagent/defaultagent.ini deleted file mode 100644 index 9300b20c46..0000000000 --- a/toolkit/mozapps/defaultagent/defaultagent.ini +++ /dev/null @@ -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 diff --git a/toolkit/mozapps/defaultagent/moz.build b/toolkit/mozapps/defaultagent/moz.build index 86b68c6371..f8ae506d9c 100644 --- a/toolkit/mozapps/defaultagent/moz.build +++ b/toolkit/mozapps/defaultagent/moz.build @@ -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"]: diff --git a/toolkit/mozapps/defaultagent/nsIDefaultAgent.idl b/toolkit/mozapps/defaultagent/nsIDefaultAgent.idl index 7e78e1b30d..8472dea3af 100644 --- a/toolkit/mozapps/defaultagent/nsIDefaultAgent.idl +++ b/toolkit/mozapps/defaultagent/nsIDefaultAgent.idl @@ -52,20 +52,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. * * @return {boolean} true if the app ran recently. diff --git a/toolkit/mozapps/defaultagent/proxy/main.cpp b/toolkit/mozapps/defaultagent/proxy/main.cpp index 53efdcb9ae..7ba63c2095 100644 --- a/toolkit/mozapps/defaultagent/proxy/main.cpp +++ b/toolkit/mozapps/defaultagent/proxy/main.cpp @@ -7,10 +7,8 @@ #include <windows.h> #include <shlwapi.h> #include <objbase.h> -#include <string.h> #include <filesystem> -#include "../ScheduledTask.h" #include "../ScheduledTaskRemove.h" #include "mozilla/CmdLineAndEnvUtils.h" diff --git a/toolkit/mozapps/defaultagent/proxy/moz.build b/toolkit/mozapps/defaultagent/proxy/moz.build index 40d7655dfa..3ff6932ac2 100644 --- a/toolkit/mozapps/defaultagent/proxy/moz.build +++ b/toolkit/mozapps/defaultagent/proxy/moz.build @@ -17,7 +17,6 @@ UNIFIED_SOURCES += [ SOURCES += [ "/browser/components/shell/WindowsDefaultBrowser.cpp", "/other-licenses/nsis/Contrib/CityHash/cityhash/city.cpp", - "/toolkit/mozapps/update/common/readstrings.cpp", ] LOCAL_INCLUDES += [ diff --git a/toolkit/mozapps/downloads/DownloadLastDir.sys.mjs b/toolkit/mozapps/downloads/DownloadLastDir.sys.mjs index da0439dcc5..dfd5d2cd26 100644 --- a/toolkit/mozapps/downloads/DownloadLastDir.sys.mjs +++ b/toolkit/mozapps/downloads/DownloadLastDir.sys.mjs @@ -48,7 +48,7 @@ var observer = { "nsISupportsWeakReference", ]), - observe(aSubject, aTopic, aData) { + observe(aSubject, aTopic) { switch (aTopic) { case "last-pb-context-exited": gDownloadLastDirFile = null; diff --git a/toolkit/mozapps/downloads/DownloadUtils.sys.mjs b/toolkit/mozapps/downloads/DownloadUtils.sys.mjs index 3bf97f3c9e..6684a73699 100644 --- a/toolkit/mozapps/downloads/DownloadUtils.sys.mjs +++ b/toolkit/mozapps/downloads/DownloadUtils.sys.mjs @@ -610,7 +610,7 @@ function convertTimeUnitsUnits(timeValue, aIndex) { * Error message to log or an array of strings to concat */ // function log(aMsg) { -// let msg = "DownloadUtils.jsm: " + (aMsg.join ? aMsg.join("") : aMsg); +// let msg = "DownloadUtils.sys.mjs: " + (aMsg.join ? aMsg.join("") : aMsg); // Services.console.logStringMessage(msg); // dump(msg + "\n"); // } diff --git a/toolkit/mozapps/downloads/HelperAppDlg.sys.mjs b/toolkit/mozapps/downloads/HelperAppDlg.sys.mjs index 66f77d38e4..01d1a518d8 100644 --- a/toolkit/mozapps/downloads/HelperAppDlg.sys.mjs +++ b/toolkit/mozapps/downloads/HelperAppDlg.sys.mjs @@ -72,33 +72,19 @@ nsUnknownContentTypeDialogProgressListener.prototype = { }, // Ignore onProgressChange, onProgressChange64, onStateChange, onLocationChange, onSecurityChange, onContentBlockingEvent and onRefreshAttempted notifications. - onProgressChange( - aWebProgress, - aRequest, - aCurSelfProgress, - aMaxSelfProgress, - aCurTotalProgress, - aMaxTotalProgress - ) {}, + onProgressChange() {}, - onProgressChange64( - aWebProgress, - aRequest, - aCurSelfProgress, - aMaxSelfProgress, - aCurTotalProgress, - aMaxTotalProgress - ) {}, + onProgressChange64() {}, - onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {}, + onStateChange() {}, - onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {}, + onLocationChange() {}, - onSecurityChange(aWebProgress, aRequest, aState) {}, + onSecurityChange() {}, - onContentBlockingEvent(aWebProgress, aRequest, aEvent) {}, + onContentBlockingEvent() {}, - onRefreshAttempted(aWebProgress, aURI, aDelay, aSameURI) { + onRefreshAttempted() { return true; }, }; @@ -309,7 +295,7 @@ nsUnknownContentTypeDialog.prototype = { var picker = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker); var windowTitle = bundle.GetStringFromName("saveDialogTitle"); - picker.init(parent, windowTitle, nsIFilePicker.modeSave); + picker.init(parent.browsingContext, windowTitle, nsIFilePicker.modeSave); if (aDefaultFileName) { picker.defaultString = this.getFinalLeafName(aDefaultFileName); } @@ -1270,7 +1256,7 @@ nsUnknownContentTypeDialog.prototype = { var nsIFilePicker = Ci.nsIFilePicker; var fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker); fp.init( - this.mDialog, + this.mDialog.browsingContext, this.dialogElement("strings").getString("chooseAppFilePickerTitle"), nsIFilePicker.modeOpen ); diff --git a/toolkit/mozapps/downloads/tests/browser/browser_save_wrongextension.js b/toolkit/mozapps/downloads/tests/browser/browser_save_wrongextension.js index 83e668076a..ad83bb3689 100644 --- a/toolkit/mozapps/downloads/tests/browser/browser_save_wrongextension.js +++ b/toolkit/mozapps/downloads/tests/browser/browser_save_wrongextension.js @@ -8,7 +8,7 @@ let url = "data:text/html,<a id='link' href='http://localhost:8000/thefile.js'>Link</a>"; let MockFilePicker = SpecialPowers.MockFilePicker; -MockFilePicker.init(window); +MockFilePicker.init(window.browsingContext); let httpServer = null; diff --git a/toolkit/mozapps/downloads/tests/browser/browser_unknownContentType_delayedbutton.js b/toolkit/mozapps/downloads/tests/browser/browser_unknownContentType_delayedbutton.js index 06a5a9d238..9bd494e245 100644 --- a/toolkit/mozapps/downloads/tests/browser/browser_unknownContentType_delayedbutton.js +++ b/toolkit/mozapps/downloads/tests/browser/browser_unknownContentType_delayedbutton.js @@ -13,14 +13,14 @@ let UCTObserver = { opened: Promise.withResolvers(), closed: Promise.withResolvers(), - observe(aSubject, aTopic, aData) { + observe(aSubject, aTopic) { let win = aSubject; switch (aTopic) { case "domwindowopened": win.addEventListener( "load", - function onLoad(event) { + function onLoad() { // Let the dialog initialize SimpleTest.executeSoon(function () { UCTObserver.opened.resolve(win); @@ -40,7 +40,7 @@ let UCTObserver = { }; function waitDelay(delay) { - return new Promise((resolve, reject) => { + return new Promise(resolve => { /* eslint-disable mozilla/no-arbitrary-setTimeout */ window.setTimeout(resolve, delay); }); diff --git a/toolkit/mozapps/downloads/tests/browser/browser_unknownContentType_dialog_layout.js b/toolkit/mozapps/downloads/tests/browser/browser_unknownContentType_dialog_layout.js index 6755c1aadb..e42951696d 100644 --- a/toolkit/mozapps/downloads/tests/browser/browser_unknownContentType_dialog_layout.js +++ b/toolkit/mozapps/downloads/tests/browser/browser_unknownContentType_dialog_layout.js @@ -38,14 +38,14 @@ add_task(async function test_unknownContentType_dialog_layout() { opened: Promise.withResolvers(), closed: Promise.withResolvers(), - observe(aSubject, aTopic, aData) { + observe(aSubject, aTopic) { let win = aSubject; switch (aTopic) { case "domwindowopened": win.addEventListener( "load", - function onLoad(event) { + function onLoad() { // Let the dialog initialize SimpleTest.executeSoon(function () { UCTObserver.opened.resolve(win); diff --git a/toolkit/mozapps/downloads/tests/unit/test_lowMinutes.js b/toolkit/mozapps/downloads/tests/unit/test_lowMinutes.js index 786da28b75..9076fa7c76 100644 --- a/toolkit/mozapps/downloads/tests/unit/test_lowMinutes.js +++ b/toolkit/mozapps/downloads/tests/unit/test_lowMinutes.js @@ -20,7 +20,7 @@ const { DownloadUtils } = ChromeUtils.importESModule( * @usage _("Hello World") -> prints "Hello World" * @usage _(1, 2, 3) -> prints "1 2 3" */ -var _ = function (some, debug, text, to) { +var _ = function () { print(Array.from(arguments).join(" ")); }; diff --git a/toolkit/mozapps/extensions/AddonManager.sys.mjs b/toolkit/mozapps/extensions/AddonManager.sys.mjs index e4e51885cf..c69cf4029e 100644 --- a/toolkit/mozapps/extensions/AddonManager.sys.mjs +++ b/toolkit/mozapps/extensions/AddonManager.sys.mjs @@ -1610,10 +1610,10 @@ var AddonManagerInternal = { // Temporary hack until bug 520124 lands. // We can get here during synchronous startup, at which point it's - // considered unsafe (and therefore disallowed by AddonManager.jsm) to + // considered unsafe (and therefore disallowed by AddonManager.sys.mjs) to // access providers that haven't been initialized yet. Since this is when // XPIProvider is starting up, XPIProvider can't access itself via APIs - // going through AddonManager.jsm. Thankfully, this is the only use + // going through AddonManager.sys.mjs. Thankfully, this is the only use // of this API, and we know it's safe to use this API with both // providers; so we have this hack to allow bypassing the normal // safetey guard. diff --git a/toolkit/mozapps/extensions/content/aboutaddons.css b/toolkit/mozapps/extensions/content/aboutaddons.css index 3f4e80797a..2f5157bea3 100644 --- a/toolkit/mozapps/extensions/content/aboutaddons.css +++ b/toolkit/mozapps/extensions/content/aboutaddons.css @@ -643,12 +643,18 @@ panel-item[action="report"]::part(button) { text-decoration: none; } -.inline-options-stack { - /* If the options browser triggers an alert we need room to show it. */ - min-height: 250px; +.addon-inline-options { width: 100%; background-color: white; margin-block: 4px; + /* + * Makes sure the browser minimal height is going to be the same as when + * this browser element was wrapper in a stack and a min-height was necessary + * for the prompts to fit inside the browser element. + * That stack element has been removed as part of Bug 1881055, but keeping + * the min-height unchanged to avoid potential regressions in the short term. + */ + min-height: 250px; } addon-permissions-list > .addon-detail-row { diff --git a/toolkit/mozapps/extensions/content/aboutaddons.js b/toolkit/mozapps/extensions/content/aboutaddons.js index 37687a8be7..39c4656210 100644 --- a/toolkit/mozapps/extensions/content/aboutaddons.js +++ b/toolkit/mozapps/extensions/content/aboutaddons.js @@ -1423,10 +1423,23 @@ class SidebarFooter extends HTMLElement { labelL10nId: "addons-settings-button", onClick: e => { e.preventDefault(); - windowRoot.ownerGlobal.switchToTabHavingURI("about:preferences", true, { - ignoreFragment: "whenComparing", - triggeringPrincipal: systemPrincipal, - }); + let hasAboutSettings = windowRoot.ownerGlobal.switchToTabHavingURI( + "about:settings", + false, + { + ignoreFragment: "whenComparing", + } + ); + if (!hasAboutSettings) { + windowRoot.ownerGlobal.switchToTabHavingURI( + "about:preferences", + true, + { + ignoreFragment: "whenComparing", + triggeringPrincipal: systemPrincipal, + } + ); + } }, }); @@ -1695,6 +1708,7 @@ class InlineOptionsBrowser extends HTMLElement { browser.setAttribute("disableglobalhistory", "true"); browser.setAttribute("messagemanagergroup", "webext-browsers"); browser.setAttribute("id", "addon-inline-options"); + browser.setAttribute("class", "addon-inline-options"); browser.setAttribute("transparent", "true"); browser.setAttribute("forcemessagemanager", "true"); browser.setAttribute("autocompletepopup", "PopupAutoComplete"); @@ -1731,10 +1745,7 @@ class InlineOptionsBrowser extends HTMLElement { readyPromise = promiseEvent("load", browser, true); } - let stack = document.createXULElement("stack"); - stack.classList.add("inline-options-stack"); - stack.appendChild(browser); - this.appendChild(stack); + this.appendChild(browser); this.browser = browser; // Force bindings to apply synchronously. diff --git a/toolkit/mozapps/extensions/content/aboutaddonsCommon.js b/toolkit/mozapps/extensions/content/aboutaddonsCommon.js index 739e7629d7..9315e35861 100644 --- a/toolkit/mozapps/extensions/content/aboutaddonsCommon.js +++ b/toolkit/mozapps/extensions/content/aboutaddonsCommon.js @@ -237,7 +237,11 @@ async function installAddonsFromFilePicker() { ]); const nsIFilePicker = Ci.nsIFilePicker; var fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker); - fp.init(window, dialogTitle.value, nsIFilePicker.modeOpenMultiple); + fp.init( + window.browsingContext, + dialogTitle.value, + nsIFilePicker.modeOpenMultiple + ); try { fp.appendFilter(filterName.value, "*.xpi;*.jar;*.zip"); fp.appendFilters(nsIFilePicker.filterAll); diff --git a/toolkit/mozapps/extensions/internal/AddonRepository.sys.mjs b/toolkit/mozapps/extensions/internal/AddonRepository.sys.mjs index e854e04b3c..e53e4af7a4 100644 --- a/toolkit/mozapps/extensions/internal/AddonRepository.sys.mjs +++ b/toolkit/mozapps/extensions/internal/AddonRepository.sys.mjs @@ -78,7 +78,7 @@ import { Log } from "resource://gre/modules/Log.sys.mjs"; const LOGGER_ID = "addons.repository"; // Create a new logger for use by the Addons Repository -// (Requires AddonManager.jsm) +// (Requires AddonManager.sys.mjs) var logger = Log.repository.getLogger(LOGGER_ID); function convertHTMLToPlainText(html) { diff --git a/toolkit/mozapps/extensions/internal/AddonTestUtils.sys.mjs b/toolkit/mozapps/extensions/internal/AddonTestUtils.sys.mjs index 7b30daa0e2..f53d32092d 100644 --- a/toolkit/mozapps/extensions/internal/AddonTestUtils.sys.mjs +++ b/toolkit/mozapps/extensions/internal/AddonTestUtils.sys.mjs @@ -298,7 +298,7 @@ export var AddonTestUtils = { // And scan for changes at startup Services.prefs.setIntPref("extensions.startupScanScopes", 15); - // By default, don't cache add-ons in AddonRepository.jsm + // By default, don't cache add-ons in AddonRepository.sys.mjs Services.prefs.setBoolPref("extensions.getAddons.cache.enabled", false); // Point update checks to the local machine for fast failures @@ -567,7 +567,7 @@ export var AddonTestUtils = { }, overrideCertDB() { - let verifyCert = async (file, result, cert, callback) => { + let verifyCert = async (file, result, signatureInfos, callback) => { if ( result == Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED && !this.useRealCertChecks && @@ -606,7 +606,16 @@ export var AddonTestUtils = { }; } - return [callback, Cr.NS_OK, fakeCert]; + return [ + callback, + Cr.NS_OK, + [ + { + signerCert: fakeCert, + signatureAlgorithm: Ci.nsIAppSignatureInfo.COSE_WITH_SHA256, + }, + ], + ]; } catch (e) { // If there is any error then just pass along the original results } finally { @@ -621,7 +630,7 @@ export var AddonTestUtils = { } } - return [callback, result, cert]; + return [callback, result, signatureInfos]; }; let FakeCertDB = { @@ -644,10 +653,14 @@ export var AddonTestUtils = { this._genuine.openSignedAppFileAsync( root, file, - (result, zipReader, cert) => { - verifyCert(file.clone(), result, cert, callback).then( - ([callback, result, cert]) => { - callback.openSignedAppFileFinished(result, zipReader, cert); + (result, zipReader, signatureInfos) => { + verifyCert(file.clone(), result, signatureInfos, callback).then( + ([callback, result, signatureInfos]) => { + callback.openSignedAppFileFinished( + result, + zipReader, + signatureInfos + ); } ); } @@ -1730,7 +1743,7 @@ export var AddonTestUtils = { * @param {object} extension * The return value of ExtensionTestUtils.loadExtension. * For browser tests, see mochitest/tests/SimpleTest/ExtensionTestUtils.js - * For xpcshell tests, see toolkit/components/extensions/ExtensionXPCShellUtils.jsm + * For xpcshell tests, see toolkit/components/extensions/ExtensionXPCShellUtils.sys.mjs * @param {object} [options] * Optional options. * @param {boolean} [options.expectPending = false] diff --git a/toolkit/mozapps/extensions/internal/AddonUpdateChecker.sys.mjs b/toolkit/mozapps/extensions/internal/AddonUpdateChecker.sys.mjs index a3935a26f9..5b7b10a764 100644 --- a/toolkit/mozapps/extensions/internal/AddonUpdateChecker.sys.mjs +++ b/toolkit/mozapps/extensions/internal/AddonUpdateChecker.sys.mjs @@ -26,7 +26,7 @@ import { Log } from "resource://gre/modules/Log.sys.mjs"; const LOGGER_ID = "addons.update-checker"; // Create a new logger for use by the Addons Update Checker -// (Requires AddonManager.jsm) +// (Requires AddonManager.sys.mjs) var logger = Log.repository.getLogger(LOGGER_ID); /** diff --git a/toolkit/mozapps/extensions/internal/SitePermsAddonProvider.sys.mjs b/toolkit/mozapps/extensions/internal/SitePermsAddonProvider.sys.mjs index ccac484a1e..7ca952dc8a 100644 --- a/toolkit/mozapps/extensions/internal/SitePermsAddonProvider.sys.mjs +++ b/toolkit/mozapps/extensions/internal/SitePermsAddonProvider.sys.mjs @@ -148,20 +148,30 @@ class SitePermsAddonWrapper { }); } - get creator() {} + get creator() { + return undefined; + } - get homepageURL() {} + get homepageURL() { + return undefined; + } - get description() {} + get description() { + return undefined; + } - get fullDescription() {} + get fullDescription() { + return undefined; + } get version() { // We consider the previous implementation attempt (signed addons) to be the initial version, // hence the 2.0 for this approach. return "2.0"; } - get updateDate() {} + get updateDate() { + return undefined; + } get isActive() { return true; @@ -273,8 +283,6 @@ class SitePermsAddonWrapper { } class SitePermsAddonInstalling extends SitePermsAddonWrapper { - #install = null; - /** * @param {string} siteOriginNoSuffix: The origin this addon is installed * for, WITHOUT the suffix generated from @@ -293,7 +301,6 @@ class SitePermsAddonInstalling extends SitePermsAddonWrapper { }; super(siteOriginNoSuffix, [permission]); - this.#install = install; } get existingAddon() { diff --git a/toolkit/mozapps/extensions/internal/XPIDatabase.sys.mjs b/toolkit/mozapps/extensions/internal/XPIDatabase.sys.mjs index 5d1d2c1970..af0b02444a 100644 --- a/toolkit/mozapps/extensions/internal/XPIDatabase.sys.mjs +++ b/toolkit/mozapps/extensions/internal/XPIDatabase.sys.mjs @@ -117,7 +117,7 @@ const nsIFile = Components.Constructor( ); // Create a new logger for use by the Addons XPI Provider Utils -// (Requires AddonManager.jsm) +// (Requires AddonManager.sys.mjs) var logger = Log.repository.getLogger(LOGGER_ID); const FILE_JSON_DB = "extensions.json"; diff --git a/toolkit/mozapps/extensions/internal/XPIInstall.sys.mjs b/toolkit/mozapps/extensions/internal/XPIInstall.sys.mjs index 1a80407ad2..0402c2f2ca 100644 --- a/toolkit/mozapps/extensions/internal/XPIInstall.sys.mjs +++ b/toolkit/mozapps/extensions/internal/XPIInstall.sys.mjs @@ -99,6 +99,17 @@ const PREF_SELECTED_THEME = "extensions.activeThemeID"; const TOOLKIT_ID = "toolkit@mozilla.org"; +ChromeUtils.defineLazyGetter(lazy, "MOZ_UNSIGNED_SCOPES", () => { + let result = 0; + if (AppConstants.MOZ_UNSIGNED_APP_SCOPE) { + result |= AddonManager.SCOPE_APPLICATION; + } + if (AppConstants.MOZ_UNSIGNED_SYSTEM_SCOPE) { + result |= AddonManager.SCOPE_SYSTEM; + } + return result; +}); + /** * Returns a nsIFile instance for the given path, relative to the given * base file, if provided. @@ -168,7 +179,7 @@ import { Log } from "resource://gre/modules/Log.sys.mjs"; const LOGGER_ID = "addons.xpi"; // Create a new logger for use by all objects in this Addons XPI Provider module -// (Requires AddonManager.jsm) +// (Requires AddonManager.sys.mjs) var logger = Log.repository.getLogger(LOGGER_ID); // Stores the ID of the theme which was selected during the last session, @@ -316,13 +327,22 @@ XPIPackage = class XPIPackage extends Package { verifySignedStateForRoot(addonId, root) { return new Promise(resolve => { let callback = { - openSignedAppFileFinished(aRv, aZipReader, aCert) { + openSignedAppFileFinished(aRv, aZipReader, aSignatureInfos) { + // aSignatureInfos is an array of nsIAppSignatureInfo. + // In the future, this code can iterate through the array to + // determine if one of the verified signatures used a satisfactory + // algorithm and signing certificate. + // For now, any verified signature is acceptable. + let cert; + if (aRv == Cr.NS_OK && aSignatureInfos.length) { + cert = aSignatureInfos[0].signerCert; + } if (aZipReader) { aZipReader.close(); } resolve({ - signedState: getSignedStatus(aRv, aCert, addonId), - cert: aCert, + signedState: getSignedStatus(aRv, cert, addonId), + cert, }); }, }; @@ -872,10 +892,7 @@ function shouldVerifySignedState(aAddonType, aLocation) { return true; } - if ( - aLocation.isBuiltin || - aLocation.scope & AppConstants.MOZ_UNSIGNED_SCOPES - ) { + if (aLocation.isBuiltin || aLocation.scope & lazy.MOZ_UNSIGNED_SCOPES) { return false; } diff --git a/toolkit/mozapps/extensions/internal/XPIProvider.sys.mjs b/toolkit/mozapps/extensions/internal/XPIProvider.sys.mjs index d5ffd06d11..12d4fa1172 100644 --- a/toolkit/mozapps/extensions/internal/XPIProvider.sys.mjs +++ b/toolkit/mozapps/extensions/internal/XPIProvider.sys.mjs @@ -185,7 +185,7 @@ import { Log } from "resource://gre/modules/Log.sys.mjs"; const LOGGER_ID = "addons.xpi"; // Create a new logger for use by all objects in this Addons XPI Provider module -// (Requires AddonManager.jsm) +// (Requires AddonManager.sys.mjs) var logger = Log.repository.getLogger(LOGGER_ID); /** diff --git a/toolkit/mozapps/extensions/test/browser/browser.toml b/toolkit/mozapps/extensions/test/browser/browser.toml index 1daf6211f8..0b4a2d8f0a 100644 --- a/toolkit/mozapps/extensions/test/browser/browser.toml +++ b/toolkit/mozapps/extensions/test/browser/browser.toml @@ -156,6 +156,8 @@ https_first_disabled = true ["browser_sidebar_hidden_categories.js"] +["browser_sidebar_preferences_button.js"] + ["browser_sidebar_restore_category.js"] ["browser_subframe_install.js"] diff --git a/toolkit/mozapps/extensions/test/browser/browser_html_options_ui.js b/toolkit/mozapps/extensions/test/browser/browser_html_options_ui.js index c5bfa1022f..efe28cf218 100644 --- a/toolkit/mozapps/extensions/test/browser/browser_html_options_ui.js +++ b/toolkit/mozapps/extensions/test/browser/browser_html_options_ui.js @@ -115,13 +115,7 @@ add_task(async function testInlineOptions() { "The browser has the expected options URL" ); is(url, card.addon.optionsURL, "Browser has the expected options URL loaded"); - let stack = browser.closest("stack"); - is( - browser.clientWidth, - stack.clientWidth, - "Browser should be the same width as its direct parent" - ); - Assert.greater(stack.clientWidth, 0, "The stack has a width"); + Assert.greater(browser.clientWidth, 0, "The browser has a width"); ok( card.querySelector('[action="preferences"]').hidden, "The preferences option is hidden now" @@ -163,8 +157,7 @@ add_task(async function testInlineOptions() { info("Switch back, check browser is shown"); prefsBtn.click(); - is(browser.clientWidth, stack.clientWidth, "The browser width is set again"); - Assert.greater(stack.clientWidth, 0, "The stack has a width"); + Assert.greater(browser.clientWidth, 0, "The browser has a width"); await closeView(win); await extension.unload(); diff --git a/toolkit/mozapps/extensions/test/browser/browser_page_options_install_addon.js b/toolkit/mozapps/extensions/test/browser/browser_page_options_install_addon.js index 5007731927..7bc7c08345 100644 --- a/toolkit/mozapps/extensions/test/browser/browser_page_options_install_addon.js +++ b/toolkit/mozapps/extensions/test/browser/browser_page_options_install_addon.js @@ -5,7 +5,7 @@ // Tests bug 567127 - Add install button to the add-ons manager var MockFilePicker = SpecialPowers.MockFilePicker; -MockFilePicker.init(window); +MockFilePicker.init(window.browsingContext); async function checkInstallConfirmation(...names) { let notificationCount = 0; diff --git a/toolkit/mozapps/extensions/test/browser/browser_sidebar_preferences_button.js b/toolkit/mozapps/extensions/test/browser/browser_sidebar_preferences_button.js new file mode 100644 index 0000000000..91a631bca2 --- /dev/null +++ b/toolkit/mozapps/extensions/test/browser/browser_sidebar_preferences_button.js @@ -0,0 +1,73 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function testNoOtherTabsPresent() { + let addonsWin = await loadInitialView("extension"); + let preferencesButton = + addonsWin.document.querySelector("#preferencesButton"); + + let preferencesPromise = BrowserTestUtils.waitForNewTab( + gBrowser, + "about:preferences" + ); + + preferencesButton.click(); + + let preferencesTab = await preferencesPromise; + + is( + gBrowser.currentURI.spec, + "about:preferences", + "about:preferences should open if neither it nor about:settings are present" + ); + + gBrowser.removeTab(preferencesTab); + + await closeView(addonsWin); +}); + +async function ensurePreferencesButtonFocusesTab(expectedUri) { + let addonsWin = await loadInitialView("extension"); + let preferencesButton = + addonsWin.document.querySelector("#preferencesButton"); + + let tabCountBeforeClick = gBrowser.tabCount; + preferencesButton.click(); + let tabCountAfterClick = gBrowser.tabCount; + + is( + tabCountAfterClick, + tabCountBeforeClick, + "preferences button should not open new tabs" + ); + is( + gBrowser.currentURI.spec, + expectedUri, + "the correct tab should be focused" + ); + + addonsWin.focus(); + await closeView(addonsWin); +} + +add_task(async function testAboutPreferencesPresent() { + await BrowserTestUtils.withNewTab("about:preferences", async () => { + await ensurePreferencesButtonFocusesTab("about:preferences"); + }); +}); + +add_task(async function testAboutSettingsPresent() { + await BrowserTestUtils.withNewTab("about:settings", async () => { + await ensurePreferencesButtonFocusesTab("about:settings"); + }); +}); + +add_task(async function testAboutSettingsAndPreferencesPresent() { + await BrowserTestUtils.withNewTab("about:settings", async () => { + await BrowserTestUtils.withNewTab("about:preferences", async () => { + await ensurePreferencesButtonFocusesTab("about:settings"); + }); + }); +}); diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_corrupt.js b/toolkit/mozapps/extensions/test/xpcshell/test_corrupt.js index 727c643763..33e81c13bd 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/test_corrupt.js +++ b/toolkit/mozapps/extensions/test/xpcshell/test_corrupt.js @@ -196,7 +196,7 @@ add_task(async function test_after_corruption() { await Assert.rejects( promiseShutdownManager(), - /NotAllowedError: Could not open the file at .+ for writing/ + /NotAllowedError: Could not write to `.+'/ ); }); @@ -211,6 +211,6 @@ add_task(async function test_after_second_restart() { await Assert.rejects( promiseShutdownManager(), - /NotAllowedError: Could not open the file at .+ for writing/ + /NotAllowedError: Could not write to `.+'/ ); }); diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_system_update_enterprisepolicy.js b/toolkit/mozapps/extensions/test/xpcshell/test_system_update_enterprisepolicy.js index d36b97a3cc..e25890515f 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/test_system_update_enterprisepolicy.js +++ b/toolkit/mozapps/extensions/test/xpcshell/test_system_update_enterprisepolicy.js @@ -44,12 +44,32 @@ const TEST_CONDITIONS = { add_task(async function test_update_disabled_by_policy() { await setupSystemAddonConditions(TEST_CONDITIONS, distroDir); + const TEST_POLICY_DATA = { + DisableSystemAddonUpdate: true, + }; await EnterprisePolicyTesting.setupPolicyEngineWithJson({ - policies: { - DisableSystemAddonUpdate: true, - }, + policies: TEST_POLICY_DATA, }); + Assert.deepEqual( + Services.policies.getActivePolicies(), + TEST_POLICY_DATA, + "Got the expected test policy data as the active policy " + + "(if this assertions fails, check your system for enterprise policies installed at system level)" + ); + + Assert.equal( + Services.policies.isAllowed("SysAddonUpdate"), + false, + "Expected SysAddonUpdate feature to be disabled by policies" + ); + + Assert.equal( + Services.prefs.getBoolPref("extensions.systemAddon.update.enabled"), + true, + "Expected system addon updates to not be already disabled through prefs" + ); + await updateAllSystemAddons( buildSystemAddonUpdates([ { diff --git a/toolkit/mozapps/handling/content/appChooser.js b/toolkit/mozapps/handling/content/appChooser.js index 2958ad68b4..6c04717783 100644 --- a/toolkit/mozapps/handling/content/appChooser.js +++ b/toolkit/mozapps/handling/content/appChooser.js @@ -225,7 +225,7 @@ let dialog = { let title = await this.getChooseAppWindowTitle(); var fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker); - fp.init(window, title, Ci.nsIFilePicker.modeOpen); + fp.init(window.browsingContext, title, Ci.nsIFilePicker.modeOpen); fp.appendFilters(Ci.nsIFilePicker.filterApps); fp.open(rv => { diff --git a/toolkit/mozapps/installer/packager.py b/toolkit/mozapps/installer/packager.py index 29364045dd..f9d28d9c0c 100644 --- a/toolkit/mozapps/installer/packager.py +++ b/toolkit/mozapps/installer/packager.py @@ -63,7 +63,7 @@ class RemovedFiles(GeneratedFile): return if self.copier.contains(f): errors.error("Removal of packaged file(s): %s" % f) - self.content = f + "\n" + self.content += f.encode("utf-8") + b"\n" def split_define(define): diff --git a/toolkit/mozapps/update/AppUpdater.sys.mjs b/toolkit/mozapps/update/AppUpdater.sys.mjs index 6133fb613b..f837982b84 100644 --- a/toolkit/mozapps/update/AppUpdater.sys.mjs +++ b/toolkit/mozapps/update/AppUpdater.sys.mjs @@ -411,20 +411,6 @@ export class AppUpdater { return Services.sysinfo.getProperty("isPackagedApp"); } - // true when updating in background is enabled. - get #updateStagingEnabled() { - LOG( - "AppUpdater:#updateStagingEnabled" + - "canStageUpdates: " + - this.aus.canStageUpdates - ); - return ( - !this.aus.disabled && - !this.#updateDisabledByPackage && - this.aus.canStageUpdates - ); - } - /** * Downloads an update mar or connects to an in-progress download. * Doesn't resolve until the update is ready to install, or a failure state diff --git a/toolkit/mozapps/update/BackgroundUpdate.sys.mjs b/toolkit/mozapps/update/BackgroundUpdate.sys.mjs index 28d0fc8538..3022c9ffde 100644 --- a/toolkit/mozapps/update/BackgroundUpdate.sys.mjs +++ b/toolkit/mozapps/update/BackgroundUpdate.sys.mjs @@ -570,7 +570,7 @@ export var BackgroundUpdate = { }; try { - // Interacting with `TaskScheduler.jsm` can throw, so we'll catch. + // Interacting with `TaskScheduler.sys.mjs` can throw, so we'll catch. if (!enabled) { lazy.log.info( `${SLUG}: not scheduling background update: '${JSON.stringify( diff --git a/toolkit/mozapps/update/UpdateService.sys.mjs b/toolkit/mozapps/update/UpdateService.sys.mjs index 16ebf64ef3..34bf1b9e19 100644 --- a/toolkit/mozapps/update/UpdateService.sys.mjs +++ b/toolkit/mozapps/update/UpdateService.sys.mjs @@ -193,7 +193,7 @@ const WRITE_ERROR_DIR_ACCESS_DENIED = 68; const WRITE_ERROR_DELETE_BACKUP = 69; const WRITE_ERROR_EXTRACT = 70; -// Error codes 80 through 99 are reserved for UpdateService.jsm and are not +// Error codes 80 through 99 are reserved for UpdateService.sys.mjs and are not // defined in common/updatererrors.h const ERR_UPDATER_CRASHED = 89; const ERR_OLDER_VERSION_OR_SAME_BUILD = 90; @@ -440,7 +440,7 @@ let gOtherInstancePollPromise; * * @return true if at least one other instance is running, false if not */ -function isOtherInstanceRunning(callback) { +function isOtherInstanceRunning() { const checkEnabled = Services.prefs.getBoolPref( PREF_APP_UPDATE_CHECK_ONLY_INSTANCE_ENABLED, true @@ -497,7 +497,7 @@ function waitForOtherInstances() { let iterations = 0; const maxIterations = Math.ceil(timeout / interval); - gOtherInstancePollPromise = new Promise(function (resolve, reject) { + gOtherInstancePollPromise = new Promise(function (resolve) { let poll = function () { iterations++; if (!isOtherInstanceRunning()) { @@ -1907,7 +1907,7 @@ function pingStateAndStatusCodes(aUpdate, aStartup, aStatus) { stateCode = 13; break; // Note: Do not use stateCode 14 here. It is defined in - // UpdateTelemetry.jsm + // UpdateTelemetry.sys.mjs default: stateCode = 1; } @@ -3352,10 +3352,10 @@ UpdateService.prototype = { /** * Notified when a timer fires - * @param timer + * @param _timer * The timer that fired */ - notify: function AUS_notify(timer) { + notify: function AUS_notify(_timer) { this._checkForBackgroundUpdates(true); }, @@ -3399,7 +3399,7 @@ UpdateService.prototype = { // See Bug 1599590. // Note that we exit unconditionally here if we are only doing manual // update checks, because manual update checking uses a completely - // different code path (AppUpdater.jsm creates its own nsIUpdateChecker), + // different code path (AppUpdater.sys.mjs creates its own nsIUpdateChecker), // bypassing this function completely. AUSTLMY.pingCheckCode(this._pingSuffix, AUSTLMY.CHK_DISABLED_BY_POLICY); return false; @@ -4907,7 +4907,7 @@ UpdateManager.prototype = { /** * See nsIUpdateService.idl */ - doInstallCleanup: async function UM_doInstallCleanup(isUninstall) { + doInstallCleanup: async function UM_doInstallCleanup() { LOG("UpdateManager:doInstallCleanup - cleaning up"); let completionPromises = []; @@ -4949,7 +4949,7 @@ UpdateManager.prototype = { /** * See nsIUpdateService.idl */ - doUninstallCleanup: async function UM_doUninstallCleanup(isUninstall) { + doUninstallCleanup: async function UM_doUninstallCleanup() { LOG("UpdateManager:doUninstallCleanup - cleaning up."); let completionPromises = []; @@ -5272,13 +5272,13 @@ export class CheckerService { return; } - let onLoad = event => { + let onLoad = _event => { request.removeEventListener("load", onLoad); LOG("CheckerService:#updateCheck - request got 'load' event"); resolve(UPDATE_CHECK_LOAD_SUCCESS); }; request.addEventListener("load", onLoad); - let onError = event => { + let onError = _event => { request.removeEventListener("error", onLoad); LOG("CheckerService:#updateCheck - request got 'error' event"); resolve(UPDATE_CHECK_LOAD_ERROR); @@ -5702,11 +5702,9 @@ Downloader.prototype = { * set of update patches. * @param update * A nsIUpdate object to select a patch from - * @param updateDir - * A nsIFile representing the update directory * @return A nsIUpdatePatch object to download */ - _selectPatch: function Downloader__selectPatch(update, updateDir) { + _selectPatch: function Downloader__selectPatch(update) { // Given an update to download, we will always try to download the patch // for a partial update over the patch for a full update. @@ -5934,7 +5932,7 @@ Downloader.prototype = { // This function may return null, which indicates that there are no patches // to download. - this._patch = this._selectPatch(update, updateDir); + this._patch = this._selectPatch(update); if (!this._patch) { LOG("Downloader:downloadUpdate - no patch to download"); AUSTLMY.pingDownloadCode(undefined, AUSTLMY.DWNLD_ERR_NO_UPDATE_PATCH); diff --git a/toolkit/mozapps/update/UpdateTelemetry.sys.mjs b/toolkit/mozapps/update/UpdateTelemetry.sys.mjs index 20fb0ab4a4..f3c350267c 100644 --- a/toolkit/mozapps/update/UpdateTelemetry.sys.mjs +++ b/toolkit/mozapps/update/UpdateTelemetry.sys.mjs @@ -316,7 +316,7 @@ export var AUSTLMY = { * This value is also used to determine the key for the keyed scalar * update.bitshresult (key is either "COMPLETE" or "PARTIAL") * @param aError - * The BitsError that occurred. See Bits.jsm for details on BitsError. + * The BitsError that occurred. See Bits.sys.mjs for details on BitsError. */ pingBitsError: function UT_pingBitsError(aIsComplete, aError) { if (AppConstants.platform != "win") { diff --git a/toolkit/mozapps/update/common/updatererrors.h b/toolkit/mozapps/update/common/updatererrors.h index f2663d5b57..d3abf14ffe 100644 --- a/toolkit/mozapps/update/common/updatererrors.h +++ b/toolkit/mozapps/update/common/updatererrors.h @@ -10,7 +10,7 @@ #define OK 0 // Error codes that are no longer used should not be used again unless they -// aren't used in client code (e.g. UpdateService.jsm, updates.js, etc.). +// aren't used in client code (e.g. UpdateService.sys.mjs, updates.js, etc.). #define MAR_ERROR_EMPTY_ACTION_LIST 1 #define LOADSOURCE_ERROR_WRONG_SIZE 2 @@ -26,8 +26,8 @@ // Error codes 10-14 are related to memory allocation failures. // Note: If more memory allocation error codes are added, the implementation of -// isMemoryAllocationErrorCode in UpdateService.jsm should be updated to account -// for them. +// isMemoryAllocationErrorCode in UpdateService.sys.mjs should be updated to +// account for them. #define READ_STRINGS_MEM_ERROR 10 #define ARCHIVE_READER_MEM_ERROR 11 #define BSPATCH_MEM_ERROR 12 @@ -49,8 +49,8 @@ // Error codes 24-33 and 49-58 are for the Windows maintenance service. // Note: If more maintenance service error codes are added, the implementations -// of IsServiceSpecificErrorCode in updater.cpp and UpdateService.jsm should be -// updated to account for them. +// of IsServiceSpecificErrorCode in updater.cpp and UpdateService.sys.mjs should +// be updated to account for them. #define SERVICE_UPDATER_COULD_NOT_BE_STARTED 24 #define SERVICE_NOT_ENOUGH_COMMAND_LINE_ARGS 25 #define SERVICE_UPDATER_SIGN_ERROR 26 @@ -79,8 +79,8 @@ // Error codes 24-33 and 49-58 are for the Windows maintenance service. // Note: If more maintenance service error codes are added, the implementations -// of IsServiceSpecificErrorCode in updater.cpp and UpdateService.jsm should be -// updated to account for them. +// of IsServiceSpecificErrorCode in updater.cpp and UpdateService.sys.mjs should +// be updated to account for them. #define SERVICE_COULD_NOT_COPY_UPDATER 49 #define SERVICE_STILL_APPLYING_TERMINATED 50 #define SERVICE_STILL_APPLYING_NO_EXIT_CODE 51 @@ -112,7 +112,7 @@ #define INVALID_CALLBACK_DIR_ERROR 78 #define UPDATE_STATUS_UNCHANGED 79 -// Error codes 80 through 99 are reserved for UpdateService.jsm +// Error codes 80 through 99 are reserved for UpdateService.sys.mjs // The following error codes are only used by updater.exe // when a fallback key exists for tests. @@ -125,6 +125,6 @@ #define SILENT_UPDATE_NEEDED_ELEVATION_ERROR 105 #define WRITE_ERROR_BACKGROUND_TASK_SHARING_VIOLATION 106 -// Error codes 110 and 111 are reserved for UpdateService.jsm +// Error codes 110 and 111 are reserved for UpdateService.sys.mjs #endif // UPDATEERRORS_H diff --git a/toolkit/mozapps/update/docs/BackgroundUpdates.rst b/toolkit/mozapps/update/docs/BackgroundUpdates.rst index 7f97f58c74..d8a154cb39 100644 --- a/toolkit/mozapps/update/docs/BackgroundUpdates.rst +++ b/toolkit/mozapps/update/docs/BackgroundUpdates.rst @@ -65,7 +65,7 @@ observe and control these settings. But there are some other pieces of state which absolutely must come from a profile, such as the telemetry client ID and logging level settings (see -`BackgroundTasksUtils.jsm <https://searchfox.org/mozilla-central/source/toolkit/components/backgroundtasks/BackgroundTasksUtils.jsm>`__). +`BackgroundTasksUtils.sys.mjs <https://searchfox.org/mozilla-central/source/toolkit/components/backgroundtasks/BackgroundTasksUtils.sys.mjs>`__). This means that, in addition to our per-installation prefs, we also need to be able to identify and load a profile. To do that, we leverage `the profile @@ -137,7 +137,7 @@ API <https://docs.microsoft.com/en-us/windows/win32/taskschd/task-scheduler-star on macOS this will use `launchd <https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html>`__. For platform-specific scheduling details, see the -`TaskScheduler.jsm <https://searchfox.org/mozilla-central/source/toolkit/components/taskscheduler/TaskScheduler.jsm>`__ +`TaskScheduler.sys.mjs <https://searchfox.org/mozilla-central/source/toolkit/components/taskscheduler/TaskScheduler.sys.mjs>`__ module. These background tasks are scheduled per OS user and run with that user’s @@ -177,7 +177,7 @@ visible to users: see `bug 1775132 After setting up this profile and reading all the configuration we need into it, the regular -`UpdateService.jsm <https://searchfox.org/mozilla-central/source/toolkit/mozapps/update/UpdateService.jsm>`__ +`UpdateService.sys.mjs <https://searchfox.org/mozilla-central/source/toolkit/mozapps/update/UpdateService.sys.mjs>`__ check process is initiated. To the greatest extent possible, this process is identical to what happens during any regular browsing session. diff --git a/toolkit/mozapps/update/tests/browser/browser_elevationDialog.js b/toolkit/mozapps/update/tests/browser/browser_elevationDialog.js index 6aa32a7fc9..639af1ffff 100644 --- a/toolkit/mozapps/update/tests/browser/browser_elevationDialog.js +++ b/toolkit/mozapps/update/tests/browser/browser_elevationDialog.js @@ -13,7 +13,7 @@ add_task(async function elevation_dialog() { let { startup } = Services; let appStartup = { QueryInterface: ChromeUtils.generateQI(["nsIAppStartup"]), - quit(mode) { + quit(_mode) { if (elevationDialog) { elevationDialog.close(); elevationDialog = null; @@ -120,7 +120,7 @@ function waitForElevationDialog() { var domwindow = aXULWindow.docShell.domWindow; domwindow.addEventListener("load", elevationDialogOnLoad, true); }, - onCloseWindow: aXULWindow => {}, + onCloseWindow: _aXULWindow => {}, }; Services.wm.addListener(listener); diff --git a/toolkit/mozapps/update/tests/browser/head.js b/toolkit/mozapps/update/tests/browser/head.js index a18a72e581..c5acdad8e4 100644 --- a/toolkit/mozapps/update/tests/browser/head.js +++ b/toolkit/mozapps/update/tests/browser/head.js @@ -487,7 +487,7 @@ function waitForAboutDialog() { var domwindow = aXULWindow.docShell.domWindow; domwindow.addEventListener("load", aboutDialogOnLoad, true); }, - onCloseWindow: aXULWindow => {}, + onCloseWindow: _aXULWindow => {}, }; Services.wm.addListener(listener); diff --git a/toolkit/mozapps/update/tests/data/app_update.sjs b/toolkit/mozapps/update/tests/data/app_update.sjs index 2081118547..1d5d23bfb5 100644 --- a/toolkit/mozapps/update/tests/data/app_update.sjs +++ b/toolkit/mozapps/update/tests/data/app_update.sjs @@ -79,7 +79,7 @@ function handleRequest(aRequest, aResponse) { let retries = 0; gSlowDownloadTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); gSlowDownloadTimer.initWithCallback( - function (aTimer) { + function (_aTimer) { let continueFile = getTestDataFile(CONTINUE_DOWNLOAD); retries++; if (continueFile.exists() || retries == MAX_SLOW_RESPONSE_RETRIES) { @@ -197,7 +197,7 @@ function respond(aResponse, aParams, aResponseString) { aResponse.processAsync(); gSlowCheckTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); gSlowCheckTimer.initWithCallback( - function (aTimer) { + function (_aTimer) { retries++; let continueFile = getTestDataFile(CONTINUE_CHECK); if (continueFile.exists() || retries == MAX_SLOW_RESPONSE_RETRIES) { diff --git a/toolkit/mozapps/update/tests/data/shared.js b/toolkit/mozapps/update/tests/data/shared.js index fc3d358586..60de6feeb8 100644 --- a/toolkit/mozapps/update/tests/data/shared.js +++ b/toolkit/mozapps/update/tests/data/shared.js @@ -922,7 +922,7 @@ async function continueFileHandler(leafName) { "Waiting for file to be deleted, path: " + continueFile.path, interval, retries - ).catch(e => { + ).catch(_e => { logTestInfo( "Continue file was not removed after checking " + retries + diff --git a/toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js b/toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js index e88a4418cb..4b12edf6f0 100644 --- a/toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js +++ b/toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js @@ -3766,7 +3766,7 @@ function checkFilesAfterUpdateSuccess( "xattr value changed" ); }, - reason => { + _reason => { Assert.fail(MAC_APP_XATTR_KEY + " xattr is missing!"); } ); @@ -4233,9 +4233,9 @@ async function waitForUpdateDownload(aUpdates, aExpectedStatus) { } return new Promise(resolve => gAUS.addDownloadListener({ - onStartRequest: aRequest => {}, - onProgress: (aRequest, aContext, aProgress, aMaxProgress) => {}, - onStatus: (aRequest, aStatus, aStatusText) => {}, + onStartRequest: _aRequest => {}, + onProgress: (_aRequest, _aContext, _aProgress, _aMaxProgress) => {}, + onStatus: (_aRequest, _aStatus, _aStatusText) => {}, onStopRequest(request, status) { gAUS.removeDownloadListener(this); Assert.equal( @@ -4552,7 +4552,7 @@ function adjustGeneralPaths() { * The timer callback to kill the process if it takes too long. */ const gAppTimerCallback = { - notify: function TC_notify(aTimer) { + notify: function TC_notify(_aTimer) { gAppTimer = null; if (gProcess.isRunning) { logTestInfo("attempting to kill process"); @@ -4662,7 +4662,7 @@ function IncrementalDownload() { IncrementalDownload.prototype = { /* nsIIncrementalDownload */ - init(uri, file, chunkSize, intervalInSeconds) { + init(uri, file, _chunkSize, _intervalInSeconds) { this._destination = file; this._URI = uri; this._finalURI = uri; @@ -4729,7 +4729,7 @@ IncrementalDownload.prototype = { }, /* nsIRequest */ - cancel(aStatus) { + cancel(_aStatus) { throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); }, suspend() { diff --git a/toolkit/mozapps/update/tests/marionette/test_no_window_update_restart.py b/toolkit/mozapps/update/tests/marionette/test_no_window_update_restart.py index a8495064ef..9452a4fcd7 100644 --- a/toolkit/mozapps/update/tests/marionette/test_no_window_update_restart.py +++ b/toolkit/mozapps/update/tests/marionette/test_no_window_update_restart.py @@ -162,7 +162,9 @@ class TestNoWindowUpdateRestart(MarionetteTestCase): let UM = Cc["@mozilla.org/updates/update-manager;1"].getService(Ci.nsIUpdateManager); UM.QueryInterface(Ci.nsIObserver).observe(null, "um-reload-update-data", "skip-files"); - let { UpdateListener } = ChromeUtils.import("resource://gre/modules/UpdateListener.jsm"); + let { UpdateListener } = ChromeUtils.importESModule( + "resource://gre/modules/UpdateListener.sys.mjs" + ); UpdateListener.reset(); let { AppMenuNotifications } = ChromeUtils.importESModule( diff --git a/toolkit/mozapps/update/tests/unit_aus_update/languagePackUpdates.js b/toolkit/mozapps/update/tests/unit_aus_update/languagePackUpdates.js index 0ae35ef77a..9e23fab5e0 100644 --- a/toolkit/mozapps/update/tests/unit_aus_update/languagePackUpdates.js +++ b/toolkit/mozapps/update/tests/unit_aus_update/languagePackUpdates.js @@ -243,9 +243,9 @@ add_task(async function testRedownload() { let downloadCount = 0; let listener = { - onStartRequest: aRequest => {}, - onProgress: (aRequest, aContext, aProgress, aMaxProgress) => {}, - onStatus: (aRequest, aStatus, aStatusText) => {}, + onStartRequest: _aRequest => {}, + onProgress: (_aRequest, _aContext, _aProgress, _aMaxProgress) => {}, + onStatus: (_aRequest, _aStatus, _aStatusText) => {}, onStopRequest: (request, status) => { Assert.equal( status, diff --git a/toolkit/mozapps/update/tests/unit_aus_update/multiUpdate.js b/toolkit/mozapps/update/tests/unit_aus_update/multiUpdate.js index 3767a44feb..411b303331 100644 --- a/toolkit/mozapps/update/tests/unit_aus_update/multiUpdate.js +++ b/toolkit/mozapps/update/tests/unit_aus_update/multiUpdate.js @@ -73,14 +73,14 @@ async function downloadUpdate(appUpdateAuto, onDownloadStartCallback) { } let waitToStartPromise = new Promise(resolve => { let listener = { - onStartRequest: aRequest => { + onStartRequest: _aRequest => { gAUS.removeDownloadListener(listener); onDownloadStartCallback(); resolve(); }, - onProgress: (aRequest, aContext, aProgress, aMaxProgress) => {}, - onStatus: (aRequest, aStatus, aStatusText) => {}, - onStopRequest(request, status) {}, + onProgress: (_aRequest, _aContext, _aProgress, _aMaxProgress) => {}, + onStatus: (_aRequest, _aStatus, _aStatusText) => {}, + onStopRequest(_request, _status) {}, QueryInterface: ChromeUtils.generateQI([ "nsIRequestObserver", "nsIProgressEventSink", @@ -149,7 +149,7 @@ async function testUpdateDoesNotDownload() { ); let updateAvailableObserved = false; - let observer = (subject, topic, status) => { + let observer = (_subject, _topic, _status) => { updateAvailableObserved = true; }; Services.obs.addObserver(observer, "update-available"); diff --git a/toolkit/mozapps/update/tests/unit_aus_update/updateSyncManager.js b/toolkit/mozapps/update/tests/unit_aus_update/updateSyncManager.js index 9123f9e1e3..c705d536a4 100644 --- a/toolkit/mozapps/update/tests/unit_aus_update/updateSyncManager.js +++ b/toolkit/mozapps/update/tests/unit_aus_update/updateSyncManager.js @@ -79,7 +79,7 @@ add_task(async function () { await TestUtils.waitForCondition( () => syncManager.isOtherInstanceRunning(), "waiting for child process to take the lock" - ).catch(e => { + ).catch(_e => { // Rather than throwing out of waitForCondition(), catch and log the failure // manually so that we get output that's a bit more readable. Assert.ok( @@ -93,7 +93,7 @@ add_task(async function () { await TestUtils.waitForCondition( () => !syncManager.isOtherInstanceRunning(), "waiting for child process to release the lock" - ).catch(e => { + ).catch(_e => { Assert.ok( !syncManager.isOtherInstanceRunning(), "child process has released the lock" diff --git a/toolkit/mozapps/update/tests/unit_background_update/test_backgroundupdate_glean.js b/toolkit/mozapps/update/tests/unit_background_update/test_backgroundupdate_glean.js index 0d9c06b2d0..0ca74e4764 100644 --- a/toolkit/mozapps/update/tests/unit_background_update/test_backgroundupdate_glean.js +++ b/toolkit/mozapps/update/tests/unit_background_update/test_backgroundupdate_glean.js @@ -140,7 +140,7 @@ async function do_readTargeting(content, beforeNextSubmitCallback) { // Missing targeting is anticipated. add_task(async function test_targeting_missing() { - await do_readTargeting(null, reason => { + await do_readTargeting(null, _reason => { Assert.equal(false, Glean.backgroundUpdate.targetingExists.testGetValue()); Assert.equal( @@ -152,7 +152,7 @@ add_task(async function test_targeting_missing() { // Malformed JSON yields an exception. add_task(async function test_targeting_exception() { - await do_readTargeting("{", reason => { + await do_readTargeting("{", _reason => { Assert.equal(false, Glean.backgroundUpdate.targetingExists.testGetValue()); Assert.equal( @@ -193,7 +193,7 @@ add_task(async function test_targeting_exists() { targets: [manager.createTargetingContext(), target], }); - await do_readTargeting(JSON.stringify(targetSnapshot), reason => { + await do_readTargeting(JSON.stringify(targetSnapshot), _reason => { Assert.equal(true, Glean.backgroundUpdate.targetingExists.testGetValue()); Assert.equal( diff --git a/toolkit/mozapps/update/updater/updater.cpp b/toolkit/mozapps/update/updater/updater.cpp index 947e84aac3..6b01450b31 100644 --- a/toolkit/mozapps/update/updater/updater.cpp +++ b/toolkit/mozapps/update/updater/updater.cpp @@ -3734,7 +3734,7 @@ int NS_main(int argc, NS_tchar** argv) { } // Set an error so we don't get into an update loop when the callback // runs. This will be reset to pending by handleUpdateFailure in - // UpdateService.jsm. + // UpdateService.sys.mjs. WriteStatusFile(SILENT_UPDATE_NEEDED_ELEVATION_ERROR); LOG(("Skipping update to avoid UAC prompt from background task.")); output_finish(); |