/* -*- 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 #include #include #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 "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 # include # include # 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; /* All of this is win10 stuff and we're compiling against win81 headers * for now, so we may need to do some legwork: */ # if WINVER_MAXVER < 0x0A00 namespace ABI { namespace Windows { namespace UI { namespace ViewManagement { enum UserInteractionMode { UserInteractionMode_Mouse = 0, UserInteractionMode_Touch = 1 }; } } // namespace UI } // namespace Windows } // namespace ABI # endif # ifndef RuntimeClass_Windows_UI_ViewManagement_UIViewSettings # define RuntimeClass_Windows_UI_ViewManagement_UIViewSettings \ L"Windows.UI.ViewManagement.UIViewSettings" # endif # if WINVER_MAXVER < 0x0A00 namespace ABI { namespace Windows { namespace UI { namespace ViewManagement { interface IUIViewSettings; MIDL_INTERFACE("C63657F6-8850-470D-88F8-455E16EA2C26") IUIViewSettings : public IInspectable { public: virtual HRESULT STDMETHODCALLTYPE get_UserInteractionMode( UserInteractionMode * value) = 0; }; extern const __declspec(selectany) IID& IID_IUIViewSettings = __uuidof(IUIViewSettings); } // namespace ViewManagement } // namespace UI } // namespace Windows } // namespace ABI # 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 : 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"; } }; // 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 __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 widget = nsGlobalWindowOuter::Cast(aWindow)->GetMainWidget(); nsWindow* window = static_cast(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 widget = nsGlobalWindowOuter::Cast(aWindow)->GetMainWidget(); nsWindow* window = static_cast(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 widget = nsGlobalWindowOuter::Cast(aWindow)->GetMainWidget(); nsWindow* window = static_cast(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 sUiSettingsAsInspectable; if (!IsWin10OrLater()) { // Windows.UI.ViewManagement.UISettings is Win10+ only. return nullptr; } if (!sUiSettingsAsInspectable) { ComPtr uiSettingsAsInspectable; ::RoActivateInstance( HStringReference(RuntimeClass_Windows_UI_ViewManagement_UISettings) .Get(), &uiSettingsAsInspectable); if (NS_WARN_IF(!uiSettingsAsInspectable)) { return nullptr; } ComPtr uiSettings5; if (SUCCEEDED(uiSettingsAsInspectable.As(&uiSettings5))) { EventRegistrationToken unusedToken; auto callback = Callback>( [](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 uiSettings2; if (SUCCEEDED(uiSettingsAsInspectable.As(&uiSettings2))) { EventRegistrationToken unusedToken; auto callback = Callback>([](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 uiSettings3; if (SUCCEEDED(uiSettingsAsInspectable.As(&uiSettings3))) { EventRegistrationToken unusedToken; auto callback = Callback>([](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 uiSettings4; if (SUCCEEDED(uiSettingsAsInspectable.As(&uiSettings4))) { EventRegistrationToken unusedToken; auto callback = Callback>([](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 WindowsUIUtils::GetAccentColor(int aTone) { MOZ_ASSERT(aTone >= -3); MOZ_ASSERT(aTone <= 3); #ifndef __MINGW32__ ComPtr settings = GetUISettings(); if (NS_WARN_IF(!settings)) { return Nothing(); } ComPtr 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 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 { # 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 settings = GetUISettings(); if (NS_WARN_IF(!settings)) { return Nothing(); } ComPtr 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 settings = GetUISettings(); if (NS_WARN_IF(!settings)) { return false; } ComPtr 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 settings = GetUISettings(); if (NS_WARN_IF(!settings)) { return 1.0; } ComPtr 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 settings = GetUISettings(); if (NS_WARN_IF(!settings)) { return kDefault; } ComPtr 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__ if (!IsWin10OrLater()) { return; } nsresult rv; nsCOMPtr winMediator( do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv)); if (NS_FAILED(rv)) { return; } nsCOMPtr widget; nsCOMPtr navWin; rv = winMediator->GetMostRecentWindow(u"navigator:browser", getter_AddRefs(navWin)); if (NS_FAILED(rv) || !navWin) { // Fall back to the hidden window nsCOMPtr appShell( do_GetService(NS_APPSHELLSERVICE_CONTRACTID)); rv = appShell->GetHiddenDOMWindow(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 uiViewSettingsInterop; HRESULT hr = GetActivationFactory( HStringReference(RuntimeClass_Windows_UI_ViewManagement_UIViewSettings) .Get(), &uiViewSettingsInterop); if (FAILED(hr)) { return; } ComPtr 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 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; Result 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 RequestShare( const std::function& aCallback) { if (!IsWin10OrLater()) { return Err(NS_ERROR_FAILURE); } HWND hwnd = GetForegroundWindow(); if (!hwnd) { return Err(NS_ERROR_FAILURE); } ComPtr dtmInterop; ComPtr 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>( [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 AddShareEventListeners( const RefPtr>>& aPromiseHolder, const ComPtr& aDataPackage) { ComPtr spDataPackage3; if (FAILED(aDataPackage.As(&spDataPackage3))) { return Err(NS_ERROR_FAILURE); } auto completedCallback = Callback>( [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 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>( [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 WindowsUIUtils::Share(nsAutoString aTitle, nsAutoString aText, nsAutoString aUrl) { auto promiseHolder = MakeRefPtr< mozilla::media::Refcountable>>(); RefPtr 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 spDataRequest; ComPtr spDataPackage; ComPtr spDataPackage2; ComPtr 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 uriFactory; ComPtr 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; }