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>
15 # include <winsdkver.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"
43 # ifndef RuntimeClass_Windows_Storage_Streams_RandomAccessStreamReference
44 # define RuntimeClass_Windows_Storage_Streams_RandomAccessStreamReference \
45 L"Windows.Storage.Streams.RandomAccessStreamReference"
48 # ifndef ISystemMediaTransportControlsInterop
49 EXTERN_C
const IID IID_ISystemMediaTransportControlsInterop
;
50 MIDL_INTERFACE("ddb0472d-c911-4a1f-86d9-dc3d71a95f5a")
51 ISystemMediaTransportControlsInterop
: public IInspectable
{
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
;
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
) {
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
);
86 return Nothing(); // Not supported Button
90 static IAsyncInfo
* GetIAsyncInfo(IAsyncOperation
<unsigned int>* 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
));
98 MOZ_ASSERT(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
109 wnd
.lpszClassName
= L
"Firefox-MediaKeys";
110 wnd
.hInstance
= nullptr;
111 wnd
.lpfnWndProc
= DefWindowProc
;
112 GetLastError(); // Clear the error
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);
120 MOZ_ASSERT(!GetLastError());
123 WindowsSMTCProvider::~WindowsSMTCProvider() {
124 // Dispose the window
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");
146 if (!InitDisplayAndControls()) {
147 LOG("Failed to initialize the SMTC and its display");
151 if (!UpdateButtons()) {
152 LOG("Failed to initialize the buttons");
156 if (!RegisterEvents()) {
157 LOG("Failed to register SMTC key-event listener");
161 if (!EnableControl(true)) {
162 LOG("Failed to enable SMTC control");
167 SetPlaybackState(mozilla::dom::MediaSessionPlaybackState::None
);
171 void WindowsSMTCProvider::Close() {
172 MediaControlKeySource::Close();
173 // Prevent calling Set methods when init failed
175 SetPlaybackState(mozilla::dom::MediaSessionPlaybackState::None
);
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
);
195 // Note: we can't return the status of put_PlaybackStatus, but we can at least
198 case mozilla::dom::MediaSessionPlaybackState::Paused
:
199 hr
= mControls
->put_PlaybackStatus(
200 ABI::Windows::Media::MediaPlaybackStatus_Paused
);
202 case mozilla::dom::MediaSessionPlaybackState::Playing
:
203 hr
= mControls
->put_PlaybackStatus(
204 ABI::Windows::Media::MediaPlaybackStatus_Playing
);
206 case mozilla::dom::MediaSessionPlaybackState::None
:
207 hr
= mControls
->put_PlaybackStatus(
208 ABI::Windows::Media::MediaPlaybackStatus_Stopped
);
210 // MediaPlaybackStatus still supports Closed and Changing, which we don't
213 MOZ_ASSERT_UNREACHABLE(
214 "Enum Inconsitency between PlaybackState and WindowsSMTCProvider");
218 MOZ_ASSERT(SUCCEEDED(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();
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");
256 LOG("Update supported keys");
257 mSupportedKeys
= supportedKeys
;
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
)
277 SystemMediaTransportControlsButton btn
;
279 if (FAILED(pArgs
->get_Button(&btn
))) {
280 LOG("SystemMediaTransportControls: ButtonPressedEvent - Could "
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());
292 if (FAILED(mControls
->add_ButtonPressed(callbackbtnPressed
.Get(),
293 &mButtonPressedToken
))) {
294 LOG("SystemMediaTransportControls: Failed at "
295 "registerEvents().add_ButtonPressed()");
302 void WindowsSMTCProvider::OnButtonPressed(
303 mozilla::dom::MediaControlKey aKey
) const {
304 if (!IsKeySupported(aKey
)) {
305 LOG("key: %s is not supported", ToMediaControlKeyStr(aKey
));
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
};
327 for (const mozilla::dom::MediaControlKey
& key
: kKeys
) {
328 if (!EnableKey(key
, IsKeySupported(key
))) {
330 LOG("Failed to set %s=%s", ToMediaControlKeyStr(key
),
331 IsKeySupported(key
) ? "true" : "false");
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
);
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
));
358 LOG("No button for %s", ToMediaControlKeyStr(aKey
));
363 bool WindowsSMTCProvider::InitDisplayAndControls() {
364 // As Open() might be called multiple times, "cache" the results of the COM
366 if (mControls
&& mDisplay
) {
369 ComPtr
<ISystemMediaTransportControlsInterop
> interop
;
370 HRESULT hr
= GetActivationFactory(
371 HStringReference(RuntimeClass_Windows_Media_SystemMediaTransportControls
)
373 interop
.GetAddressOf());
375 LOG("SystemMediaTransportControls: Failed at instantiating the "
381 if (!mControls
&& FAILED(interop
->GetForWindow(
382 mWindow
, IID_PPV_ARGS(mControls
.GetAddressOf())))) {
383 LOG("SystemMediaTransportControls: Failed at GetForWindow()");
386 MOZ_ASSERT(mControls
);
389 FAILED(mControls
->get_DisplayUpdater(mDisplay
.GetAddressOf()))) {
390 LOG("SystemMediaTransportControls: Failed at get_DisplayUpdater()");
393 MOZ_ASSERT(mDisplay
);
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
));
405 hr
= mDisplay
->get_MusicProperties(musicProps
.GetAddressOf());
407 LOG("Failed to get music properties");
411 hr
= musicProps
->put_Artist(HStringReference(aArtist
.get()).Get());
413 LOG("Failed to set the music's artist");
417 hr
= musicProps
->put_Title(HStringReference(aTitle
.get()).Get());
419 LOG("Failed to set the music's title");
423 hr
= mDisplay
->Update();
425 LOG("Failed to refresh the display");
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.
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 "
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");
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
461 CancelPendingStoreAsyncOperation();
462 // Remove the current thumbnail on the interface
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();
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
++);
492 mImageFetchRequest
.DisconnectIfExists();
493 mProcessingUrl
= image
.mSrc
;
495 mImageFetcher
= mozilla::MakeUnique
<mozilla::dom::FetchImageHelper
>(image
);
496 RefPtr
<WindowsSMTCProvider
> self
= this;
497 mImageFetcher
->FetchImage()
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.
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
++);
521 LoadImage(src
, size
);
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(
543 RuntimeClass_Windows_Storage_Streams_InMemoryRandomAccessStream
)
545 mImageStream
.GetAddressOf());
547 LOG("Failed to make mImageStream refer to an instance of "
548 "InMemoryRandomAccessStream");
552 ComPtr
<IOutputStream
> outputStream
;
553 hr
= mImageStream
.As(&outputStream
);
555 LOG("Failed when query IOutputStream interface from mImageStream");
559 ComPtr
<IDataWriterFactory
> dataWriterFactory
;
560 hr
= GetActivationFactory(
561 HStringReference(RuntimeClass_Windows_Storage_Streams_DataWriter
).Get(),
562 dataWriterFactory
.GetAddressOf());
564 LOG("Failed to get an activation factory for IDataWriterFactory");
568 hr
= dataWriterFactory
->CreateDataWriter(outputStream
.Get(),
569 mImageDataWriter
.GetAddressOf());
571 LOG("Failed to create mImageDataWriter that writes data to mImageStream");
575 hr
= mImageDataWriter
->WriteBytes(
576 aDataSize
, reinterpret_cast<BYTE
*>(const_cast<char*>(aImageData
)));
578 LOG("Failed to write data to mImageStream");
582 hr
= mImageDataWriter
->StoreAsync(&mStoreAsyncOperation
);
584 LOG("Failed to create a DataWriterStoreOperation for mStoreAsyncOperation");
588 // Upon the image is stored in mImageStream, set the image to the SMTC
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");
603 IAsyncInfo
* asyncInfo
= GetIAsyncInfo(aAsyncOp
);
604 asyncInfo
->get_ErrorCode(&hr
);
606 LOG("Failed to get termination status of the asynchronous operation");
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.
623 hr
= mStoreAsyncOperation
->put_Completed(onStoreCompleted
.Get());
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(
638 RuntimeClass_Windows_Storage_Streams_RandomAccessStreamReference
)
640 streamRefFactory
.GetAddressOf());
642 MakeScopeExit([this, self
= RefPtr
<WindowsSMTCProvider
>(this)] {
643 LOG("Clean mThumbnailUrl");
644 mThumbnailUrl
.Truncate();
648 LOG("Failed to get an activation factory for "
649 "IRandomAccessStreamReferenceStatics type");
653 hr
= streamRefFactory
->CreateFromStream(mImageStream
.Get(),
654 mImageStreamReference
.GetAddressOf());
656 LOG("Failed to create mImageStreamReference from mImageStream");
660 hr
= mDisplay
->put_Thumbnail(mImageStreamReference
.Get());
662 LOG("Failed to update thumbnail");
666 hr
= mDisplay
->Update();
668 LOG("Failed to refresh display");
672 // No need to clean mThumbnailUrl since thumbnail is set successfully
674 mThumbnailUrl
= aUrl
;
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
));
686 mThumbnailUrl
.Truncate();
689 bool WindowsSMTCProvider::UpdateThumbnail(const nsAString
& aUrl
) {
690 MOZ_ASSERT(NS_IsMainThread());
693 LOG("Abort the thumbnail update: SMTC is closed");
697 if (aUrl
!= mProcessingUrl
) {
698 LOG("Abort the thumbnail update: The image from %s is out of date",
699 NS_ConvertUTF16toUTF8(aUrl
).get());
703 mProcessingUrl
.Truncate();
705 if (!SetThumbnail(aUrl
)) {
706 LOG("Failed to update thumbnail");
710 MOZ_ASSERT(mThumbnailUrl
== aUrl
);
711 LOG("The thumbnail is updated to the image from: %s",
712 NS_ConvertUTF16toUTF8(mThumbnailUrl
).get());
716 void WindowsSMTCProvider::CancelPendingStoreAsyncOperation() const {
717 if (mStoreAsyncOperation
) {
718 IAsyncInfo
* asyncInfo
= GetIAsyncInfo(mStoreAsyncOperation
.Get());
723 #endif // __MINGW32__