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