summaryrefslogtreecommitdiffstats
path: root/browser/components/shell
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/shell')
-rw-r--r--browser/components/shell/Windows11LimitedAccessFeatures.cpp281
-rw-r--r--browser/components/shell/Windows11LimitedAccessFeatures.h53
-rw-r--r--browser/components/shell/Windows11TaskbarPinning.cpp344
-rw-r--r--browser/components/shell/Windows11TaskbarPinning.h35
-rw-r--r--browser/components/shell/moz.build3
-rw-r--r--browser/components/shell/nsWindowsShellService.cpp27
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;
}