Bug 1777519 [wpt PR 34660] - Avoid OSError from --kill-safari stat'ing random paths...
[gecko.git] / widget / windows / WindowsSMTCProvider.cpp
blob91eea77918c76cc441043c097a834d78c3152ab4
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 /* mingw currently doesn't support windows.media.h, so we disable
7 * the whole related class until this is fixed.
8 * @TODO: Maybe contact MinGW Team for inclusion?*/
9 #ifndef __MINGW32__
11 # include "WindowsSMTCProvider.h"
13 # include <windows.h>
14 # include <windows.media.h>
15 # include <winsdkver.h>
16 # include <wrl.h>
18 # include "nsMimeTypes.h"
19 # include "mozilla/Assertions.h"
20 # include "mozilla/Logging.h"
21 # include "mozilla/Maybe.h"
22 # include "mozilla/WidgetUtils.h"
23 # include "mozilla/WindowsVersion.h"
24 # include "mozilla/ScopeExit.h"
25 # include "mozilla/dom/MediaControlUtils.h"
26 # include "mozilla/media/MediaUtils.h"
27 # include "nsThreadUtils.h"
29 # pragma comment(lib, "runtimeobject.lib")
31 using namespace ABI::Windows::Foundation;
32 using namespace ABI::Windows::Media;
33 using namespace ABI::Windows::Storage::Streams;
34 using namespace Microsoft::WRL;
35 using namespace Microsoft::WRL::Wrappers;
36 using namespace mozilla;
38 # ifndef RuntimeClass_Windows_Media_SystemMediaTransportControls
39 # define RuntimeClass_Windows_Media_SystemMediaTransportControls \
40 L"Windows.Media.SystemMediaTransportControls"
41 # endif
43 # ifndef RuntimeClass_Windows_Storage_Streams_RandomAccessStreamReference
44 # define RuntimeClass_Windows_Storage_Streams_RandomAccessStreamReference \
45 L"Windows.Storage.Streams.RandomAccessStreamReference"
46 # endif
48 # ifndef ISystemMediaTransportControlsInterop
49 EXTERN_C const IID IID_ISystemMediaTransportControlsInterop;
50 MIDL_INTERFACE("ddb0472d-c911-4a1f-86d9-dc3d71a95f5a")
51 ISystemMediaTransportControlsInterop : public IInspectable {
52 public:
53 virtual HRESULT STDMETHODCALLTYPE GetForWindow(
54 /* [in] */ __RPC__in HWND appWindow,
55 /* [in] */ __RPC__in REFIID riid,
56 /* [iid_is][retval][out] */
57 __RPC__deref_out_opt void** mediaTransportControl) = 0;
59 # endif /* __ISystemMediaTransportControlsInterop_INTERFACE_DEFINED__ */
61 extern mozilla::LazyLogModule gMediaControlLog;
63 # undef LOG
64 # define LOG(msg, ...) \
65 MOZ_LOG(gMediaControlLog, LogLevel::Debug, \
66 ("WindowSMTCProvider=%p, " msg, this, ##__VA_ARGS__))
68 static inline Maybe<mozilla::dom::MediaControlKey> TranslateKeycode(
69 SystemMediaTransportControlsButton keycode) {
70 switch (keycode) {
71 case SystemMediaTransportControlsButton_Play:
72 return Some(mozilla::dom::MediaControlKey::Play);
73 case SystemMediaTransportControlsButton_Pause:
74 return Some(mozilla::dom::MediaControlKey::Pause);
75 case SystemMediaTransportControlsButton_Next:
76 return Some(mozilla::dom::MediaControlKey::Nexttrack);
77 case SystemMediaTransportControlsButton_Previous:
78 return Some(mozilla::dom::MediaControlKey::Previoustrack);
79 case SystemMediaTransportControlsButton_Stop:
80 return Some(mozilla::dom::MediaControlKey::Stop);
81 case SystemMediaTransportControlsButton_FastForward:
82 return Some(mozilla::dom::MediaControlKey::Seekforward);
83 case SystemMediaTransportControlsButton_Rewind:
84 return Some(mozilla::dom::MediaControlKey::Seekbackward);
85 default:
86 return Nothing(); // Not supported Button
90 static IAsyncInfo* GetIAsyncInfo(IAsyncOperation<unsigned int>* aAsyncOp) {
91 MOZ_ASSERT(aAsyncOp);
92 IAsyncInfo* asyncInfo;
93 HRESULT hr = aAsyncOp->QueryInterface(IID_IAsyncInfo,
94 reinterpret_cast<void**>(&asyncInfo));
95 // The assertion always works since IAsyncOperation implements IAsyncInfo
96 MOZ_ASSERT(SUCCEEDED(hr));
97 Unused << hr;
98 MOZ_ASSERT(asyncInfo);
99 return asyncInfo;
102 WindowsSMTCProvider::WindowsSMTCProvider() {
103 LOG("Creating an empty and invisible window");
105 // In order to create a SMTC-Provider, we need a hWnd, which shall be created
106 // dynamically from an invisible window. This leads to the following
107 // boilerplate code.
108 WNDCLASS wnd{};
109 wnd.lpszClassName = L"Firefox-MediaKeys";
110 wnd.hInstance = nullptr;
111 wnd.lpfnWndProc = DefWindowProc;
112 GetLastError(); // Clear the error
113 RegisterClass(&wnd);
114 MOZ_ASSERT(!GetLastError());
116 mWindow = CreateWindowExW(0, L"Firefox-MediaKeys", L"Firefox Media Keys", 0,
117 CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, nullptr,
118 nullptr, nullptr, nullptr);
119 MOZ_ASSERT(mWindow);
120 MOZ_ASSERT(!GetLastError());
123 WindowsSMTCProvider::~WindowsSMTCProvider() {
124 // Dispose the window
125 MOZ_ASSERT(mWindow);
126 if (!DestroyWindow(mWindow)) {
127 LOG("Failed to destroy the hidden window. Error Code: %lu", GetLastError());
129 if (!UnregisterClass(L"Firefox-MediaKeys", nullptr)) {
130 // Note that this is logged when the class wasn't even registered.
131 LOG("Failed to unregister the class. Error Code: %lu", GetLastError());
135 bool WindowsSMTCProvider::IsOpened() const { return mInitialized; }
137 bool WindowsSMTCProvider::Open() {
138 LOG("Opening Source");
139 MOZ_ASSERT(!mInitialized);
141 if (!IsWin8Point1OrLater()) {
142 LOG("Windows 8.1 or later is required for Media Key Support");
143 return false;
146 if (!InitDisplayAndControls()) {
147 LOG("Failed to initialize the SMTC and its display");
148 return false;
151 if (!UpdateButtons()) {
152 LOG("Failed to initialize the buttons");
153 return false;
156 if (!RegisterEvents()) {
157 LOG("Failed to register SMTC key-event listener");
158 return false;
161 if (!EnableControl(true)) {
162 LOG("Failed to enable SMTC control");
163 return false;
166 mInitialized = true;
167 SetPlaybackState(mozilla::dom::MediaSessionPlaybackState::None);
168 return mInitialized;
171 void WindowsSMTCProvider::Close() {
172 MediaControlKeySource::Close();
173 // Prevent calling Set methods when init failed
174 if (mInitialized) {
175 SetPlaybackState(mozilla::dom::MediaSessionPlaybackState::None);
176 UnregisterEvents();
177 ClearMetadata();
178 // We have observed an Windows issue, if we modify `mControls` , (such as
179 // setting metadata, disable buttons) before disabling control, and those
180 // operations are not done sequentially within a same main thread task,
181 // then it would cause a problem where the SMTC wasn't clean up completely
182 // and show the executable name.
183 EnableControl(false);
184 mInitialized = false;
188 void WindowsSMTCProvider::SetPlaybackState(
189 mozilla::dom::MediaSessionPlaybackState aState) {
190 MOZ_ASSERT(mInitialized);
191 MediaControlKeySource::SetPlaybackState(aState);
193 HRESULT hr;
195 // Note: we can't return the status of put_PlaybackStatus, but we can at least
196 // assert it.
197 switch (aState) {
198 case mozilla::dom::MediaSessionPlaybackState::Paused:
199 hr = mControls->put_PlaybackStatus(
200 ABI::Windows::Media::MediaPlaybackStatus_Paused);
201 break;
202 case mozilla::dom::MediaSessionPlaybackState::Playing:
203 hr = mControls->put_PlaybackStatus(
204 ABI::Windows::Media::MediaPlaybackStatus_Playing);
205 break;
206 case mozilla::dom::MediaSessionPlaybackState::None:
207 hr = mControls->put_PlaybackStatus(
208 ABI::Windows::Media::MediaPlaybackStatus_Stopped);
209 break;
210 // MediaPlaybackStatus still supports Closed and Changing, which we don't
211 // use (yet)
212 default:
213 MOZ_ASSERT_UNREACHABLE(
214 "Enum Inconsitency between PlaybackState and WindowsSMTCProvider");
215 break;
218 MOZ_ASSERT(SUCCEEDED(hr));
219 Unused << hr;
222 void WindowsSMTCProvider::SetMediaMetadata(
223 const mozilla::dom::MediaMetadataBase& aMetadata) {
224 MOZ_ASSERT(mInitialized);
225 SetMusicMetadata(aMetadata.mArtist, aMetadata.mTitle);
226 LoadThumbnail(aMetadata.mArtwork);
229 void WindowsSMTCProvider::ClearMetadata() {
230 MOZ_ASSERT(mDisplay);
231 if (FAILED(mDisplay->ClearAll())) {
232 LOG("Failed to clear SMTC display");
234 mImageFetchRequest.DisconnectIfExists();
235 CancelPendingStoreAsyncOperation();
236 mThumbnailUrl.Truncate();
237 mProcessingUrl.Truncate();
238 mNextImageIndex = 0;
239 mSupportedKeys = 0;
242 void WindowsSMTCProvider::SetSupportedMediaKeys(
243 const MediaKeysArray& aSupportedKeys) {
244 MOZ_ASSERT(mInitialized);
246 uint32_t supportedKeys = 0;
247 for (const mozilla::dom::MediaControlKey& key : aSupportedKeys) {
248 supportedKeys |= GetMediaKeyMask(key);
251 if (supportedKeys == mSupportedKeys) {
252 LOG("Supported keys stay the same");
253 return;
256 LOG("Update supported keys");
257 mSupportedKeys = supportedKeys;
258 UpdateButtons();
261 void WindowsSMTCProvider::UnregisterEvents() {
262 if (mControls && mButtonPressedToken.value != 0) {
263 mControls->remove_ButtonPressed(mButtonPressedToken);
267 bool WindowsSMTCProvider::RegisterEvents() {
268 MOZ_ASSERT(mControls);
269 auto self = RefPtr<WindowsSMTCProvider>(this);
270 auto callbackbtnPressed = Callback<
271 ITypedEventHandler<SystemMediaTransportControls*,
272 SystemMediaTransportControlsButtonPressedEventArgs*>>(
273 [this, self](ISystemMediaTransportControls*,
274 ISystemMediaTransportControlsButtonPressedEventArgs* pArgs)
275 -> HRESULT {
276 MOZ_ASSERT(pArgs);
277 SystemMediaTransportControlsButton btn;
279 if (FAILED(pArgs->get_Button(&btn))) {
280 LOG("SystemMediaTransportControls: ButtonPressedEvent - Could "
281 "not get Button.");
282 return S_OK; // Propagating the error probably wouldn't help.
285 Maybe<mozilla::dom::MediaControlKey> keyCode = TranslateKeycode(btn);
286 if (keyCode.isSome() && IsOpened()) {
287 OnButtonPressed(keyCode.value());
289 return S_OK;
292 if (FAILED(mControls->add_ButtonPressed(callbackbtnPressed.Get(),
293 &mButtonPressedToken))) {
294 LOG("SystemMediaTransportControls: Failed at "
295 "registerEvents().add_ButtonPressed()");
296 return false;
299 return true;
302 void WindowsSMTCProvider::OnButtonPressed(
303 mozilla::dom::MediaControlKey aKey) const {
304 if (!IsKeySupported(aKey)) {
305 LOG("key: %s is not supported", ToMediaControlKeyStr(aKey));
306 return;
309 for (auto& listener : mListeners) {
310 listener->OnActionPerformed(mozilla::dom::MediaControlAction(aKey));
314 bool WindowsSMTCProvider::EnableControl(bool aEnabled) const {
315 MOZ_ASSERT(mControls);
316 return SUCCEEDED(mControls->put_IsEnabled(aEnabled));
319 bool WindowsSMTCProvider::UpdateButtons() const {
320 static const mozilla::dom::MediaControlKey kKeys[] = {
321 mozilla::dom::MediaControlKey::Play, mozilla::dom::MediaControlKey::Pause,
322 mozilla::dom::MediaControlKey::Previoustrack,
323 mozilla::dom::MediaControlKey::Nexttrack,
324 mozilla::dom::MediaControlKey::Stop};
326 bool success = true;
327 for (const mozilla::dom::MediaControlKey& key : kKeys) {
328 if (!EnableKey(key, IsKeySupported(key))) {
329 success = false;
330 LOG("Failed to set %s=%s", ToMediaControlKeyStr(key),
331 IsKeySupported(key) ? "true" : "false");
335 return success;
338 bool WindowsSMTCProvider::IsKeySupported(
339 mozilla::dom::MediaControlKey aKey) const {
340 return mSupportedKeys & GetMediaKeyMask(aKey);
343 bool WindowsSMTCProvider::EnableKey(mozilla::dom::MediaControlKey aKey,
344 bool aEnable) const {
345 MOZ_ASSERT(mControls);
346 switch (aKey) {
347 case mozilla::dom::MediaControlKey::Play:
348 return SUCCEEDED(mControls->put_IsPlayEnabled(aEnable));
349 case mozilla::dom::MediaControlKey::Pause:
350 return SUCCEEDED(mControls->put_IsPauseEnabled(aEnable));
351 case mozilla::dom::MediaControlKey::Previoustrack:
352 return SUCCEEDED(mControls->put_IsPreviousEnabled(aEnable));
353 case mozilla::dom::MediaControlKey::Nexttrack:
354 return SUCCEEDED(mControls->put_IsNextEnabled(aEnable));
355 case mozilla::dom::MediaControlKey::Stop:
356 return SUCCEEDED(mControls->put_IsStopEnabled(aEnable));
357 default:
358 LOG("No button for %s", ToMediaControlKeyStr(aKey));
359 return false;
363 bool WindowsSMTCProvider::InitDisplayAndControls() {
364 // As Open() might be called multiple times, "cache" the results of the COM
365 // API
366 if (mControls && mDisplay) {
367 return true;
369 ComPtr<ISystemMediaTransportControlsInterop> interop;
370 HRESULT hr = GetActivationFactory(
371 HStringReference(RuntimeClass_Windows_Media_SystemMediaTransportControls)
372 .Get(),
373 interop.GetAddressOf());
374 if (FAILED(hr)) {
375 LOG("SystemMediaTransportControls: Failed at instantiating the "
376 "Interop object");
377 return false;
379 MOZ_ASSERT(interop);
381 if (!mControls && FAILED(interop->GetForWindow(
382 mWindow, IID_PPV_ARGS(mControls.GetAddressOf())))) {
383 LOG("SystemMediaTransportControls: Failed at GetForWindow()");
384 return false;
386 MOZ_ASSERT(mControls);
388 if (!mDisplay &&
389 FAILED(mControls->get_DisplayUpdater(mDisplay.GetAddressOf()))) {
390 LOG("SystemMediaTransportControls: Failed at get_DisplayUpdater()");
393 MOZ_ASSERT(mDisplay);
394 return true;
397 bool WindowsSMTCProvider::SetMusicMetadata(const nsString& aArtist,
398 const nsString& aTitle) {
399 MOZ_ASSERT(mDisplay);
400 ComPtr<IMusicDisplayProperties> musicProps;
402 HRESULT hr = mDisplay->put_Type(MediaPlaybackType::MediaPlaybackType_Music);
403 MOZ_ASSERT(SUCCEEDED(hr));
404 Unused << hr;
405 hr = mDisplay->get_MusicProperties(musicProps.GetAddressOf());
406 if (FAILED(hr)) {
407 LOG("Failed to get music properties");
408 return false;
411 hr = musicProps->put_Artist(HStringReference(aArtist.get()).Get());
412 if (FAILED(hr)) {
413 LOG("Failed to set the music's artist");
414 return false;
417 hr = musicProps->put_Title(HStringReference(aTitle.get()).Get());
418 if (FAILED(hr)) {
419 LOG("Failed to set the music's title");
420 return false;
423 hr = mDisplay->Update();
424 if (FAILED(hr)) {
425 LOG("Failed to refresh the display");
426 return false;
429 return true;
432 void WindowsSMTCProvider::LoadThumbnail(
433 const nsTArray<mozilla::dom::MediaImage>& aArtwork) {
434 MOZ_ASSERT(NS_IsMainThread());
436 // TODO: Sort the images by the preferred size or format.
437 mArtwork = aArtwork;
438 mNextImageIndex = 0;
440 // Abort the loading if
441 // 1) thumbnail is being updated, and one in processing is in the artwork
442 // 2) thumbnail is not being updated, and one in use is in the artwork
443 if (!mProcessingUrl.IsEmpty()) {
444 LOG("Load thumbnail while image: %s is being processed",
445 NS_ConvertUTF16toUTF8(mProcessingUrl).get());
446 if (mozilla::dom::IsImageIn(mArtwork, mProcessingUrl)) {
447 LOG("No need to load thumbnail. The one being processed is in the "
448 "artwork");
449 return;
451 } else if (!mThumbnailUrl.IsEmpty()) {
452 if (mozilla::dom::IsImageIn(mArtwork, mThumbnailUrl)) {
453 LOG("No need to load thumbnail. The one in use is in the artwork");
454 return;
458 // If there is a pending image store operation, that image must be different
459 // from the new image will be loaded below, so the pending one should be
460 // cancelled.
461 CancelPendingStoreAsyncOperation();
462 // Remove the current thumbnail on the interface
463 ClearThumbnail();
464 // Then load the new thumbnail asynchronously
465 LoadImageAtIndex(mNextImageIndex++);
468 void WindowsSMTCProvider::LoadImageAtIndex(const size_t aIndex) {
469 MOZ_ASSERT(NS_IsMainThread());
471 if (aIndex >= mArtwork.Length()) {
472 LOG("Stop loading thumbnail. No more available images");
473 mImageFetchRequest.DisconnectIfExists();
474 mProcessingUrl.Truncate();
475 return;
478 const mozilla::dom::MediaImage& image = mArtwork[aIndex];
480 // TODO: No need to fetch the default image and do image processing since the
481 // the default image is local file and it's trustworthy. For the default
482 // image, we can use `CreateFromFile` to create the IRandomAccessStream. We
483 // should probably cache it since it could be used very often (Bug 1643102)
485 if (!mozilla::dom::IsValidImageUrl(image.mSrc)) {
486 LOG("Skip the image with invalid URL. Try next image");
487 mImageFetchRequest.DisconnectIfExists();
488 LoadImageAtIndex(mNextImageIndex++);
489 return;
492 mImageFetchRequest.DisconnectIfExists();
493 mProcessingUrl = image.mSrc;
495 mImageFetcher = mozilla::MakeUnique<mozilla::dom::FetchImageHelper>(image);
496 RefPtr<WindowsSMTCProvider> self = this;
497 mImageFetcher->FetchImage()
498 ->Then(
499 AbstractThread::MainThread(), __func__,
500 [this, self](const nsCOMPtr<imgIContainer>& aImage) {
501 LOG("The image is fetched successfully");
502 mImageFetchRequest.Complete();
504 // Although IMAGE_JPEG or IMAGE_BMP are valid types as well, but a
505 // png image with transparent background will be converted into a
506 // jpeg/bmp file with a colored background. IMAGE_PNG format seems
507 // to be the best choice for now.
508 uint32_t size = 0;
509 char* src = nullptr;
510 // Only used to hold the image data
511 nsCOMPtr<nsIInputStream> inputStream;
512 nsresult rv = mozilla::dom::GetEncodedImageBuffer(
513 aImage, nsLiteralCString(IMAGE_PNG),
514 getter_AddRefs(inputStream), &size, &src);
515 if (NS_FAILED(rv) || !inputStream || size == 0 || !src) {
516 LOG("Failed to get the image buffer info. Try next image");
517 LoadImageAtIndex(mNextImageIndex++);
518 return;
521 LoadImage(src, size);
523 [this, self](bool) {
524 LOG("Failed to fetch image. Try next image");
525 mImageFetchRequest.Complete();
526 LoadImageAtIndex(mNextImageIndex++);
528 ->Track(mImageFetchRequest);
531 void WindowsSMTCProvider::LoadImage(const char* aImageData,
532 uint32_t aDataSize) {
533 MOZ_ASSERT(NS_IsMainThread());
535 // 1. Use mImageDataWriter to write the binary data of image into mImageStream
536 // 2. Refer the image by mImageStreamReference and then set it to the SMTC
537 // In case of the race condition between they are being destroyed and the
538 // async operation for image loading, mImageDataWriter, mImageStream, and
539 // mImageStreamReference are member variables
541 HRESULT hr = ActivateInstance(
542 HStringReference(
543 RuntimeClass_Windows_Storage_Streams_InMemoryRandomAccessStream)
544 .Get(),
545 mImageStream.GetAddressOf());
546 if (FAILED(hr)) {
547 LOG("Failed to make mImageStream refer to an instance of "
548 "InMemoryRandomAccessStream");
549 return;
552 ComPtr<IOutputStream> outputStream;
553 hr = mImageStream.As(&outputStream);
554 if (FAILED(hr)) {
555 LOG("Failed when query IOutputStream interface from mImageStream");
556 return;
559 ComPtr<IDataWriterFactory> dataWriterFactory;
560 hr = GetActivationFactory(
561 HStringReference(RuntimeClass_Windows_Storage_Streams_DataWriter).Get(),
562 dataWriterFactory.GetAddressOf());
563 if (FAILED(hr)) {
564 LOG("Failed to get an activation factory for IDataWriterFactory");
565 return;
568 hr = dataWriterFactory->CreateDataWriter(outputStream.Get(),
569 mImageDataWriter.GetAddressOf());
570 if (FAILED(hr)) {
571 LOG("Failed to create mImageDataWriter that writes data to mImageStream");
572 return;
575 hr = mImageDataWriter->WriteBytes(
576 aDataSize, reinterpret_cast<BYTE*>(const_cast<char*>(aImageData)));
577 if (FAILED(hr)) {
578 LOG("Failed to write data to mImageStream");
579 return;
582 hr = mImageDataWriter->StoreAsync(&mStoreAsyncOperation);
583 if (FAILED(hr)) {
584 LOG("Failed to create a DataWriterStoreOperation for mStoreAsyncOperation");
585 return;
588 // Upon the image is stored in mImageStream, set the image to the SMTC
589 // interface
590 auto onStoreCompleted = Callback<
591 IAsyncOperationCompletedHandler<unsigned int>>(
592 [this, self = RefPtr<WindowsSMTCProvider>(this),
593 aImageUrl = nsString(mProcessingUrl)](
594 IAsyncOperation<unsigned int>* aAsyncOp, AsyncStatus aStatus) {
595 MOZ_ASSERT(NS_IsMainThread());
597 if (aStatus != AsyncStatus::Completed) {
598 LOG("Asynchronous operation is not completed");
599 return E_ABORT;
602 HRESULT hr = S_OK;
603 IAsyncInfo* asyncInfo = GetIAsyncInfo(aAsyncOp);
604 asyncInfo->get_ErrorCode(&hr);
605 if (FAILED(hr)) {
606 LOG("Failed to get termination status of the asynchronous operation");
607 return hr;
610 if (!UpdateThumbnail(aImageUrl)) {
611 LOG("Failed to update thumbnail");
614 // If an error occurs above:
615 // - If aImageUrl is not mProcessingUrl. It's fine.
616 // - If aImageUrl is mProcessingUrl, then mProcessingUrl won't be reset.
617 // Therefore the thumbnail will remain empty until a new image whose
618 // url is different from mProcessingUrl is loaded.
620 return S_OK;
623 hr = mStoreAsyncOperation->put_Completed(onStoreCompleted.Get());
624 if (FAILED(hr)) {
625 LOG("Failed to set callback on completeing the asynchronous operation");
629 bool WindowsSMTCProvider::SetThumbnail(const nsAString& aUrl) {
630 MOZ_ASSERT(mDisplay);
631 MOZ_ASSERT(mImageStream);
632 MOZ_ASSERT(!aUrl.IsEmpty());
634 ComPtr<IRandomAccessStreamReferenceStatics> streamRefFactory;
636 HRESULT hr = GetActivationFactory(
637 HStringReference(
638 RuntimeClass_Windows_Storage_Streams_RandomAccessStreamReference)
639 .Get(),
640 streamRefFactory.GetAddressOf());
641 auto cleanup =
642 MakeScopeExit([this, self = RefPtr<WindowsSMTCProvider>(this)] {
643 LOG("Clean mThumbnailUrl");
644 mThumbnailUrl.Truncate();
647 if (FAILED(hr)) {
648 LOG("Failed to get an activation factory for "
649 "IRandomAccessStreamReferenceStatics type");
650 return false;
653 hr = streamRefFactory->CreateFromStream(mImageStream.Get(),
654 mImageStreamReference.GetAddressOf());
655 if (FAILED(hr)) {
656 LOG("Failed to create mImageStreamReference from mImageStream");
657 return false;
660 hr = mDisplay->put_Thumbnail(mImageStreamReference.Get());
661 if (FAILED(hr)) {
662 LOG("Failed to update thumbnail");
663 return false;
666 hr = mDisplay->Update();
667 if (FAILED(hr)) {
668 LOG("Failed to refresh display");
669 return false;
672 // No need to clean mThumbnailUrl since thumbnail is set successfully
673 cleanup.release();
674 mThumbnailUrl = aUrl;
676 return true;
679 void WindowsSMTCProvider::ClearThumbnail() {
680 MOZ_ASSERT(mDisplay);
681 HRESULT hr = mDisplay->put_Thumbnail(nullptr);
682 MOZ_ASSERT(SUCCEEDED(hr));
683 hr = mDisplay->Update();
684 MOZ_ASSERT(SUCCEEDED(hr));
685 Unused << hr;
686 mThumbnailUrl.Truncate();
689 bool WindowsSMTCProvider::UpdateThumbnail(const nsAString& aUrl) {
690 MOZ_ASSERT(NS_IsMainThread());
692 if (!IsOpened()) {
693 LOG("Abort the thumbnail update: SMTC is closed");
694 return false;
697 if (aUrl != mProcessingUrl) {
698 LOG("Abort the thumbnail update: The image from %s is out of date",
699 NS_ConvertUTF16toUTF8(aUrl).get());
700 return false;
703 mProcessingUrl.Truncate();
705 if (!SetThumbnail(aUrl)) {
706 LOG("Failed to update thumbnail");
707 return false;
710 MOZ_ASSERT(mThumbnailUrl == aUrl);
711 LOG("The thumbnail is updated to the image from: %s",
712 NS_ConvertUTF16toUTF8(mThumbnailUrl).get());
713 return true;
716 void WindowsSMTCProvider::CancelPendingStoreAsyncOperation() const {
717 if (mStoreAsyncOperation) {
718 IAsyncInfo* asyncInfo = GetIAsyncInfo(mStoreAsyncOperation.Get());
719 asyncInfo->Cancel();
723 #endif // __MINGW32__