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?*/
11 # include "WindowsSMTCProvider.h"
14 # include <windows.media.h>
17 # include "nsMimeTypes.h"
18 # include "mozilla/Assertions.h"
19 # include "mozilla/Logging.h"
20 # include "mozilla/Maybe.h"
21 # include "mozilla/WidgetUtils.h"
22 # include "mozilla/ScopeExit.h"
23 # include "mozilla/dom/MediaControlUtils.h"
24 # include "mozilla/media/MediaUtils.h"
25 # include "nsThreadUtils.h"
27 # pragma comment(lib, "runtimeobject.lib")
29 using namespace ABI::Windows::Foundation
;
30 using namespace ABI::Windows::Media
;
31 using namespace ABI::Windows::Storage::Streams
;
32 using namespace Microsoft::WRL
;
33 using namespace Microsoft::WRL::Wrappers
;
34 using namespace mozilla
;
36 # ifndef RuntimeClass_Windows_Media_SystemMediaTransportControls
37 # define RuntimeClass_Windows_Media_SystemMediaTransportControls \
38 L"Windows.Media.SystemMediaTransportControls"
41 # ifndef RuntimeClass_Windows_Storage_Streams_RandomAccessStreamReference
42 # define RuntimeClass_Windows_Storage_Streams_RandomAccessStreamReference \
43 L"Windows.Storage.Streams.RandomAccessStreamReference"
46 # ifndef ISystemMediaTransportControlsInterop
47 EXTERN_C
const IID IID_ISystemMediaTransportControlsInterop
;
48 MIDL_INTERFACE("ddb0472d-c911-4a1f-86d9-dc3d71a95f5a")
49 ISystemMediaTransportControlsInterop
: public IInspectable
{
51 virtual HRESULT STDMETHODCALLTYPE
GetForWindow(
52 /* [in] */ __RPC__in HWND appWindow
,
53 /* [in] */ __RPC__in REFIID riid
,
54 /* [iid_is][retval][out] */
55 __RPC__deref_out_opt
void** mediaTransportControl
) = 0;
57 # endif /* __ISystemMediaTransportControlsInterop_INTERFACE_DEFINED__ */
59 extern mozilla::LazyLogModule gMediaControlLog
;
62 # define LOG(msg, ...) \
63 MOZ_LOG(gMediaControlLog, LogLevel::Debug, \
64 ("WindowSMTCProvider=%p, " msg, this, ##__VA_ARGS__))
66 static inline Maybe
<mozilla::dom::MediaControlKey
> TranslateKeycode(
67 SystemMediaTransportControlsButton keycode
) {
69 case SystemMediaTransportControlsButton_Play
:
70 return Some(mozilla::dom::MediaControlKey::Play
);
71 case SystemMediaTransportControlsButton_Pause
:
72 return Some(mozilla::dom::MediaControlKey::Pause
);
73 case SystemMediaTransportControlsButton_Next
:
74 return Some(mozilla::dom::MediaControlKey::Nexttrack
);
75 case SystemMediaTransportControlsButton_Previous
:
76 return Some(mozilla::dom::MediaControlKey::Previoustrack
);
77 case SystemMediaTransportControlsButton_Stop
:
78 return Some(mozilla::dom::MediaControlKey::Stop
);
79 case SystemMediaTransportControlsButton_FastForward
:
80 return Some(mozilla::dom::MediaControlKey::Seekforward
);
81 case SystemMediaTransportControlsButton_Rewind
:
82 return Some(mozilla::dom::MediaControlKey::Seekbackward
);
84 return Nothing(); // Not supported Button
88 static IAsyncInfo
* GetIAsyncInfo(IAsyncOperation
<unsigned int>* aAsyncOp
) {
90 IAsyncInfo
* asyncInfo
;
91 HRESULT hr
= aAsyncOp
->QueryInterface(IID_IAsyncInfo
,
92 reinterpret_cast<void**>(&asyncInfo
));
93 // The assertion always works since IAsyncOperation implements IAsyncInfo
94 MOZ_ASSERT(SUCCEEDED(hr
));
96 MOZ_ASSERT(asyncInfo
);
100 WindowsSMTCProvider::WindowsSMTCProvider() {
101 LOG("Creating an empty and invisible window");
103 // In order to create a SMTC-Provider, we need a hWnd, which shall be created
104 // dynamically from an invisible window. This leads to the following
107 wnd
.lpszClassName
= L
"Firefox-MediaKeys";
108 wnd
.hInstance
= nullptr;
109 wnd
.lpfnWndProc
= DefWindowProc
;
110 GetLastError(); // Clear the error
112 MOZ_ASSERT(!GetLastError());
114 mWindow
= CreateWindowExW(0, L
"Firefox-MediaKeys", L
"Firefox Media Keys", 0,
115 CW_USEDEFAULT
, CW_USEDEFAULT
, 0, 0, nullptr,
116 nullptr, nullptr, nullptr);
118 MOZ_ASSERT(!GetLastError());
121 WindowsSMTCProvider::~WindowsSMTCProvider() {
122 // Dispose the window
124 if (!DestroyWindow(mWindow
)) {
125 LOG("Failed to destroy the hidden window. Error Code: %lu", GetLastError());
127 if (!UnregisterClass(L
"Firefox-MediaKeys", nullptr)) {
128 // Note that this is logged when the class wasn't even registered.
129 LOG("Failed to unregister the class. Error Code: %lu", GetLastError());
133 bool WindowsSMTCProvider::IsOpened() const { return mInitialized
; }
135 bool WindowsSMTCProvider::Open() {
136 LOG("Opening Source");
137 MOZ_ASSERT(!mInitialized
);
139 if (!InitDisplayAndControls()) {
140 LOG("Failed to initialize the SMTC and its display");
144 if (!UpdateButtons()) {
145 LOG("Failed to initialize the buttons");
149 if (!RegisterEvents()) {
150 LOG("Failed to register SMTC key-event listener");
154 if (!EnableControl(true)) {
155 LOG("Failed to enable SMTC control");
160 SetPlaybackState(mozilla::dom::MediaSessionPlaybackState::None
);
164 void WindowsSMTCProvider::Close() {
165 MediaControlKeySource::Close();
166 // Prevent calling Set methods when init failed
168 SetPlaybackState(mozilla::dom::MediaSessionPlaybackState::None
);
171 // We have observed an Windows issue, if we modify `mControls` , (such as
172 // setting metadata, disable buttons) before disabling control, and those
173 // operations are not done sequentially within a same main thread task,
174 // then it would cause a problem where the SMTC wasn't clean up completely
175 // and show the executable name.
176 EnableControl(false);
177 mInitialized
= false;
181 void WindowsSMTCProvider::SetPlaybackState(
182 mozilla::dom::MediaSessionPlaybackState aState
) {
183 MOZ_ASSERT(mInitialized
);
184 MediaControlKeySource::SetPlaybackState(aState
);
188 // Note: we can't return the status of put_PlaybackStatus, but we can at least
191 case mozilla::dom::MediaSessionPlaybackState::Paused
:
192 hr
= mControls
->put_PlaybackStatus(
193 ABI::Windows::Media::MediaPlaybackStatus_Paused
);
195 case mozilla::dom::MediaSessionPlaybackState::Playing
:
196 hr
= mControls
->put_PlaybackStatus(
197 ABI::Windows::Media::MediaPlaybackStatus_Playing
);
199 case mozilla::dom::MediaSessionPlaybackState::None
:
200 hr
= mControls
->put_PlaybackStatus(
201 ABI::Windows::Media::MediaPlaybackStatus_Stopped
);
203 // MediaPlaybackStatus still supports Closed and Changing, which we don't
206 MOZ_ASSERT_UNREACHABLE(
207 "Enum Inconsitency between PlaybackState and WindowsSMTCProvider");
211 MOZ_ASSERT(SUCCEEDED(hr
));
215 void WindowsSMTCProvider::SetMediaMetadata(
216 const mozilla::dom::MediaMetadataBase
& aMetadata
) {
217 MOZ_ASSERT(mInitialized
);
218 SetMusicMetadata(aMetadata
.mArtist
, aMetadata
.mTitle
);
219 LoadThumbnail(aMetadata
.mArtwork
);
222 void WindowsSMTCProvider::ClearMetadata() {
223 MOZ_ASSERT(mDisplay
);
224 if (FAILED(mDisplay
->ClearAll())) {
225 LOG("Failed to clear SMTC display");
227 mImageFetchRequest
.DisconnectIfExists();
228 CancelPendingStoreAsyncOperation();
229 mThumbnailUrl
.Truncate();
230 mProcessingUrl
.Truncate();
235 void WindowsSMTCProvider::SetSupportedMediaKeys(
236 const MediaKeysArray
& aSupportedKeys
) {
237 MOZ_ASSERT(mInitialized
);
239 uint32_t supportedKeys
= 0;
240 for (const mozilla::dom::MediaControlKey
& key
: aSupportedKeys
) {
241 supportedKeys
|= GetMediaKeyMask(key
);
244 if (supportedKeys
== mSupportedKeys
) {
245 LOG("Supported keys stay the same");
249 LOG("Update supported keys");
250 mSupportedKeys
= supportedKeys
;
254 void WindowsSMTCProvider::UnregisterEvents() {
255 if (mControls
&& mButtonPressedToken
.value
!= 0) {
256 mControls
->remove_ButtonPressed(mButtonPressedToken
);
259 if (mControls
&& mSeekRegistrationToken
.value
!= 0) {
260 ComPtr
<ISystemMediaTransportControls2
> smtc2
;
261 HRESULT hr
= mControls
.As(&smtc2
);
263 LOG("Failed to cast controls to ISystemMediaTransportControls2 (hr=%lx)",
269 hr
= smtc2
->remove_PlaybackPositionChangeRequested(mSeekRegistrationToken
);
271 LOG("SystemMediaTransportControls: Failed unregister position change "
279 bool WindowsSMTCProvider::RegisterEvents() {
280 MOZ_ASSERT(mControls
);
281 auto self
= RefPtr
<WindowsSMTCProvider
>(this);
282 auto callbackbtnPressed
= Callback
<
283 ITypedEventHandler
<SystemMediaTransportControls
*,
284 SystemMediaTransportControlsButtonPressedEventArgs
*>>(
285 [this, self
](ISystemMediaTransportControls
*,
286 ISystemMediaTransportControlsButtonPressedEventArgs
* pArgs
)
289 SystemMediaTransportControlsButton btn
;
291 if (FAILED(pArgs
->get_Button(&btn
))) {
292 LOG("SystemMediaTransportControls: ButtonPressedEvent - Could "
294 return S_OK
; // Propagating the error probably wouldn't help.
297 Maybe
<mozilla::dom::MediaControlKey
> keyCode
= TranslateKeycode(btn
);
298 if (keyCode
.isSome() && IsOpened()) {
299 OnButtonPressed(keyCode
.value());
304 if (FAILED(mControls
->add_ButtonPressed(callbackbtnPressed
.Get(),
305 &mButtonPressedToken
))) {
306 LOG("SystemMediaTransportControls: Failed at "
307 "registerEvents().add_ButtonPressed()");
311 ComPtr
<ISystemMediaTransportControls2
> smtc2
;
312 HRESULT hr
= mControls
.As(&smtc2
);
314 LOG("Failed to cast controls to ISystemMediaTransportControls2 (hr=%lx)",
320 auto positionChangeRequested
=
321 Callback
<ITypedEventHandler
<SystemMediaTransportControls
*,
322 PlaybackPositionChangeRequestedEventArgs
*>>(
324 ISystemMediaTransportControls
*,
325 IPlaybackPositionChangeRequestedEventArgs
* pArgs
) -> HRESULT
{
329 HRESULT hr
= pArgs
->get_RequestedPlaybackPosition(&value
);
331 LOG("SystemMediaTransportControls: Playback Position Change - "
332 "failed to get requested position (hr=%lx)",
334 return S_OK
; // Propagating the error probably wouldn't help.
338 static_cast<double>(value
.Duration
) / (1e6
* 10.0);
339 this->OnPositionChangeRequested(position
);
344 hr
= smtc2
->add_PlaybackPositionChangeRequested(positionChangeRequested
.Get(),
345 &mSeekRegistrationToken
);
347 LOG("SystemMediaTransportControls: Failed to register position change "
356 void WindowsSMTCProvider::OnButtonPressed(
357 mozilla::dom::MediaControlKey aKey
) const {
358 if (!IsKeySupported(aKey
)) {
359 LOG("key: %s is not supported", dom::GetEnumString(aKey
).get());
363 for (auto& listener
: mListeners
) {
364 listener
->OnActionPerformed(mozilla::dom::MediaControlAction(aKey
));
368 bool WindowsSMTCProvider::EnableControl(bool aEnabled
) const {
369 MOZ_ASSERT(mControls
);
370 return SUCCEEDED(mControls
->put_IsEnabled(aEnabled
));
373 bool WindowsSMTCProvider::UpdateButtons() {
374 static const mozilla::dom::MediaControlKey kKeys
[] = {
375 mozilla::dom::MediaControlKey::Play
,
376 mozilla::dom::MediaControlKey::Pause
,
377 mozilla::dom::MediaControlKey::Previoustrack
,
378 mozilla::dom::MediaControlKey::Nexttrack
,
379 mozilla::dom::MediaControlKey::Stop
,
380 mozilla::dom::MediaControlKey::Seekto
};
383 for (const mozilla::dom::MediaControlKey
& key
: kKeys
) {
384 if (!EnableKey(key
, IsKeySupported(key
))) {
386 LOG("Failed to set %s=%s", dom::GetEnumString(key
).get(),
387 IsKeySupported(key
) ? "true" : "false");
394 bool WindowsSMTCProvider::IsKeySupported(
395 mozilla::dom::MediaControlKey aKey
) const {
396 return mSupportedKeys
& GetMediaKeyMask(aKey
);
399 bool WindowsSMTCProvider::EnableKey(mozilla::dom::MediaControlKey aKey
,
400 bool aEnable
) const {
401 MOZ_ASSERT(mControls
);
403 case mozilla::dom::MediaControlKey::Play
:
404 return SUCCEEDED(mControls
->put_IsPlayEnabled(aEnable
));
405 case mozilla::dom::MediaControlKey::Pause
:
406 return SUCCEEDED(mControls
->put_IsPauseEnabled(aEnable
));
407 case mozilla::dom::MediaControlKey::Previoustrack
:
408 return SUCCEEDED(mControls
->put_IsPreviousEnabled(aEnable
));
409 case mozilla::dom::MediaControlKey::Nexttrack
:
410 return SUCCEEDED(mControls
->put_IsNextEnabled(aEnable
));
411 case mozilla::dom::MediaControlKey::Stop
:
412 return SUCCEEDED(mControls
->put_IsStopEnabled(aEnable
));
413 case mozilla::dom::MediaControlKey::Seekto
:
414 // The callback for the event checks if the key is supported
415 return mSeekRegistrationToken
.value
!= 0;
417 LOG("No button for %s", dom::GetEnumString(aKey
).get());
422 void WindowsSMTCProvider::OnPositionChangeRequested(double aPosition
) const {
423 if (!IsKeySupported(mozilla::dom::MediaControlKey::Seekto
)) {
424 LOG("Seekto is not supported");
428 for (const auto& listener
: mListeners
) {
429 listener
->OnActionPerformed(
430 mozilla::dom::MediaControlAction(mozilla::dom::MediaControlKey::Seekto
,
431 mozilla::dom::SeekDetails(aPosition
)));
435 bool WindowsSMTCProvider::InitDisplayAndControls() {
436 // As Open() might be called multiple times, "cache" the results of the COM
438 if (mControls
&& mDisplay
) {
441 ComPtr
<ISystemMediaTransportControlsInterop
> interop
;
442 HRESULT hr
= GetActivationFactory(
443 HStringReference(RuntimeClass_Windows_Media_SystemMediaTransportControls
)
445 interop
.GetAddressOf());
447 LOG("SystemMediaTransportControls: Failed at instantiating the "
453 if (!mControls
&& FAILED(interop
->GetForWindow(
454 mWindow
, IID_PPV_ARGS(mControls
.GetAddressOf())))) {
455 LOG("SystemMediaTransportControls: Failed at GetForWindow()");
458 MOZ_ASSERT(mControls
);
461 FAILED(mControls
->get_DisplayUpdater(mDisplay
.GetAddressOf()))) {
462 LOG("SystemMediaTransportControls: Failed at get_DisplayUpdater()");
465 MOZ_ASSERT(mDisplay
);
469 bool WindowsSMTCProvider::SetMusicMetadata(const nsString
& aArtist
,
470 const nsString
& aTitle
) {
471 MOZ_ASSERT(mDisplay
);
472 ComPtr
<IMusicDisplayProperties
> musicProps
;
474 HRESULT hr
= mDisplay
->put_Type(MediaPlaybackType::MediaPlaybackType_Music
);
475 MOZ_ASSERT(SUCCEEDED(hr
));
477 hr
= mDisplay
->get_MusicProperties(musicProps
.GetAddressOf());
479 LOG("Failed to get music properties");
483 hr
= musicProps
->put_Artist(HStringReference(aArtist
.get()).Get());
485 LOG("Failed to set the music's artist");
489 hr
= musicProps
->put_Title(HStringReference(aTitle
.get()).Get());
491 LOG("Failed to set the music's title");
495 hr
= mDisplay
->Update();
497 LOG("Failed to refresh the display");
504 void WindowsSMTCProvider::SetPositionState(
505 const mozilla::Maybe
<mozilla::dom::PositionState
>& aState
) {
506 ComPtr
<ISystemMediaTransportControls2
> smtc2
;
507 HRESULT hr
= mControls
.As(&smtc2
);
509 LOG("Failed to cast controls to ISystemMediaTransportControls2 (hr=%lx)",
515 ComPtr
<ISystemMediaTransportControlsTimelineProperties
> properties
;
516 hr
= RoActivateInstance(
518 RuntimeClass_Windows_Media_SystemMediaTransportControlsTimelineProperties
)
522 LOG("Failed to create timeline properties (hr=%lx)", hr
);
525 MOZ_ASSERT(properties
);
527 // Converts a value in seconds to a TimeSpan
528 // The TimeSpan's Duration is a value in 100ns ticks
529 // https://learn.microsoft.com/en-us/windows/win32/api/windows.foundation/ns-windows-foundation-timespan#members
530 auto toTimeSpan
= [this](double seconds
) {
531 constexpr double kMaxMicroseconds
=
532 static_cast<double>(std::numeric_limits
<LONG64
>::max() / 10);
534 double microseconds
= seconds
* 1e6
;
536 if (microseconds
> kMaxMicroseconds
) {
537 LOG("Failed to convert %f microseconds to TimeSpan (overflow)",
542 return TimeSpan
{static_cast<LONG64
>(microseconds
* 10.0)};
546 TimeSpan position
{0};
547 double playbackRate
= 1.0;
550 endTime
= toTimeSpan(aState
->mDuration
);
551 position
= toTimeSpan(aState
->CurrentPlaybackPosition());
552 playbackRate
= aState
->mPlaybackRate
;
555 hr
= properties
->put_StartTime({0});
557 LOG("Failed to set the start time (hr=%lx)", hr
);
561 hr
= properties
->put_MinSeekTime({0});
563 LOG("Failed to set the min seek time (hr=%lx)", hr
);
567 hr
= properties
->put_EndTime(endTime
);
569 LOG("Failed to set the end time (hr=%lx)", hr
);
573 hr
= properties
->put_MaxSeekTime(endTime
);
575 LOG("Failed to set the max seek time (hr=%lx)", hr
);
579 hr
= properties
->put_Position(position
);
581 LOG("Failed to set the playback position (hr=%lx)", hr
);
585 hr
= smtc2
->UpdateTimelineProperties(properties
.Get());
587 LOG("Failed to update timeline properties (hr=%lx)", hr
);
591 hr
= smtc2
->put_PlaybackRate(playbackRate
);
593 LOG("Failed to set the playback rate (hr=%lx)", hr
);
598 void WindowsSMTCProvider::LoadThumbnail(
599 const nsTArray
<mozilla::dom::MediaImage
>& aArtwork
) {
600 MOZ_ASSERT(NS_IsMainThread());
602 // TODO: Sort the images by the preferred size or format.
606 // Abort the loading if
607 // 1) thumbnail is being updated, and one in processing is in the artwork
608 // 2) thumbnail is not being updated, and one in use is in the artwork
609 if (!mProcessingUrl
.IsEmpty()) {
610 LOG("Load thumbnail while image: %s is being processed",
611 NS_ConvertUTF16toUTF8(mProcessingUrl
).get());
612 if (mozilla::dom::IsImageIn(mArtwork
, mProcessingUrl
)) {
613 LOG("No need to load thumbnail. The one being processed is in the "
617 } else if (!mThumbnailUrl
.IsEmpty()) {
618 if (mozilla::dom::IsImageIn(mArtwork
, mThumbnailUrl
)) {
619 LOG("No need to load thumbnail. The one in use is in the artwork");
624 // If there is a pending image store operation, that image must be different
625 // from the new image will be loaded below, so the pending one should be
627 CancelPendingStoreAsyncOperation();
628 // Remove the current thumbnail on the interface
630 // Then load the new thumbnail asynchronously
631 LoadImageAtIndex(mNextImageIndex
++);
634 void WindowsSMTCProvider::LoadImageAtIndex(const size_t aIndex
) {
635 MOZ_ASSERT(NS_IsMainThread());
637 if (aIndex
>= mArtwork
.Length()) {
638 LOG("Stop loading thumbnail. No more available images");
639 mImageFetchRequest
.DisconnectIfExists();
640 mProcessingUrl
.Truncate();
644 const mozilla::dom::MediaImage
& image
= mArtwork
[aIndex
];
646 // TODO: No need to fetch the default image and do image processing since the
647 // the default image is local file and it's trustworthy. For the default
648 // image, we can use `CreateFromFile` to create the IRandomAccessStream. We
649 // should probably cache it since it could be used very often (Bug 1643102)
651 if (!mozilla::dom::IsValidImageUrl(image
.mSrc
)) {
652 LOG("Skip the image with invalid URL. Try next image");
653 mImageFetchRequest
.DisconnectIfExists();
654 LoadImageAtIndex(mNextImageIndex
++);
658 mImageFetchRequest
.DisconnectIfExists();
659 mProcessingUrl
= image
.mSrc
;
661 mImageFetcher
= mozilla::MakeUnique
<mozilla::dom::FetchImageHelper
>(image
);
662 RefPtr
<WindowsSMTCProvider
> self
= this;
663 mImageFetcher
->FetchImage()
665 AbstractThread::MainThread(), __func__
,
666 [this, self
](const nsCOMPtr
<imgIContainer
>& aImage
) {
667 LOG("The image is fetched successfully");
668 mImageFetchRequest
.Complete();
670 // Although IMAGE_JPEG or IMAGE_BMP are valid types as well, but a
671 // png image with transparent background will be converted into a
672 // jpeg/bmp file with a colored background. IMAGE_PNG format seems
673 // to be the best choice for now.
676 // Only used to hold the image data
677 nsCOMPtr
<nsIInputStream
> inputStream
;
678 nsresult rv
= mozilla::dom::GetEncodedImageBuffer(
679 aImage
, nsLiteralCString(IMAGE_PNG
),
680 getter_AddRefs(inputStream
), &size
, &src
);
681 if (NS_FAILED(rv
) || !inputStream
|| size
== 0 || !src
) {
682 LOG("Failed to get the image buffer info. Try next image");
683 LoadImageAtIndex(mNextImageIndex
++);
687 LoadImage(src
, size
);
690 LOG("Failed to fetch image. Try next image");
691 mImageFetchRequest
.Complete();
692 LoadImageAtIndex(mNextImageIndex
++);
694 ->Track(mImageFetchRequest
);
697 void WindowsSMTCProvider::LoadImage(const char* aImageData
,
698 uint32_t aDataSize
) {
699 MOZ_ASSERT(NS_IsMainThread());
701 // 1. Use mImageDataWriter to write the binary data of image into mImageStream
702 // 2. Refer the image by mImageStreamReference and then set it to the SMTC
703 // In case of the race condition between they are being destroyed and the
704 // async operation for image loading, mImageDataWriter, mImageStream, and
705 // mImageStreamReference are member variables
707 HRESULT hr
= ActivateInstance(
709 RuntimeClass_Windows_Storage_Streams_InMemoryRandomAccessStream
)
711 mImageStream
.GetAddressOf());
713 LOG("Failed to make mImageStream refer to an instance of "
714 "InMemoryRandomAccessStream");
718 ComPtr
<IOutputStream
> outputStream
;
719 hr
= mImageStream
.As(&outputStream
);
721 LOG("Failed when query IOutputStream interface from mImageStream");
725 ComPtr
<IDataWriterFactory
> dataWriterFactory
;
726 hr
= GetActivationFactory(
727 HStringReference(RuntimeClass_Windows_Storage_Streams_DataWriter
).Get(),
728 dataWriterFactory
.GetAddressOf());
730 LOG("Failed to get an activation factory for IDataWriterFactory");
734 hr
= dataWriterFactory
->CreateDataWriter(outputStream
.Get(),
735 mImageDataWriter
.GetAddressOf());
737 LOG("Failed to create mImageDataWriter that writes data to mImageStream");
741 hr
= mImageDataWriter
->WriteBytes(
742 aDataSize
, reinterpret_cast<BYTE
*>(const_cast<char*>(aImageData
)));
744 LOG("Failed to write data to mImageStream");
748 hr
= mImageDataWriter
->StoreAsync(&mStoreAsyncOperation
);
750 LOG("Failed to create a DataWriterStoreOperation for mStoreAsyncOperation");
754 // Upon the image is stored in mImageStream, set the image to the SMTC
756 auto onStoreCompleted
= Callback
<
757 IAsyncOperationCompletedHandler
<unsigned int>>(
758 [this, self
= RefPtr
<WindowsSMTCProvider
>(this),
759 aImageUrl
= nsString(mProcessingUrl
)](
760 IAsyncOperation
<unsigned int>* aAsyncOp
, AsyncStatus aStatus
) {
761 MOZ_ASSERT(NS_IsMainThread());
763 if (aStatus
!= AsyncStatus::Completed
) {
764 LOG("Asynchronous operation is not completed");
769 IAsyncInfo
* asyncInfo
= GetIAsyncInfo(aAsyncOp
);
770 asyncInfo
->get_ErrorCode(&hr
);
772 LOG("Failed to get termination status of the asynchronous operation");
776 if (!UpdateThumbnail(aImageUrl
)) {
777 LOG("Failed to update thumbnail");
780 // If an error occurs above:
781 // - If aImageUrl is not mProcessingUrl. It's fine.
782 // - If aImageUrl is mProcessingUrl, then mProcessingUrl won't be reset.
783 // Therefore the thumbnail will remain empty until a new image whose
784 // url is different from mProcessingUrl is loaded.
789 hr
= mStoreAsyncOperation
->put_Completed(onStoreCompleted
.Get());
791 LOG("Failed to set callback on completeing the asynchronous operation");
795 bool WindowsSMTCProvider::SetThumbnail(const nsAString
& aUrl
) {
796 MOZ_ASSERT(mDisplay
);
797 MOZ_ASSERT(mImageStream
);
798 MOZ_ASSERT(!aUrl
.IsEmpty());
800 ComPtr
<IRandomAccessStreamReferenceStatics
> streamRefFactory
;
802 HRESULT hr
= GetActivationFactory(
804 RuntimeClass_Windows_Storage_Streams_RandomAccessStreamReference
)
806 streamRefFactory
.GetAddressOf());
808 MakeScopeExit([this, self
= RefPtr
<WindowsSMTCProvider
>(this)] {
809 LOG("Clean mThumbnailUrl");
810 mThumbnailUrl
.Truncate();
814 LOG("Failed to get an activation factory for "
815 "IRandomAccessStreamReferenceStatics type");
819 hr
= streamRefFactory
->CreateFromStream(mImageStream
.Get(),
820 mImageStreamReference
.GetAddressOf());
822 LOG("Failed to create mImageStreamReference from mImageStream");
826 hr
= mDisplay
->put_Thumbnail(mImageStreamReference
.Get());
828 LOG("Failed to update thumbnail");
832 hr
= mDisplay
->Update();
834 LOG("Failed to refresh display");
838 // No need to clean mThumbnailUrl since thumbnail is set successfully
840 mThumbnailUrl
= aUrl
;
845 void WindowsSMTCProvider::ClearThumbnail() {
846 MOZ_ASSERT(mDisplay
);
847 HRESULT hr
= mDisplay
->put_Thumbnail(nullptr);
848 MOZ_ASSERT(SUCCEEDED(hr
));
849 hr
= mDisplay
->Update();
850 MOZ_ASSERT(SUCCEEDED(hr
));
852 mThumbnailUrl
.Truncate();
855 bool WindowsSMTCProvider::UpdateThumbnail(const nsAString
& aUrl
) {
856 MOZ_ASSERT(NS_IsMainThread());
859 LOG("Abort the thumbnail update: SMTC is closed");
863 if (aUrl
!= mProcessingUrl
) {
864 LOG("Abort the thumbnail update: The image from %s is out of date",
865 NS_ConvertUTF16toUTF8(aUrl
).get());
869 mProcessingUrl
.Truncate();
871 if (!SetThumbnail(aUrl
)) {
872 LOG("Failed to update thumbnail");
876 MOZ_ASSERT(mThumbnailUrl
== aUrl
);
877 LOG("The thumbnail is updated to the image from: %s",
878 NS_ConvertUTF16toUTF8(mThumbnailUrl
).get());
882 void WindowsSMTCProvider::CancelPendingStoreAsyncOperation() const {
883 if (mStoreAsyncOperation
) {
884 IAsyncInfo
* asyncInfo
= GetIAsyncInfo(mStoreAsyncOperation
.Get());
889 #endif // __MINGW32__