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/. */
10 #include "nsServiceManagerUtils.h"
12 #include "WindowsUIUtils.h"
14 #include "nsIObserverService.h"
15 #include "nsIAppShellService.h"
16 #include "nsAppShellCID.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"
26 #include "nsIWidget.h"
27 #include "nsIWindowMediator.h"
28 #include "nsPIDOMWindow.h"
29 #include "nsWindowGfx.h"
32 /* mingw currently doesn't support windows.ui.viewmanagement.h, so we disable it
33 * until it's fixed. */
36 # include <inspectable.h>
38 # include <windows.ui.viewmanagement.h>
40 # pragma comment(lib, "runtimeobject.lib")
42 using namespace ABI::Windows::UI
;
43 using namespace ABI::Windows::UI::ViewManagement
;
44 using namespace Microsoft::WRL
;
45 using namespace Microsoft::WRL::Wrappers
;
46 using namespace ABI::Windows::Foundation
;
47 using namespace ABI::Windows::ApplicationModel::DataTransfer
;
49 /* All of this is win10 stuff and we're compiling against win81 headers
50 * for now, so we may need to do some legwork: */
51 # if WINVER_MAXVER < 0x0A00
55 namespace ViewManagement
{
56 enum UserInteractionMode
{
57 UserInteractionMode_Mouse
= 0,
58 UserInteractionMode_Touch
= 1
62 } // namespace Windows
67 # ifndef RuntimeClass_Windows_UI_ViewManagement_UIViewSettings
68 # define RuntimeClass_Windows_UI_ViewManagement_UIViewSettings \
69 L"Windows.UI.ViewManagement.UIViewSettings"
72 # if WINVER_MAXVER < 0x0A00
76 namespace ViewManagement
{
77 interface IUIViewSettings
;
78 MIDL_INTERFACE("C63657F6-8850-470D-88F8-455E16EA2C26")
79 IUIViewSettings
: public IInspectable
{
81 virtual HRESULT STDMETHODCALLTYPE
get_UserInteractionMode(
82 UserInteractionMode
* value
) = 0;
85 extern const __declspec(selectany
) IID
& IID_IUIViewSettings
=
86 __uuidof(IUIViewSettings
);
87 } // namespace ViewManagement
89 } // namespace Windows
93 # ifndef IUIViewSettingsInterop
95 using IUIViewSettingsInterop
= interface IUIViewSettingsInterop
;
97 MIDL_INTERFACE("3694dbf9-8f68-44be-8ff5-195c98ede8a6")
98 IUIViewSettingsInterop
: public IInspectable
{
100 virtual HRESULT STDMETHODCALLTYPE
GetForWindow(HWND hwnd
, REFIID riid
,
105 # ifndef __IDataTransferManagerInterop_INTERFACE_DEFINED__
106 # define __IDataTransferManagerInterop_INTERFACE_DEFINED__
108 using IDataTransferManagerInterop
= interface IDataTransferManagerInterop
;
110 MIDL_INTERFACE("3A3DCD6C-3EAB-43DC-BCDE-45671CE800C8")
111 IDataTransferManagerInterop
: public IUnknown
{
113 virtual HRESULT STDMETHODCALLTYPE
GetForWindow(
114 HWND appWindow
, REFIID riid
, void** dataTransferManager
) = 0;
115 virtual HRESULT STDMETHODCALLTYPE
ShowShareUIForWindow(HWND appWindow
) = 0;
121 ____x_ABI_CWindows_CApplicationModel_CDataTransfer_CIDataPackage4_INTERFACE_DEFINED__)
122 # define ____x_ABI_CWindows_CApplicationModel_CDataTransfer_CIDataPackage4_INTERFACE_DEFINED__
124 MIDL_INTERFACE("13a24ec8-9382-536f-852a-3045e1b29a3b")
125 IDataPackage4
: public IInspectable
{
127 virtual HRESULT STDMETHODCALLTYPE
add_ShareCanceled(
128 __FITypedEventHandler_2_Windows__CApplicationModel__CDataTransfer__CDataPackage_IInspectable
*
130 EventRegistrationToken
* token
) = 0;
131 virtual HRESULT STDMETHODCALLTYPE
remove_ShareCanceled(
132 EventRegistrationToken token
) = 0;
137 # ifndef RuntimeClass_Windows_UI_ViewManagement_UISettings
138 # define RuntimeClass_Windows_UI_ViewManagement_UISettings \
139 L"Windows.UI.ViewManagement.UISettings"
141 # if WINDOWS_FOUNDATION_UNIVERSALAPICONTRACT_VERSION < 0x80000
145 namespace ViewManagement
{
148 class UISettingsAutoHideScrollBarsChangedEventArgs
;
149 interface IUISettingsAutoHideScrollBarsChangedEventArgs
;
150 MIDL_INTERFACE("87afd4b2-9146-5f02-8f6b-06d454174c0f")
151 IUISettingsAutoHideScrollBarsChangedEventArgs
: public IInspectable
{};
153 } // namespace ViewManagement
155 } // namespace Windows
160 namespace Foundation
{
163 struct __declspec(uuid("808aef30-2660-51b0-9c11-f75dd42006b4"))
164 ITypedEventHandler
<ABI::Windows::UI::ViewManagement::UISettings
*,
165 ABI::Windows::UI::ViewManagement::
166 UISettingsAutoHideScrollBarsChangedEventArgs
*>
167 : ITypedEventHandler_impl
<
168 ABI::Windows::Foundation::Internal::AggregateType
<
169 ABI::Windows::UI::ViewManagement::UISettings
*,
170 ABI::Windows::UI::ViewManagement::IUISettings
*>,
171 ABI::Windows::Foundation::Internal::AggregateType
<
172 ABI::Windows::UI::ViewManagement::
173 UISettingsAutoHideScrollBarsChangedEventArgs
*,
174 ABI::Windows::UI::ViewManagement::
175 IUISettingsAutoHideScrollBarsChangedEventArgs
*>> {
176 static const wchar_t* z_get_rc_name_impl() {
177 return L
"Windows.Foundation.TypedEventHandler`2<Windows.UI.ViewManagement."
179 L
"Windows.UI.ViewManagement."
180 L
"UISettingsAutoHideScrollBarsChangedEventArgs>";
183 // Define a typedef for the parameterized interface specialization's mangled
184 // name. This allows code which uses the mangled name for the parameterized
185 // interface to access the correct parameterized interface specialization.
186 typedef ITypedEventHandler
<ABI::Windows::UI::ViewManagement::UISettings
*,
187 ABI::Windows::UI::ViewManagement::
188 UISettingsAutoHideScrollBarsChangedEventArgs
*>
189 __FITypedEventHandler_2_Windows__CUI__CViewManagement__CUISettings_Windows__CUI__CViewManagement__CUISettingsAutoHideScrollBarsChangedEventArgs_t
;
190 # define __FITypedEventHandler_2_Windows__CUI__CViewManagement__CUISettings_Windows__CUI__CViewManagement__CUISettingsAutoHideScrollBarsChangedEventArgs \
191 ABI::Windows::Foundation:: \
192 __FITypedEventHandler_2_Windows__CUI__CViewManagement__CUISettings_Windows__CUI__CViewManagement__CUISettingsAutoHideScrollBarsChangedEventArgs_t
194 } // namespace Foundation
195 } // namespace Windows
201 namespace ViewManagement
{
203 class UISettingsAutoHideScrollBarsChangedEventArgs
;
204 interface IUISettings5
;
205 MIDL_INTERFACE("5349d588-0cb5-5f05-bd34-706b3231f0bd")
206 IUISettings5
: public IInspectable
{
208 virtual HRESULT STDMETHODCALLTYPE
get_AutoHideScrollBars(boolean
* value
) = 0;
209 virtual HRESULT STDMETHODCALLTYPE
add_AutoHideScrollBarsChanged(
210 __FITypedEventHandler_2_Windows__CUI__CViewManagement__CUISettings_Windows__CUI__CViewManagement__CUISettingsAutoHideScrollBarsChangedEventArgs
*
212 EventRegistrationToken
* token
) = 0;
213 virtual HRESULT STDMETHODCALLTYPE
remove_AutoHideScrollBarsChanged(
214 EventRegistrationToken token
) = 0;
216 } // namespace ViewManagement
218 } // namespace Windows
223 using namespace mozilla
;
225 enum class TabletModeState
: uint8_t { Unknown
, Off
, On
};
226 static TabletModeState sInTabletModeState
;
228 WindowsUIUtils::WindowsUIUtils() = default;
229 WindowsUIUtils::~WindowsUIUtils() = default;
231 NS_IMPL_ISUPPORTS(WindowsUIUtils
, nsIWindowsUIUtils
)
234 WindowsUIUtils::GetSystemSmallIconSize(int32_t* aSize
) {
235 NS_ENSURE_ARG(aSize
);
237 mozilla::LayoutDeviceIntSize size
=
238 nsWindowGfx::GetIconMetrics(nsWindowGfx::kSmallIcon
);
239 *aSize
= std::max(size
.width
, size
.height
);
244 WindowsUIUtils::GetSystemLargeIconSize(int32_t* aSize
) {
245 NS_ENSURE_ARG(aSize
);
247 mozilla::LayoutDeviceIntSize size
=
248 nsWindowGfx::GetIconMetrics(nsWindowGfx::kRegularIcon
);
249 *aSize
= std::max(size
.width
, size
.height
);
254 WindowsUIUtils::SetWindowIcon(mozIDOMWindowProxy
* aWindow
,
255 imgIContainer
* aSmallIcon
,
256 imgIContainer
* aBigIcon
) {
257 NS_ENSURE_ARG(aWindow
);
259 nsCOMPtr
<nsIWidget
> widget
=
260 nsGlobalWindowOuter::Cast(aWindow
)->GetMainWidget();
261 nsWindow
* window
= static_cast<nsWindow
*>(widget
.get());
266 HICON hIcon
= nullptr;
267 rv
= nsWindowGfx::CreateIcon(
268 aSmallIcon
, false, mozilla::LayoutDeviceIntPoint(),
269 nsWindowGfx::GetIconMetrics(nsWindowGfx::kSmallIcon
), &hIcon
);
270 NS_ENSURE_SUCCESS(rv
, rv
);
272 window
->SetSmallIcon(hIcon
);
276 HICON hIcon
= nullptr;
277 rv
= nsWindowGfx::CreateIcon(
278 aBigIcon
, false, mozilla::LayoutDeviceIntPoint(),
279 nsWindowGfx::GetIconMetrics(nsWindowGfx::kRegularIcon
), &hIcon
);
280 NS_ENSURE_SUCCESS(rv
, rv
);
282 window
->SetBigIcon(hIcon
);
289 WindowsUIUtils::SetWindowIconFromExe(mozIDOMWindowProxy
* aWindow
,
290 const nsAString
& aExe
, uint16_t aIndex
) {
291 NS_ENSURE_ARG(aWindow
);
293 nsCOMPtr
<nsIWidget
> widget
=
294 nsGlobalWindowOuter::Cast(aWindow
)->GetMainWidget();
295 nsWindow
* window
= static_cast<nsWindow
*>(widget
.get());
297 HICON icon
= ::LoadIconW(::GetModuleHandleW(PromiseFlatString(aExe
).get()),
298 MAKEINTRESOURCEW(aIndex
));
299 window
->SetBigIcon(icon
);
300 window
->SetSmallIcon(icon
);
306 WindowsUIUtils::SetWindowIconNoData(mozIDOMWindowProxy
* aWindow
) {
307 NS_ENSURE_ARG(aWindow
);
309 nsCOMPtr
<nsIWidget
> widget
=
310 nsGlobalWindowOuter::Cast(aWindow
)->GetMainWidget();
311 nsWindow
* window
= static_cast<nsWindow
*>(widget
.get());
313 window
->SetSmallIconNoData();
314 window
->SetBigIconNoData();
319 bool WindowsUIUtils::GetInTabletMode() {
320 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
321 if (sInTabletModeState
== TabletModeState::Unknown
) {
322 UpdateInTabletMode();
324 return sInTabletModeState
== TabletModeState::On
;
328 WindowsUIUtils::GetInTabletMode(bool* aResult
) {
329 *aResult
= GetInTabletMode();
333 bool WindowsUIUtils::ComputeOverlayScrollbars() {
334 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
337 // We need to keep this alive for ~ever so that change callbacks work as
339 static ComPtr
<IUISettings5
> sUiSettings
;
341 if (!IsWin11OrLater()) {
342 // While in theory Windows 10 supports overlay scrollbar settings, it's off
343 // by default and it's untested whether our Win10 scrollbar drawing code
344 // deals with it properly.
348 if (!StaticPrefs::widget_windows_overlay_scrollbars_enabled()) {
353 ComPtr
<IInspectable
> uiSettingsAsInspectable
;
354 ::RoActivateInstance(
355 HStringReference(RuntimeClass_Windows_UI_ViewManagement_UISettings
)
357 &uiSettingsAsInspectable
);
358 if (NS_WARN_IF(!uiSettingsAsInspectable
)) {
362 if (NS_WARN_IF(FAILED(uiSettingsAsInspectable
.As(&sUiSettings
)))) {
366 EventRegistrationToken unusedToken
;
367 auto callback
= Callback
<ITypedEventHandler
<
368 UISettings
*, UISettingsAutoHideScrollBarsChangedEventArgs
*>>(
370 // Scrollbar sizes change layout.
371 LookAndFeel::NotifyChangedAllWindows(
372 widget::ThemeChangeKind::StyleAndLayout
);
375 (void)NS_WARN_IF(FAILED(sUiSettings
->add_AutoHideScrollBarsChanged(
376 callback
.Get(), &unusedToken
)));
379 boolean autoHide
= false;
380 sUiSettings
->get_AutoHideScrollBars(&autoHide
);
386 void WindowsUIUtils::UpdateInTabletMode() {
388 if (!IsWin10OrLater()) {
393 nsCOMPtr
<nsIWindowMediator
> winMediator(
394 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID
, &rv
));
399 nsCOMPtr
<nsIWidget
> widget
;
400 nsCOMPtr
<mozIDOMWindowProxy
> navWin
;
402 rv
= winMediator
->GetMostRecentWindow(u
"navigator:browser",
403 getter_AddRefs(navWin
));
404 if (NS_FAILED(rv
) || !navWin
) {
405 // Fall back to the hidden window
406 nsCOMPtr
<nsIAppShellService
> appShell(
407 do_GetService(NS_APPSHELLSERVICE_CONTRACTID
));
409 rv
= appShell
->GetHiddenDOMWindow(getter_AddRefs(navWin
));
410 if (NS_FAILED(rv
) || !navWin
) {
415 nsPIDOMWindowOuter
* win
= nsPIDOMWindowOuter::From(navWin
);
416 widget
= widget::WidgetUtils::DOMWindowToWidget(win
);
422 HWND winPtr
= (HWND
)widget
->GetNativeData(NS_NATIVE_WINDOW
);
423 ComPtr
<IUIViewSettingsInterop
> uiViewSettingsInterop
;
425 HRESULT hr
= GetActivationFactory(
426 HStringReference(RuntimeClass_Windows_UI_ViewManagement_UIViewSettings
)
428 &uiViewSettingsInterop
);
432 ComPtr
<IUIViewSettings
> uiViewSettings
;
433 hr
= uiViewSettingsInterop
->GetForWindow(winPtr
,
434 IID_PPV_ARGS(&uiViewSettings
));
438 UserInteractionMode mode
;
439 hr
= uiViewSettings
->get_UserInteractionMode(&mode
);
444 TabletModeState oldTabletModeState
= sInTabletModeState
;
445 sInTabletModeState
= mode
== UserInteractionMode_Touch
? TabletModeState::On
446 : TabletModeState::Off
;
447 if (sInTabletModeState
!= oldTabletModeState
) {
448 nsCOMPtr
<nsIObserverService
> observerService
=
449 mozilla::services::GetObserverService();
450 observerService
->NotifyObservers(nullptr, "tablet-mode-change",
451 sInTabletModeState
== TabletModeState::On
459 struct HStringDeleter
{
460 using pointer
= HSTRING
;
461 void operator()(pointer aString
) { WindowsDeleteString(aString
); }
464 using HStringUniquePtr
= UniquePtr
<HSTRING
, HStringDeleter
>;
466 Result
<HStringUniquePtr
, HRESULT
> ConvertToWindowsString(
467 const nsAString
& aStr
) {
469 HRESULT hr
= WindowsCreateString(PromiseFlatString(aStr
).get(), aStr
.Length(),
474 return HStringUniquePtr(rawStr
);
477 static Result
<Ok
, nsresult
> RequestShare(
478 const std::function
<HRESULT(IDataRequestedEventArgs
* pArgs
)>& aCallback
) {
479 if (!IsWin10OrLater()) {
480 return Err(NS_ERROR_FAILURE
);
483 HWND hwnd
= GetForegroundWindow();
485 return Err(NS_ERROR_FAILURE
);
488 ComPtr
<IDataTransferManagerInterop
> dtmInterop
;
489 ComPtr
<IDataTransferManager
> dtm
;
491 HRESULT hr
= RoGetActivationFactory(
493 RuntimeClass_Windows_ApplicationModel_DataTransfer_DataTransferManager
)
495 IID_PPV_ARGS(&dtmInterop
));
497 FAILED(dtmInterop
->GetForWindow(hwnd
, IID_PPV_ARGS(&dtm
)))) {
498 return Err(NS_ERROR_FAILURE
);
501 auto callback
= Callback
<
502 ITypedEventHandler
<DataTransferManager
*, DataRequestedEventArgs
*>>(
503 [aCallback
](IDataTransferManager
*,
504 IDataRequestedEventArgs
* pArgs
) -> HRESULT
{
505 return aCallback(pArgs
);
508 EventRegistrationToken dataRequestedToken
;
509 if (FAILED(dtm
->add_DataRequested(callback
.Get(), &dataRequestedToken
)) ||
510 FAILED(dtmInterop
->ShowShareUIForWindow(hwnd
))) {
511 return Err(NS_ERROR_FAILURE
);
517 static Result
<Ok
, nsresult
> AddShareEventListeners(
518 const RefPtr
<mozilla::media::Refcountable
<MozPromiseHolder
<SharePromise
>>>&
520 const ComPtr
<IDataPackage
>& aDataPackage
) {
521 ComPtr
<IDataPackage3
> spDataPackage3
;
523 if (FAILED(aDataPackage
.As(&spDataPackage3
))) {
524 return Err(NS_ERROR_FAILURE
);
527 auto completedCallback
=
528 Callback
<ITypedEventHandler
<DataPackage
*, ShareCompletedEventArgs
*>>(
529 [aPromiseHolder
](IDataPackage
*,
530 IShareCompletedEventArgs
*) -> HRESULT
{
531 aPromiseHolder
->Resolve(true, __func__
);
535 EventRegistrationToken dataRequestedToken
;
536 if (FAILED(spDataPackage3
->add_ShareCompleted(completedCallback
.Get(),
537 &dataRequestedToken
))) {
538 return Err(NS_ERROR_FAILURE
);
541 ComPtr
<IDataPackage4
> spDataPackage4
;
542 if (SUCCEEDED(aDataPackage
.As(&spDataPackage4
))) {
543 // Use SharedCanceled API only on supported versions of Windows
544 // So that the older ones can still use ShareUrl()
546 auto canceledCallback
=
547 Callback
<ITypedEventHandler
<DataPackage
*, IInspectable
*>>(
548 [aPromiseHolder
](IDataPackage
*, IInspectable
*) -> HRESULT
{
549 aPromiseHolder
->Reject(NS_ERROR_FAILURE
, __func__
);
553 if (FAILED(spDataPackage4
->add_ShareCanceled(canceledCallback
.Get(),
554 &dataRequestedToken
))) {
555 return Err(NS_ERROR_FAILURE
);
563 RefPtr
<SharePromise
> WindowsUIUtils::Share(nsAutoString aTitle
,
566 auto promiseHolder
= MakeRefPtr
<
567 mozilla::media::Refcountable
<MozPromiseHolder
<SharePromise
>>>();
568 RefPtr
<SharePromise
> promise
= promiseHolder
->Ensure(__func__
);
571 auto result
= RequestShare([promiseHolder
, title
= std::move(aTitle
),
572 text
= std::move(aText
), url
= std::move(aUrl
)](
573 IDataRequestedEventArgs
* pArgs
) {
574 ComPtr
<IDataRequest
> spDataRequest
;
575 ComPtr
<IDataPackage
> spDataPackage
;
576 ComPtr
<IDataPackage2
> spDataPackage2
;
577 ComPtr
<IDataPackagePropertySet
> spDataPackageProperties
;
579 if (FAILED(pArgs
->get_Request(&spDataRequest
)) ||
580 FAILED(spDataRequest
->get_Data(&spDataPackage
)) ||
581 FAILED(spDataPackage
.As(&spDataPackage2
)) ||
582 FAILED(spDataPackage
->get_Properties(&spDataPackageProperties
))) {
583 promiseHolder
->Reject(NS_ERROR_FAILURE
, __func__
);
588 * Windows always requires a title, and an empty string does not work.
589 * Thus we trick the API by passing a whitespace when we have no title.
590 * https://docs.microsoft.com/en-us/windows/uwp/app-to-app/share-data
592 auto wTitle
= ConvertToWindowsString((title
.IsVoid() || title
.Length() == 0)
593 ? nsAutoString(u
" "_ns
)
595 if (wTitle
.isErr() ||
596 FAILED(spDataPackageProperties
->put_Title(wTitle
.unwrap().get()))) {
597 promiseHolder
->Reject(NS_ERROR_FAILURE
, __func__
);
601 // Assign even if empty, as Windows requires some data to share
602 auto wText
= ConvertToWindowsString(text
);
603 if (wText
.isErr() || FAILED(spDataPackage
->SetText(wText
.unwrap().get()))) {
604 promiseHolder
->Reject(NS_ERROR_FAILURE
, __func__
);
609 auto wUrl
= ConvertToWindowsString(url
);
611 promiseHolder
->Reject(NS_ERROR_FAILURE
, __func__
);
612 return wUrl
.unwrapErr();
615 ComPtr
<IUriRuntimeClassFactory
> uriFactory
;
616 ComPtr
<IUriRuntimeClass
> uri
;
618 auto hr
= GetActivationFactory(
619 HStringReference(RuntimeClass_Windows_Foundation_Uri
).Get(),
623 FAILED(uriFactory
->CreateUri(wUrl
.unwrap().get(), &uri
)) ||
624 FAILED(spDataPackage2
->SetWebLink(uri
.Get()))) {
625 promiseHolder
->Reject(NS_ERROR_FAILURE
, __func__
);
630 if (!StaticPrefs::widget_windows_share_wait_action_enabled()) {
631 promiseHolder
->Resolve(true, __func__
);
632 } else if (AddShareEventListeners(promiseHolder
, spDataPackage
).isErr()) {
633 promiseHolder
->Reject(NS_ERROR_FAILURE
, __func__
);
639 if (result
.isErr()) {
640 promiseHolder
->Reject(result
.unwrapErr(), __func__
);
643 promiseHolder
->Reject(NS_ERROR_FAILURE
, __func__
);
650 WindowsUIUtils::ShareUrl(const nsAString
& aUrlToShare
,
651 const nsAString
& aShareTitle
) {
653 text
.SetIsVoid(true);
654 WindowsUIUtils::Share(nsAutoString(aShareTitle
), text
,
655 nsAutoString(aUrlToShare
));