Files
tubestation/widget/windows/WindowsUIUtils.cpp
Gregory Pappas 0e57f440a0 Bug 1914559 - Remove fallback to hidden window in WindowsUIUtils::UpdateInTabletMode r=win-reviewers,rkraesig
If there aren't any browser windows, we're either very early in startup
or shutting down. In these cases, it's probably fine to not know if
tablet mode is enabled.

Differential Revision: https://phabricator.services.mozilla.com/D219945
2024-08-28 19:30:43 +00:00

802 lines
26 KiB
C++

/* -*- 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 <windows.h>
#include <wrl.h>
#include "nsServiceManagerUtils.h"
#include "WindowsUIUtils.h"
#include "nsIObserverService.h"
#include "nsIAppShellService.h"
#include "nsAppShellCID.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/ResultVariant.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPrefs_widget.h"
#include "mozilla/WidgetUtils.h"
#include "mozilla/WindowsVersion.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/media/MediaUtils.h"
#include "nsString.h"
#include "nsGlobalWindowOuter.h"
#include "nsIWidget.h"
#include "nsIWindowMediator.h"
#include "nsPIDOMWindow.h"
#include "nsWindowGfx.h"
#include "Units.h"
/* mingw currently doesn't support windows.ui.viewmanagement.h, so we disable it
* until it's fixed. */
// See
// https://github.com/tpn/winsdk-10/blob/master/Include/10.0.14393.0/winrt/windows.ui.viewmanagement.h
// for the source of some of these definitions for older SDKs.
#ifndef __MINGW32__
# include <inspectable.h>
# include <roapi.h>
# include <windows.ui.viewmanagement.h>
# pragma comment(lib, "runtimeobject.lib")
using namespace ABI::Windows::UI;
using namespace ABI::Windows::UI::ViewManagement;
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::ApplicationModel::DataTransfer;
# ifndef RuntimeClass_Windows_UI_ViewManagement_UIViewSettings
# define RuntimeClass_Windows_UI_ViewManagement_UIViewSettings \
L"Windows.UI.ViewManagement.UIViewSettings"
# endif
# ifndef IUIViewSettingsInterop
using IUIViewSettingsInterop = interface IUIViewSettingsInterop;
MIDL_INTERFACE("3694dbf9-8f68-44be-8ff5-195c98ede8a6")
IUIViewSettingsInterop : public IInspectable {
public:
virtual HRESULT STDMETHODCALLTYPE GetForWindow(HWND hwnd, REFIID riid,
void** ppv) = 0;
};
# endif
# ifndef __IDataTransferManagerInterop_INTERFACE_DEFINED__
# define __IDataTransferManagerInterop_INTERFACE_DEFINED__
using IDataTransferManagerInterop = interface IDataTransferManagerInterop;
MIDL_INTERFACE("3A3DCD6C-3EAB-43DC-BCDE-45671CE800C8")
IDataTransferManagerInterop : public IUnknown {
public:
virtual HRESULT STDMETHODCALLTYPE GetForWindow(
HWND appWindow, REFIID riid, void** dataTransferManager) = 0;
virtual HRESULT STDMETHODCALLTYPE ShowShareUIForWindow(HWND appWindow) = 0;
};
# endif
# if !defined( \
____x_ABI_CWindows_CApplicationModel_CDataTransfer_CIDataPackage4_INTERFACE_DEFINED__)
# define ____x_ABI_CWindows_CApplicationModel_CDataTransfer_CIDataPackage4_INTERFACE_DEFINED__
MIDL_INTERFACE("13a24ec8-9382-536f-852a-3045e1b29a3b")
IDataPackage4 : public IInspectable {
public:
virtual HRESULT STDMETHODCALLTYPE add_ShareCanceled(
__FITypedEventHandler_2_Windows__CApplicationModel__CDataTransfer__CDataPackage_IInspectable *
handler,
EventRegistrationToken * token) = 0;
virtual HRESULT STDMETHODCALLTYPE remove_ShareCanceled(
EventRegistrationToken token) = 0;
};
# endif
# ifndef RuntimeClass_Windows_UI_ViewManagement_UISettings
# define RuntimeClass_Windows_UI_ViewManagement_UISettings \
L"Windows.UI.ViewManagement.UISettings"
# endif
# if WINDOWS_FOUNDATION_UNIVERSALAPICONTRACT_VERSION < 0x80000
namespace ABI {
namespace Windows {
namespace UI {
namespace ViewManagement {
class UISettings;
class UISettingsAutoHideScrollBarsChangedEventArgs;
interface IUISettingsAutoHideScrollBarsChangedEventArgs;
MIDL_INTERFACE("87afd4b2-9146-5f02-8f6b-06d454174c0f")
IUISettingsAutoHideScrollBarsChangedEventArgs : public IInspectable{};
} // namespace ViewManagement
} // namespace UI
} // namespace Windows
} // namespace ABI
namespace ABI {
namespace Windows {
namespace Foundation {
template <>
struct __declspec(uuid("808aef30-2660-51b0-9c11-f75dd42006b4"))
ITypedEventHandler<ABI::Windows::UI::ViewManagement::UISettings*,
ABI::Windows::UI::ViewManagement::
UISettingsAutoHideScrollBarsChangedEventArgs*>
: ITypedEventHandler_impl<
ABI::Windows::Foundation::Internal::AggregateType<
ABI::Windows::UI::ViewManagement::UISettings*,
ABI::Windows::UI::ViewManagement::IUISettings*>,
ABI::Windows::Foundation::Internal::AggregateType<
ABI::Windows::UI::ViewManagement::
UISettingsAutoHideScrollBarsChangedEventArgs*,
ABI::Windows::UI::ViewManagement::
IUISettingsAutoHideScrollBarsChangedEventArgs*>> {
static const wchar_t* z_get_rc_name_impl() {
return L"Windows.Foundation.TypedEventHandler`2<Windows.UI.ViewManagement."
L"UISettings, "
L"Windows.UI.ViewManagement."
L"UISettingsAutoHideScrollBarsChangedEventArgs>";
}
};
// Define a typedef for the parameterized interface specialization's mangled
// name. This allows code which uses the mangled name for the parameterized
// interface to access the correct parameterized interface specialization.
typedef ITypedEventHandler<ABI::Windows::UI::ViewManagement::UISettings*,
ABI::Windows::UI::ViewManagement::
UISettingsAutoHideScrollBarsChangedEventArgs*>
__FITypedEventHandler_2_Windows__CUI__CViewManagement__CUISettings_Windows__CUI__CViewManagement__CUISettingsAutoHideScrollBarsChangedEventArgs_t;
# define __FITypedEventHandler_2_Windows__CUI__CViewManagement__CUISettings_Windows__CUI__CViewManagement__CUISettingsAutoHideScrollBarsChangedEventArgs \
ABI::Windows::Foundation:: \
__FITypedEventHandler_2_Windows__CUI__CViewManagement__CUISettings_Windows__CUI__CViewManagement__CUISettingsAutoHideScrollBarsChangedEventArgs_t
} // namespace Foundation
} // namespace Windows
} // namespace ABI
namespace ABI {
namespace Windows {
namespace UI {
namespace ViewManagement {
class UISettings;
class UISettingsAutoHideScrollBarsChangedEventArgs;
interface IUISettings5;
MIDL_INTERFACE("5349d588-0cb5-5f05-bd34-706b3231f0bd")
IUISettings5 : public IInspectable {
public:
virtual HRESULT STDMETHODCALLTYPE get_AutoHideScrollBars(boolean * value) = 0;
virtual HRESULT STDMETHODCALLTYPE add_AutoHideScrollBarsChanged(
__FITypedEventHandler_2_Windows__CUI__CViewManagement__CUISettings_Windows__CUI__CViewManagement__CUISettingsAutoHideScrollBarsChangedEventArgs *
handler,
EventRegistrationToken * token) = 0;
virtual HRESULT STDMETHODCALLTYPE remove_AutoHideScrollBarsChanged(
EventRegistrationToken token) = 0;
};
} // namespace ViewManagement
} // namespace UI
} // namespace Windows
} // namespace ABI
# endif
#endif
using namespace mozilla;
enum class TabletModeState : uint8_t { Unknown, Off, On };
static TabletModeState sInTabletModeState;
WindowsUIUtils::WindowsUIUtils() = default;
WindowsUIUtils::~WindowsUIUtils() = default;
NS_IMPL_ISUPPORTS(WindowsUIUtils, nsIWindowsUIUtils)
NS_IMETHODIMP
WindowsUIUtils::GetSystemSmallIconSize(int32_t* aSize) {
NS_ENSURE_ARG(aSize);
mozilla::LayoutDeviceIntSize size =
nsWindowGfx::GetIconMetrics(nsWindowGfx::kSmallIcon);
*aSize = std::max(size.width, size.height);
return NS_OK;
}
NS_IMETHODIMP
WindowsUIUtils::GetSystemLargeIconSize(int32_t* aSize) {
NS_ENSURE_ARG(aSize);
mozilla::LayoutDeviceIntSize size =
nsWindowGfx::GetIconMetrics(nsWindowGfx::kRegularIcon);
*aSize = std::max(size.width, size.height);
return NS_OK;
}
NS_IMETHODIMP
WindowsUIUtils::SetWindowIcon(mozIDOMWindowProxy* aWindow,
imgIContainer* aSmallIcon,
imgIContainer* aBigIcon) {
NS_ENSURE_ARG(aWindow);
nsCOMPtr<nsIWidget> widget =
nsGlobalWindowOuter::Cast(aWindow)->GetMainWidget();
nsWindow* window = static_cast<nsWindow*>(widget.get());
nsresult rv;
if (aSmallIcon) {
HICON hIcon = nullptr;
rv = nsWindowGfx::CreateIcon(
aSmallIcon, false, mozilla::LayoutDeviceIntPoint(),
nsWindowGfx::GetIconMetrics(nsWindowGfx::kSmallIcon), &hIcon);
NS_ENSURE_SUCCESS(rv, rv);
window->SetSmallIcon(hIcon);
}
if (aBigIcon) {
HICON hIcon = nullptr;
rv = nsWindowGfx::CreateIcon(
aBigIcon, false, mozilla::LayoutDeviceIntPoint(),
nsWindowGfx::GetIconMetrics(nsWindowGfx::kRegularIcon), &hIcon);
NS_ENSURE_SUCCESS(rv, rv);
window->SetBigIcon(hIcon);
}
return NS_OK;
}
NS_IMETHODIMP
WindowsUIUtils::SetWindowIconFromExe(mozIDOMWindowProxy* aWindow,
const nsAString& aExe, uint16_t aIndex) {
NS_ENSURE_ARG(aWindow);
nsCOMPtr<nsIWidget> widget =
nsGlobalWindowOuter::Cast(aWindow)->GetMainWidget();
nsWindow* window = static_cast<nsWindow*>(widget.get());
HICON icon = ::LoadIconW(::GetModuleHandleW(PromiseFlatString(aExe).get()),
MAKEINTRESOURCEW(aIndex));
window->SetBigIcon(icon);
window->SetSmallIcon(icon);
return NS_OK;
}
NS_IMETHODIMP
WindowsUIUtils::SetWindowIconNoData(mozIDOMWindowProxy* aWindow) {
NS_ENSURE_ARG(aWindow);
nsCOMPtr<nsIWidget> widget =
nsGlobalWindowOuter::Cast(aWindow)->GetMainWidget();
nsWindow* window = static_cast<nsWindow*>(widget.get());
window->SetSmallIconNoData();
window->SetBigIconNoData();
return NS_OK;
}
bool WindowsUIUtils::GetInTabletMode() {
MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
if (sInTabletModeState == TabletModeState::Unknown) {
UpdateInTabletMode();
}
return sInTabletModeState == TabletModeState::On;
}
NS_IMETHODIMP
WindowsUIUtils::GetInTabletMode(bool* aResult) {
*aResult = GetInTabletMode();
return NS_OK;
}
static IInspectable* GetUISettings() {
MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
#ifndef __MINGW32__
// We need to keep this alive for ~ever so that change callbacks work as
// expected, sigh.
static StaticRefPtr<IInspectable> sUiSettingsAsInspectable;
if (!sUiSettingsAsInspectable) {
ComPtr<IInspectable> uiSettingsAsInspectable;
::RoActivateInstance(
HStringReference(RuntimeClass_Windows_UI_ViewManagement_UISettings)
.Get(),
&uiSettingsAsInspectable);
if (NS_WARN_IF(!uiSettingsAsInspectable)) {
return nullptr;
}
ComPtr<IUISettings5> uiSettings5;
if (SUCCEEDED(uiSettingsAsInspectable.As(&uiSettings5))) {
EventRegistrationToken unusedToken;
auto callback = Callback<ITypedEventHandler<
UISettings*, UISettingsAutoHideScrollBarsChangedEventArgs*>>(
[](auto...) {
// Scrollbar sizes change layout.
LookAndFeel::NotifyChangedAllWindows(
widget::ThemeChangeKind::StyleAndLayout);
return S_OK;
});
(void)NS_WARN_IF(FAILED(uiSettings5->add_AutoHideScrollBarsChanged(
callback.Get(), &unusedToken)));
}
ComPtr<IUISettings2> uiSettings2;
if (SUCCEEDED(uiSettingsAsInspectable.As(&uiSettings2))) {
EventRegistrationToken unusedToken;
auto callback =
Callback<ITypedEventHandler<UISettings*, IInspectable*>>([](auto...) {
// Text scale factor changes style and layout.
LookAndFeel::NotifyChangedAllWindows(
widget::ThemeChangeKind::StyleAndLayout);
return S_OK;
});
(void)NS_WARN_IF(FAILED(uiSettings2->add_TextScaleFactorChanged(
callback.Get(), &unusedToken)));
}
ComPtr<IUISettings3> uiSettings3;
if (SUCCEEDED(uiSettingsAsInspectable.As(&uiSettings3))) {
EventRegistrationToken unusedToken;
auto callback =
Callback<ITypedEventHandler<UISettings*, IInspectable*>>([](auto...) {
// System color changes change style only.
LookAndFeel::NotifyChangedAllWindows(
widget::ThemeChangeKind::Style);
return S_OK;
});
(void)NS_WARN_IF(FAILED(
uiSettings3->add_ColorValuesChanged(callback.Get(), &unusedToken)));
}
ComPtr<IUISettings4> uiSettings4;
if (SUCCEEDED(uiSettingsAsInspectable.As(&uiSettings4))) {
EventRegistrationToken unusedToken;
auto callback =
Callback<ITypedEventHandler<UISettings*, IInspectable*>>([](auto...) {
// Transparent effects changes change media queries only.
LookAndFeel::NotifyChangedAllWindows(
widget::ThemeChangeKind::MediaQueriesOnly);
return S_OK;
});
(void)NS_WARN_IF(FAILED(uiSettings4->add_AdvancedEffectsEnabledChanged(
callback.Get(), &unusedToken)));
}
sUiSettingsAsInspectable = dont_AddRef(uiSettingsAsInspectable.Detach());
ClearOnShutdown(&sUiSettingsAsInspectable);
}
return sUiSettingsAsInspectable.get();
#else
return nullptr;
#endif
}
Maybe<nscolor> WindowsUIUtils::GetAccentColor(int aTone) {
MOZ_ASSERT(aTone >= -3);
MOZ_ASSERT(aTone <= 3);
#ifndef __MINGW32__
ComPtr<IInspectable> settings = GetUISettings();
if (NS_WARN_IF(!settings)) {
return Nothing();
}
ComPtr<IUISettings3> uiSettings3;
if (NS_WARN_IF(FAILED(settings.As(&uiSettings3)))) {
return Nothing();
}
Color color;
auto colorType = UIColorType(int(UIColorType_Accent) + aTone);
if (NS_WARN_IF(FAILED(uiSettings3->GetColorValue(colorType, &color)))) {
return Nothing();
}
return Some(NS_RGBA(color.R, color.G, color.B, color.A));
#else
return Nothing();
#endif
}
Maybe<nscolor> WindowsUIUtils::GetSystemColor(ColorScheme aScheme,
int aSysColor) {
#ifndef __MINGW32__
if (!StaticPrefs::widget_windows_uwp_system_colors_enabled()) {
return Nothing();
}
// https://docs.microsoft.com/en-us/windows/apps/design/style/color
// Is a useful resource to see which values have decent contrast.
if (StaticPrefs::widget_windows_uwp_system_colors_highlight_accent()) {
if (aSysColor == COLOR_HIGHLIGHT) {
int tone = aScheme == ColorScheme::Light ? 0 : -1;
if (auto c = GetAccentColor(tone)) {
return c;
}
}
if (aSysColor == COLOR_HIGHLIGHTTEXT && GetAccentColor()) {
return Some(NS_RGBA(255, 255, 255, 255));
}
}
if (aScheme == ColorScheme::Dark) {
// There are no explicitly dark colors in UWP, other than the highlight
// colors above.
return Nothing();
}
auto knownType = [&]() -> Maybe<UIElementType> {
# define MAP(_win32, _uwp) \
case COLOR_##_win32: \
return Some(UIElementType_##_uwp)
switch (aSysColor) {
MAP(HIGHLIGHT, Highlight);
MAP(HIGHLIGHTTEXT, HighlightText);
MAP(ACTIVECAPTION, ActiveCaption);
MAP(BTNFACE, ButtonFace);
MAP(BTNTEXT, ButtonText);
MAP(CAPTIONTEXT, CaptionText);
MAP(GRAYTEXT, GrayText);
MAP(HOTLIGHT, Hotlight);
MAP(INACTIVECAPTION, InactiveCaption);
MAP(INACTIVECAPTIONTEXT, InactiveCaptionText);
MAP(WINDOW, Window);
MAP(WINDOWTEXT, WindowText);
default:
return Nothing();
}
# undef MAP
}();
if (!knownType) {
return Nothing();
}
ComPtr<IInspectable> settings = GetUISettings();
if (NS_WARN_IF(!settings)) {
return Nothing();
}
ComPtr<IUISettings> uiSettings;
if (NS_WARN_IF(FAILED(settings.As(&uiSettings)))) {
return Nothing();
}
Color color;
if (NS_WARN_IF(FAILED(uiSettings->UIElementColor(*knownType, &color)))) {
return Nothing();
}
return Some(NS_RGBA(color.R, color.G, color.B, color.A));
#else
return Nothing();
#endif
}
bool WindowsUIUtils::ComputeOverlayScrollbars() {
#ifndef __MINGW32__
if (!IsWin11OrLater()) {
// While in theory Windows 10 supports overlay scrollbar settings, it's off
// by default and it's untested whether our Win10 scrollbar drawing code
// deals with it properly.
return false;
}
if (!StaticPrefs::widget_windows_overlay_scrollbars_enabled()) {
return false;
}
ComPtr<IInspectable> settings = GetUISettings();
if (NS_WARN_IF(!settings)) {
return false;
}
ComPtr<IUISettings5> uiSettings5;
if (NS_WARN_IF(FAILED(settings.As(&uiSettings5)))) {
return false;
}
boolean autoHide = false;
if (NS_WARN_IF(FAILED(uiSettings5->get_AutoHideScrollBars(&autoHide)))) {
return false;
}
return autoHide;
#else
return false;
#endif
}
double WindowsUIUtils::ComputeTextScaleFactor() {
#ifndef __MINGW32__
ComPtr<IInspectable> settings = GetUISettings();
if (NS_WARN_IF(!settings)) {
return 1.0;
}
ComPtr<IUISettings2> uiSettings2;
if (NS_WARN_IF(FAILED(settings.As(&uiSettings2)))) {
return false;
}
double scaleFactor = 1.0;
if (NS_WARN_IF(FAILED(uiSettings2->get_TextScaleFactor(&scaleFactor)))) {
return 1.0;
}
return scaleFactor;
#else
return 1.0;
#endif
}
bool WindowsUIUtils::ComputeTransparencyEffects() {
constexpr bool kDefault = true;
#ifndef __MINGW32__
ComPtr<IInspectable> settings = GetUISettings();
if (NS_WARN_IF(!settings)) {
return kDefault;
}
ComPtr<IUISettings4> uiSettings4;
if (NS_WARN_IF(FAILED(settings.As(&uiSettings4)))) {
return kDefault;
}
boolean transparencyEffects = kDefault;
if (NS_WARN_IF(FAILED(
uiSettings4->get_AdvancedEffectsEnabled(&transparencyEffects)))) {
return kDefault;
}
return transparencyEffects;
#else
return kDefault;
#endif
}
void WindowsUIUtils::UpdateInTabletMode() {
#ifndef __MINGW32__
nsresult rv;
nsCOMPtr<nsIWindowMediator> winMediator(
do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv));
if (NS_FAILED(rv)) {
return;
}
nsCOMPtr<nsIWidget> widget;
nsCOMPtr<mozIDOMWindowProxy> navWin;
rv = winMediator->GetMostRecentBrowserWindow(getter_AddRefs(navWin));
if (NS_FAILED(rv) || !navWin) {
return;
}
nsPIDOMWindowOuter* win = nsPIDOMWindowOuter::From(navWin);
widget = widget::WidgetUtils::DOMWindowToWidget(win);
if (!widget) {
return;
}
HWND winPtr = (HWND)widget->GetNativeData(NS_NATIVE_WINDOW);
ComPtr<IUIViewSettingsInterop> uiViewSettingsInterop;
HRESULT hr = GetActivationFactory(
HStringReference(RuntimeClass_Windows_UI_ViewManagement_UIViewSettings)
.Get(),
&uiViewSettingsInterop);
if (FAILED(hr)) {
return;
}
ComPtr<IUIViewSettings> uiViewSettings;
hr = uiViewSettingsInterop->GetForWindow(winPtr,
IID_PPV_ARGS(&uiViewSettings));
if (FAILED(hr)) {
return;
}
UserInteractionMode mode;
hr = uiViewSettings->get_UserInteractionMode(&mode);
if (FAILED(hr)) {
return;
}
TabletModeState oldTabletModeState = sInTabletModeState;
sInTabletModeState = mode == UserInteractionMode_Touch ? TabletModeState::On
: TabletModeState::Off;
if (sInTabletModeState != oldTabletModeState) {
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
observerService->NotifyObservers(nullptr, "tablet-mode-change",
sInTabletModeState == TabletModeState::On
? u"tablet-mode"
: u"normal-mode");
}
#endif
}
#ifndef __MINGW32__
struct HStringDeleter {
using pointer = HSTRING;
void operator()(pointer aString) { WindowsDeleteString(aString); }
};
using HStringUniquePtr = UniquePtr<HSTRING, HStringDeleter>;
Result<HStringUniquePtr, HRESULT> ConvertToWindowsString(
const nsAString& aStr) {
HSTRING rawStr;
HRESULT hr = WindowsCreateString(PromiseFlatString(aStr).get(), aStr.Length(),
&rawStr);
if (FAILED(hr)) {
return Err(hr);
}
return HStringUniquePtr(rawStr);
}
static Result<Ok, nsresult> RequestShare(
const std::function<HRESULT(IDataRequestedEventArgs* pArgs)>& aCallback) {
HWND hwnd = GetForegroundWindow();
if (!hwnd) {
return Err(NS_ERROR_FAILURE);
}
ComPtr<IDataTransferManagerInterop> dtmInterop;
ComPtr<IDataTransferManager> dtm;
HRESULT hr = RoGetActivationFactory(
HStringReference(
RuntimeClass_Windows_ApplicationModel_DataTransfer_DataTransferManager)
.Get(),
IID_PPV_ARGS(&dtmInterop));
if (FAILED(hr) ||
FAILED(dtmInterop->GetForWindow(hwnd, IID_PPV_ARGS(&dtm)))) {
return Err(NS_ERROR_FAILURE);
}
auto callback = Callback<
ITypedEventHandler<DataTransferManager*, DataRequestedEventArgs*>>(
[aCallback](IDataTransferManager*,
IDataRequestedEventArgs* pArgs) -> HRESULT {
return aCallback(pArgs);
});
EventRegistrationToken dataRequestedToken;
if (FAILED(dtm->add_DataRequested(callback.Get(), &dataRequestedToken)) ||
FAILED(dtmInterop->ShowShareUIForWindow(hwnd))) {
return Err(NS_ERROR_FAILURE);
}
return Ok();
}
static Result<Ok, nsresult> AddShareEventListeners(
const RefPtr<mozilla::media::Refcountable<MozPromiseHolder<SharePromise>>>&
aPromiseHolder,
const ComPtr<IDataPackage>& aDataPackage) {
ComPtr<IDataPackage3> spDataPackage3;
if (FAILED(aDataPackage.As(&spDataPackage3))) {
return Err(NS_ERROR_FAILURE);
}
auto completedCallback =
Callback<ITypedEventHandler<DataPackage*, ShareCompletedEventArgs*>>(
[aPromiseHolder](IDataPackage*,
IShareCompletedEventArgs*) -> HRESULT {
aPromiseHolder->Resolve(true, __func__);
return S_OK;
});
EventRegistrationToken dataRequestedToken;
if (FAILED(spDataPackage3->add_ShareCompleted(completedCallback.Get(),
&dataRequestedToken))) {
return Err(NS_ERROR_FAILURE);
}
ComPtr<IDataPackage4> spDataPackage4;
if (SUCCEEDED(aDataPackage.As(&spDataPackage4))) {
// Use SharedCanceled API only on supported versions of Windows
// So that the older ones can still use ShareUrl()
auto canceledCallback =
Callback<ITypedEventHandler<DataPackage*, IInspectable*>>(
[aPromiseHolder](IDataPackage*, IInspectable*) -> HRESULT {
aPromiseHolder->Reject(NS_ERROR_FAILURE, __func__);
return S_OK;
});
if (FAILED(spDataPackage4->add_ShareCanceled(canceledCallback.Get(),
&dataRequestedToken))) {
return Err(NS_ERROR_FAILURE);
}
}
return Ok();
}
#endif
RefPtr<SharePromise> WindowsUIUtils::Share(nsAutoString aTitle,
nsAutoString aText,
nsAutoString aUrl) {
auto promiseHolder = MakeRefPtr<
mozilla::media::Refcountable<MozPromiseHolder<SharePromise>>>();
RefPtr<SharePromise> promise = promiseHolder->Ensure(__func__);
#ifndef __MINGW32__
auto result = RequestShare([promiseHolder, title = std::move(aTitle),
text = std::move(aText), url = std::move(aUrl)](
IDataRequestedEventArgs* pArgs) {
ComPtr<IDataRequest> spDataRequest;
ComPtr<IDataPackage> spDataPackage;
ComPtr<IDataPackage2> spDataPackage2;
ComPtr<IDataPackagePropertySet> spDataPackageProperties;
if (FAILED(pArgs->get_Request(&spDataRequest)) ||
FAILED(spDataRequest->get_Data(&spDataPackage)) ||
FAILED(spDataPackage.As(&spDataPackage2)) ||
FAILED(spDataPackage->get_Properties(&spDataPackageProperties))) {
promiseHolder->Reject(NS_ERROR_FAILURE, __func__);
return E_FAIL;
}
/*
* Windows always requires a title, and an empty string does not work.
* Thus we trick the API by passing a whitespace when we have no title.
* https://docs.microsoft.com/en-us/windows/uwp/app-to-app/share-data
*/
auto wTitle = ConvertToWindowsString((title.IsVoid() || title.Length() == 0)
? nsAutoString(u" "_ns)
: title);
if (wTitle.isErr() ||
FAILED(spDataPackageProperties->put_Title(wTitle.unwrap().get()))) {
promiseHolder->Reject(NS_ERROR_FAILURE, __func__);
return E_FAIL;
}
// Assign even if empty, as Windows requires some data to share
auto wText = ConvertToWindowsString(text);
if (wText.isErr() || FAILED(spDataPackage->SetText(wText.unwrap().get()))) {
promiseHolder->Reject(NS_ERROR_FAILURE, __func__);
return E_FAIL;
}
if (!url.IsVoid()) {
auto wUrl = ConvertToWindowsString(url);
if (wUrl.isErr()) {
promiseHolder->Reject(NS_ERROR_FAILURE, __func__);
return wUrl.unwrapErr();
}
ComPtr<IUriRuntimeClassFactory> uriFactory;
ComPtr<IUriRuntimeClass> uri;
auto hr = GetActivationFactory(
HStringReference(RuntimeClass_Windows_Foundation_Uri).Get(),
&uriFactory);
if (FAILED(hr) ||
FAILED(uriFactory->CreateUri(wUrl.unwrap().get(), &uri)) ||
FAILED(spDataPackage2->SetWebLink(uri.Get()))) {
promiseHolder->Reject(NS_ERROR_FAILURE, __func__);
return E_FAIL;
}
}
if (!StaticPrefs::widget_windows_share_wait_action_enabled()) {
promiseHolder->Resolve(true, __func__);
} else if (AddShareEventListeners(promiseHolder, spDataPackage).isErr()) {
promiseHolder->Reject(NS_ERROR_FAILURE, __func__);
return E_FAIL;
}
return S_OK;
});
if (result.isErr()) {
promiseHolder->Reject(result.unwrapErr(), __func__);
}
#else
promiseHolder->Reject(NS_ERROR_FAILURE, __func__);
#endif
return promise;
}
NS_IMETHODIMP
WindowsUIUtils::ShareUrl(const nsAString& aUrlToShare,
const nsAString& aShareTitle) {
nsAutoString text;
text.SetIsVoid(true);
WindowsUIUtils::Share(nsAutoString(aShareTitle), text,
nsAutoString(aUrlToShare));
return NS_OK;
}