Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / widget / windows / WindowsUIUtils.cpp
blob3d5cff7e23e494a76e653e3ce4e60098813f40a0
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include <windows.h>
7 #include <wrl.h>
9 #include "nsServiceManagerUtils.h"
11 #include "WindowsUIUtils.h"
13 #include "nsIObserverService.h"
14 #include "nsIAppShellService.h"
15 #include "nsAppShellCID.h"
16 #include "mozilla/ClearOnShutdown.h"
17 #include "mozilla/ResultVariant.h"
18 #include "mozilla/Services.h"
19 #include "mozilla/StaticPrefs_widget.h"
20 #include "mozilla/WidgetUtils.h"
21 #include "mozilla/WindowsVersion.h"
22 #include "mozilla/LookAndFeel.h"
23 #include "mozilla/ScopeExit.h"
24 #include "mozilla/media/MediaUtils.h"
25 #include "nsString.h"
26 #include "nsGlobalWindowOuter.h"
27 #include "nsIWidget.h"
28 #include "nsIWindowMediator.h"
29 #include "nsPIDOMWindow.h"
30 #include "nsWindowGfx.h"
31 #include "Units.h"
33 /* mingw currently doesn't support windows.ui.viewmanagement.h, so we disable it
34 * until it's fixed. */
36 // See
37 // https://github.com/tpn/winsdk-10/blob/master/Include/10.0.14393.0/winrt/windows.ui.viewmanagement.h
38 // for the source of some of these definitions for older SDKs.
39 #ifndef __MINGW32__
41 # include <inspectable.h>
42 # include <roapi.h>
43 # include <windows.ui.viewmanagement.h>
45 # pragma comment(lib, "runtimeobject.lib")
47 using namespace ABI::Windows::UI;
48 using namespace ABI::Windows::UI::ViewManagement;
49 using namespace Microsoft::WRL;
50 using namespace Microsoft::WRL::Wrappers;
51 using namespace ABI::Windows::Foundation;
52 using namespace ABI::Windows::ApplicationModel::DataTransfer;
54 # ifndef RuntimeClass_Windows_UI_ViewManagement_UIViewSettings
55 # define RuntimeClass_Windows_UI_ViewManagement_UIViewSettings \
56 L"Windows.UI.ViewManagement.UIViewSettings"
57 # endif
59 # ifndef IUIViewSettingsInterop
61 using IUIViewSettingsInterop = interface IUIViewSettingsInterop;
63 MIDL_INTERFACE("3694dbf9-8f68-44be-8ff5-195c98ede8a6")
64 IUIViewSettingsInterop : public IInspectable {
65 public:
66 virtual HRESULT STDMETHODCALLTYPE GetForWindow(HWND hwnd, REFIID riid,
67 void** ppv) = 0;
69 # endif
71 # ifndef __IDataTransferManagerInterop_INTERFACE_DEFINED__
72 # define __IDataTransferManagerInterop_INTERFACE_DEFINED__
74 using IDataTransferManagerInterop = interface IDataTransferManagerInterop;
76 MIDL_INTERFACE("3A3DCD6C-3EAB-43DC-BCDE-45671CE800C8")
77 IDataTransferManagerInterop : public IUnknown {
78 public:
79 virtual HRESULT STDMETHODCALLTYPE GetForWindow(
80 HWND appWindow, REFIID riid, void** dataTransferManager) = 0;
81 virtual HRESULT STDMETHODCALLTYPE ShowShareUIForWindow(HWND appWindow) = 0;
84 # endif
86 # if !defined( \
87 ____x_ABI_CWindows_CApplicationModel_CDataTransfer_CIDataPackage4_INTERFACE_DEFINED__)
88 # define ____x_ABI_CWindows_CApplicationModel_CDataTransfer_CIDataPackage4_INTERFACE_DEFINED__
90 MIDL_INTERFACE("13a24ec8-9382-536f-852a-3045e1b29a3b")
91 IDataPackage4 : public IInspectable {
92 public:
93 virtual HRESULT STDMETHODCALLTYPE add_ShareCanceled(
94 __FITypedEventHandler_2_Windows__CApplicationModel__CDataTransfer__CDataPackage_IInspectable *
95 handler,
96 EventRegistrationToken * token) = 0;
97 virtual HRESULT STDMETHODCALLTYPE remove_ShareCanceled(
98 EventRegistrationToken token) = 0;
101 # endif
103 # ifndef RuntimeClass_Windows_UI_ViewManagement_UISettings
104 # define RuntimeClass_Windows_UI_ViewManagement_UISettings \
105 L"Windows.UI.ViewManagement.UISettings"
106 # endif
107 # if WINDOWS_FOUNDATION_UNIVERSALAPICONTRACT_VERSION < 0x80000
108 namespace ABI {
109 namespace Windows {
110 namespace UI {
111 namespace ViewManagement {
113 class UISettings;
114 class UISettingsAutoHideScrollBarsChangedEventArgs;
115 interface IUISettingsAutoHideScrollBarsChangedEventArgs;
116 MIDL_INTERFACE("87afd4b2-9146-5f02-8f6b-06d454174c0f")
117 IUISettingsAutoHideScrollBarsChangedEventArgs : public IInspectable{};
119 } // namespace ViewManagement
120 } // namespace UI
121 } // namespace Windows
122 } // namespace ABI
124 namespace ABI {
125 namespace Windows {
126 namespace Foundation {
128 template <>
129 struct __declspec(uuid("808aef30-2660-51b0-9c11-f75dd42006b4"))
130 ITypedEventHandler<ABI::Windows::UI::ViewManagement::UISettings*,
131 ABI::Windows::UI::ViewManagement::
132 UISettingsAutoHideScrollBarsChangedEventArgs*>
133 : ITypedEventHandler_impl<
134 ABI::Windows::Foundation::Internal::AggregateType<
135 ABI::Windows::UI::ViewManagement::UISettings*,
136 ABI::Windows::UI::ViewManagement::IUISettings*>,
137 ABI::Windows::Foundation::Internal::AggregateType<
138 ABI::Windows::UI::ViewManagement::
139 UISettingsAutoHideScrollBarsChangedEventArgs*,
140 ABI::Windows::UI::ViewManagement::
141 IUISettingsAutoHideScrollBarsChangedEventArgs*>> {
142 static const wchar_t* z_get_rc_name_impl() {
143 return L"Windows.Foundation.TypedEventHandler`2<Windows.UI.ViewManagement."
144 L"UISettings, "
145 L"Windows.UI.ViewManagement."
146 L"UISettingsAutoHideScrollBarsChangedEventArgs>";
149 // Define a typedef for the parameterized interface specialization's mangled
150 // name. This allows code which uses the mangled name for the parameterized
151 // interface to access the correct parameterized interface specialization.
152 typedef ITypedEventHandler<ABI::Windows::UI::ViewManagement::UISettings*,
153 ABI::Windows::UI::ViewManagement::
154 UISettingsAutoHideScrollBarsChangedEventArgs*>
155 __FITypedEventHandler_2_Windows__CUI__CViewManagement__CUISettings_Windows__CUI__CViewManagement__CUISettingsAutoHideScrollBarsChangedEventArgs_t;
156 # define __FITypedEventHandler_2_Windows__CUI__CViewManagement__CUISettings_Windows__CUI__CViewManagement__CUISettingsAutoHideScrollBarsChangedEventArgs \
157 ABI::Windows::Foundation:: \
158 __FITypedEventHandler_2_Windows__CUI__CViewManagement__CUISettings_Windows__CUI__CViewManagement__CUISettingsAutoHideScrollBarsChangedEventArgs_t
160 } // namespace Foundation
161 } // namespace Windows
162 } // namespace ABI
164 namespace ABI {
165 namespace Windows {
166 namespace UI {
167 namespace ViewManagement {
168 class UISettings;
169 class UISettingsAutoHideScrollBarsChangedEventArgs;
170 interface IUISettings5;
171 MIDL_INTERFACE("5349d588-0cb5-5f05-bd34-706b3231f0bd")
172 IUISettings5 : public IInspectable {
173 public:
174 virtual HRESULT STDMETHODCALLTYPE get_AutoHideScrollBars(boolean * value) = 0;
175 virtual HRESULT STDMETHODCALLTYPE add_AutoHideScrollBarsChanged(
176 __FITypedEventHandler_2_Windows__CUI__CViewManagement__CUISettings_Windows__CUI__CViewManagement__CUISettingsAutoHideScrollBarsChangedEventArgs *
177 handler,
178 EventRegistrationToken * token) = 0;
179 virtual HRESULT STDMETHODCALLTYPE remove_AutoHideScrollBarsChanged(
180 EventRegistrationToken token) = 0;
182 } // namespace ViewManagement
183 } // namespace UI
184 } // namespace Windows
185 } // namespace ABI
186 # endif
187 #endif
189 using namespace mozilla;
191 enum class TabletModeState : uint8_t { Unknown, Off, On };
192 static TabletModeState sInTabletModeState;
194 WindowsUIUtils::WindowsUIUtils() = default;
195 WindowsUIUtils::~WindowsUIUtils() = default;
197 NS_IMPL_ISUPPORTS(WindowsUIUtils, nsIWindowsUIUtils)
199 NS_IMETHODIMP
200 WindowsUIUtils::GetSystemSmallIconSize(int32_t* aSize) {
201 NS_ENSURE_ARG(aSize);
203 mozilla::LayoutDeviceIntSize size =
204 nsWindowGfx::GetIconMetrics(nsWindowGfx::kSmallIcon);
205 *aSize = std::max(size.width, size.height);
206 return NS_OK;
209 NS_IMETHODIMP
210 WindowsUIUtils::GetSystemLargeIconSize(int32_t* aSize) {
211 NS_ENSURE_ARG(aSize);
213 mozilla::LayoutDeviceIntSize size =
214 nsWindowGfx::GetIconMetrics(nsWindowGfx::kRegularIcon);
215 *aSize = std::max(size.width, size.height);
216 return NS_OK;
219 NS_IMETHODIMP
220 WindowsUIUtils::SetWindowIcon(mozIDOMWindowProxy* aWindow,
221 imgIContainer* aSmallIcon,
222 imgIContainer* aBigIcon) {
223 NS_ENSURE_ARG(aWindow);
225 nsCOMPtr<nsIWidget> widget =
226 nsGlobalWindowOuter::Cast(aWindow)->GetMainWidget();
227 nsWindow* window = static_cast<nsWindow*>(widget.get());
229 nsresult rv;
231 if (aSmallIcon) {
232 HICON hIcon = nullptr;
233 rv = nsWindowGfx::CreateIcon(
234 aSmallIcon, false, mozilla::LayoutDeviceIntPoint(),
235 nsWindowGfx::GetIconMetrics(nsWindowGfx::kSmallIcon), &hIcon);
236 NS_ENSURE_SUCCESS(rv, rv);
238 window->SetSmallIcon(hIcon);
241 if (aBigIcon) {
242 HICON hIcon = nullptr;
243 rv = nsWindowGfx::CreateIcon(
244 aBigIcon, false, mozilla::LayoutDeviceIntPoint(),
245 nsWindowGfx::GetIconMetrics(nsWindowGfx::kRegularIcon), &hIcon);
246 NS_ENSURE_SUCCESS(rv, rv);
248 window->SetBigIcon(hIcon);
251 return NS_OK;
254 NS_IMETHODIMP
255 WindowsUIUtils::SetWindowIconFromExe(mozIDOMWindowProxy* aWindow,
256 const nsAString& aExe, uint16_t aIndex) {
257 NS_ENSURE_ARG(aWindow);
259 nsCOMPtr<nsIWidget> widget =
260 nsGlobalWindowOuter::Cast(aWindow)->GetMainWidget();
261 nsWindow* window = static_cast<nsWindow*>(widget.get());
263 HICON icon = ::LoadIconW(::GetModuleHandleW(PromiseFlatString(aExe).get()),
264 MAKEINTRESOURCEW(aIndex));
265 window->SetBigIcon(icon);
266 window->SetSmallIcon(icon);
268 return NS_OK;
271 NS_IMETHODIMP
272 WindowsUIUtils::SetWindowIconNoData(mozIDOMWindowProxy* aWindow) {
273 NS_ENSURE_ARG(aWindow);
275 nsCOMPtr<nsIWidget> widget =
276 nsGlobalWindowOuter::Cast(aWindow)->GetMainWidget();
277 nsWindow* window = static_cast<nsWindow*>(widget.get());
279 window->SetSmallIconNoData();
280 window->SetBigIconNoData();
282 return NS_OK;
285 bool WindowsUIUtils::GetInTabletMode() {
286 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
287 if (sInTabletModeState == TabletModeState::Unknown) {
288 UpdateInTabletMode();
290 return sInTabletModeState == TabletModeState::On;
293 NS_IMETHODIMP
294 WindowsUIUtils::GetInTabletMode(bool* aResult) {
295 *aResult = GetInTabletMode();
296 return NS_OK;
299 static IInspectable* GetUISettings() {
300 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
301 #ifndef __MINGW32__
302 // We need to keep this alive for ~ever so that change callbacks work as
303 // expected, sigh.
304 static StaticRefPtr<IInspectable> sUiSettingsAsInspectable;
306 if (!sUiSettingsAsInspectable) {
307 ComPtr<IInspectable> uiSettingsAsInspectable;
308 ::RoActivateInstance(
309 HStringReference(RuntimeClass_Windows_UI_ViewManagement_UISettings)
310 .Get(),
311 &uiSettingsAsInspectable);
312 if (NS_WARN_IF(!uiSettingsAsInspectable)) {
313 return nullptr;
316 ComPtr<IUISettings5> uiSettings5;
317 if (SUCCEEDED(uiSettingsAsInspectable.As(&uiSettings5))) {
318 EventRegistrationToken unusedToken;
319 auto callback = Callback<ITypedEventHandler<
320 UISettings*, UISettingsAutoHideScrollBarsChangedEventArgs*>>(
321 [](auto...) {
322 // Scrollbar sizes change layout.
323 LookAndFeel::NotifyChangedAllWindows(
324 widget::ThemeChangeKind::StyleAndLayout);
325 return S_OK;
327 (void)NS_WARN_IF(FAILED(uiSettings5->add_AutoHideScrollBarsChanged(
328 callback.Get(), &unusedToken)));
331 ComPtr<IUISettings2> uiSettings2;
332 if (SUCCEEDED(uiSettingsAsInspectable.As(&uiSettings2))) {
333 EventRegistrationToken unusedToken;
334 auto callback =
335 Callback<ITypedEventHandler<UISettings*, IInspectable*>>([](auto...) {
336 // Text scale factor changes style and layout.
337 LookAndFeel::NotifyChangedAllWindows(
338 widget::ThemeChangeKind::StyleAndLayout);
339 return S_OK;
341 (void)NS_WARN_IF(FAILED(uiSettings2->add_TextScaleFactorChanged(
342 callback.Get(), &unusedToken)));
345 ComPtr<IUISettings3> uiSettings3;
346 if (SUCCEEDED(uiSettingsAsInspectable.As(&uiSettings3))) {
347 EventRegistrationToken unusedToken;
348 auto callback =
349 Callback<ITypedEventHandler<UISettings*, IInspectable*>>([](auto...) {
350 // System color changes change style only.
351 LookAndFeel::NotifyChangedAllWindows(
352 widget::ThemeChangeKind::Style);
353 return S_OK;
355 (void)NS_WARN_IF(FAILED(
356 uiSettings3->add_ColorValuesChanged(callback.Get(), &unusedToken)));
359 ComPtr<IUISettings4> uiSettings4;
360 if (SUCCEEDED(uiSettingsAsInspectable.As(&uiSettings4))) {
361 EventRegistrationToken unusedToken;
362 auto callback =
363 Callback<ITypedEventHandler<UISettings*, IInspectable*>>([](auto...) {
364 // Transparent effects changes change media queries only.
365 LookAndFeel::NotifyChangedAllWindows(
366 widget::ThemeChangeKind::MediaQueriesOnly);
367 return S_OK;
369 (void)NS_WARN_IF(FAILED(uiSettings4->add_AdvancedEffectsEnabledChanged(
370 callback.Get(), &unusedToken)));
373 sUiSettingsAsInspectable = dont_AddRef(uiSettingsAsInspectable.Detach());
374 ClearOnShutdown(&sUiSettingsAsInspectable);
377 return sUiSettingsAsInspectable.get();
378 #else
379 return nullptr;
380 #endif
383 Maybe<nscolor> WindowsUIUtils::GetAccentColor(int aTone) {
384 MOZ_ASSERT(aTone >= -3);
385 MOZ_ASSERT(aTone <= 3);
386 #ifndef __MINGW32__
387 ComPtr<IInspectable> settings = GetUISettings();
388 if (NS_WARN_IF(!settings)) {
389 return Nothing();
391 ComPtr<IUISettings3> uiSettings3;
392 if (NS_WARN_IF(FAILED(settings.As(&uiSettings3)))) {
393 return Nothing();
395 Color color;
396 auto colorType = UIColorType(int(UIColorType_Accent) + aTone);
397 if (NS_WARN_IF(FAILED(uiSettings3->GetColorValue(colorType, &color)))) {
398 return Nothing();
400 return Some(NS_RGBA(color.R, color.G, color.B, color.A));
401 #else
402 return Nothing();
403 #endif
406 Maybe<nscolor> WindowsUIUtils::GetSystemColor(ColorScheme aScheme,
407 int aSysColor) {
408 #ifndef __MINGW32__
409 if (!StaticPrefs::widget_windows_uwp_system_colors_enabled()) {
410 return Nothing();
413 // https://docs.microsoft.com/en-us/windows/apps/design/style/color
414 // Is a useful resource to see which values have decent contrast.
415 if (StaticPrefs::widget_windows_uwp_system_colors_highlight_accent()) {
416 if (aSysColor == COLOR_HIGHLIGHT) {
417 int tone = aScheme == ColorScheme::Light ? 0 : -1;
418 if (auto c = GetAccentColor(tone)) {
419 return c;
422 if (aSysColor == COLOR_HIGHLIGHTTEXT && GetAccentColor()) {
423 return Some(NS_RGBA(255, 255, 255, 255));
427 if (aScheme == ColorScheme::Dark) {
428 // There are no explicitly dark colors in UWP, other than the highlight
429 // colors above.
430 return Nothing();
433 auto knownType = [&]() -> Maybe<UIElementType> {
434 # define MAP(_win32, _uwp) \
435 case COLOR_##_win32: \
436 return Some(UIElementType_##_uwp)
437 switch (aSysColor) {
438 MAP(HIGHLIGHT, Highlight);
439 MAP(HIGHLIGHTTEXT, HighlightText);
440 MAP(ACTIVECAPTION, ActiveCaption);
441 MAP(BTNFACE, ButtonFace);
442 MAP(BTNTEXT, ButtonText);
443 MAP(CAPTIONTEXT, CaptionText);
444 MAP(GRAYTEXT, GrayText);
445 MAP(HOTLIGHT, Hotlight);
446 MAP(INACTIVECAPTION, InactiveCaption);
447 MAP(INACTIVECAPTIONTEXT, InactiveCaptionText);
448 MAP(WINDOW, Window);
449 MAP(WINDOWTEXT, WindowText);
450 default:
451 return Nothing();
453 # undef MAP
454 }();
455 if (!knownType) {
456 return Nothing();
458 ComPtr<IInspectable> settings = GetUISettings();
459 if (NS_WARN_IF(!settings)) {
460 return Nothing();
462 ComPtr<IUISettings> uiSettings;
463 if (NS_WARN_IF(FAILED(settings.As(&uiSettings)))) {
464 return Nothing();
466 Color color;
467 if (NS_WARN_IF(FAILED(uiSettings->UIElementColor(*knownType, &color)))) {
468 return Nothing();
470 return Some(NS_RGBA(color.R, color.G, color.B, color.A));
471 #else
472 return Nothing();
473 #endif
475 bool WindowsUIUtils::ComputeOverlayScrollbars() {
476 #ifndef __MINGW32__
477 if (!IsWin11OrLater()) {
478 // While in theory Windows 10 supports overlay scrollbar settings, it's off
479 // by default and it's untested whether our Win10 scrollbar drawing code
480 // deals with it properly.
481 return false;
483 if (!StaticPrefs::widget_windows_overlay_scrollbars_enabled()) {
484 return false;
486 ComPtr<IInspectable> settings = GetUISettings();
487 if (NS_WARN_IF(!settings)) {
488 return false;
490 ComPtr<IUISettings5> uiSettings5;
491 if (NS_WARN_IF(FAILED(settings.As(&uiSettings5)))) {
492 return false;
494 boolean autoHide = false;
495 if (NS_WARN_IF(FAILED(uiSettings5->get_AutoHideScrollBars(&autoHide)))) {
496 return false;
498 return autoHide;
499 #else
500 return false;
501 #endif
504 double WindowsUIUtils::ComputeTextScaleFactor() {
505 #ifndef __MINGW32__
506 ComPtr<IInspectable> settings = GetUISettings();
507 if (NS_WARN_IF(!settings)) {
508 return 1.0;
510 ComPtr<IUISettings2> uiSettings2;
511 if (NS_WARN_IF(FAILED(settings.As(&uiSettings2)))) {
512 return false;
514 double scaleFactor = 1.0;
515 if (NS_WARN_IF(FAILED(uiSettings2->get_TextScaleFactor(&scaleFactor)))) {
516 return 1.0;
518 return scaleFactor;
519 #else
520 return 1.0;
521 #endif
524 bool WindowsUIUtils::ComputeTransparencyEffects() {
525 constexpr bool kDefault = true;
526 #ifndef __MINGW32__
527 ComPtr<IInspectable> settings = GetUISettings();
528 if (NS_WARN_IF(!settings)) {
529 return kDefault;
531 ComPtr<IUISettings4> uiSettings4;
532 if (NS_WARN_IF(FAILED(settings.As(&uiSettings4)))) {
533 return kDefault;
535 boolean transparencyEffects = kDefault;
536 if (NS_WARN_IF(FAILED(
537 uiSettings4->get_AdvancedEffectsEnabled(&transparencyEffects)))) {
538 return kDefault;
540 return transparencyEffects;
541 #else
542 return kDefault;
543 #endif
546 void WindowsUIUtils::UpdateInTabletMode() {
547 #ifndef __MINGW32__
548 nsresult rv;
549 nsCOMPtr<nsIWindowMediator> winMediator(
550 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv));
551 if (NS_FAILED(rv)) {
552 return;
555 nsCOMPtr<nsIWidget> widget;
556 nsCOMPtr<mozIDOMWindowProxy> navWin;
558 rv = winMediator->GetMostRecentWindow(u"navigator:browser",
559 getter_AddRefs(navWin));
560 if (NS_FAILED(rv) || !navWin) {
561 // Fall back to the hidden window
562 nsCOMPtr<nsIAppShellService> appShell(
563 do_GetService(NS_APPSHELLSERVICE_CONTRACTID));
565 rv = appShell->GetHiddenDOMWindow(getter_AddRefs(navWin));
566 if (NS_FAILED(rv) || !navWin) {
567 return;
571 nsPIDOMWindowOuter* win = nsPIDOMWindowOuter::From(navWin);
572 widget = widget::WidgetUtils::DOMWindowToWidget(win);
574 if (!widget) {
575 return;
578 HWND winPtr = (HWND)widget->GetNativeData(NS_NATIVE_WINDOW);
579 ComPtr<IUIViewSettingsInterop> uiViewSettingsInterop;
581 HRESULT hr = GetActivationFactory(
582 HStringReference(RuntimeClass_Windows_UI_ViewManagement_UIViewSettings)
583 .Get(),
584 &uiViewSettingsInterop);
585 if (FAILED(hr)) {
586 return;
588 ComPtr<IUIViewSettings> uiViewSettings;
589 hr = uiViewSettingsInterop->GetForWindow(winPtr,
590 IID_PPV_ARGS(&uiViewSettings));
591 if (FAILED(hr)) {
592 return;
594 UserInteractionMode mode;
595 hr = uiViewSettings->get_UserInteractionMode(&mode);
596 if (FAILED(hr)) {
597 return;
600 TabletModeState oldTabletModeState = sInTabletModeState;
601 sInTabletModeState = mode == UserInteractionMode_Touch ? TabletModeState::On
602 : TabletModeState::Off;
603 if (sInTabletModeState != oldTabletModeState) {
604 nsCOMPtr<nsIObserverService> observerService =
605 mozilla::services::GetObserverService();
606 observerService->NotifyObservers(nullptr, "tablet-mode-change",
607 sInTabletModeState == TabletModeState::On
608 ? u"tablet-mode"
609 : u"normal-mode");
611 #endif
614 #ifndef __MINGW32__
615 struct HStringDeleter {
616 using pointer = HSTRING;
617 void operator()(pointer aString) { WindowsDeleteString(aString); }
620 using HStringUniquePtr = UniquePtr<HSTRING, HStringDeleter>;
622 Result<HStringUniquePtr, HRESULT> ConvertToWindowsString(
623 const nsAString& aStr) {
624 HSTRING rawStr;
625 HRESULT hr = WindowsCreateString(PromiseFlatString(aStr).get(), aStr.Length(),
626 &rawStr);
627 if (FAILED(hr)) {
628 return Err(hr);
630 return HStringUniquePtr(rawStr);
633 static Result<Ok, nsresult> RequestShare(
634 const std::function<HRESULT(IDataRequestedEventArgs* pArgs)>& aCallback) {
635 HWND hwnd = GetForegroundWindow();
636 if (!hwnd) {
637 return Err(NS_ERROR_FAILURE);
640 ComPtr<IDataTransferManagerInterop> dtmInterop;
641 ComPtr<IDataTransferManager> dtm;
643 HRESULT hr = RoGetActivationFactory(
644 HStringReference(
645 RuntimeClass_Windows_ApplicationModel_DataTransfer_DataTransferManager)
646 .Get(),
647 IID_PPV_ARGS(&dtmInterop));
648 if (FAILED(hr) ||
649 FAILED(dtmInterop->GetForWindow(hwnd, IID_PPV_ARGS(&dtm)))) {
650 return Err(NS_ERROR_FAILURE);
653 auto callback = Callback<
654 ITypedEventHandler<DataTransferManager*, DataRequestedEventArgs*>>(
655 [aCallback](IDataTransferManager*,
656 IDataRequestedEventArgs* pArgs) -> HRESULT {
657 return aCallback(pArgs);
660 EventRegistrationToken dataRequestedToken;
661 if (FAILED(dtm->add_DataRequested(callback.Get(), &dataRequestedToken)) ||
662 FAILED(dtmInterop->ShowShareUIForWindow(hwnd))) {
663 return Err(NS_ERROR_FAILURE);
666 return Ok();
669 static Result<Ok, nsresult> AddShareEventListeners(
670 const RefPtr<mozilla::media::Refcountable<MozPromiseHolder<SharePromise>>>&
671 aPromiseHolder,
672 const ComPtr<IDataPackage>& aDataPackage) {
673 ComPtr<IDataPackage3> spDataPackage3;
675 if (FAILED(aDataPackage.As(&spDataPackage3))) {
676 return Err(NS_ERROR_FAILURE);
679 auto completedCallback =
680 Callback<ITypedEventHandler<DataPackage*, ShareCompletedEventArgs*>>(
681 [aPromiseHolder](IDataPackage*,
682 IShareCompletedEventArgs*) -> HRESULT {
683 aPromiseHolder->Resolve(true, __func__);
684 return S_OK;
687 EventRegistrationToken dataRequestedToken;
688 if (FAILED(spDataPackage3->add_ShareCompleted(completedCallback.Get(),
689 &dataRequestedToken))) {
690 return Err(NS_ERROR_FAILURE);
693 ComPtr<IDataPackage4> spDataPackage4;
694 if (SUCCEEDED(aDataPackage.As(&spDataPackage4))) {
695 // Use SharedCanceled API only on supported versions of Windows
696 // So that the older ones can still use ShareUrl()
698 auto canceledCallback =
699 Callback<ITypedEventHandler<DataPackage*, IInspectable*>>(
700 [aPromiseHolder](IDataPackage*, IInspectable*) -> HRESULT {
701 aPromiseHolder->Reject(NS_ERROR_FAILURE, __func__);
702 return S_OK;
705 if (FAILED(spDataPackage4->add_ShareCanceled(canceledCallback.Get(),
706 &dataRequestedToken))) {
707 return Err(NS_ERROR_FAILURE);
711 return Ok();
713 #endif
715 RefPtr<SharePromise> WindowsUIUtils::Share(nsAutoString aTitle,
716 nsAutoString aText,
717 nsAutoString aUrl) {
718 auto promiseHolder = MakeRefPtr<
719 mozilla::media::Refcountable<MozPromiseHolder<SharePromise>>>();
720 RefPtr<SharePromise> promise = promiseHolder->Ensure(__func__);
722 #ifndef __MINGW32__
723 auto result = RequestShare([promiseHolder, title = std::move(aTitle),
724 text = std::move(aText), url = std::move(aUrl)](
725 IDataRequestedEventArgs* pArgs) {
726 ComPtr<IDataRequest> spDataRequest;
727 ComPtr<IDataPackage> spDataPackage;
728 ComPtr<IDataPackage2> spDataPackage2;
729 ComPtr<IDataPackagePropertySet> spDataPackageProperties;
731 if (FAILED(pArgs->get_Request(&spDataRequest)) ||
732 FAILED(spDataRequest->get_Data(&spDataPackage)) ||
733 FAILED(spDataPackage.As(&spDataPackage2)) ||
734 FAILED(spDataPackage->get_Properties(&spDataPackageProperties))) {
735 promiseHolder->Reject(NS_ERROR_FAILURE, __func__);
736 return E_FAIL;
740 * Windows always requires a title, and an empty string does not work.
741 * Thus we trick the API by passing a whitespace when we have no title.
742 * https://docs.microsoft.com/en-us/windows/uwp/app-to-app/share-data
744 auto wTitle = ConvertToWindowsString((title.IsVoid() || title.Length() == 0)
745 ? nsAutoString(u" "_ns)
746 : title);
747 if (wTitle.isErr() ||
748 FAILED(spDataPackageProperties->put_Title(wTitle.unwrap().get()))) {
749 promiseHolder->Reject(NS_ERROR_FAILURE, __func__);
750 return E_FAIL;
753 // Assign even if empty, as Windows requires some data to share
754 auto wText = ConvertToWindowsString(text);
755 if (wText.isErr() || FAILED(spDataPackage->SetText(wText.unwrap().get()))) {
756 promiseHolder->Reject(NS_ERROR_FAILURE, __func__);
757 return E_FAIL;
760 if (!url.IsVoid()) {
761 auto wUrl = ConvertToWindowsString(url);
762 if (wUrl.isErr()) {
763 promiseHolder->Reject(NS_ERROR_FAILURE, __func__);
764 return wUrl.unwrapErr();
767 ComPtr<IUriRuntimeClassFactory> uriFactory;
768 ComPtr<IUriRuntimeClass> uri;
770 auto hr = GetActivationFactory(
771 HStringReference(RuntimeClass_Windows_Foundation_Uri).Get(),
772 &uriFactory);
774 if (FAILED(hr) ||
775 FAILED(uriFactory->CreateUri(wUrl.unwrap().get(), &uri)) ||
776 FAILED(spDataPackage2->SetWebLink(uri.Get()))) {
777 promiseHolder->Reject(NS_ERROR_FAILURE, __func__);
778 return E_FAIL;
782 if (!StaticPrefs::widget_windows_share_wait_action_enabled()) {
783 promiseHolder->Resolve(true, __func__);
784 } else if (AddShareEventListeners(promiseHolder, spDataPackage).isErr()) {
785 promiseHolder->Reject(NS_ERROR_FAILURE, __func__);
786 return E_FAIL;
789 return S_OK;
791 if (result.isErr()) {
792 promiseHolder->Reject(result.unwrapErr(), __func__);
794 #else
795 promiseHolder->Reject(NS_ERROR_FAILURE, __func__);
796 #endif
798 return promise;
801 NS_IMETHODIMP
802 WindowsUIUtils::ShareUrl(const nsAString& aUrlToShare,
803 const nsAString& aShareTitle) {
804 nsAutoString text;
805 text.SetIsVoid(true);
806 WindowsUIUtils::Share(nsAutoString(aShareTitle), text,
807 nsAutoString(aUrlToShare));
808 return NS_OK;