FetchImageHelper.cpp (4745B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "FetchImageHelper.h" 6 7 #include "mozilla/Logging.h" 8 #include "mozilla/NullPrincipal.h" 9 #include "mozilla/gfx/2D.h" 10 #include "nsIChannel.h" 11 #include "nsNetUtil.h" 12 13 mozilla::LazyLogModule gFetchImageLog("FetchImageHelper"); 14 15 #undef LOG 16 #define LOG(msg, ...) \ 17 MOZ_LOG(gFetchImageLog, LogLevel::Debug, \ 18 ("FetchImageHelper=%p, " msg, this, ##__VA_ARGS__)) 19 20 using namespace mozilla::gfx; 21 22 namespace mozilla::dom { 23 24 FetchImageHelper::FetchImageHelper(const MediaImage& aImage) 25 : mSrc(aImage.mSrc) {} 26 27 FetchImageHelper::~FetchImageHelper() { AbortFetchingImage(); } 28 29 RefPtr<ImagePromise> FetchImageHelper::FetchImage() { 30 MOZ_ASSERT(NS_IsMainThread()); 31 if (IsFetchingImage()) { 32 return mPromise.Ensure(__func__); 33 } 34 35 LOG("Start fetching image from %s", NS_ConvertUTF16toUTF8(mSrc).get()); 36 nsCOMPtr<nsIURI> uri; 37 if (NS_FAILED(NS_NewURI(getter_AddRefs(uri), mSrc))) { 38 LOG("Failed to create URI"); 39 return ImagePromise::CreateAndReject(false, __func__); 40 } 41 42 MOZ_ASSERT(!mListener); 43 mListener = new ImageFetchListener(); 44 if (NS_FAILED(mListener->FetchDecodedImageFromURI(uri, this))) { 45 LOG("Failed to decode image from async channel"); 46 return ImagePromise::CreateAndReject(false, __func__); 47 } 48 return mPromise.Ensure(__func__); 49 } 50 51 void FetchImageHelper::AbortFetchingImage() { 52 MOZ_ASSERT(NS_IsMainThread()); 53 LOG("AbortFetchingImage"); 54 mPromise.RejectIfExists(false, __func__); 55 ClearListenerIfNeeded(); 56 } 57 58 void FetchImageHelper::ClearListenerIfNeeded() { 59 if (mListener) { 60 mListener->Clear(); 61 mListener = nullptr; 62 } 63 } 64 65 bool FetchImageHelper::IsFetchingImage() const { 66 MOZ_ASSERT(NS_IsMainThread()); 67 return !mPromise.IsEmpty() && mListener; 68 } 69 70 void FetchImageHelper::HandleFetchSuccess(imgIContainer* aImage) { 71 MOZ_ASSERT(aImage); 72 MOZ_ASSERT(NS_IsMainThread()); 73 MOZ_ASSERT(IsFetchingImage()); 74 LOG("Finished fetching image"); 75 mPromise.Resolve(aImage, __func__); 76 ClearListenerIfNeeded(); 77 } 78 79 void FetchImageHelper::HandleFetchFail() { 80 MOZ_ASSERT(NS_IsMainThread()); 81 MOZ_ASSERT(IsFetchingImage()); 82 LOG("Reject the promise because of fetching failed"); 83 mPromise.RejectIfExists(false, __func__); 84 ClearListenerIfNeeded(); 85 } 86 87 /** 88 * Implementation for FetchImageHelper::ImageFetchListener 89 */ 90 NS_IMPL_ISUPPORTS(FetchImageHelper::ImageFetchListener, imgIContainerCallback) 91 92 FetchImageHelper::ImageFetchListener::~ImageFetchListener() { 93 MOZ_ASSERT(NS_IsMainThread()); 94 MOZ_ASSERT(!mHelper, "Cancel() should be called before desturction!"); 95 } 96 97 nsresult FetchImageHelper::ImageFetchListener::FetchDecodedImageFromURI( 98 nsIURI* aURI, FetchImageHelper* aHelper) { 99 MOZ_ASSERT(!mHelper && !mChannel, 100 "Should call Clear() berfore running another fetching process!"); 101 RefPtr<nsIPrincipal> nullPrincipal = 102 NullPrincipal::CreateWithoutOriginAttributes(); 103 nsCOMPtr<nsIChannel> channel; 104 nsresult rv = 105 NS_NewChannel(getter_AddRefs(channel), aURI, nullPrincipal, 106 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, 107 nsIContentPolicy::TYPE_INTERNAL_IMAGE, nullptr, nullptr, 108 nullptr, nullptr, nsIRequest::LOAD_ANONYMOUS); 109 if (NS_FAILED(rv)) { 110 return rv; 111 } 112 113 nsCOMPtr<imgITools> imgTools = do_GetService("@mozilla.org/image/tools;1"); 114 if (!imgTools) { 115 return NS_ERROR_FAILURE; 116 } 117 118 rv = imgTools->DecodeImageFromChannelAsync(aURI, channel, this, nullptr); 119 if (NS_FAILED(rv)) { 120 return NS_ERROR_FAILURE; 121 } 122 MOZ_ASSERT(aHelper); 123 mHelper = aHelper; 124 mChannel = channel; 125 return NS_OK; 126 } 127 128 void FetchImageHelper::ImageFetchListener::Clear() { 129 MOZ_ASSERT(NS_IsMainThread()); 130 if (mChannel) { 131 mChannel->CancelWithReason( 132 NS_BINDING_ABORTED, "FetchImageHelper::ImageFetchListener::Clear"_ns); 133 mChannel = nullptr; 134 } 135 mHelper = nullptr; 136 } 137 138 bool FetchImageHelper::ImageFetchListener::IsFetchingImage() const { 139 MOZ_ASSERT(NS_IsMainThread()); 140 return mHelper ? mHelper->IsFetchingImage() : false; 141 } 142 143 NS_IMETHODIMP FetchImageHelper::ImageFetchListener::OnImageReady( 144 imgIContainer* aImage, nsresult aStatus) { 145 MOZ_ASSERT(NS_IsMainThread()); 146 if (!IsFetchingImage()) { 147 return NS_OK; 148 } 149 // We have received image, so we don't need the channel anymore. 150 mChannel = nullptr; 151 152 MOZ_ASSERT(mHelper); 153 if (NS_FAILED(aStatus) || !aImage) { 154 mHelper->HandleFetchFail(); 155 Clear(); 156 return aStatus; 157 } 158 159 mHelper->HandleFetchSuccess(aImage); 160 161 return NS_OK; 162 } 163 164 } // namespace mozilla::dom