FetchDecodedImage.cpp (4421B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "FetchDecodedImage.h" 8 9 #include "imgINotificationObserver.h" 10 #include "imgITools.h" 11 #include "nsIChannel.h" 12 #include "nsNetUtil.h" 13 #include "nsServiceManagerUtils.h" 14 15 namespace mozilla::image { 16 17 namespace { 18 19 class FetchDecodedImageHelper; 20 21 MOZ_RUNINIT 22 HashSet<RefPtr<FetchDecodedImageHelper>, 23 PointerHasher<FetchDecodedImageHelper*>> 24 gDecodeRequests; 25 26 class FetchDecodedImageHelper : public imgIContainerCallback, 27 public imgINotificationObserver { 28 public: 29 NS_DECL_ISUPPORTS 30 31 explicit FetchDecodedImageHelper( 32 gfx::IntSize aSize, RefPtr<FetchDecodedImagePromise::Private> aPromise) 33 : mSize(aSize), mPromise(aPromise) { 34 // Let's make sure we are alive until the request completes 35 MOZ_ALWAYS_TRUE(gDecodeRequests.putNew(this)); 36 } 37 38 NS_IMETHOD 39 OnImageReady(imgIContainer* aImage, nsresult aStatus) override { 40 if (NS_FAILED(aStatus)) { 41 OnError(aStatus); 42 return NS_OK; 43 } 44 45 mImage = aImage; 46 RequestDecode(); 47 return NS_OK; 48 } 49 50 void Notify(imgIRequest* aRequest, int32_t aType, 51 const nsIntRect* aData) override { 52 if (!mImage) { 53 return; 54 } 55 56 if (aType == imgINotificationObserver::LOAD_COMPLETE || 57 aType == imgINotificationObserver::FRAME_UPDATE || 58 aType == imgINotificationObserver::FRAME_COMPLETE) { 59 RequestDecode(); 60 } 61 62 if (aType == imgINotificationObserver::DECODE_COMPLETE) { 63 OnDecodeComplete(); 64 } 65 } 66 67 void OnError(nsresult aStatus) { 68 gDecodeRequests.remove(this); 69 mImage = nullptr; 70 mPromise->Reject(aStatus, __func__); 71 mPromise = nullptr; 72 } 73 74 private: 75 virtual ~FetchDecodedImageHelper() {} 76 77 void RequestDecode() { 78 if (mSize.Width() && mSize.Height()) { 79 if (NS_FAILED(mImage->RequestDecodeForSize( 80 mSize, imgIContainer::FLAG_ASYNC_NOTIFY, 81 imgIContainer::FRAME_FIRST))) { 82 OnError(NS_ERROR_DOM_IMAGE_BROKEN); 83 } 84 85 return; 86 } 87 88 switch (mImage->RequestDecodeWithResult(imgIContainer::FLAG_ASYNC_NOTIFY, 89 imgIContainer::FRAME_FIRST)) { 90 case imgIContainer::DecodeResult::DECODE_REQUEST_FAILED: 91 OnError(NS_ERROR_DOM_IMAGE_BROKEN); 92 break; 93 case imgIContainer::DecodeResult::DECODE_SURFACE_AVAILABLE: 94 OnDecodeComplete(); 95 break; 96 case imgIContainer::DecodeResult::DECODE_REQUESTED: 97 break; 98 } 99 } 100 101 void OnDecodeComplete() { 102 gDecodeRequests.remove(this); 103 mPromise->Resolve(mImage.forget(), __func__); 104 mPromise = nullptr; 105 } 106 107 gfx::IntSize mSize; 108 RefPtr<FetchDecodedImagePromise::Private> mPromise; 109 nsCOMPtr<imgIContainer> mImage{}; 110 }; 111 112 NS_IMPL_ISUPPORTS(FetchDecodedImageHelper, imgIContainerCallback, 113 imgINotificationObserver) 114 115 } // namespace 116 117 RefPtr<FetchDecodedImagePromise> FetchDecodedImage( 118 nsIURI* aURI, gfx::IntSize aSize, nsIPrincipal* aLoadingPrincipal, 119 nsSecurityFlags aSecurityFlags, nsContentPolicyType aContentPolicyType) { 120 nsCOMPtr<nsIChannel> channel; 121 nsresult rv = NS_NewChannel(getter_AddRefs(channel), aURI, aLoadingPrincipal, 122 aSecurityFlags, aContentPolicyType); 123 if (NS_FAILED(rv)) { 124 return FetchDecodedImagePromise::CreateAndReject(rv, __func__); 125 } 126 127 return FetchDecodedImage(aURI, channel, aSize); 128 } 129 130 RefPtr<FetchDecodedImagePromise> FetchDecodedImage(nsIURI* aURI, 131 nsIChannel* aChannel, 132 gfx::IntSize aSize) { 133 nsresult rv; 134 nsCOMPtr<imgITools> imgTools = 135 do_GetService("@mozilla.org/image/tools;1", &rv); 136 if (NS_FAILED(rv)) { 137 return FetchDecodedImagePromise::CreateAndReject(rv, __func__); 138 } 139 140 auto promise = MakeRefPtr<FetchDecodedImagePromise::Private>(__func__); 141 142 RefPtr<FetchDecodedImageHelper> helper = 143 new FetchDecodedImageHelper(aSize, promise); 144 145 rv = imgTools->DecodeImageFromChannelAsync(aURI, aChannel, helper, helper); 146 if (NS_FAILED(rv)) { 147 helper->OnError(rv); 148 } 149 150 return promise; 151 } 152 153 } // namespace mozilla::image