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/. */ 5 6 #include <windows.h> 7 #include <winsdkver.h> 8 #include <wrl.h> 9 10 #include "nsServiceManagerUtils.h" 11 12 #include "WindowsUIUtils.h" 13 14 #include "nsIObserverService.h" 15 #include "nsIAppShellService.h" 16 #include "nsAppShellCID.h" 17 #include "mozilla/Services.h" 18 #include "mozilla/WidgetUtils.h" 19 #include "mozilla/WindowsVersion.h" 20 #include "mozilla/media/MediaUtils.h" 21 #include "nsString.h" 22 #include "nsIWidget.h" 23 #include "nsIWindowMediator.h" 24 #include "nsPIDOMWindow.h" 25 #include "nsWindowGfx.h" 26 #include "Units.h" 27 28 /* mingw currently doesn't support windows.ui.viewmanagement.h, so we disable it 29 * until it's fixed. */ 30 #ifndef __MINGW32__ 31 32 # include <windows.ui.viewmanagement.h> 33 34 # pragma comment(lib, "runtimeobject.lib") 35 36 using namespace ABI::Windows::UI; 37 using namespace ABI::Windows::UI::ViewManagement; 38 using namespace Microsoft::WRL; 39 using namespace Microsoft::WRL::Wrappers; 40 using namespace ABI::Windows::Foundation; 41 using namespace ABI::Windows::ApplicationModel::DataTransfer; 42 43 /* All of this is win10 stuff and we're compiling against win81 headers 44 * for now, so we may need to do some legwork: */ 45 # if WINVER_MAXVER < 0x0A00 46 namespace ABI { 47 namespace Windows { 48 namespace UI { 49 namespace ViewManagement { 50 enum UserInteractionMode { 51 UserInteractionMode_Mouse = 0, 52 UserInteractionMode_Touch = 1 53 }; 54 } 55 } // namespace UI 56 } // namespace Windows 57 } // namespace ABI 58 59 # endif 60 61 # ifndef RuntimeClass_Windows_UI_ViewManagement_UIViewSettings 62 # define RuntimeClass_Windows_UI_ViewManagement_UIViewSettings \ 63 L"Windows.UI.ViewManagement.UIViewSettings" 64 # endif 65 66 # if WINVER_MAXVER < 0x0A00 67 namespace ABI { 68 namespace Windows { 69 namespace UI { 70 namespace ViewManagement { 71 interface IUIViewSettings; 72 MIDL_INTERFACE("C63657F6-8850-470D-88F8-455E16EA2C26") 73 IUIViewSettings : public IInspectable { 74 public: 75 virtual HRESULT STDMETHODCALLTYPE get_UserInteractionMode( 76 UserInteractionMode * value) = 0; 77 }; 78 79 extern const __declspec(selectany) IID& IID_IUIViewSettings = 80 __uuidof(IUIViewSettings); 81 } // namespace ViewManagement 82 } // namespace UI 83 } // namespace Windows 84 } // namespace ABI 85 # endif 86 87 # ifndef IUIViewSettingsInterop 88 89 typedef interface IUIViewSettingsInterop IUIViewSettingsInterop; 90 91 MIDL_INTERFACE("3694dbf9-8f68-44be-8ff5-195c98ede8a6") 92 IUIViewSettingsInterop : public IInspectable { 93 public: 94 virtual HRESULT STDMETHODCALLTYPE GetForWindow(HWND hwnd, REFIID riid, 95 void** ppv) = 0; 96 }; 97 # endif 98 99 # ifndef __IDataTransferManagerInterop_INTERFACE_DEFINED__ 100 # define __IDataTransferManagerInterop_INTERFACE_DEFINED__ 101 102 typedef interface IDataTransferManagerInterop IDataTransferManagerInterop; 103 104 MIDL_INTERFACE("3A3DCD6C-3EAB-43DC-BCDE-45671CE800C8") 105 IDataTransferManagerInterop : public IUnknown { 106 public: 107 virtual HRESULT STDMETHODCALLTYPE GetForWindow( 108 HWND appWindow, REFIID riid, void** dataTransferManager) = 0; 109 virtual HRESULT STDMETHODCALLTYPE ShowShareUIForWindow(HWND appWindow) = 0; 110 }; 111 112 # endif 113 114 # if !defined( \ 115 ____x_ABI_CWindows_CApplicationModel_CDataTransfer_CIDataPackage4_INTERFACE_DEFINED__) 116 # define ____x_ABI_CWindows_CApplicationModel_CDataTransfer_CIDataPackage4_INTERFACE_DEFINED__ 117 118 MIDL_INTERFACE("13a24ec8-9382-536f-852a-3045e1b29a3b") 119 IDataPackage4 : public IInspectable { 120 public: 121 virtual HRESULT STDMETHODCALLTYPE add_ShareCanceled( 122 __FITypedEventHandler_2_Windows__CApplicationModel__CDataTransfer__CDataPackage_IInspectable * 123 handler, 124 EventRegistrationToken * token) = 0; 125 virtual HRESULT STDMETHODCALLTYPE remove_ShareCanceled( 126 EventRegistrationToken token) = 0; 127 }; 128 129 # endif 130 131 #endif 132 133 using namespace mozilla; 134 135 WindowsUIUtils::WindowsUIUtils() : mInTabletMode(eTabletModeUnknown) {} 136 137 WindowsUIUtils::~WindowsUIUtils() {} 138 139 /* 140 * Implement the nsISupports methods... 141 */ 142 NS_IMPL_ISUPPORTS(WindowsUIUtils, nsIWindowsUIUtils) 143 144 NS_IMETHODIMP 145 WindowsUIUtils::GetSystemSmallIconSize(int32_t* aSize) { 146 NS_ENSURE_ARG(aSize); 147 148 mozilla::LayoutDeviceIntSize size = 149 nsWindowGfx::GetIconMetrics(nsWindowGfx::kSmallIcon); 150 *aSize = std::max(size.width, size.height); 151 return NS_OK; 152 } 153 154 NS_IMETHODIMP 155 WindowsUIUtils::GetSystemLargeIconSize(int32_t* aSize) { 156 NS_ENSURE_ARG(aSize); 157 158 mozilla::LayoutDeviceIntSize size = 159 nsWindowGfx::GetIconMetrics(nsWindowGfx::kRegularIcon); 160 *aSize = std::max(size.width, size.height); 161 return NS_OK; 162 } 163 164 NS_IMETHODIMP 165 WindowsUIUtils::SetWindowIcon(mozIDOMWindowProxy* aWindow, 166 imgIContainer* aSmallIcon, 167 imgIContainer* aBigIcon) { 168 NS_ENSURE_ARG(aWindow); 169 170 nsCOMPtr<nsIWidget> widget = 171 nsGlobalWindowOuter::Cast(aWindow)->GetMainWidget(); 172 nsWindow* window = static_cast<nsWindow*>(widget.get()); 173 174 nsresult rv; 175 176 if (aSmallIcon) { 177 HICON hIcon = nullptr; 178 rv = nsWindowGfx::CreateIcon( 179 aSmallIcon, false, mozilla::LayoutDeviceIntPoint(), 180 nsWindowGfx::GetIconMetrics(nsWindowGfx::kSmallIcon), &hIcon); 181 NS_ENSURE_SUCCESS(rv, rv); 182 183 window->SetSmallIcon(hIcon); 184 } 185 186 if (aBigIcon) { 187 HICON hIcon = nullptr; 188 rv = nsWindowGfx::CreateIcon( 189 aBigIcon, false, mozilla::LayoutDeviceIntPoint(), 190 nsWindowGfx::GetIconMetrics(nsWindowGfx::kRegularIcon), &hIcon); 191 NS_ENSURE_SUCCESS(rv, rv); 192 193 window->SetBigIcon(hIcon); 194 } 195 196 return NS_OK; 197 } 198 199 NS_IMETHODIMP 200 WindowsUIUtils::GetInTabletMode(bool* aResult) { 201 if (mInTabletMode == eTabletModeUnknown) { 202 UpdateTabletModeState(); 203 } 204 *aResult = mInTabletMode == eTabletModeOn; 205 return NS_OK; 206 } 207 208 NS_IMETHODIMP 209 WindowsUIUtils::UpdateTabletModeState() { 210 #ifndef __MINGW32__ 211 if (!IsWin10OrLater()) { 212 return NS_OK; 213 } 214 215 nsresult rv; 216 nsCOMPtr<nsIWindowMediator> winMediator( 217 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv)); 218 if (NS_FAILED(rv)) { 219 return rv; 220 } 221 222 nsCOMPtr<nsIWidget> widget; 223 nsCOMPtr<mozIDOMWindowProxy> navWin; 224 225 rv = winMediator->GetMostRecentWindow(u"navigator:browser", 226 getter_AddRefs(navWin)); 227 if (NS_FAILED(rv) || !navWin) { 228 // Fall back to the hidden window 229 nsCOMPtr<nsIAppShellService> appShell( 230 do_GetService(NS_APPSHELLSERVICE_CONTRACTID)); 231 232 rv = appShell->GetHiddenDOMWindow(getter_AddRefs(navWin)); 233 if (NS_FAILED(rv) || !navWin) { 234 return rv; 235 } 236 } 237 238 nsPIDOMWindowOuter* win = nsPIDOMWindowOuter::From(navWin); 239 widget = widget::WidgetUtils::DOMWindowToWidget(win); 240 241 if (!widget) return NS_ERROR_FAILURE; 242 243 HWND winPtr = (HWND)widget->GetNativeData(NS_NATIVE_WINDOW); 244 ComPtr<IUIViewSettingsInterop> uiViewSettingsInterop; 245 246 HRESULT hr = GetActivationFactory( 247 HStringReference(RuntimeClass_Windows_UI_ViewManagement_UIViewSettings) 248 .Get(), 249 &uiViewSettingsInterop); 250 if (SUCCEEDED(hr)) { 251 ComPtr<IUIViewSettings> uiViewSettings; 252 hr = uiViewSettingsInterop->GetForWindow(winPtr, 253 IID_PPV_ARGS(&uiViewSettings)); 254 if (SUCCEEDED(hr)) { 255 UserInteractionMode mode; 256 hr = uiViewSettings->get_UserInteractionMode(&mode); 257 if (SUCCEEDED(hr)) { 258 TabletModeState oldTabletModeState = mInTabletMode; 259 mInTabletMode = (mode == UserInteractionMode_Touch) ? eTabletModeOn 260 : eTabletModeOff; 261 if (mInTabletMode != oldTabletModeState) { 262 nsCOMPtr<nsIObserverService> observerService = 263 mozilla::services::GetObserverService(); 264 observerService->NotifyObservers( 265 nullptr, "tablet-mode-change", 266 ((mInTabletMode == eTabletModeOn) ? u"tablet-mode" 267 : u"normal-mode")); 268 } 269 } 270 } 271 } 272 #endif 273 274 return NS_OK; 275 } 276 277 #ifndef __MINGW32__ 278 struct HStringDeleter { 279 typedef HSTRING pointer; 280 void operator()(pointer aString) { WindowsDeleteString(aString); } 281 }; 282 283 typedef UniquePtr<HSTRING, HStringDeleter> HStringUniquePtr; 284 285 Result<HStringUniquePtr, HRESULT> ConvertToWindowsString( 286 const nsAString& aStr) { 287 HSTRING rawStr; 288 HRESULT hr = WindowsCreateString(PromiseFlatString(aStr).get(), aStr.Length(), 289 &rawStr); 290 if (FAILED(hr)) { 291 return Err(hr); 292 } 293 return HStringUniquePtr(rawStr); 294 } 295 296 Result<Ok, nsresult> RequestShare( 297 const std::function<HRESULT(IDataRequestedEventArgs* pArgs)>& aCallback) { 298 if (!IsWin10OrLater()) { 299 return Err(NS_ERROR_FAILURE); 300 } 301 302 HWND hwnd = GetForegroundWindow(); 303 if (!hwnd) { 304 return Err(NS_ERROR_FAILURE); 305 } 306 307 ComPtr<IDataTransferManagerInterop> dtmInterop; 308 ComPtr<IDataTransferManager> dtm; 309 310 HRESULT hr = RoGetActivationFactory( 311 HStringReference( 312 RuntimeClass_Windows_ApplicationModel_DataTransfer_DataTransferManager) 313 .Get(), 314 IID_PPV_ARGS(&dtmInterop)); 315 if (FAILED(hr) || 316 FAILED(dtmInterop->GetForWindow(hwnd, IID_PPV_ARGS(&dtm)))) { 317 return Err(NS_ERROR_FAILURE); 318 } 319 320 auto callback = Callback< 321 ITypedEventHandler<DataTransferManager*, DataRequestedEventArgs*>>( 322 [aCallback](IDataTransferManager*, 323 IDataRequestedEventArgs* pArgs) -> HRESULT { 324 return aCallback(pArgs); 325 }); 326 327 EventRegistrationToken dataRequestedToken; 328 if (FAILED(dtm->add_DataRequested(callback.Get(), &dataRequestedToken)) || 329 FAILED(dtmInterop->ShowShareUIForWindow(hwnd))) { 330 return Err(NS_ERROR_FAILURE); 331 } 332 333 return Ok(); 334 } 335 #endif 336 337 RefPtr<SharePromise> WindowsUIUtils::Share(nsAutoString aTitle, 338 nsAutoString aText, 339 nsAutoString aUrl) { 340 auto promiseHolder = MakeRefPtr< 341 mozilla::media::Refcountable<MozPromiseHolder<SharePromise>>>(); 342 RefPtr<SharePromise> promise = promiseHolder->Ensure(__func__); 343 344 #ifndef __MINGW32__ 345 auto result = RequestShare([promiseHolder, title = std::move(aTitle), 346 text = std::move(aText), url = std::move(aUrl)]( 347 IDataRequestedEventArgs* pArgs) { 348 ComPtr<IDataRequest> spDataRequest; 349 ComPtr<IDataPackage> spDataPackage; 350 ComPtr<IDataPackage2> spDataPackage2; 351 ComPtr<IDataPackage3> spDataPackage3; 352 ComPtr<IDataPackagePropertySet> spDataPackageProperties; 353 354 if (FAILED(pArgs->get_Request(&spDataRequest)) || 355 FAILED(spDataRequest->get_Data(&spDataPackage)) || 356 FAILED(spDataPackage.As(&spDataPackage2)) || 357 FAILED(spDataPackage.As(&spDataPackage3)) || 358 FAILED(spDataPackage->get_Properties(&spDataPackageProperties))) { 359 promiseHolder->Reject(NS_ERROR_FAILURE, __func__); 360 return E_FAIL; 361 } 362 363 /* 364 * Windows always requires a title, and an empty string does not work. 365 * Thus we trick the API by passing a whitespace when we have no title. 366 * https://docs.microsoft.com/en-us/windows/uwp/app-to-app/share-data 367 */ 368 auto wTitle = 369 ConvertToWindowsString((title.IsVoid() || title.Length() == 0) 370 ? nsAutoString(NS_LITERAL_STRING(" ")) 371 : title); 372 if (wTitle.isErr() || 373 FAILED(spDataPackageProperties->put_Title(wTitle.unwrap().get()))) { 374 promiseHolder->Reject(NS_ERROR_FAILURE, __func__); 375 return E_FAIL; 376 } 377 378 // Assign even if empty, as Windows requires some data to share 379 auto wText = ConvertToWindowsString(text); 380 if (wText.isErr() || FAILED(spDataPackage->SetText(wText.unwrap().get()))) { 381 promiseHolder->Reject(NS_ERROR_FAILURE, __func__); 382 return E_FAIL; 383 } 384 385 if (!url.IsVoid()) { 386 auto wUrl = ConvertToWindowsString(url); 387 if (wUrl.isErr()) { 388 promiseHolder->Reject(NS_ERROR_FAILURE, __func__); 389 return wUrl.unwrapErr(); 390 } 391 392 ComPtr<IUriRuntimeClassFactory> uriFactory; 393 ComPtr<IUriRuntimeClass> uri; 394 395 auto hr = GetActivationFactory( 396 HStringReference(RuntimeClass_Windows_Foundation_Uri).Get(), 397 &uriFactory); 398 399 if (FAILED(hr) || 400 FAILED(uriFactory->CreateUri(wUrl.unwrap().get(), &uri)) || 401 FAILED(spDataPackage2->SetWebLink(uri.Get()))) { 402 promiseHolder->RejectIfExists(NS_ERROR_FAILURE, __func__); 403 return E_FAIL; 404 } 405 } 406 407 auto completedCallback = 408 Callback<ITypedEventHandler<DataPackage*, ShareCompletedEventArgs*>>( 409 [promiseHolder](IDataPackage*, 410 IShareCompletedEventArgs*) -> HRESULT { 411 promiseHolder->ResolveIfExists(true, __func__); 412 return S_OK; 413 }); 414 415 EventRegistrationToken dataRequestedToken; 416 if (FAILED(spDataPackage3->add_ShareCompleted(completedCallback.Get(), 417 &dataRequestedToken))) { 418 promiseHolder->Reject(NS_ERROR_FAILURE, __func__); 419 return E_FAIL; 420 } 421 422 ComPtr<IDataPackage4> spDataPackage4; 423 if (SUCCEEDED(spDataPackage.As(&spDataPackage4))) { 424 // Use SharedCanceled API only on supported versions of Windows 425 // So that the older ones can still use ShareUrl() 426 427 auto canceledCallback = 428 Callback<ITypedEventHandler<DataPackage*, IInspectable*>>( 429 [promiseHolder](IDataPackage*, IInspectable*) -> HRESULT { 430 promiseHolder->Reject(NS_ERROR_FAILURE, __func__); 431 return S_OK; 432 }); 433 434 if (FAILED(spDataPackage4->add_ShareCanceled(canceledCallback.Get(), 435 &dataRequestedToken))) { 436 promiseHolder->Reject(NS_ERROR_FAILURE, __func__); 437 return E_FAIL; 438 } 439 } 440 441 return S_OK; 442 }); 443 if (result.isErr()) { 444 promiseHolder->Reject(result.unwrapErr(), __func__); 445 } 446 #else 447 promiseHolder->Reject(NS_ERROR_FAILURE, __func__); 448 #endif 449 450 return promise; 451 } 452 453 NS_IMETHODIMP 454 WindowsUIUtils::ShareUrl(const nsAString& aUrlToShare, 455 const nsAString& aShareTitle) { 456 nsAutoString text; 457 text.SetIsVoid(true); 458 WindowsUIUtils::Share(nsAutoString(aShareTitle), text, 459 nsAutoString(aUrlToShare)); 460 return NS_OK; 461 } 462