ImageDecoderReadRequest.cpp (9084B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */ 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 "ImageDecoderReadRequest.h" 8 9 #include "MediaResult.h" 10 #include "mozilla/CycleCollectedJSContext.h" 11 #include "mozilla/Logging.h" 12 #include "mozilla/dom/ImageDecoder.h" 13 #include "mozilla/dom/ReadableStream.h" 14 #include "mozilla/dom/ReadableStreamDefaultReader.h" 15 #include "mozilla/image/SourceBuffer.h" 16 17 extern mozilla::LazyLogModule gWebCodecsLog; 18 19 namespace mozilla::dom { 20 21 NS_IMPL_CYCLE_COLLECTION_INHERITED(ImageDecoderReadRequest, ReadRequest, 22 mDecoder, mReader) 23 NS_IMPL_ADDREF_INHERITED(ImageDecoderReadRequest, ReadRequest) 24 NS_IMPL_RELEASE_INHERITED(ImageDecoderReadRequest, ReadRequest) 25 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ImageDecoderReadRequest) 26 NS_INTERFACE_MAP_END_INHERITING(ReadRequest) 27 28 ImageDecoderReadRequest::ImageDecoderReadRequest( 29 image::SourceBuffer* aSourceBuffer) 30 : mSourceBuffer(std::move(aSourceBuffer)) { 31 MOZ_LOG(gWebCodecsLog, LogLevel::Debug, 32 ("ImageDecoderReadRequest %p ImageDecoderReadRequest", this)); 33 } 34 35 ImageDecoderReadRequest::~ImageDecoderReadRequest() { 36 MOZ_LOG(gWebCodecsLog, LogLevel::Debug, 37 ("ImageDecoderReadRequest %p ~ImageDecoderReadRequest", this)); 38 } 39 40 bool ImageDecoderReadRequest::Initialize(const GlobalObject& aGlobal, 41 ImageDecoder* aDecoder, 42 ReadableStream& aStream) { 43 IgnoredErrorResult rv; 44 mReader = aStream.GetReader(rv); 45 if (NS_WARN_IF(rv.Failed())) { 46 MOZ_LOG( 47 gWebCodecsLog, LogLevel::Error, 48 ("ImageDecoderReadRequest %p Initialize -- cannot get stream reader", 49 this)); 50 mSourceBuffer->Complete(NS_ERROR_FAILURE); 51 Destroy(/* aCancel */ false); 52 return false; 53 } 54 55 mDecoder = aDecoder; 56 QueueRead(); 57 return true; 58 } 59 60 void ImageDecoderReadRequest::Destroy(bool aCancel) { 61 MOZ_LOG(gWebCodecsLog, LogLevel::Debug, 62 ("ImageDecoderReadRequest %p Destroy", this)); 63 64 if (aCancel) { 65 // Ensure we stop reading from the ReadableStream. 66 Cancel(); 67 } 68 69 if (mSourceBuffer) { 70 if (!mSourceBuffer->IsComplete()) { 71 mSourceBuffer->Complete(NS_ERROR_ABORT); 72 } 73 mSourceBuffer = nullptr; 74 } 75 76 mDecoder = nullptr; 77 mReader = nullptr; 78 } 79 80 void ImageDecoderReadRequest::QueueRead() { 81 class ReadRunnable final : public CancelableRunnable { 82 public: 83 explicit ReadRunnable(ImageDecoderReadRequest* aOwner) 84 : CancelableRunnable( 85 "mozilla::dom::ImageDecoderReadRequest::QueueRead"), 86 mOwner(aOwner) {} 87 88 NS_IMETHODIMP Run() override { 89 mOwner->Read(); 90 mOwner = nullptr; 91 return NS_OK; 92 } 93 94 nsresult Cancel() override { 95 mOwner->Complete( 96 MediaResult(NS_ERROR_DOM_MEDIA_ABORT_ERR, "Read cancelled"_ns)); 97 mOwner = nullptr; 98 return NS_OK; 99 } 100 101 private: 102 virtual ~ReadRunnable() { 103 if (mOwner) { 104 Cancel(); 105 } 106 } 107 108 RefPtr<ImageDecoderReadRequest> mOwner; 109 }; 110 111 if (!mReader) { 112 MOZ_LOG(gWebCodecsLog, LogLevel::Debug, 113 ("ImageDecoderReadRequest %p QueueRead -- destroyed", this)); 114 return; 115 } 116 117 MOZ_LOG(gWebCodecsLog, LogLevel::Debug, 118 ("ImageDecoderReadRequest %p QueueRead -- queue", this)); 119 auto task = MakeRefPtr<ReadRunnable>(this); 120 NS_DispatchToCurrentThread(task.forget()); 121 } 122 123 void ImageDecoderReadRequest::Read() { 124 if (!mReader || !mDecoder) { 125 MOZ_LOG(gWebCodecsLog, LogLevel::Debug, 126 ("ImageDecoderReadRequest %p Read -- destroyed", this)); 127 return; 128 } 129 130 AutoJSAPI jsapi; 131 if (!jsapi.Init(mDecoder->GetParentObject())) { 132 MOZ_LOG(gWebCodecsLog, LogLevel::Debug, 133 ("ImageDecoderReadRequest %p Read -- no jsapi", this)); 134 Complete(MediaResult(NS_ERROR_DOM_FILE_NOT_READABLE_ERR, 135 "Reader cannot init jsapi"_ns)); 136 return; 137 } 138 139 RefPtr<ImageDecoderReadRequest> self(this); 140 RefPtr<ReadableStreamDefaultReader> reader(mReader); 141 142 MOZ_LOG(gWebCodecsLog, LogLevel::Debug, 143 ("ImageDecoderReadRequest %p Read -- begin read chunk", this)); 144 145 IgnoredErrorResult err; 146 reader->ReadChunk(jsapi.cx(), *self, err); 147 if (NS_WARN_IF(err.Failed())) { 148 MOZ_LOG(gWebCodecsLog, LogLevel::Error, 149 ("ImageDecoderReadRequest %p Read -- read chunk failed", this)); 150 Complete(MediaResult(NS_ERROR_DOM_FILE_NOT_READABLE_ERR, 151 "Reader cannot read chunk from stream"_ns)); 152 } 153 154 MOZ_LOG(gWebCodecsLog, LogLevel::Debug, 155 ("ImageDecoderReadRequest %p Read -- end read chunk", this)); 156 } 157 158 void ImageDecoderReadRequest::Cancel() { 159 RefPtr<ReadableStreamDefaultReader> reader = std::move(mReader); 160 if (!reader || !mDecoder) { 161 return; 162 } 163 164 RefPtr<ImageDecoderReadRequest> self(this); 165 166 AutoJSAPI jsapi; 167 if (!jsapi.Init(mDecoder->GetParentObject())) { 168 MOZ_LOG(gWebCodecsLog, LogLevel::Debug, 169 ("ImageDecoderReadRequest %p Cancel -- no jsapi", this)); 170 return; 171 } 172 173 ErrorResult rv; 174 rv.ThrowAbortError("ImageDecoderReadRequest destroyed"); 175 176 JS::Rooted<JS::Value> errorValue(jsapi.cx()); 177 if (ToJSValue(jsapi.cx(), std::move(rv), &errorValue)) { 178 IgnoredErrorResult ignoredRv; 179 if (RefPtr<Promise> p = reader->Cancel(jsapi.cx(), errorValue, ignoredRv)) { 180 MOZ_ALWAYS_TRUE(p->SetAnyPromiseIsHandled()); 181 } 182 } 183 184 jsapi.ClearException(); 185 } 186 187 void ImageDecoderReadRequest::Complete(const MediaResult& aResult) { 188 if (!mReader) { 189 return; 190 } 191 192 MOZ_LOG(gWebCodecsLog, LogLevel::Debug, 193 ("ImageDecoderReadRequest %p Read -- complete, success %d", this, 194 NS_SUCCEEDED(aResult.Code()))); 195 196 if (mSourceBuffer && !mSourceBuffer->IsComplete()) { 197 mSourceBuffer->Complete(aResult.Code()); 198 } 199 200 if (mDecoder) { 201 mDecoder->OnSourceBufferComplete(aResult); 202 } 203 204 Destroy(/* aCancel */ false); 205 } 206 207 void ImageDecoderReadRequest::ChunkSteps(JSContext* aCx, 208 JS::Handle<JS::Value> aChunk, 209 ErrorResult& aRv) { 210 // 10.2.5. Fetch Stream Data Loop (with reader) - chunk steps 211 212 // 1. If [[closed]] is true, abort these steps. 213 if (!mSourceBuffer) { 214 return; 215 } 216 217 // 2. If chunk is not a Uint8Array object, queue a task to run the Close 218 // ImageDecoder algorithm with a DataError DOMException and abort these steps. 219 RootedSpiderMonkeyInterface<Uint8Array> chunk(aCx); 220 if (!aChunk.isObject() || !chunk.Init(&aChunk.toObject())) { 221 MOZ_LOG(gWebCodecsLog, LogLevel::Error, 222 ("ImageDecoderReadRequest %p ChunkSteps -- bad chunk", this)); 223 Complete(MediaResult(NS_ERROR_DOM_DATA_ERR, 224 "Reader cannot read chunk from stream"_ns)); 225 return; 226 } 227 228 chunk.ProcessFixedData([&](const Span<uint8_t>& aData) { 229 MOZ_LOG(gWebCodecsLog, LogLevel::Debug, 230 ("ImageDecoderReadRequest %p ChunkSteps -- write %zu bytes", this, 231 aData.Length())); 232 233 // 3. Let bytes be the byte sequence represented by the Uint8Array object. 234 // 4. Append bytes to the [[encoded data]] internal slot. 235 nsresult rv = mSourceBuffer->Append( 236 reinterpret_cast<const char*>(aData.Elements()), aData.Length()); 237 if (NS_WARN_IF(NS_FAILED(rv))) { 238 MOZ_LOG( 239 gWebCodecsLog, LogLevel::Debug, 240 ("ImageDecoderReadRequest %p ChunkSteps -- failed to append", this)); 241 Complete(MediaResult(NS_ERROR_DOM_UNKNOWN_ERR, 242 "Reader cannot allocate storage for chunk"_ns)); 243 } 244 245 // 5. If [[tracks established]] is false, run the Establish Tracks 246 // algorithm. 247 // 6. Otherwise, run the Update Tracks algorithm. 248 // 249 // Note that these steps will be triggered by the decoder promise callbacks. 250 }); 251 252 // 7. Run the Fetch Stream Data Loop algorithm with reader. 253 QueueRead(); 254 } 255 256 void ImageDecoderReadRequest::CloseSteps(JSContext* aCx, ErrorResult& aRv) { 257 MOZ_LOG(gWebCodecsLog, LogLevel::Debug, 258 ("ImageDecoderReadRequest %p CloseSteps", this)); 259 260 // 10.2.5. Fetch Stream Data Loop (with reader) - close steps 261 // 1. Assign true to [[complete]] 262 // 2. Resolve [[completed promise]]. 263 Complete(MediaResult(NS_OK)); 264 } 265 266 void ImageDecoderReadRequest::ErrorSteps(JSContext* aCx, 267 JS::Handle<JS::Value> aError, 268 ErrorResult& aRv) { 269 MOZ_LOG(gWebCodecsLog, LogLevel::Debug, 270 ("ImageDecoderReadRequest %p ErrorSteps", this)); 271 272 // 10.2.5. Fetch Stream Data Loop (with reader) - error steps 273 // 1. Queue a task to run the Close ImageDecoder algorithm with a 274 // NotReadableError DOMException 275 Complete(MediaResult(NS_ERROR_DOM_FILE_NOT_READABLE_ERR, 276 "Reader failed while waiting for chunk from stream"_ns)); 277 } 278 279 } // namespace mozilla::dom