ImageDecoder.cpp (39384B)
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 "mozilla/dom/ImageDecoder.h" 8 9 #include <algorithm> 10 #include <cstdint> 11 12 #include "ImageContainer.h" 13 #include "ImageDecoderReadRequest.h" 14 #include "MediaResult.h" 15 #include "mozilla/Logging.h" 16 #include "mozilla/StaticPrefs_dom.h" 17 #include "mozilla/dom/ImageTrack.h" 18 #include "mozilla/dom/ImageTrackList.h" 19 #include "mozilla/dom/Promise.h" 20 #include "mozilla/dom/ReadableStream.h" 21 #include "mozilla/dom/VideoFrame.h" 22 #include "mozilla/dom/VideoFrameBinding.h" 23 #include "mozilla/dom/WebCodecsUtils.h" 24 #include "mozilla/image/ImageUtils.h" 25 #include "mozilla/image/SourceBuffer.h" 26 #include "nsComponentManagerUtils.h" 27 #include "nsTHashSet.h" 28 29 extern mozilla::LazyLogModule gWebCodecsLog; 30 31 namespace mozilla::dom { 32 33 class ImageDecoder::ControlMessage { 34 public: 35 ControlMessage() = default; 36 virtual ~ControlMessage() = default; 37 38 virtual ConfigureMessage* AsConfigureMessage() { return nullptr; } 39 virtual DecodeMetadataMessage* AsDecodeMetadataMessage() { return nullptr; } 40 virtual DecodeFrameMessage* AsDecodeFrameMessage() { return nullptr; } 41 virtual SelectTrackMessage* AsSelectTrackMessage() { return nullptr; } 42 }; 43 44 class ImageDecoder::ConfigureMessage final 45 : public ImageDecoder::ControlMessage { 46 public: 47 explicit ConfigureMessage(const Maybe<gfx::IntSize>& aOutputSize, 48 ColorSpaceConversion aColorSpaceConversion) 49 : mOutputSize(aOutputSize), 50 mColorSpaceConversion(aColorSpaceConversion) {} 51 52 ConfigureMessage* AsConfigureMessage() override { return this; } 53 54 const Maybe<gfx::IntSize> mOutputSize; 55 const ColorSpaceConversion mColorSpaceConversion; 56 }; 57 58 class ImageDecoder::DecodeMetadataMessage final 59 : public ImageDecoder::ControlMessage { 60 public: 61 DecodeMetadataMessage* AsDecodeMetadataMessage() override { return this; } 62 }; 63 64 class ImageDecoder::DecodeFrameMessage final 65 : public ImageDecoder::ControlMessage { 66 public: 67 DecodeFrameMessage* AsDecodeFrameMessage() override { return this; } 68 }; 69 70 class ImageDecoder::SelectTrackMessage final 71 : public ImageDecoder::ControlMessage { 72 public: 73 explicit SelectTrackMessage(uint32_t aSelectedTrack) 74 : mSelectedTrack(aSelectedTrack) {} 75 76 SelectTrackMessage* AsSelectTrackMessage() override { return this; } 77 78 const uint32_t mSelectedTrack; 79 }; 80 81 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(ImageDecoder) 82 83 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ImageDecoder) 84 tmp->Destroy(); 85 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent) 86 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTracks) 87 NS_IMPL_CYCLE_COLLECTION_UNLINK(mReadRequest) 88 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCompletePromise) 89 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutstandingDecodes) 90 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER 91 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 92 93 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ImageDecoder) 94 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent) 95 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTracks) 96 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReadRequest) 97 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCompletePromise) 98 for (uint32_t i = 0; i < tmp->mOutstandingDecodes.Length(); ++i) { 99 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOutstandingDecodes[i].mPromise); 100 } 101 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 102 103 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ImageDecoder) 104 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 105 NS_INTERFACE_MAP_ENTRY(nsISupports) 106 NS_INTERFACE_MAP_END 107 108 NS_IMPL_CYCLE_COLLECTING_ADDREF(ImageDecoder) 109 NS_IMPL_CYCLE_COLLECTING_RELEASE(ImageDecoder) 110 111 ImageDecoder::ImageDecoder(nsCOMPtr<nsIGlobalObject>&& aParent, 112 const nsAString& aType) 113 : mParent(std::move(aParent)), 114 mType(aType), 115 mFramesTimestamp(image::FrameTimeout::Zero()) { 116 MOZ_LOG(gWebCodecsLog, LogLevel::Debug, 117 ("ImageDecoder %p ImageDecoder", this)); 118 } 119 120 ImageDecoder::~ImageDecoder() { 121 MOZ_LOG(gWebCodecsLog, LogLevel::Debug, 122 ("ImageDecoder %p ~ImageDecoder", this)); 123 Destroy(); 124 } 125 126 JSObject* ImageDecoder::WrapObject(JSContext* aCx, 127 JS::Handle<JSObject*> aGivenProto) { 128 AssertIsOnOwningThread(); 129 return ImageDecoder_Binding::Wrap(aCx, this, aGivenProto); 130 } 131 132 void ImageDecoder::Destroy() { 133 MOZ_LOG(gWebCodecsLog, LogLevel::Debug, ("ImageDecoder %p Destroy", this)); 134 MOZ_ASSERT(mOutstandingDecodes.IsEmpty()); 135 136 if (mReadRequest) { 137 mReadRequest->Destroy(/* aCancel */ false); 138 mReadRequest = nullptr; 139 } 140 141 if (mDecoder) { 142 mDecoder->Destroy(); 143 } 144 145 if (mTracks) { 146 mTracks->Destroy(); 147 } 148 149 if (mShutdownWatcher) { 150 mShutdownWatcher->Destroy(); 151 mShutdownWatcher = nullptr; 152 } 153 154 mSourceBuffer = nullptr; 155 mDecoder = nullptr; 156 mParent = nullptr; 157 } 158 159 void ImageDecoder::QueueConfigureMessage( 160 const Maybe<gfx::IntSize>& aOutputSize, 161 ColorSpaceConversion aColorSpaceConversion) { 162 mControlMessageQueue.push( 163 MakeUnique<ConfigureMessage>(aOutputSize, aColorSpaceConversion)); 164 } 165 166 void ImageDecoder::QueueDecodeMetadataMessage() { 167 mControlMessageQueue.push(MakeUnique<DecodeMetadataMessage>()); 168 } 169 170 void ImageDecoder::QueueDecodeFrameMessage() { 171 mControlMessageQueue.push(MakeUnique<DecodeFrameMessage>()); 172 } 173 174 void ImageDecoder::QueueSelectTrackMessage(uint32_t aSelectedIndex) { 175 mControlMessageQueue.push(MakeUnique<SelectTrackMessage>(aSelectedIndex)); 176 } 177 178 void ImageDecoder::ResumeControlMessageQueue() { 179 MOZ_ASSERT(mMessageQueueBlocked); 180 mMessageQueueBlocked = false; 181 ProcessControlMessageQueue(); 182 } 183 184 void ImageDecoder::ProcessControlMessageQueue() { 185 while (!mMessageQueueBlocked && !mControlMessageQueue.empty()) { 186 auto& msg = mControlMessageQueue.front(); 187 auto result = MessageProcessedResult::Processed; 188 if (auto* submsg = msg->AsConfigureMessage()) { 189 result = ProcessConfigureMessage(submsg); 190 } else if (auto* submsg = msg->AsDecodeMetadataMessage()) { 191 result = ProcessDecodeMetadataMessage(submsg); 192 } else if (auto* submsg = msg->AsDecodeFrameMessage()) { 193 result = ProcessDecodeFrameMessage(submsg); 194 } else if (auto* submsg = msg->AsSelectTrackMessage()) { 195 result = ProcessSelectTrackMessage(submsg); 196 } else { 197 MOZ_ASSERT_UNREACHABLE("Unhandled control message type!"); 198 } 199 200 if (result == MessageProcessedResult::NotProcessed) { 201 break; 202 } 203 204 mControlMessageQueue.pop(); 205 } 206 } 207 208 MessageProcessedResult ImageDecoder::ProcessConfigureMessage( 209 ConfigureMessage* aMsg) { 210 // 10.2.2. Running a control message to configure the image decoder means 211 // running these steps: 212 213 // 1. Let supported be the result of running the Check Type Support algorithm 214 // with init.type. 215 // 216 // 2. If supported is false, run the Close ImageDecoder algorithm with a 217 // NotSupportedError DOMException and return "processed". 218 // 219 // Note that DecoderType::ICON is mostly an internal type that we use for 220 // system icons and shouldn't be exposed for general use on the web. This is 221 // not to be confused with DecoderType::ICO which is for .ico files. 222 NS_ConvertUTF16toUTF8 mimeType(mType); 223 image::DecoderType type = image::ImageUtils::GetDecoderType(mimeType); 224 if (NS_WARN_IF(type == image::DecoderType::UNKNOWN) || 225 NS_WARN_IF(type == image::DecoderType::ICON)) { 226 MOZ_LOG(gWebCodecsLog, LogLevel::Error, 227 ("ImageDecoder %p Initialize -- unsupported mime type '%s'", this, 228 mimeType.get())); 229 Close(MediaResult(NS_ERROR_DOM_NOT_SUPPORTED_ERR, 230 "Unsupported mime type"_ns)); 231 return MessageProcessedResult::Processed; 232 } 233 234 image::SurfaceFlags surfaceFlags = image::DefaultSurfaceFlags(); 235 switch (aMsg->mColorSpaceConversion) { 236 case ColorSpaceConversion::None: 237 surfaceFlags |= image::SurfaceFlags::NO_COLORSPACE_CONVERSION; 238 break; 239 case ColorSpaceConversion::Default: 240 break; 241 default: 242 MOZ_LOG( 243 gWebCodecsLog, LogLevel::Error, 244 ("ImageDecoder %p Initialize -- unsupported colorspace conversion", 245 this)); 246 Close(MediaResult(NS_ERROR_DOM_NOT_SUPPORTED_ERR, 247 "Unsupported colorspace conversion"_ns)); 248 return MessageProcessedResult::Processed; 249 } 250 251 // 3. Otherwise, assign the [[codec implementation]] internal slot with an 252 // implementation supporting init.type 253 mDecoder = image::ImageUtils::CreateDecoder(mSourceBuffer, type, 254 aMsg->mOutputSize, surfaceFlags); 255 if (NS_WARN_IF(!mDecoder)) { 256 MOZ_LOG(gWebCodecsLog, LogLevel::Error, 257 ("ImageDecoder %p Initialize -- failed to create platform decoder", 258 this)); 259 Close(MediaResult(NS_ERROR_DOM_NOT_SUPPORTED_ERR, 260 "Failed to create platform decoder"_ns)); 261 return MessageProcessedResult::Processed; 262 } 263 264 // 4. Assign true to [[message queue blocked]]. 265 mMessageQueueBlocked = true; 266 267 NS_DispatchToCurrentThread(NS_NewCancelableRunnableFunction( 268 "ImageDecoder::ProcessConfigureMessage", [self = RefPtr{this}] { 269 // 5. Enqueue the following steps to the [[codec work queue]]: 270 // 5.1. Configure [[codec implementation]] in accordance with the values 271 // given for colorSpaceConversion, desiredWidth, and desiredHeight. 272 // 5.2. Assign false to [[message queue blocked]]. 273 // 5.3. Queue a task to Process the control message queue. 274 self->ResumeControlMessageQueue(); 275 })); 276 277 // 6. Return "processed". 278 return MessageProcessedResult::Processed; 279 } 280 281 MessageProcessedResult ImageDecoder::ProcessDecodeMetadataMessage( 282 DecodeMetadataMessage* aMsg) { 283 // 10.2.2. Running a control message to decode track metadata means running 284 // these steps: 285 286 if (!mDecoder) { 287 return MessageProcessedResult::Processed; 288 } 289 290 // 1. Enqueue the following steps to the [[codec work queue]]: 291 // 1.1. Run the Establish Tracks algorithm. 292 mDecoder->DecodeMetadata()->Then( 293 GetCurrentSerialEventTarget(), __func__, 294 [self = RefPtr{this}](const image::DecodeMetadataResult& aMetadata) { 295 self->OnMetadataSuccess(aMetadata); 296 }, 297 [self = RefPtr{this}](const nsresult& aErr) { 298 self->OnMetadataFailed(aErr); 299 }); 300 return MessageProcessedResult::Processed; 301 } 302 303 MessageProcessedResult ImageDecoder::ProcessDecodeFrameMessage( 304 DecodeFrameMessage* aMsg) { 305 // 10.4.2. Running a control message to decode the image means running these 306 // steps: 307 // 308 // 1. Enqueue the following steps to the [[codec work queue]]: 309 // 1.1. Wait for [[tracks established]] to become true. 310 // 311 // 1.2. If options.completeFramesOnly is false and the image is a 312 // Progressive Image for which the User Agent supports progressive 313 // decoding, run the Decode Progressive Frame algorithm with 314 // options.frameIndex and promise. 315 // 316 // 1.3. Otherwise, run the Decode Complete Frame algorithm with 317 // options.frameIndex and promise. 318 NS_DispatchToCurrentThread(NS_NewCancelableRunnableFunction( 319 "ImageDecoder::ProcessDecodeFrameMessage", 320 [self = RefPtr{this}] { self->CheckOutstandingDecodes(); })); 321 return MessageProcessedResult::Processed; 322 } 323 324 MessageProcessedResult ImageDecoder::ProcessSelectTrackMessage( 325 SelectTrackMessage* aMsg) { 326 // 10.7.2. Running a control message to update the internal selected track 327 // index means running these steps: 328 // 329 // 1. Enqueue the following steps to [[ImageDecoder]]'s [[codec work queue]]: 330 // 1.1. Assign selectedIndex to [[internal selected track index]]. 331 // 1.2. Remove all entries from [[progressive frame generations]]. 332 // 333 // At this time, progressive images and multi-track images are not supported. 334 return MessageProcessedResult::Processed; 335 } 336 337 void ImageDecoder::CheckOutstandingDecodes() { 338 // 10.2.5. Resolve Decode (with promise and result) 339 340 // 1. If [[closed]], abort these steps. 341 if (mClosed || !mTracks) { 342 return; 343 } 344 345 ImageTrack* track = mTracks->GetDefaultTrack(); 346 if (!track) { 347 return; 348 } 349 350 const uint32_t decodedFrameCount = track->DecodedFrameCount(); 351 const uint32_t frameCount = track->FrameCount(); 352 const bool frameCountComplete = track->FrameCountComplete(); 353 const bool decodedFramesComplete = track->DecodedFramesComplete(); 354 355 AutoTArray<OutstandingDecode, 4> resolved; 356 AutoTArray<OutstandingDecode, 4> rejectedRange; 357 AutoTArray<OutstandingDecode, 4> rejectedState; 358 uint32_t minFrameIndex = UINT32_MAX; 359 360 // 3. Remove promise from [[pending decode promises]]. 361 for (uint32_t i = 0; i < mOutstandingDecodes.Length();) { 362 auto& decode = mOutstandingDecodes[i]; 363 const auto frameIndex = decode.mFrameIndex; 364 if (frameIndex < decodedFrameCount) { 365 MOZ_LOG(gWebCodecsLog, LogLevel::Debug, 366 ("ImageDecoder %p CheckOutstandingDecodes -- resolved index %u", 367 this, frameIndex)); 368 resolved.AppendElement(std::move(decode)); 369 mOutstandingDecodes.RemoveElementAt(i); 370 } else if (frameCountComplete && frameCount <= frameIndex) { 371 // We have gotten the frame count from the decoder, so we must reject any 372 // unfulfilled requests that are beyond it with a RangeError. 373 MOZ_LOG(gWebCodecsLog, LogLevel::Warning, 374 ("ImageDecoder %p CheckOutstandingDecodes -- rejected index %u " 375 "out-of-bounds", 376 this, frameIndex)); 377 rejectedRange.AppendElement(std::move(decode)); 378 mOutstandingDecodes.RemoveElementAt(i); 379 } else if (frameCountComplete && decodedFramesComplete) { 380 // We have decoded all of the frames, but we produced fewer than the frame 381 // count indicated. This means we ran into problems while decoding and 382 // aborted. We must reject any unfulfilled requests with an 383 // InvalidStateError. 384 MOZ_LOG(gWebCodecsLog, LogLevel::Warning, 385 ("ImageDecoder %p CheckOutstandingDecodes -- rejected index %u " 386 "decode error", 387 this, frameIndex)); 388 rejectedState.AppendElement(std::move(decode)); 389 mOutstandingDecodes.RemoveElementAt(i); 390 } else if (!decodedFramesComplete) { 391 // We haven't gotten the last frame yet, so we can advance to the next 392 // one. 393 MOZ_LOG(gWebCodecsLog, LogLevel::Debug, 394 ("ImageDecoder %p CheckOutstandingDecodes -- pending index %u", 395 this, frameIndex)); 396 if (frameCount > frameIndex) { 397 minFrameIndex = std::min(minFrameIndex, frameIndex); 398 } 399 ++i; 400 } else { 401 // If none of the above, we have finished decoding all the frames we can, 402 // but we raced against the frame count completion. Once that finishes, we 403 // will run again, and we can appropriately fail frame requests as either 404 // out-of-bounds or decoding failures. 405 MOZ_ASSERT(!frameCountComplete); 406 } 407 } 408 409 if (minFrameIndex < UINT32_MAX) { 410 RequestDecodeFrames(minFrameIndex + 1 - decodedFrameCount); 411 } 412 413 // 4. Resolve promise with result. 414 for (const auto& i : resolved) { 415 ImageDecodeResult result; 416 result.mImage = track->GetDecodedFrame(i.mFrameIndex); 417 // TODO(aosmond): progressive images 418 result.mComplete = true; 419 i.mPromise->MaybeResolve(result); 420 } 421 422 for (const auto& i : rejectedRange) { 423 i.mPromise->MaybeRejectWithRangeError("No more frames available"_ns); 424 } 425 426 for (const auto& i : rejectedState) { 427 i.mPromise->MaybeRejectWithInvalidStateError("Error decoding frame"_ns); 428 } 429 } 430 431 /* static */ already_AddRefed<ImageDecoder> ImageDecoder::Constructor( 432 const GlobalObject& aGlobal, const ImageDecoderInit& aInit, 433 ErrorResult& aRv) { 434 // 10.2.2.1. If init is not valid ImageDecoderInit, throw a TypeError. 435 // 10.3.1. If type is not a valid image MIME type, return false. 436 const auto mimeType = Substring(aInit.mType, 0, 6); 437 if (!mimeType.Equals(u"image/"_ns)) { 438 MOZ_LOG(gWebCodecsLog, LogLevel::Error, 439 ("ImageDecoder Constructor -- bad mime type")); 440 aRv.ThrowTypeError("Invalid MIME type, must be 'image'"); 441 return nullptr; 442 } 443 444 RefPtr<ImageDecoderReadRequest> readRequest; 445 446 if (aInit.mData.IsReadableStream()) { 447 const auto& stream = aInit.mData.GetAsReadableStream(); 448 // 10.3.2. If data is of type ReadableStream and the ReadableStream is 449 // disturbed or locked, return false. 450 if (stream->Disturbed() || stream->Locked()) { 451 MOZ_LOG(gWebCodecsLog, LogLevel::Error, 452 ("ImageDecoder Constructor -- bad stream")); 453 aRv.ThrowTypeError("ReadableStream data is disturbed and/or locked"); 454 return nullptr; 455 } 456 } else { 457 // 10.3.3. If data is of type BufferSource: 458 bool empty; 459 if (aInit.mData.IsArrayBufferView()) { 460 const auto& view = aInit.mData.GetAsArrayBufferView(); 461 empty = view.ProcessData( 462 [](const Span<uint8_t>& aData, JS::AutoCheckCannotGC&&) { 463 return aData.IsEmpty(); 464 }); 465 } else if (aInit.mData.IsArrayBuffer()) { 466 const auto& buffer = aInit.mData.GetAsArrayBuffer(); 467 empty = buffer.ProcessData( 468 [](const Span<uint8_t>& aData, JS::AutoCheckCannotGC&&) { 469 return aData.IsEmpty(); 470 }); 471 } else { 472 MOZ_ASSERT_UNREACHABLE("Unsupported data type!"); 473 aRv.ThrowNotSupportedError("Unsupported data type"); 474 return nullptr; 475 } 476 477 // 10.3.3.1. If data is [detached], return false. 478 // 10.3.3.2. If data is empty, return false. 479 if (empty) { 480 MOZ_LOG(gWebCodecsLog, LogLevel::Error, 481 ("ImageDecoder Constructor -- detached/empty BufferSource")); 482 aRv.ThrowTypeError("BufferSource is detached/empty"); 483 return nullptr; 484 } 485 } 486 487 // 10.3.4. If desiredWidth exists and desiredHeight does not exist, return 488 // false. 489 // 10.3.5. If desiredHeight exists and desiredWidth does not exist, return 490 // false. 491 if (aInit.mDesiredHeight.WasPassed() != aInit.mDesiredWidth.WasPassed()) { 492 MOZ_LOG(gWebCodecsLog, LogLevel::Error, 493 ("ImageDecoder Constructor -- both/neither desiredHeight/width " 494 "needed")); 495 aRv.ThrowTypeError( 496 "Both or neither of desiredHeight and desiredWidth must be passed"); 497 return nullptr; 498 } 499 500 nsTHashSet<const JSObject*> transferSet; 501 for (const auto& buffer : aInit.mTransfer) { 502 // 10.2.2.2. If init.transfer contains more than one reference to the same 503 // ArrayBuffer, then throw a DataCloneError DOMException. 504 if (transferSet.Contains(buffer.Obj())) { 505 MOZ_LOG( 506 gWebCodecsLog, LogLevel::Error, 507 ("ImageDecoder Constructor -- duplicate transferred ArrayBuffer")); 508 aRv.ThrowDataCloneError( 509 "Transfer contains duplicate ArrayBuffer objects"); 510 return nullptr; 511 } 512 transferSet.Insert(buffer.Obj()); 513 // 10.2.2.3.1. If [[Detached]] internal slot is true, then throw a 514 // DataCloneError DOMException. 515 bool empty = buffer.ProcessData( 516 [&](const Span<uint8_t>& aData, JS::AutoCheckCannotGC&&) { 517 return aData.IsEmpty(); 518 }); 519 if (empty) { 520 MOZ_LOG(gWebCodecsLog, LogLevel::Error, 521 ("ImageDecoder Constructor -- empty/detached transferred " 522 "ArrayBuffer")); 523 aRv.ThrowDataCloneError( 524 "Transfer contains empty/detached ArrayBuffer objects"); 525 return nullptr; 526 } 527 } 528 529 // 10.2.2.4. Let d be a new ImageDecoder object. In the steps below, all 530 // mentions of ImageDecoder members apply to d unless stated 531 // otherwise. 532 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); 533 auto imageDecoder = MakeRefPtr<ImageDecoder>(std::move(global), aInit.mType); 534 imageDecoder->Initialize(aGlobal, aInit, aRv); 535 if (NS_WARN_IF(aRv.Failed())) { 536 MOZ_LOG(gWebCodecsLog, LogLevel::Error, 537 ("ImageDecoder Constructor -- initialize failed")); 538 return nullptr; 539 } 540 541 // 10.2.2.19. For each transferable in init.transfer: 542 // 10.2.2.19.1. Perform DetachArrayBuffer on transferable 543 for (const auto& buffer : aInit.mTransfer) { 544 JS::Rooted<JSObject*> obj(aGlobal.Context(), buffer.Obj()); 545 JS::DetachArrayBuffer(aGlobal.Context(), obj); 546 } 547 548 // 10.2.2.20. return d. 549 return imageDecoder.forget(); 550 } 551 552 /* static */ already_AddRefed<Promise> ImageDecoder::IsTypeSupported( 553 const GlobalObject& aGlobal, const nsAString& aType, ErrorResult& aRv) { 554 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); 555 RefPtr<Promise> promise = Promise::Create(global, aRv); 556 if (NS_WARN_IF(aRv.Failed())) { 557 return nullptr; 558 } 559 560 const auto subType = Substring(aType, 0, 6); 561 if (!subType.Equals(u"image/"_ns)) { 562 promise->MaybeRejectWithTypeError("Invalid MIME type, must be 'image'"_ns); 563 return promise.forget(); 564 } 565 566 NS_ConvertUTF16toUTF8 mimeType(aType); 567 image::DecoderType type = image::ImageUtils::GetDecoderType(mimeType); 568 promise->MaybeResolve(type != image::DecoderType::UNKNOWN); 569 return promise.forget(); 570 } 571 572 void ImageDecoder::Initialize(const GlobalObject& aGlobal, 573 const ImageDecoderInit& aInit, ErrorResult& aRv) { 574 mShutdownWatcher = media::ShutdownWatcher::Create(this); 575 if (!mShutdownWatcher) { 576 MOZ_LOG( 577 gWebCodecsLog, LogLevel::Error, 578 ("ImageDecoder %p Initialize -- create shutdown watcher failed", this)); 579 aRv.ThrowInvalidStateError("Could not create shutdown watcher"); 580 return; 581 } 582 583 mCompletePromise = Promise::Create(mParent, aRv); 584 if (NS_WARN_IF(aRv.Failed())) { 585 MOZ_LOG(gWebCodecsLog, LogLevel::Error, 586 ("ImageDecoder %p Initialize -- create promise failed", this)); 587 return; 588 } 589 590 // 10.2.2.8. Assign [[ImageTrackList]] a new ImageTrackList initialized as 591 // follows: 592 // 10.2.2.8.1. Assign a new list to [[track list]]. 593 mTracks = MakeAndAddRef<ImageTrackList>(mParent, this); 594 mTracks->Initialize(aRv); 595 if (NS_WARN_IF(aRv.Failed())) { 596 MOZ_LOG(gWebCodecsLog, LogLevel::Error, 597 ("ImageDecoder %p Initialize -- create tracks failed", this)); 598 return; 599 } 600 601 mSourceBuffer = MakeRefPtr<image::SourceBuffer>(); 602 603 bool transferOwnership = false; 604 const auto fnSourceBufferFromSpan = [&](const Span<uint8_t>& aData) { 605 if (transferOwnership) { 606 // 10.2.2.18.2.1 Let [[encoded data]] reference bytes in data 607 // representing an encoded image. 608 nsresult rv = 609 mSourceBuffer->AdoptData(reinterpret_cast<char*>(aData.Elements()), 610 aData.Length(), js_realloc, js_free); 611 if (NS_WARN_IF(NS_FAILED(rv))) { 612 MOZ_LOG(gWebCodecsLog, LogLevel::Error, 613 ("ImageDecoder %p Initialize -- failed to adopt source buffer", 614 this)); 615 aRv.ThrowRangeError("Could not allocate for encoded source buffer"); 616 return; 617 } 618 } else { 619 nsresult rv = mSourceBuffer->ExpectLength(aData.Length()); 620 if (NS_WARN_IF(NS_FAILED(rv))) { 621 MOZ_LOG(gWebCodecsLog, LogLevel::Error, 622 ("ImageDecoder %p Initialize -- failed to pre-allocate source " 623 "buffer", 624 this)); 625 aRv.ThrowRangeError("Could not allocate for encoded source buffer"); 626 return; 627 } 628 629 // 10.2.2.18.3.2. Assign a copy of init.data to [[encoded data]]. 630 rv = mSourceBuffer->Append( 631 reinterpret_cast<const char*>(aData.Elements()), aData.Length()); 632 if (NS_WARN_IF(NS_FAILED(rv))) { 633 MOZ_LOG(gWebCodecsLog, LogLevel::Error, 634 ("ImageDecoder %p Initialize -- failed to append source buffer", 635 this)); 636 aRv.ThrowRangeError("Could not allocate for encoded source buffer"); 637 return; 638 } 639 } 640 mSourceBuffer->Complete(NS_OK); 641 642 // 10.2.2.18.4. Assign true to [[complete]]. 643 // 10.2.2.18.5. Resolve [[completed promise]]. 644 OnCompleteSuccess(); 645 }; 646 647 if (aInit.mData.IsReadableStream()) { 648 // 10.2.2.17. If init’s data member is of type ReadableStream: 649 const auto& stream = aInit.mData.GetAsReadableStream(); 650 651 // 10.2.2.17.2. Assign false to [[complete]] 652 MOZ_ASSERT(!mComplete); 653 654 // 10.2.2.17.5. Let reader be the result of getting a reader for data. 655 // 10.2.2.17.6. In parallel, perform the Fetch Stream Data Loop on d with 656 // reader. 657 mReadRequest = MakeAndAddRef<ImageDecoderReadRequest>(mSourceBuffer); 658 if (NS_WARN_IF(!mReadRequest->Initialize(aGlobal, this, stream))) { 659 MOZ_LOG( 660 gWebCodecsLog, LogLevel::Error, 661 ("ImageDecoder %p Initialize -- create read request failed", this)); 662 aRv.ThrowInvalidStateError("Could not create reader for ReadableStream"); 663 return; 664 } 665 } else if (aInit.mData.IsArrayBufferView()) { 666 // 10.2.2.18.3.1. Assert that init.data is of type BufferSource. 667 const auto& view = aInit.mData.GetAsArrayBufferView(); 668 bool isShared; 669 JS::Rooted<JSObject*> viewObj(aGlobal.Context(), view.Obj()); 670 JSObject* arrayBuffer = 671 JS_GetArrayBufferViewBuffer(aGlobal.Context(), viewObj, &isShared); 672 bool inTransferList = false; 673 for (const auto& transferBuffer : aInit.mTransfer) { 674 if (arrayBuffer == transferBuffer.Obj()) { 675 inTransferList = true; 676 break; 677 } 678 } 679 size_t length; 680 if (inTransferList) { 681 length = JS_GetArrayBufferViewByteLength(view.Obj()); 682 // Only transfer ownership if the view's byte offset is 0 683 // (Otherewise we would have problems with freeing a pointer 684 // to halfway through the ArrayBuffer data) 685 transferOwnership = JS_GetArrayBufferViewByteOffset(view.Obj()) == 0; 686 } 687 if (transferOwnership) { 688 JS::Rooted<JSObject*> bufferObj(aGlobal.Context(), arrayBuffer); 689 void* data = JS::StealArrayBufferContents(aGlobal.Context(), bufferObj); 690 fnSourceBufferFromSpan(Span(static_cast<uint8_t*>(data), length)); 691 } else { 692 view.ProcessFixedData(fnSourceBufferFromSpan); 693 } 694 if (aRv.Failed()) { 695 return; 696 } 697 } else if (aInit.mData.IsArrayBuffer()) { 698 // 10.2.2.18.3.1. Assert that init.data is of type BufferSource. 699 const auto& buffer = aInit.mData.GetAsArrayBuffer(); 700 for (const auto& transferBuffer : aInit.mTransfer) { 701 if (buffer.Obj() == transferBuffer.Obj()) { 702 transferOwnership = true; 703 break; 704 } 705 } 706 if (transferOwnership) { 707 JS::Rooted<JSObject*> bufferObj(aGlobal.Context(), buffer.Obj()); 708 size_t length = JS::GetArrayBufferByteLength(bufferObj); 709 void* data = JS::StealArrayBufferContents(aGlobal.Context(), bufferObj); 710 fnSourceBufferFromSpan(Span(static_cast<uint8_t*>(data), length)); 711 } else { 712 buffer.ProcessFixedData(fnSourceBufferFromSpan); 713 } 714 if (aRv.Failed()) { 715 return; 716 } 717 } else { 718 MOZ_ASSERT_UNREACHABLE("Unsupported data type!"); 719 aRv.ThrowNotSupportedError("Unsupported data type"); 720 return; 721 } 722 723 Maybe<gfx::IntSize> desiredSize; 724 if (aInit.mDesiredWidth.WasPassed() && aInit.mDesiredHeight.WasPassed()) { 725 desiredSize.emplace( 726 std::min(aInit.mDesiredWidth.Value(), static_cast<uint32_t>(INT32_MAX)), 727 std::min(aInit.mDesiredHeight.Value(), 728 static_cast<uint32_t>(INT32_MAX))); 729 } 730 731 // 10.2.2.17.3 / 10.2.2.18.6. 732 // Queue a control message to configure the image decoder with init. 733 QueueConfigureMessage(desiredSize, aInit.mColorSpaceConversion); 734 735 // 10.2.10.2.2.18.7. Queue a control message to decode track metadata. 736 // 737 // Note that for readable streams it doesn't ever say to decode the metadata, 738 // but we can reasonably assume it means to decode the metadata in parallel 739 // with the reading of the stream. 740 QueueDecodeMetadataMessage(); 741 742 // 10.2.2.18.8. Process the control message queue. 743 ProcessControlMessageQueue(); 744 } 745 746 void ImageDecoder::OnSourceBufferComplete(const MediaResult& aResult) { 747 MOZ_LOG(gWebCodecsLog, LogLevel::Debug, 748 ("ImageDecoder %p OnSourceBufferComplete -- success %d", this, 749 NS_SUCCEEDED(aResult.Code()))); 750 751 MOZ_ASSERT(mSourceBuffer->IsComplete()); 752 753 if (NS_WARN_IF(NS_FAILED(aResult.Code()))) { 754 OnCompleteFailed(aResult); 755 return; 756 } 757 758 OnCompleteSuccess(); 759 } 760 761 void ImageDecoder::OnCompleteSuccess() { 762 if (mComplete) { 763 return; 764 } 765 766 // There are two conditions we need to fulfill before we are complete: 767 // 768 // 10.2.1. Internal Slots - [[complete]] 769 // A boolean indicating whether [[encoded data]] is completely buffered. 770 // 771 // 10.6.1. Internal Slots - [[ready promise]] 772 // NOTE: ImageTrack frameCount can receive subsequent updates until complete 773 // is true. 774 if (!mSourceBuffer->IsComplete() || !mHasFrameCount) { 775 MOZ_LOG(gWebCodecsLog, LogLevel::Debug, 776 ("ImageDecoder %p OnCompleteSuccess -- not complete yet; " 777 "sourceBuffer %d, hasFrameCount %d", 778 this, mSourceBuffer->IsComplete(), mHasFrameCount)); 779 return; 780 } 781 782 MOZ_LOG(gWebCodecsLog, LogLevel::Debug, 783 ("ImageDecoder %p OnCompleteSuccess -- complete", this)); 784 mComplete = true; 785 mCompletePromise->MaybeResolveWithUndefined(); 786 } 787 788 void ImageDecoder::OnCompleteFailed(const MediaResult& aResult) { 789 if (mComplete) { 790 return; 791 } 792 793 MOZ_LOG(gWebCodecsLog, LogLevel::Error, 794 ("ImageDecoder %p OnCompleteFailed -- complete", this)); 795 mComplete = true; 796 aResult.RejectTo(mCompletePromise); 797 } 798 799 void ImageDecoder::OnMetadataSuccess( 800 const image::DecodeMetadataResult& aMetadata) { 801 if (mClosed || !mTracks) { 802 return; 803 } 804 805 // 10.2.5. Establish Tracks 806 807 // 1. Assert [[tracks established]] is false. 808 MOZ_ASSERT(!mTracksEstablished); 809 810 // 2. and 3. See ImageDecoder::OnMetadataFailed. 811 812 MOZ_LOG(gWebCodecsLog, LogLevel::Debug, 813 ("ImageDecoder %p OnMetadataSuccess -- %dx%d, repetitions %d, " 814 "animated %d, frameCount %u, frameCountComplete %d", 815 this, aMetadata.mWidth, aMetadata.mHeight, aMetadata.mRepetitions, 816 aMetadata.mAnimated, aMetadata.mFrameCount, 817 aMetadata.mFrameCountComplete)); 818 819 // 4. - 9., 11. See ImageTrackList::OnMetadataSuccess 820 mTracks->OnMetadataSuccess(aMetadata); 821 822 // 10. Assign true to [[tracks established]]. 823 mTracksEstablished = true; 824 825 // If our encoded data comes from a ReadableStream, we may not have reached 826 // the end of the stream yet. As such, our frame count may be incomplete. 827 OnFrameCountSuccess(image::DecodeFrameCountResult{ 828 aMetadata.mFrameCount, aMetadata.mFrameCountComplete}); 829 } 830 831 void ImageDecoder::OnMetadataFailed(const nsresult& aErr) { 832 MOZ_LOG(gWebCodecsLog, LogLevel::Error, 833 ("ImageDecoder %p OnMetadataFailed 0x%08x", this, 834 static_cast<uint32_t>(aErr))); 835 836 // 10.2.5. Establish Tracks 837 838 // 1. Assert [[tracks established]] is false. 839 MOZ_ASSERT(!mTracksEstablished); 840 841 // 2. If [[encoded data]] does not contain enough data to determine the 842 // number of tracks: 843 // 2.1. If complete is true, queue a task to run the Close ImageDecoder 844 // algorithm. 845 // 2.2. Abort these steps. 846 // 3. If the number of tracks is found to be 0, queue a task to run the Close 847 // ImageDecoder algorithm and abort these steps. 848 Close(MediaResult(NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR, 849 "Metadata decoding failed"_ns)); 850 } 851 852 void ImageDecoder::RequestFrameCount(uint32_t aKnownFrameCount) { 853 MOZ_ASSERT(!mHasFrameCount); 854 855 if (NS_WARN_IF(!mDecoder)) { 856 return; 857 } 858 859 MOZ_LOG(gWebCodecsLog, LogLevel::Debug, 860 ("ImageDecoder %p RequestFrameCount -- knownFrameCount %u", this, 861 aKnownFrameCount)); 862 mDecoder->DecodeFrameCount(aKnownFrameCount) 863 ->Then( 864 GetCurrentSerialEventTarget(), __func__, 865 [self = RefPtr{this}](const image::DecodeFrameCountResult& aResult) { 866 self->OnFrameCountSuccess(aResult); 867 }, 868 [self = RefPtr{this}](const nsresult& aErr) { 869 self->OnFrameCountFailed(aErr); 870 }); 871 } 872 873 void ImageDecoder::RequestDecodeFrames(uint32_t aFramesToDecode) { 874 if (!mDecoder || mHasFramePending) { 875 return; 876 } 877 878 mHasFramePending = true; 879 880 MOZ_LOG(gWebCodecsLog, LogLevel::Debug, 881 ("ImageDecoder %p RequestDecodeFrames -- framesToDecode %u", this, 882 aFramesToDecode)); 883 884 mDecoder->DecodeFrames(aFramesToDecode) 885 ->Then( 886 GetCurrentSerialEventTarget(), __func__, 887 [self = RefPtr{this}](const image::DecodeFramesResult& aResult) { 888 self->OnDecodeFramesSuccess(aResult); 889 }, 890 [self = RefPtr{this}](const nsresult& aErr) { 891 self->OnDecodeFramesFailed(aErr); 892 }); 893 } 894 895 void ImageDecoder::OnFrameCountSuccess( 896 const image::DecodeFrameCountResult& aResult) { 897 if (mClosed || !mTracks) { 898 return; 899 } 900 901 MOZ_LOG(gWebCodecsLog, LogLevel::Debug, 902 ("ImageDecoder %p OnFrameCountSuccess -- frameCount %u, finished %d", 903 this, aResult.mFrameCount, aResult.mFinished)); 904 905 // 10.2.5. Update Tracks. 906 907 // 1. Assert [[tracks established]] is true. 908 MOZ_ASSERT(mTracksEstablished); 909 910 // 2. - 6. See ImageTrackList::OnFrameCountSuccess. 911 mTracks->OnFrameCountSuccess(aResult); 912 913 if (aResult.mFinished) { 914 mHasFrameCount = true; 915 OnCompleteSuccess(); 916 } else { 917 RequestFrameCount(aResult.mFrameCount); 918 } 919 920 CheckOutstandingDecodes(); 921 } 922 923 void ImageDecoder::OnFrameCountFailed(const nsresult& aErr) { 924 MOZ_LOG(gWebCodecsLog, LogLevel::Error, 925 ("ImageDecoder %p OnFrameCountFailed", this)); 926 Close(MediaResult(NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR, 927 "Frame count decoding failed"_ns)); 928 } 929 930 void ImageDecoder::GetType(nsAString& aType) const { aType.Assign(mType); } 931 932 already_AddRefed<Promise> ImageDecoder::Decode( 933 const ImageDecodeOptions& aOptions, ErrorResult& aRv) { 934 // 10.2.4. decode(options) 935 936 // 4. Let promise be a new Promise. 937 RefPtr<Promise> promise = Promise::Create(mParent, aRv); 938 if (NS_WARN_IF(aRv.Failed())) { 939 MOZ_LOG(gWebCodecsLog, LogLevel::Error, 940 ("ImageDecoder %p Decode -- create promise failed", this)); 941 return nullptr; 942 } 943 944 // NOTE: Calling decode() on the constructed ImageDecoder will trigger a 945 // NotSupportedError if the User Agent does not support type. This would have 946 // been set in Close by ProcessConfigureMessage. 947 if (mTypeNotSupported) { 948 MOZ_LOG(gWebCodecsLog, LogLevel::Error, 949 ("ImageDecoder %p Decode -- not supported", this)); 950 promise->MaybeRejectWithNotSupportedError("Unsupported MIME type"_ns); 951 return promise.forget(); 952 } 953 954 // 1. If [[closed]] is true, return a Promise rejected with an 955 // InvalidStateError DOMException. 956 if (mClosed || !mTracks || !mDecoder) { 957 MOZ_LOG(gWebCodecsLog, LogLevel::Error, 958 ("ImageDecoder %p Decode -- closed", this)); 959 promise->MaybeRejectWithInvalidStateError("Closed decoder"_ns); 960 return promise.forget(); 961 } 962 963 // 2. If [[ImageTrackList]]'s [[selected index]] is '-1', return a Promise 964 // rejected with an InvalidStateError DOMException. 965 // 966 // This must be balanced with the fact that we might get a decode call before 967 // the tracks are established and we are supposed to wait. 968 ImageTrack* track = mTracks->GetSelectedTrack(); 969 if (mTracksEstablished && !track) { 970 MOZ_LOG(gWebCodecsLog, LogLevel::Error, 971 ("ImageDecoder %p Decode -- no track selected", this)); 972 promise->MaybeRejectWithInvalidStateError("No track selected"_ns); 973 return promise.forget(); 974 } 975 976 // 3. If options is undefined, assign a new ImageDecodeOptions to options. 977 // 5. Append promise to [[pending decode promises]]. 978 mOutstandingDecodes.AppendElement(OutstandingDecode{ 979 promise, aOptions.mFrameIndex, aOptions.mCompleteFramesOnly}); 980 981 // 6. Queue a control message to decode the image with options, and promise. 982 QueueDecodeFrameMessage(); 983 984 // 7. Process the control message queue. 985 ProcessControlMessageQueue(); 986 987 // 8. Return promise. 988 return promise.forget(); 989 } 990 991 void ImageDecoder::OnDecodeFramesSuccess( 992 const image::DecodeFramesResult& aResult) { 993 // 10.2.5. Decode Complete Frame (with frameIndex and promise) 994 MOZ_ASSERT(mHasFramePending); 995 mHasFramePending = false; 996 997 // 1. Assert that [[tracks established]] is true. 998 MOZ_ASSERT(mTracksEstablished); 999 1000 if (mClosed || !mTracks) { 1001 return; 1002 } 1003 1004 ImageTrack* track = mTracks->GetDefaultTrack(); 1005 if (NS_WARN_IF(!track)) { 1006 MOZ_ASSERT_UNREACHABLE("Must have default track!"); 1007 return; 1008 } 1009 1010 track->OnDecodeFramesSuccess(aResult); 1011 1012 CheckOutstandingDecodes(); 1013 } 1014 1015 void ImageDecoder::OnDecodeFramesFailed(const nsresult& aErr) { 1016 MOZ_ASSERT(mHasFramePending); 1017 mHasFramePending = false; 1018 1019 MOZ_LOG(gWebCodecsLog, LogLevel::Error, 1020 ("ImageDecoder %p OnDecodeFramesFailed", this)); 1021 1022 AutoTArray<OutstandingDecode, 1> rejected = std::move(mOutstandingDecodes); 1023 for (const auto& i : rejected) { 1024 MOZ_LOG(gWebCodecsLog, LogLevel::Error, 1025 ("ImageDecoder %p OnDecodeFramesFailed -- reject index %u", this, 1026 i.mFrameIndex)); 1027 i.mPromise->MaybeRejectWithRangeError("No more frames available"_ns); 1028 } 1029 } 1030 1031 void ImageDecoder::Reset(const MediaResult& aResult) { 1032 MOZ_LOG(gWebCodecsLog, LogLevel::Debug, ("ImageDecoder %p Reset", this)); 1033 // 10.2.5. Reset ImageDecoder (with exception) 1034 1035 // 1. Signal [[codec implementation]] to abort any active decoding operation. 1036 if (mDecoder) { 1037 mDecoder->CancelDecodeFrames(); 1038 } 1039 1040 // 2. For each decodePromise in [[pending decode promises]]: 1041 // 2.1. Reject decodePromise with exception. 1042 // 2.3. Remove decodePromise from [[pending decode promises]]. 1043 AutoTArray<OutstandingDecode, 1> rejected = std::move(mOutstandingDecodes); 1044 for (const auto& i : rejected) { 1045 MOZ_LOG(gWebCodecsLog, LogLevel::Debug, 1046 ("ImageDecoder %p Reset -- reject index %u", this, i.mFrameIndex)); 1047 aResult.RejectTo(i.mPromise); 1048 } 1049 } 1050 1051 void ImageDecoder::Close(const MediaResult& aResult) { 1052 MOZ_LOG(gWebCodecsLog, LogLevel::Debug, ("ImageDecoder %p Close", this)); 1053 1054 // 10.2.5. Algorithms - Close ImageDecoder (with exception) 1055 mClosed = true; 1056 mTypeNotSupported = aResult.Code() == NS_ERROR_DOM_NOT_SUPPORTED_ERR; 1057 1058 // 1. Run the Reset ImageDecoder algorithm with exception. 1059 Reset(aResult); 1060 1061 // 3. Clear [[codec implementation]] and release associated system resources. 1062 if (mDecoder) { 1063 mDecoder->Destroy(); 1064 } 1065 1066 if (mReadRequest) { 1067 mReadRequest->Destroy(/* aCancel */ true); 1068 mReadRequest = nullptr; 1069 } 1070 1071 mSourceBuffer = nullptr; 1072 mDecoder = nullptr; 1073 mType = u""_ns; 1074 1075 // 4. Remove all entries from [[ImageTrackList]]. 1076 // 5. Assign -1 to [[ImageTrackList]]'s [[selected index]]. 1077 if (mTracks) { 1078 mTracks->MaybeRejectReady(aResult); 1079 mTracks->Destroy(); 1080 } 1081 1082 if (!mComplete) { 1083 aResult.RejectTo(mCompletePromise); 1084 mComplete = true; 1085 } 1086 1087 if (mShutdownWatcher) { 1088 mShutdownWatcher->Destroy(); 1089 mShutdownWatcher = nullptr; 1090 } 1091 } 1092 1093 void ImageDecoder::Reset() { 1094 Reset(MediaResult(NS_ERROR_DOM_ABORT_ERR, "Reset decoder"_ns)); 1095 } 1096 1097 void ImageDecoder::Close() { 1098 Close(MediaResult(NS_ERROR_DOM_ABORT_ERR, "Closed decoder"_ns)); 1099 } 1100 1101 void ImageDecoder::OnShutdown() { 1102 Close(MediaResult(NS_ERROR_DOM_ABORT_ERR, "Shutdown"_ns)); 1103 } 1104 1105 } // namespace mozilla::dom