diff options
Diffstat (limited to '')
-rw-r--r-- | browser/components/shell/Windows11LimitedAccessFeatures.cpp | 281 | ||||
-rw-r--r-- | browser/components/shell/Windows11LimitedAccessFeatures.h | 53 | ||||
-rw-r--r-- | browser/components/shell/Windows11TaskbarPinning.cpp | 344 | ||||
-rw-r--r-- | browser/components/shell/Windows11TaskbarPinning.h | 35 | ||||
-rw-r--r-- | browser/components/shell/moz.build | 3 | ||||
-rw-r--r-- | browser/components/shell/nsWindowsShellService.cpp | 27 |
6 files changed, 741 insertions, 2 deletions
diff --git a/browser/components/shell/Windows11LimitedAccessFeatures.cpp b/browser/components/shell/Windows11LimitedAccessFeatures.cpp new file mode 100644 index 0000000000..6bf20b6706 --- /dev/null +++ b/browser/components/shell/Windows11LimitedAccessFeatures.cpp @@ -0,0 +1,281 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 exists so that LaunchModernSettingsDialogDefaultApps can be called + * without linking to libxul. + */ +#include "Windows11LimitedAccessFeatures.h" + +#include "mozilla/Logging.h" + +static mozilla::LazyLogModule sLog("Windows11LimitedAccessFeatures"); + +#define LAF_LOG(level, msg, ...) MOZ_LOG(sLog, level, (msg, ##__VA_ARGS__)) + +// MINGW32 is not supported for these features +// Fall back function defined in the #else +#ifndef __MINGW32__ + +# include "nsString.h" +# include "nsWindowsHelpers.h" + +# include "mozilla/Atomics.h" + +# include <wrl.h> +# include <inspectable.h> +# include <roapi.h> +# include <windows.services.store.h> +# include <windows.foundation.h> + +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; +using namespace ABI::Windows; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::ApplicationModel; + +using namespace mozilla; + +/** + * To unlock features, we need: + * a feature identifier + * a token, + * an attestation string + * a token + * + * The token is generated by Microsoft and must + * match the publisher id Microsoft thinks we have, for a particular + * feature. + * + * To get a token, find the right microsoft email address by doing + * a search on the web for the feature you want unlocked and reach + * out to the right people at Microsoft. + * + * The token is generated from Microsoft. + * The jumbled code in the attestation string is a publisher id and + * must match the code in the resources / .rc file for the identity, + * looking like this for non-MSIX builds: + * + * Identity LimitedAccessFeature {{ L"MozillaFirefox_pcsmm0jrprpb2" }} + * + * Broken down: + * Identity LimitedAccessFeature {{ L"PRODUCTNAME_PUBLISHERID" }} + * + * That is injected into our build in create_rc.py and is necessary + * to unlock the taskbar pinning feature / APIs from an unpackaged + * build. + * + * In the above, the token is generated from the publisher id (pcsmm0jrprpb2) + * and the product name (MozillaFirefox) + * + * All tokens listed here were provided to us by Microsoft. + * + * Below and in create_rc.py, we used this set: + * + * Token: "kRFiWpEK5uS6PMJZKmR7MQ==" + * Product Name: "MozillaFirefox" + * Publisher ID: "pcsmm0jrprpb2" + * + * Microsoft also provided these other tokens, which will will + * work if accompanied by the matching changes to create_rc.py: + + * ----- + * Token: "RGEhsYgKhmPLKyzkEHnMhQ==" + * Product Name: "FirefoxBeta" + * Publisher ID: "pcsmm0jrprpb2" + * + * ----- + * + * Token: "qbVzns/9kT+t15YbIwT4Jw==" + * Product Name: "FirefoxNightly" + * Publisher ID: "pcsmm0jrprpb2" + * + * To use those instead, you have to ensure that the LimitedAccessFeature + * generated in create_rc.py has the product name and publisher id + * matching the token used in this file. + * + * For non-packaged (non-MSIX) builds, any of the above sets will work. + * Just make sure the right (ProductName_PublisherID) value is in the + * generated resource data for the executable, and the matching +* (Token) and attestation string + * + * To get MSIX/packaged builds to work, the product name and publisher in + * the final manifest (searchfox.org/mozilla-central/search?q=APPX_PUBLISHER) + * should match the token in this file. For that case, the identity value + * in the resources does not matter. + * + * See here for Microsoft examples: +https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/TaskbarManager/CppUnpackagedDesktopTaskbarPin + */ + +struct LimitedAccessFeatureInfo { + const char* debugName; + const WCHAR* feature; + const WCHAR* token; + const WCHAR* attestation; +}; + +static LimitedAccessFeatureInfo limitedAccessFeatureInfo[] = { + {// Win11LimitedAccessFeatureType::Taskbar + "Win11LimitedAccessFeatureType::Taskbar", + L"com.microsoft.windows.taskbar.pin", L"kRFiWpEK5uS6PMJZKmR7MQ==", + L"pcsmm0jrprpb2 has registered their use of " + L"com.microsoft.windows.taskbar.pin with Microsoft and agrees to the " + L"terms " + L"of use."}}; + +static_assert(mozilla::ArrayLength(limitedAccessFeatureInfo) == + kWin11LimitedAccessFeatureTypeCount); + +/** + Implementation of the Win11LimitedAccessFeaturesInterface. + */ +class Win11LimitedAccessFeatures : public Win11LimitedAccessFeaturesInterface { + public: + using AtomicState = Atomic<int, SequentiallyConsistent>; + + Result<bool, HRESULT> Unlock(Win11LimitedAccessFeatureType feature) override; + + private: + AtomicState& GetState(Win11LimitedAccessFeatureType feature); + Result<bool, HRESULT> UnlockImplementation( + Win11LimitedAccessFeatureType feature); + + /** + * Store the state as an atomic so that it can be safely accessed from + * different threads. + */ + static AtomicState mTaskbarState; + static AtomicState mDefaultState; + + enum State { + Uninitialized, + Locked, + Unlocked, + }; +}; + +Win11LimitedAccessFeatures::AtomicState + Win11LimitedAccessFeatures::mTaskbarState( + Win11LimitedAccessFeatures::Uninitialized); +Win11LimitedAccessFeatures::AtomicState + Win11LimitedAccessFeatures::mDefaultState( + Win11LimitedAccessFeatures::Uninitialized); + +RefPtr<Win11LimitedAccessFeaturesInterface> +CreateWin11LimitedAccessFeaturesInterface() { + RefPtr<Win11LimitedAccessFeaturesInterface> result( + new Win11LimitedAccessFeatures()); + return result; +} + +Result<bool, HRESULT> Win11LimitedAccessFeatures::Unlock( + Win11LimitedAccessFeatureType feature) { + AtomicState& atomicState = GetState(feature); + + const auto& lafInfo = limitedAccessFeatureInfo[static_cast<int>(feature)]; + + LAF_LOG( + LogLevel::Debug, "Limited Access Feature Info for %s. Feature %S, %S, %S", + lafInfo.debugName, lafInfo.feature, lafInfo.token, lafInfo.attestation); + + int state = atomicState; + if (state != Uninitialized) { + LAF_LOG(LogLevel::Debug, "%s already initialized! State = %s", + lafInfo.debugName, (state == Unlocked) ? "true" : "false"); + return (state == Unlocked); + } + + // If multiple threads read the state at the same time, and it's unitialized, + // both threads will unlock the feature. This situation is unlikely, but even + // if it happens, it's not a problem. + + auto result = UnlockImplementation(feature); + + int newState = Locked; + if (!result.isErr() && result.unwrap()) { + newState = Unlocked; + } + + atomicState = newState; + + return result; +} + +Win11LimitedAccessFeatures::AtomicState& Win11LimitedAccessFeatures::GetState( + Win11LimitedAccessFeatureType feature) { + switch (feature) { + case Win11LimitedAccessFeatureType::Taskbar: + return mTaskbarState; + + default: + LAF_LOG(LogLevel::Debug, "Missing feature type for %d", + static_cast<int>(feature)); + MOZ_ASSERT(false, + "Unhandled feature type! Add a new atomic state variable, add " + "that entry to the switch statement above, and add the proper " + "entries for the feature and the token."); + return mDefaultState; + } +} + +Result<bool, HRESULT> Win11LimitedAccessFeatures::UnlockImplementation( + Win11LimitedAccessFeatureType feature) { + ComPtr<ILimitedAccessFeaturesStatics> limitedAccessFeatures; + ComPtr<ILimitedAccessFeatureRequestResult> limitedAccessFeaturesResult; + + const auto& lafInfo = limitedAccessFeatureInfo[static_cast<int>(feature)]; + + HRESULT hr = RoGetActivationFactory( + HStringReference( + RuntimeClass_Windows_ApplicationModel_LimitedAccessFeatures) + .Get(), + IID_ILimitedAccessFeaturesStatics, &limitedAccessFeatures); + + if (!SUCCEEDED(hr)) { + LAF_LOG(LogLevel::Debug, "%s activation error. HRESULT = 0x%lx", + lafInfo.debugName, hr); + return Err(hr); + } + + hr = limitedAccessFeatures->TryUnlockFeature( + HStringReference(lafInfo.feature).Get(), + HStringReference(lafInfo.token).Get(), + HStringReference(lafInfo.attestation).Get(), + &limitedAccessFeaturesResult); + if (!SUCCEEDED(hr)) { + LAF_LOG(LogLevel::Debug, "%s unlock error. HRESULT = 0x%lx", + lafInfo.debugName, hr); + return Err(hr); + } + + LimitedAccessFeatureStatus status; + hr = limitedAccessFeaturesResult->get_Status(&status); + if (!SUCCEEDED(hr)) { + LAF_LOG(LogLevel::Debug, "%s get status error. HRESULT = 0x%lx", + lafInfo.debugName, hr); + return Err(hr); + } + + int state = Unlocked; + if ((status != LimitedAccessFeatureStatus_Available) && + (status != LimitedAccessFeatureStatus_AvailableWithoutToken)) { + LAF_LOG(LogLevel::Debug, "%s not available. HRESULT = 0x%lx", + lafInfo.debugName, hr); + state = Locked; + } + + return (state == Unlocked); +} + +#else // MINGW32 implementation + +RefPtr<Win11LimitedAccessFeaturesInterface> +CreateWin11LimitedAccessFeaturesInterface() { + RefPtr<Win11LimitedAccessFeaturesInterface> result; + return result; +} + +#endif diff --git a/browser/components/shell/Windows11LimitedAccessFeatures.h b/browser/components/shell/Windows11LimitedAccessFeatures.h new file mode 100644 index 0000000000..8e1ae5db7a --- /dev/null +++ b/browser/components/shell/Windows11LimitedAccessFeatures.h @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#ifndef SHELL_WINDOWS11LIMITEDACCESSFEATURES_H__ +#define SHELL_WINDOWS11LIMITEDACCESSFEATURES_H__ + +#include "nsISupportsImpl.h" +#include "mozilla/Result.h" +#include "mozilla/ResultVariant.h" +#include <winerror.h> +#include <windows.h> // for HRESULT +#include "mozilla/DefineEnum.h" +#include <winerror.h> + +MOZ_DEFINE_ENUM_CLASS(Win11LimitedAccessFeatureType, (Taskbar)); + +/** + * Class to manage unlocking limited access features on Windows 11. + * Unless stubbing for testing purposes, create objects of this + * class with CreateWin11LimitedAccessFeaturesInterface. + * + * Windows 11 requires certain features to be unlocked in order to work + * (for instance, the Win11 Taskbar pinning APIs). Call Unlock() + * to unlock them. Generally, results will be cached in atomic variables + * and future calls to Unlock will be as long as it takes + * to fetch an atomic variable. + */ +class Win11LimitedAccessFeaturesInterface { + public: + /** + * Unlocks the limited access features, if possible. + * + * Returns an error code on error, true on successful unlock, + * false on unlock failed (but with no error). + */ + virtual mozilla::Result<bool, HRESULT> Unlock( + Win11LimitedAccessFeatureType feature) = 0; + + /** + * Reference counting and cycle collection. + */ + NS_INLINE_DECL_REFCOUNTING(Win11LimitedAccessFeaturesInterface) + + protected: + virtual ~Win11LimitedAccessFeaturesInterface() {} +}; + +RefPtr<Win11LimitedAccessFeaturesInterface> +CreateWin11LimitedAccessFeaturesInterface(); + +#endif // SHELL_WINDOWS11LIMITEDACCESSFEATURES_H__ diff --git a/browser/components/shell/Windows11TaskbarPinning.cpp b/browser/components/shell/Windows11TaskbarPinning.cpp new file mode 100644 index 0000000000..350a58d59a --- /dev/null +++ b/browser/components/shell/Windows11TaskbarPinning.cpp @@ -0,0 +1,344 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#include "Windows11TaskbarPinning.h" +#include "Windows11LimitedAccessFeatures.h" + +#include "nsWindowsHelpers.h" +#include "MainThreadUtils.h" +#include "nsThreadUtils.h" +#include <strsafe.h> + +#include "mozilla/Result.h" +#include "mozilla/ResultVariant.h" + +#include "mozilla/Logging.h" + +static mozilla::LazyLogModule sLog("Windows11TaskbarPinning"); + +#define TASKBAR_PINNING_LOG(level, msg, ...) \ + MOZ_LOG(sLog, level, (msg, ##__VA_ARGS__)) + +#ifndef __MINGW32__ // WinRT headers not yet supported by MinGW + +# include <wrl.h> + +# include <inspectable.h> +# include <roapi.h> +# include <windows.services.store.h> +# include <windows.foundation.h> +# include <windows.ui.shell.h> + +using namespace mozilla; + +/** + * The Win32 SetEvent and WaitForSingleObject functions take HANDLE parameters + * which are typedefs of void*. When using nsAutoHandle, that means if you + * forget to call .get() first, everything still compiles and then doesn't work + * at runtime. For instance, calling SetEvent(mEvent) below would compile but + * not work at runtime and the waits would block forever. + * To ensure this isn't an issue, we wrap the event in a custom class here + * with the simple methods that we want on an event. + */ +class EventWrapper { + public: + EventWrapper() : mEvent(CreateEventW(nullptr, true, false, nullptr)) {} + + void Set() { SetEvent(mEvent.get()); } + + void Reset() { ResetEvent(mEvent.get()); } + + void Wait() { WaitForSingleObject(mEvent.get(), INFINITE); } + + private: + nsAutoHandle mEvent; +}; + +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; +using namespace ABI::Windows; +using namespace ABI::Windows::UI::Shell; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::ApplicationModel; + +static Result<ComPtr<ITaskbarManager>, HRESULT> InitializeTaskbar() { + ComPtr<IInspectable> taskbarStaticsInspectable; + + TASKBAR_PINNING_LOG(LogLevel::Debug, "Initializing taskbar"); + + HRESULT hr = RoGetActivationFactory( + HStringReference(RuntimeClass_Windows_UI_Shell_TaskbarManager).Get(), + IID_ITaskbarManagerStatics, &taskbarStaticsInspectable); + if (FAILED(hr)) { + TASKBAR_PINNING_LOG(LogLevel::Debug, + "Taskbar: Failed to activate. HRESULT = 0x%lx", hr); + return Err(hr); + } + + ComPtr<ITaskbarManagerStatics> taskbarStatics; + + hr = taskbarStaticsInspectable.As(&taskbarStatics); + if (FAILED(hr)) { + TASKBAR_PINNING_LOG(LogLevel::Debug, "Failed statistics. HRESULT = 0x%lx", + hr); + return Err(hr); + } + + ComPtr<ITaskbarManager> taskbarManager; + + hr = taskbarStatics->GetDefault(&taskbarManager); + if (FAILED(hr)) { + TASKBAR_PINNING_LOG(LogLevel::Debug, + "Error getting TaskbarManager. HRESULT = 0x%lx", hr); + return Err(hr); + } + + TASKBAR_PINNING_LOG(LogLevel::Debug, + "TaskbarManager retrieved successfully!"); + return taskbarManager; +} + +Win11PinToTaskBarResult PinCurrentAppToTaskbarWin11( + bool aCheckOnly, const nsAString& aAppUserModelId, + nsAutoString aShortcutPath) { + MOZ_DIAGNOSTIC_ASSERT(!NS_IsMainThread(), + "PinCurrentAppToTaskbarWin11 should be called off main " + "thread only. It blocks, waiting on things to execute " + "asynchronously on the main thread."); + + { + RefPtr<Win11LimitedAccessFeaturesInterface> limitedAccessFeatures = + CreateWin11LimitedAccessFeaturesInterface(); + auto result = + limitedAccessFeatures->Unlock(Win11LimitedAccessFeatureType::Taskbar); + if (result.isErr()) { + auto hr = result.unwrapErr(); + TASKBAR_PINNING_LOG(LogLevel::Debug, + "Taskbar unlock: Error. HRESULT = 0x%lx", hr); + return {hr, Win11PinToTaskBarResultStatus::NotSupported}; + } + + if (result.unwrap() == false) { + TASKBAR_PINNING_LOG( + LogLevel::Debug, + "Taskbar unlock: failed. Not supported on this version of Windows."); + return {S_OK, Win11PinToTaskBarResultStatus::NotSupported}; + } + } + + HRESULT hr; + Win11PinToTaskBarResultStatus resultStatus = + Win11PinToTaskBarResultStatus::NotSupported; + + EventWrapper event; + + // Everything related to the taskbar and pinning must be done on the main / + // user interface thread or Windows will cause them to fail. + NS_DispatchToMainThread(NS_NewRunnableFunction( + "PinCurrentAppToTaskbarWin11", [&event, &hr, &resultStatus, aCheckOnly] { + auto CompletedOperations = + [&event, &resultStatus](Win11PinToTaskBarResultStatus status) { + resultStatus = status; + event.Set(); + }; + + auto result = InitializeTaskbar(); + if (result.isErr()) { + hr = result.unwrapErr(); + return CompletedOperations( + Win11PinToTaskBarResultStatus::NotSupported); + } + + ComPtr<ITaskbarManager> taskbar = result.unwrap(); + boolean supported; + hr = taskbar->get_IsSupported(&supported); + if (FAILED(hr) || !supported) { + if (FAILED(hr)) { + TASKBAR_PINNING_LOG( + LogLevel::Debug, + "Taskbar: error checking if supported. HRESULT = 0x%lx", hr); + } else { + TASKBAR_PINNING_LOG(LogLevel::Debug, "Taskbar: not supported."); + } + return CompletedOperations( + Win11PinToTaskBarResultStatus::NotSupported); + } + + if (aCheckOnly) { + TASKBAR_PINNING_LOG(LogLevel::Debug, "Taskbar: check succeeded."); + return CompletedOperations(Win11PinToTaskBarResultStatus::Success); + } + + boolean isAllowed = false; + hr = taskbar->get_IsPinningAllowed(&isAllowed); + if (FAILED(hr) || !isAllowed) { + if (FAILED(hr)) { + TASKBAR_PINNING_LOG( + LogLevel::Debug, + "Taskbar: error checking if pinning is allowed. HRESULT = " + "0x%lx", + hr); + } else { + TASKBAR_PINNING_LOG( + LogLevel::Debug, + "Taskbar: is pinning allowed error or isn't allowed right now. " + "It's not clear when it will be allowed. Possibly after a " + "reboot."); + } + return CompletedOperations( + Win11PinToTaskBarResultStatus::NotCurrentlyAllowed); + } + + ComPtr<IAsyncOperation<bool>> isPinnedOperation = nullptr; + hr = taskbar->IsCurrentAppPinnedAsync(&isPinnedOperation); + if (FAILED(hr)) { + TASKBAR_PINNING_LOG( + LogLevel::Debug, + "Taskbar: is current app pinned operation failed. HRESULT = " + "0x%lx", + hr); + return CompletedOperations(Win11PinToTaskBarResultStatus::Failed); + } + + // Copy the taskbar; don't use it as a reference. + // With the async calls, it's not guaranteed to still be valid + // if sent as a reference. + // resultStatus and event are not defined on the main thread and will + // be alive until the async functions complete, so they can be used as + // references. + auto isPinnedCallback = Callback<IAsyncOperationCompletedHandler< + bool>>([taskbar, &event, &resultStatus, &hr]( + IAsyncOperation<bool>* asyncInfo, + AsyncStatus status) mutable -> HRESULT { + auto CompletedOperations = + [&event, + &resultStatus](Win11PinToTaskBarResultStatus status) -> HRESULT { + resultStatus = status; + event.Set(); + return S_OK; + }; + + bool asyncOpSucceeded = status == AsyncStatus::Completed; + if (!asyncOpSucceeded) { + TASKBAR_PINNING_LOG( + LogLevel::Debug, + "Taskbar: is pinned operation failed to complete."); + return CompletedOperations(Win11PinToTaskBarResultStatus::Failed); + } + + unsigned char isCurrentAppPinned = false; + hr = asyncInfo->GetResults(&isCurrentAppPinned); + if (FAILED(hr)) { + TASKBAR_PINNING_LOG( + LogLevel::Debug, + "Taskbar: is current app pinned check failed. HRESULT = 0x%lx", + hr); + return CompletedOperations(Win11PinToTaskBarResultStatus::Failed); + } + + if (isCurrentAppPinned) { + TASKBAR_PINNING_LOG(LogLevel::Debug, + "Taskbar: current app is already pinned."); + return CompletedOperations( + Win11PinToTaskBarResultStatus::AlreadyPinned); + } + + ComPtr<IAsyncOperation<bool>> requestPinOperation = nullptr; + hr = taskbar->RequestPinCurrentAppAsync(&requestPinOperation); + if (FAILED(hr)) { + TASKBAR_PINNING_LOG( + LogLevel::Debug, + "Taskbar: request pin current app operation creation failed. " + "HRESULT = 0x%lx", + hr); + return CompletedOperations(Win11PinToTaskBarResultStatus::Failed); + } + + auto pinAppCallback = Callback<IAsyncOperationCompletedHandler< + bool>>([CompletedOperations, &hr]( + IAsyncOperation<bool>* asyncInfo, + AsyncStatus status) -> HRESULT { + bool asyncOpSucceeded = status == AsyncStatus::Completed; + if (!asyncOpSucceeded) { + TASKBAR_PINNING_LOG( + LogLevel::Debug, + "Taskbar: request pin current app operation did not " + "complete."); + return CompletedOperations(Win11PinToTaskBarResultStatus::Failed); + } + + unsigned char successfullyPinned = 0; + hr = asyncInfo->GetResults(&successfullyPinned); + if (FAILED(hr) || !successfullyPinned) { + if (FAILED(hr)) { + TASKBAR_PINNING_LOG( + LogLevel::Debug, + "Taskbar: request pin current app operation failed to pin " + "due to error. HRESULT = 0x%lx", + hr); + } else { + TASKBAR_PINNING_LOG( + LogLevel::Debug, + "Taskbar: request pin current app operation failed to pin"); + } + return CompletedOperations(Win11PinToTaskBarResultStatus::Failed); + } + + TASKBAR_PINNING_LOG( + LogLevel::Debug, + "Taskbar: request pin current app operation succeeded"); + return CompletedOperations(Win11PinToTaskBarResultStatus::Success); + }); + + HRESULT pinOperationHR = + requestPinOperation->put_Completed(pinAppCallback.Get()); + if (FAILED(pinOperationHR)) { + TASKBAR_PINNING_LOG( + LogLevel::Debug, + "Taskbar: request pin operation failed when setting completion " + "callback. HRESULT = 0x%lx", + hr); + hr = pinOperationHR; + return CompletedOperations(Win11PinToTaskBarResultStatus::Failed); + } + + // DO NOT SET event HERE. It will be set in the pin operation + // callback As in, operations are not completed, so don't call + // CompletedOperations + return S_OK; + }); + + HRESULT isPinnedOperationHR = + isPinnedOperation->put_Completed(isPinnedCallback.Get()); + if (FAILED(isPinnedOperationHR)) { + hr = isPinnedOperationHR; + TASKBAR_PINNING_LOG( + LogLevel::Debug, + "Taskbar: is pinned operation failed when setting completion " + "callback. HRESULT = 0x%lx", + hr); + return CompletedOperations(Win11PinToTaskBarResultStatus::Failed); + } + + // DO NOT SET event HERE. It will be set in the is pin operation + // callback As in, operations are not completed, so don't call + // CompletedOperations + })); + + // block until the pinning is completed on the main thread + event.Wait(); + + return {hr, resultStatus}; +} + +#else // MINGW32 implementation below + +Win11PinToTaskBarResult PinCurrentAppToTaskbarWin11( + bool aCheckOnly, const nsAString& aAppUserModelId, + nsAutoString aShortcutPath) { + return {S_OK, Win11PinToTaskBarResultStatus::NotSupported}; +} + +#endif // #ifndef __MINGW32__ // WinRT headers not yet supported by MinGW diff --git a/browser/components/shell/Windows11TaskbarPinning.h b/browser/components/shell/Windows11TaskbarPinning.h new file mode 100644 index 0000000000..c45fd66a5b --- /dev/null +++ b/browser/components/shell/Windows11TaskbarPinning.h @@ -0,0 +1,35 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 exists to keep the Windows 11 Taskbar Pinning API + * related code as self-contained as possible. + */ + +#ifndef SHELL_WINDOWS11TASKBARPINNING_H__ +#define SHELL_WINDOWS11TASKBARPINNING_H__ + +#include "nsString.h" +#include <wrl.h> +#include <windows.h> // for HRESULT + +enum class Win11PinToTaskBarResultStatus { + Failed, + NotCurrentlyAllowed, + AlreadyPinned, + Success, + NotSupported, +}; + +struct Win11PinToTaskBarResult { + HRESULT errorCode; + Win11PinToTaskBarResultStatus result; +}; + +Win11PinToTaskBarResult PinCurrentAppToTaskbarWin11( + bool aCheckOnly, const nsAString& aAppUserModelId, + nsAutoString aShortcutPath); + +#endif // SHELL_WINDOWS11TASKBARPINNING_H__ diff --git a/browser/components/shell/moz.build b/browser/components/shell/moz.build index fe70623907..82e5afade7 100644 --- a/browser/components/shell/moz.build +++ b/browser/components/shell/moz.build @@ -50,6 +50,8 @@ elif CONFIG["OS_ARCH"] == "WINNT": ] SOURCES += [ "nsWindowsShellService.cpp", + "Windows11LimitedAccessFeatures.cpp", + "Windows11TaskbarPinning.cpp", "WindowsDefaultBrowser.cpp", "WindowsUserChoice.cpp", ] @@ -82,6 +84,7 @@ for var in ( ): DEFINES[var] = '"%s"' % CONFIG[var] + if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk": CXXFLAGS += CONFIG["MOZ_GTK3_CFLAGS"] diff --git a/browser/components/shell/nsWindowsShellService.cpp b/browser/components/shell/nsWindowsShellService.cpp index 86c7694bf8..f7bca95b09 100644 --- a/browser/components/shell/nsWindowsShellService.cpp +++ b/browser/components/shell/nsWindowsShellService.cpp @@ -39,6 +39,7 @@ #include "nsIXULAppInfo.h" #include "nsINIParser.h" #include "nsNativeAppSupportWin.h" +#include "Windows11TaskbarPinning.h" #include <windows.h> #include <shellapi.h> @@ -1626,7 +1627,7 @@ nsWindowsShellService::GetTaskbarTabPins(nsTArray<nsString>& aShortcutPaths) { static nsresult PinCurrentAppToTaskbarWin10(bool aCheckOnly, const nsAString& aAppUserModelId, - nsAutoString aShortcutPath) { + const nsAString& aShortcutPath) { // The behavior here is identical if we're only checking or if we try to pin // but the app is already pinned so we update the variable accordingly. if (!aCheckOnly) { @@ -1695,6 +1696,28 @@ static nsresult PinCurrentAppToTaskbarImpl( } } + auto pinWithWin11TaskbarAPIResults = + PinCurrentAppToTaskbarWin11(aCheckOnly, aAppUserModelId, shortcutPath); + switch (pinWithWin11TaskbarAPIResults.result) { + case Win11PinToTaskBarResultStatus::NotSupported: + // Fall through to the win 10 mechanism + break; + + case Win11PinToTaskBarResultStatus::Success: + case Win11PinToTaskBarResultStatus::AlreadyPinned: + return NS_OK; + + case Win11PinToTaskBarResultStatus::NotCurrentlyAllowed: + case Win11PinToTaskBarResultStatus::Failed: + // return NS_ERROR_FAILURE; + + // Fall through to the old mechanism for now + // In future, we should be sending telemetry for when + // an error occurs or for when pinning is not allowed + // with the Win 11 APIs. + break; + } + return PinCurrentAppToTaskbarWin10(aCheckOnly, aAppUserModelId, shortcutPath); } @@ -1720,7 +1743,7 @@ static nsresult PinCurrentAppToTaskbarAsyncImpl(bool aCheckOnly, } nsAutoString aumid; - if (NS_WARN_IF(!mozilla::widget::WinTaskbar::GenerateAppUserModelID( + if (NS_WARN_IF(!mozilla::widget::WinTaskbar::GetAppUserModelID( aumid, aPrivateBrowsing))) { return NS_ERROR_FAILURE; } |