ImageBitmap.cpp (80591B)
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/ImageBitmap.h" 8 9 #include "imgLoader.h" 10 #include "imgTools.h" 11 #include "jsapi.h" 12 #include "mozilla/AppShutdown.h" 13 #include "mozilla/CheckedInt.h" 14 #include "mozilla/Mutex.h" 15 #include "mozilla/ScopeExit.h" 16 #include "mozilla/dom/BlobImpl.h" 17 #include "mozilla/dom/CanvasRenderingContext2D.h" 18 #include "mozilla/dom/CanvasUtils.h" 19 #include "mozilla/dom/Document.h" 20 #include "mozilla/dom/HTMLCanvasElement.h" 21 #include "mozilla/dom/HTMLImageElement.h" 22 #include "mozilla/dom/HTMLMediaElementBinding.h" 23 #include "mozilla/dom/HTMLVideoElement.h" 24 #include "mozilla/dom/ImageBitmapBinding.h" 25 #include "mozilla/dom/OffscreenCanvas.h" 26 #include "mozilla/dom/Promise.h" 27 #include "mozilla/dom/SVGImageElement.h" 28 #include "mozilla/dom/StructuredCloneTags.h" 29 #include "mozilla/dom/VideoFrame.h" 30 #include "mozilla/dom/WorkerPrivate.h" 31 #include "mozilla/dom/WorkerRef.h" 32 #include "mozilla/dom/WorkerRunnable.h" 33 #include "mozilla/gfx/2D.h" 34 #include "mozilla/gfx/Logging.h" 35 #include "mozilla/gfx/Scale.h" 36 #include "mozilla/gfx/Swizzle.h" 37 #include "nsGlobalWindowInner.h" 38 #include "nsIAsyncInputStream.h" 39 #include "nsISerialEventTarget.h" 40 #include "nsLayoutUtils.h" 41 #include "nsNetUtil.h" 42 #include "nsStreamUtils.h" 43 44 using namespace mozilla::gfx; 45 using namespace mozilla::layers; 46 using mozilla::dom::HTMLMediaElement_Binding::HAVE_METADATA; 47 using mozilla::dom::HTMLMediaElement_Binding::NETWORK_EMPTY; 48 49 namespace mozilla::dom { 50 51 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ImageBitmap, mParent) 52 NS_IMPL_CYCLE_COLLECTING_ADDREF(ImageBitmap) 53 NS_IMPL_CYCLE_COLLECTING_RELEASE(ImageBitmap) 54 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ImageBitmap) 55 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 56 NS_INTERFACE_MAP_ENTRY(nsISupports) 57 NS_INTERFACE_MAP_END 58 59 class ImageBitmapShutdownObserver; 60 61 static StaticMutex sShutdownMutex; 62 static ImageBitmapShutdownObserver* sShutdownObserver = nullptr; 63 64 class SendShutdownToWorkerThread : public MainThreadWorkerControlRunnable { 65 public: 66 explicit SendShutdownToWorkerThread(ImageBitmap* aImageBitmap) 67 : MainThreadWorkerControlRunnable("SendShutdownToWorkerThread"), 68 mImageBitmap(aImageBitmap) { 69 MOZ_ASSERT(GetCurrentThreadWorkerPrivate()); 70 mTarget = GetCurrentThreadWorkerPrivate()->ControlEventTarget(); 71 MOZ_ASSERT(mTarget); 72 } 73 74 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { 75 if (mImageBitmap) { 76 mImageBitmap->OnShutdown(); 77 mImageBitmap = nullptr; 78 } 79 return true; 80 } 81 82 void DispatchToWorker() { 83 MOZ_ASSERT(mTarget); 84 (void)NS_WARN_IF(NS_FAILED(mTarget->Dispatch(this, NS_DISPATCH_NORMAL))); 85 mTarget = nullptr; 86 } 87 88 nsCOMPtr<nsISerialEventTarget> mTarget; 89 ImageBitmap* mImageBitmap; 90 }; 91 92 /* This class observes shutdown notifications and sends that notification 93 * to the worker thread if the image bitmap is on a worker thread. 94 */ 95 class ImageBitmapShutdownObserver final : public nsIObserver { 96 public: 97 void Init() { 98 sShutdownMutex.AssertCurrentThreadOwns(); 99 if (NS_IsMainThread()) { 100 RegisterObserver(); 101 return; 102 } 103 104 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 105 MOZ_ASSERT(workerPrivate); 106 auto* mainThreadEventTarget = workerPrivate->MainThreadEventTarget(); 107 MOZ_ASSERT(mainThreadEventTarget); 108 RefPtr<ImageBitmapShutdownObserver> self = this; 109 nsCOMPtr<nsIRunnable> r = 110 NS_NewRunnableFunction("ImageBitmapShutdownObserver::RegisterObserver", 111 [self]() { self->RegisterObserver(); }); 112 mainThreadEventTarget->Dispatch(r.forget()); 113 } 114 115 void RegisterObserver() { 116 MOZ_ASSERT(NS_IsMainThread()); 117 nsContentUtils::RegisterShutdownObserver(this); 118 } 119 120 already_AddRefed<SendShutdownToWorkerThread> Track( 121 ImageBitmap* aImageBitmap) { 122 sShutdownMutex.AssertCurrentThreadOwns(); 123 MOZ_ASSERT(!mBitmaps.Contains(aImageBitmap)); 124 125 RefPtr<SendShutdownToWorkerThread> runnable = nullptr; 126 if (!NS_IsMainThread()) { 127 runnable = new SendShutdownToWorkerThread(aImageBitmap); 128 } 129 130 RefPtr<SendShutdownToWorkerThread> retval = runnable; 131 mBitmaps.Insert(aImageBitmap); 132 return retval.forget(); 133 } 134 135 void Untrack(ImageBitmap* aImageBitmap) { 136 sShutdownMutex.AssertCurrentThreadOwns(); 137 MOZ_ASSERT(mBitmaps.Contains(aImageBitmap)); 138 139 mBitmaps.Remove(aImageBitmap); 140 } 141 142 NS_DECL_THREADSAFE_ISUPPORTS 143 NS_DECL_NSIOBSERVER 144 private: 145 ~ImageBitmapShutdownObserver() = default; 146 147 nsTHashSet<ImageBitmap*> mBitmaps; 148 }; 149 150 NS_IMPL_ISUPPORTS(ImageBitmapShutdownObserver, nsIObserver) 151 152 NS_IMETHODIMP 153 ImageBitmapShutdownObserver::Observe(nsISupports* aSubject, const char* aTopic, 154 const char16_t* aData) { 155 if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) { 156 StaticMutexAutoLock lock(sShutdownMutex); 157 158 for (const auto& bitmap : mBitmaps) { 159 const auto& runnable = bitmap->mShutdownRunnable; 160 if (runnable) { 161 runnable->DispatchToWorker(); 162 } else { 163 bitmap->OnShutdown(); 164 } 165 } 166 167 nsContentUtils::UnregisterShutdownObserver(this); 168 169 sShutdownObserver = nullptr; 170 } 171 172 return NS_OK; 173 } 174 175 /* 176 * If either aRect.width or aRect.height are negative, then return a new IntRect 177 * which represents the same rectangle as the aRect does but with positive width 178 * and height. 179 */ 180 static IntRect FixUpNegativeDimension(const IntRect& aRect, ErrorResult& aRv) { 181 gfx::IntRect rect = aRect; 182 183 // fix up negative dimensions 184 if (rect.width < 0) { 185 CheckedInt32 checkedX = CheckedInt32(rect.x) + rect.width; 186 187 if (!checkedX.isValid()) { 188 aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); 189 return rect; 190 } 191 192 rect.x = checkedX.value(); 193 rect.width = -(rect.width); 194 } 195 196 if (rect.height < 0) { 197 CheckedInt32 checkedY = CheckedInt32(rect.y) + rect.height; 198 199 if (!checkedY.isValid()) { 200 aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); 201 return rect; 202 } 203 204 rect.y = checkedY.value(); 205 rect.height = -(rect.height); 206 } 207 208 return rect; 209 } 210 211 /* 212 * This helper function copies the data of the given DataSourceSurface, 213 * _aSurface_, in the given area, _aCropRect_, into a new DataSourceSurface. 214 * This might return null if it can not create a new SourceSurface or it cannot 215 * read data from the given _aSurface_. 216 * 217 * Warning: Even though the area of _aCropRect_ is just the same as the size of 218 * _aSurface_, this function still copy data into a new 219 * DataSourceSurface. 220 */ 221 static already_AddRefed<DataSourceSurface> CropAndCopyDataSourceSurface( 222 DataSourceSurface* aSurface, const IntRect& aCropRect) { 223 MOZ_ASSERT(aSurface); 224 225 // Check the aCropRect 226 ErrorResult error; 227 const IntRect positiveCropRect = FixUpNegativeDimension(aCropRect, error); 228 if (NS_WARN_IF(error.Failed())) { 229 error.SuppressException(); 230 return nullptr; 231 } 232 233 // Calculate the size of the new SourceSurface. 234 // We cannot keep using aSurface->GetFormat() to create new DataSourceSurface, 235 // since it might be SurfaceFormat::B8G8R8X8 which does not handle opacity, 236 // however the specification explicitly define that "If any of the pixels on 237 // this rectangle are outside the area where the input bitmap was placed, then 238 // they will be transparent black in output." 239 // So, instead, we force the output format to be SurfaceFormat::B8G8R8A8. 240 const SurfaceFormat format = SurfaceFormat::B8G8R8A8; 241 const int bytesPerPixel = BytesPerPixel(format); 242 const IntSize dstSize = 243 IntSize(positiveCropRect.width, positiveCropRect.height); 244 const uint32_t dstStride = dstSize.width * bytesPerPixel; 245 246 // Create a new SourceSurface. 247 RefPtr<DataSourceSurface> dstDataSurface = 248 Factory::CreateDataSourceSurfaceWithStride(dstSize, format, dstStride, 249 true); 250 251 if (NS_WARN_IF(!dstDataSurface)) { 252 return nullptr; 253 } 254 255 // Only do copying and cropping when the positiveCropRect intersects with 256 // the size of aSurface. 257 const IntRect surfRect(IntPoint(0, 0), aSurface->GetSize()); 258 if (surfRect.Intersects(positiveCropRect)) { 259 const IntRect surfPortion = surfRect.Intersect(positiveCropRect); 260 const IntPoint dest(std::max(0, surfPortion.X() - positiveCropRect.X()), 261 std::max(0, surfPortion.Y() - positiveCropRect.Y())); 262 263 // Copy the raw data into the newly created DataSourceSurface. 264 DataSourceSurface::ScopedMap srcMap(aSurface, DataSourceSurface::READ); 265 DataSourceSurface::ScopedMap dstMap(dstDataSurface, 266 DataSourceSurface::WRITE); 267 if (NS_WARN_IF(!srcMap.IsMapped()) || NS_WARN_IF(!dstMap.IsMapped())) { 268 return nullptr; 269 } 270 271 uint8_t* srcBufferPtr = srcMap.GetData() + 272 surfPortion.y * srcMap.GetStride() + 273 surfPortion.x * bytesPerPixel; 274 uint8_t* dstBufferPtr = 275 dstMap.GetData() + dest.y * dstMap.GetStride() + dest.x * bytesPerPixel; 276 CheckedInt<uint32_t> copiedBytesPerRaw = 277 CheckedInt<uint32_t>(surfPortion.width) * bytesPerPixel; 278 if (!copiedBytesPerRaw.isValid()) { 279 return nullptr; 280 } 281 282 for (int i = 0; i < surfPortion.height; ++i) { 283 memcpy(dstBufferPtr, srcBufferPtr, copiedBytesPerRaw.value()); 284 srcBufferPtr += srcMap.GetStride(); 285 dstBufferPtr += dstMap.GetStride(); 286 } 287 } 288 289 return dstDataSurface.forget(); 290 } 291 292 /* 293 * This helper function scales the data of the given DataSourceSurface, 294 * _aSurface_, in the given area, _aCropRect_, into a new DataSourceSurface. 295 * This might return null if it can not create a new SourceSurface or it cannot 296 * read data from the given _aSurface_. 297 * 298 */ 299 static already_AddRefed<DataSourceSurface> ScaleDataSourceSurface( 300 DataSourceSurface* aSurface, const ImageBitmapOptions& aOptions) { 301 if (NS_WARN_IF(!aSurface)) { 302 return nullptr; 303 } 304 305 const SurfaceFormat format = aSurface->GetFormat(); 306 const int bytesPerPixel = BytesPerPixel(format); 307 308 const IntSize srcSize = aSurface->GetSize(); 309 int32_t tmp; 310 311 CheckedInt<int32_t> checked; 312 CheckedInt<int32_t> dstWidth( 313 aOptions.mResizeWidth.WasPassed() ? aOptions.mResizeWidth.Value() : 0); 314 CheckedInt<int32_t> dstHeight( 315 aOptions.mResizeHeight.WasPassed() ? aOptions.mResizeHeight.Value() : 0); 316 317 if (!dstWidth.isValid() || !dstHeight.isValid()) { 318 return nullptr; 319 } 320 321 if (!dstWidth.value()) { 322 checked = srcSize.width * dstHeight; 323 if (!checked.isValid()) { 324 return nullptr; 325 } 326 327 tmp = ceil(checked.value() / double(srcSize.height)); 328 dstWidth = tmp; 329 } else if (!dstHeight.value()) { 330 checked = srcSize.height * dstWidth; 331 if (!checked.isValid()) { 332 return nullptr; 333 } 334 335 tmp = ceil(checked.value() / double(srcSize.width)); 336 dstHeight = tmp; 337 } 338 339 const IntSize dstSize(dstWidth.value(), dstHeight.value()); 340 const int32_t dstStride = dstSize.width * bytesPerPixel; 341 342 // Create a new SourceSurface. 343 RefPtr<DataSourceSurface> dstDataSurface = 344 Factory::CreateDataSourceSurfaceWithStride(dstSize, format, dstStride, 345 true); 346 347 if (NS_WARN_IF(!dstDataSurface)) { 348 return nullptr; 349 } 350 351 // Copy the raw data into the newly created DataSourceSurface. 352 DataSourceSurface::ScopedMap srcMap(aSurface, DataSourceSurface::READ); 353 DataSourceSurface::ScopedMap dstMap(dstDataSurface, DataSourceSurface::WRITE); 354 if (NS_WARN_IF(!srcMap.IsMapped()) || NS_WARN_IF(!dstMap.IsMapped())) { 355 return nullptr; 356 } 357 358 uint8_t* srcBufferPtr = srcMap.GetData(); 359 uint8_t* dstBufferPtr = dstMap.GetData(); 360 361 bool res = Scale(srcBufferPtr, srcSize.width, srcSize.height, 362 srcMap.GetStride(), dstBufferPtr, dstSize.width, 363 dstSize.height, dstMap.GetStride(), aSurface->GetFormat()); 364 if (!res) { 365 return nullptr; 366 } 367 368 return dstDataSurface.forget(); 369 } 370 371 static DataSourceSurface* FlipYDataSourceSurface(DataSourceSurface* aSurface) { 372 MOZ_ASSERT(aSurface); 373 374 // Invert in y direction. 375 DataSourceSurface::ScopedMap srcMap(aSurface, DataSourceSurface::READ_WRITE); 376 if (NS_WARN_IF(!srcMap.IsMapped())) { 377 return nullptr; 378 } 379 380 const int bpp = BytesPerPixel(aSurface->GetFormat()); 381 const IntSize srcSize = aSurface->GetSize(); 382 uint8_t* srcBufferPtr = srcMap.GetData(); 383 const uint32_t stride = srcMap.GetStride(); 384 385 CheckedInt<uint32_t> copiedBytesPerRaw = CheckedInt<uint32_t>(stride); 386 if (!copiedBytesPerRaw.isValid()) { 387 return nullptr; 388 } 389 390 for (int i = 0; i < srcSize.height / 2; ++i) { 391 std::swap_ranges(srcBufferPtr + stride * i, 392 srcBufferPtr + stride * i + srcSize.width * bpp, 393 srcBufferPtr + stride * (srcSize.height - 1 - i)); 394 } 395 396 return aSurface; 397 } 398 399 static DataSourceSurface* AlphaPremultiplyDataSourceSurface( 400 DataSourceSurface* aSurface, const bool forward = true) { 401 MOZ_ASSERT(aSurface); 402 403 DataSourceSurface::MappedSurface surfaceMap; 404 405 if (aSurface->Map(DataSourceSurface::MapType::READ_WRITE, &surfaceMap)) { 406 if (forward) { 407 PremultiplyData(surfaceMap.mData, surfaceMap.mStride, 408 aSurface->GetFormat(), surfaceMap.mData, 409 surfaceMap.mStride, aSurface->GetFormat(), 410 aSurface->GetSize()); 411 } else { 412 UnpremultiplyData(surfaceMap.mData, surfaceMap.mStride, 413 aSurface->GetFormat(), surfaceMap.mData, 414 surfaceMap.mStride, aSurface->GetFormat(), 415 aSurface->GetSize()); 416 } 417 418 aSurface->Unmap(); 419 } else { 420 return nullptr; 421 } 422 423 return aSurface; 424 } 425 426 /* 427 * Encapsulate the given _aSurface_ into a layers::SourceSurfaceImage. 428 */ 429 static already_AddRefed<layers::Image> CreateImageFromSurface( 430 SourceSurface* aSurface) { 431 MOZ_ASSERT(aSurface); 432 RefPtr<layers::SourceSurfaceImage> image = 433 new layers::SourceSurfaceImage(aSurface->GetSize(), aSurface); 434 return image.forget(); 435 } 436 437 /* 438 * CreateImageFromRawData(), CreateSurfaceFromRawData() and 439 * CreateImageFromRawDataInMainThreadSyncTask are helpers for 440 * create-from-ImageData case 441 */ 442 static already_AddRefed<SourceSurface> CreateSurfaceFromRawData( 443 const gfx::IntSize& aSize, uint32_t aStride, gfx::SurfaceFormat aFormat, 444 uint8_t* aBuffer, uint32_t aBufferLength, const Maybe<IntRect>& aCropRect, 445 const ImageBitmapOptions& aOptions) { 446 MOZ_ASSERT(!aSize.IsEmpty()); 447 MOZ_ASSERT(aBuffer); 448 449 // Wrap the source buffer into a SourceSurface. 450 RefPtr<DataSourceSurface> dataSurface = 451 Factory::CreateWrappingDataSourceSurface(aBuffer, aStride, aSize, 452 aFormat); 453 454 if (NS_WARN_IF(!dataSurface)) { 455 return nullptr; 456 } 457 458 // The temporary cropRect variable is equal to the size of source buffer if we 459 // do not need to crop, or it equals to the given cropping size. 460 const IntRect cropRect = 461 aCropRect.valueOr(IntRect(0, 0, aSize.width, aSize.height)); 462 463 // Copy the source buffer in the _cropRect_ area into a new SourceSurface. 464 RefPtr<DataSourceSurface> result = 465 CropAndCopyDataSourceSurface(dataSurface, cropRect); 466 if (NS_WARN_IF(!result)) { 467 return nullptr; 468 } 469 470 if (aOptions.mImageOrientation == ImageOrientation::FlipY) { 471 result = FlipYDataSourceSurface(result); 472 473 if (NS_WARN_IF(!result)) { 474 return nullptr; 475 } 476 } 477 478 if (aOptions.mPremultiplyAlpha == PremultiplyAlpha::Premultiply) { 479 result = AlphaPremultiplyDataSourceSurface(result); 480 481 if (NS_WARN_IF(!result)) { 482 return nullptr; 483 } 484 } 485 486 if (aOptions.mResizeWidth.WasPassed() || aOptions.mResizeHeight.WasPassed()) { 487 dataSurface = result->GetDataSurface(); 488 result = ScaleDataSourceSurface(dataSurface, aOptions); 489 if (NS_WARN_IF(!result)) { 490 return nullptr; 491 } 492 } 493 494 return result.forget(); 495 } 496 497 static already_AddRefed<layers::Image> CreateImageFromRawData( 498 const gfx::IntSize& aSize, uint32_t aStride, gfx::SurfaceFormat aFormat, 499 uint8_t* aBuffer, uint32_t aBufferLength, const Maybe<IntRect>& aCropRect, 500 const ImageBitmapOptions& aOptions) { 501 MOZ_ASSERT(NS_IsMainThread()); 502 503 // Copy and crop the source buffer into a SourceSurface. 504 RefPtr<SourceSurface> rgbaSurface = CreateSurfaceFromRawData( 505 aSize, aStride, aFormat, aBuffer, aBufferLength, aCropRect, aOptions); 506 507 if (NS_WARN_IF(!rgbaSurface)) { 508 return nullptr; 509 } 510 511 // Convert RGBA to BGRA 512 RefPtr<DataSourceSurface> rgbaDataSurface = rgbaSurface->GetDataSurface(); 513 DataSourceSurface::ScopedMap rgbaMap(rgbaDataSurface, 514 DataSourceSurface::READ); 515 if (NS_WARN_IF(!rgbaMap.IsMapped())) { 516 return nullptr; 517 } 518 519 RefPtr<DataSourceSurface> bgraDataSurface = 520 Factory::CreateDataSourceSurfaceWithStride(rgbaDataSurface->GetSize(), 521 SurfaceFormat::B8G8R8A8, 522 rgbaMap.GetStride()); 523 if (NS_WARN_IF(!bgraDataSurface)) { 524 return nullptr; 525 } 526 527 DataSourceSurface::ScopedMap bgraMap(bgraDataSurface, 528 DataSourceSurface::WRITE); 529 if (NS_WARN_IF(!bgraMap.IsMapped())) { 530 return nullptr; 531 } 532 533 SwizzleData(rgbaMap.GetData(), rgbaMap.GetStride(), SurfaceFormat::R8G8B8A8, 534 bgraMap.GetData(), bgraMap.GetStride(), SurfaceFormat::B8G8R8A8, 535 bgraDataSurface->GetSize()); 536 537 // Create an Image from the BGRA SourceSurface. 538 return CreateImageFromSurface(bgraDataSurface); 539 } 540 541 /* 542 * This is a synchronous task. 543 * This class is used to create a layers::SourceSurfaceImage from raw data in 544 * the main thread. While creating an ImageBitmap from an ImageData, we need to 545 * create a SouceSurface from the ImageData's raw data and then set the 546 * SourceSurface into a layers::SourceSurfaceImage. However, the 547 * layers::SourceSurfaceImage asserts the setting operation in the main thread, 548 * so if we are going to create an ImageBitmap from an ImageData off the main 549 * thread, we post an event to the main thread to create a 550 * layers::SourceSurfaceImage from an ImageData's raw data. 551 */ 552 class CreateImageFromRawDataInMainThreadSyncTask final 553 : public WorkerMainThreadRunnable { 554 public: 555 CreateImageFromRawDataInMainThreadSyncTask( 556 uint8_t* aBuffer, uint32_t aBufferLength, uint32_t aStride, 557 gfx::SurfaceFormat aFormat, const gfx::IntSize& aSize, 558 const Maybe<IntRect>& aCropRect, layers::Image** aImage, 559 const ImageBitmapOptions& aOptions) 560 : WorkerMainThreadRunnable( 561 GetCurrentThreadWorkerPrivate(), 562 "ImageBitmap :: Create Image from Raw Data"_ns), 563 mImage(aImage), 564 mBuffer(aBuffer), 565 mBufferLength(aBufferLength), 566 mStride(aStride), 567 mFormat(aFormat), 568 mSize(aSize), 569 mCropRect(aCropRect), 570 mOptions(aOptions) { 571 MOZ_ASSERT(!(*aImage), 572 "Don't pass an existing Image into " 573 "CreateImageFromRawDataInMainThreadSyncTask."); 574 } 575 576 bool MainThreadRun() override { 577 RefPtr<layers::Image> image = CreateImageFromRawData( 578 mSize, mStride, mFormat, mBuffer, mBufferLength, mCropRect, mOptions); 579 580 if (NS_WARN_IF(!image)) { 581 return false; 582 } 583 584 image.forget(mImage); 585 586 return true; 587 } 588 589 private: 590 layers::Image** mImage; 591 uint8_t* mBuffer; 592 uint32_t mBufferLength; 593 uint32_t mStride; 594 gfx::SurfaceFormat mFormat; 595 gfx::IntSize mSize; 596 const Maybe<IntRect>& mCropRect; 597 const ImageBitmapOptions mOptions; 598 }; 599 600 /* 601 * A wrapper to the nsLayoutUtils::SurfaceFromElement() function followed by the 602 * security checking. 603 */ 604 template <class ElementType> 605 static already_AddRefed<SourceSurface> GetSurfaceFromElement( 606 nsIGlobalObject* aGlobal, ElementType& aElement, bool* aWriteOnly, 607 const ImageBitmapOptions& aOptions, gfxAlphaType* aAlphaType, 608 ErrorResult& aRv) { 609 uint32_t flags = nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE | 610 nsLayoutUtils::SFE_ORIENTATION_FROM_IMAGE | 611 nsLayoutUtils::SFE_EXACT_SIZE_SURFACE; 612 613 // by default surfaces have premultiplied alpha 614 // attempt to get non premultiplied if required 615 if (aOptions.mPremultiplyAlpha == PremultiplyAlpha::None) { 616 flags |= nsLayoutUtils::SFE_ALLOW_NON_PREMULT; 617 } 618 619 if (aOptions.mColorSpaceConversion == ColorSpaceConversion::None && 620 aElement.IsHTMLElement(nsGkAtoms::img)) { 621 flags |= nsLayoutUtils::SFE_NO_COLORSPACE_CONVERSION; 622 } 623 624 Maybe<int32_t> resizeWidth, resizeHeight; 625 if (aOptions.mResizeWidth.WasPassed()) { 626 if (!CheckedInt32(aOptions.mResizeWidth.Value()).isValid()) { 627 aRv.ThrowInvalidStateError("resizeWidth is too large"); 628 return nullptr; 629 } 630 resizeWidth.emplace(aOptions.mResizeWidth.Value()); 631 } 632 if (aOptions.mResizeHeight.WasPassed()) { 633 if (!CheckedInt32(aOptions.mResizeHeight.Value()).isValid()) { 634 aRv.ThrowInvalidStateError("resizeHeight is too large"); 635 return nullptr; 636 } 637 resizeHeight.emplace(aOptions.mResizeHeight.Value()); 638 } 639 SurfaceFromElementResult res = nsLayoutUtils::SurfaceFromElement( 640 &aElement, resizeWidth, resizeHeight, flags); 641 642 RefPtr<SourceSurface> surface = res.GetSourceSurface(); 643 if (NS_WARN_IF(!surface)) { 644 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 645 return nullptr; 646 } 647 648 *aWriteOnly = res.mIsWriteOnly; 649 *aAlphaType = res.mAlphaType; 650 651 return surface.forget(); 652 } 653 654 ImageBitmap::ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData, 655 bool aAllocatedImageData, bool aWriteOnly, 656 gfxAlphaType aAlphaType) 657 : mParent(aGlobal), 658 mData(aData), 659 mSurface(nullptr), 660 mPictureRect(aData->GetPictureRect()), 661 mAlphaType(aAlphaType), 662 mAllocatedImageData(aAllocatedImageData), 663 mWriteOnly(aWriteOnly) { 664 MOZ_ASSERT(aData, "aData is null in ImageBitmap constructor."); 665 666 StaticMutexAutoLock lock(sShutdownMutex); 667 if (!sShutdownObserver && 668 !AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdown)) { 669 sShutdownObserver = new ImageBitmapShutdownObserver(); 670 sShutdownObserver->Init(); 671 } 672 if (sShutdownObserver) { 673 mShutdownRunnable = sShutdownObserver->Track(this); 674 } 675 } 676 677 ImageBitmap::~ImageBitmap() { 678 StaticMutexAutoLock lock(sShutdownMutex); 679 if (mShutdownRunnable) { 680 mShutdownRunnable->mImageBitmap = nullptr; 681 } 682 mShutdownRunnable = nullptr; 683 if (sShutdownObserver) { 684 sShutdownObserver->Untrack(this); 685 } 686 } 687 688 JSObject* ImageBitmap::WrapObject(JSContext* aCx, 689 JS::Handle<JSObject*> aGivenProto) { 690 return ImageBitmap_Binding::Wrap(aCx, this, aGivenProto); 691 } 692 693 void ImageBitmap::Close() { 694 RemoveAssociatedMemory(); 695 mData = nullptr; 696 mSurface = nullptr; 697 mPictureRect.SetEmpty(); 698 } 699 700 void ImageBitmap::OnShutdown() { Close(); } 701 702 void ImageBitmap::SetPictureRect(const IntRect& aRect, ErrorResult& aRv) { 703 mPictureRect = FixUpNegativeDimension(aRect, aRv); 704 mSurface = nullptr; 705 } 706 707 SurfaceFromElementResult ImageBitmap::SurfaceFrom(uint32_t aSurfaceFlags) { 708 SurfaceFromElementResult sfer; 709 710 if (!mData) { 711 return sfer; 712 } 713 714 // An ImageBitmap, not being a DOM element, only has `origin-clean` 715 // (via our `IsWriteOnly`), and does not participate in CORS. 716 // Right now we mark this by setting mCORSUsed to true. 717 sfer.mCORSUsed = true; 718 sfer.mIsWriteOnly = mWriteOnly; 719 720 if (mParent) { 721 sfer.mPrincipal = mParent->PrincipalOrNull(); 722 } 723 724 IntSize imageSize(mData->GetSize()); 725 IntRect imageRect(IntPoint(0, 0), imageSize); 726 bool hasCropRect = mPictureRect.IsEqualEdges(imageRect); 727 728 bool wantExactSize = 729 bool(aSurfaceFlags & nsLayoutUtils::SFE_EXACT_SIZE_SURFACE); 730 bool allowNonPremult = 731 bool(aSurfaceFlags & nsLayoutUtils::SFE_ALLOW_NON_PREMULT); 732 bool allowUncropped = 733 bool(aSurfaceFlags & nsLayoutUtils::SFE_ALLOW_UNCROPPED_UNSCALED); 734 bool requiresPremult = 735 !allowNonPremult && mAlphaType == gfxAlphaType::NonPremult; 736 bool requiresCrop = !allowUncropped && hasCropRect; 737 if (wantExactSize || requiresPremult || requiresCrop || mSurface) { 738 RefPtr<DrawTarget> dt = Factory::CreateDrawTarget( 739 BackendType::SKIA, IntSize(1, 1), SurfaceFormat::B8G8R8A8); 740 sfer.mSourceSurface = PrepareForDrawTarget(dt); 741 742 if (!sfer.mSourceSurface) { 743 return sfer; 744 } 745 746 MOZ_ASSERT(mSurface); 747 748 sfer.mSize = sfer.mIntrinsicSize = sfer.mSourceSurface->GetSize(); 749 sfer.mHasSize = true; 750 sfer.mAlphaType = IsOpaque(sfer.mSourceSurface->GetFormat()) 751 ? gfxAlphaType::Opaque 752 : gfxAlphaType::Premult; 753 return sfer; 754 } 755 756 if (hasCropRect) { 757 IntRect imagePortion = imageRect.Intersect(mPictureRect); 758 759 // the crop lies entirely outside the surface area, nothing to draw 760 if (imagePortion.IsEmpty()) { 761 return sfer; 762 } 763 764 sfer.mCropRect = Some(imagePortion); 765 sfer.mIntrinsicSize = imagePortion.Size(); 766 } else { 767 sfer.mIntrinsicSize = imageSize; 768 } 769 770 sfer.mSize = imageSize; 771 sfer.mHasSize = true; 772 sfer.mAlphaType = mAlphaType; 773 sfer.mLayersImage = mData; 774 return sfer; 775 } 776 777 /* 778 * The functionality of PrepareForDrawTarget method: 779 * (1) Get a SourceSurface from the mData (which is a layers::Image). 780 * (2) Convert the SourceSurface to format B8G8R8A8 if the original format is 781 * R8G8B8, B8G8R8, HSV or Lab. 782 * Note: if the original format is A8 or Depth, then return null directly. 783 * (3) Do cropping if the size of SourceSurface does not equal to the 784 * mPictureRect. 785 * (4) Pre-multiply alpha if needed. 786 */ 787 already_AddRefed<SourceSurface> ImageBitmap::PrepareForDrawTarget( 788 gfx::DrawTarget* aTarget) { 789 MOZ_ASSERT(aTarget); 790 791 if (!mData) { 792 return nullptr; 793 } 794 795 // We may have a cached surface optimized for the last DrawTarget. If it was 796 // created via DrawTargetRecording, it may not be suitable for use with the 797 // current DrawTarget, as we can only do readbacks via the 798 // PersistentBufferProvider for the canvas, and not for individual 799 // SourceSurfaceRecording objects. In such situations, the only thing we can 800 // do is clear our cache and extract a new SourceSurface from mData. 801 if (mSurface && mSurface->GetType() == gfx::SurfaceType::RECORDING && 802 !aTarget->IsRecording()) { 803 RefPtr<gfx::DataSourceSurface> dataSurface = mSurface->GetDataSurface(); 804 if (!dataSurface) { 805 mSurface = nullptr; 806 } 807 } 808 809 // If we have a surface, then it is already cropped and premultiplied. 810 if (mSurface) { 811 return do_AddRef(mSurface); 812 } 813 814 RefPtr<gfx::SourceSurface> surface = mData->GetAsSourceSurface(); 815 if (NS_WARN_IF(!surface)) { 816 return nullptr; 817 } 818 819 IntRect surfRect(0, 0, surface->GetSize().width, surface->GetSize().height); 820 SurfaceFormat format = surface->GetFormat(); 821 bool isOpaque = IsOpaque(format); 822 bool mustPremultiply = mAlphaType == gfxAlphaType::NonPremult && !isOpaque; 823 bool hasCopied = false; 824 825 // Check if we still need to crop our surface 826 if (!mPictureRect.IsEqualEdges(surfRect)) { 827 IntRect surfPortion = surfRect.Intersect(mPictureRect); 828 829 // the crop lies entirely outside the surface area, nothing to draw 830 if (surfPortion.IsEmpty()) { 831 return nullptr; 832 } 833 834 IntPoint dest(std::max(0, surfPortion.X() - mPictureRect.X()), 835 std::max(0, surfPortion.Y() - mPictureRect.Y())); 836 837 // We must initialize this target with mPictureRect.Size() because the 838 // specification states that if the cropping area is given, then return an 839 // ImageBitmap with the size equals to the cropping area. Ensure that the 840 // format matches the surface, even though the DT type is similar to the 841 // destination, i.e. blending an alpha surface to an opaque DT. However, 842 // any pixels outside the surface portion must be filled with transparent 843 // black, even if the surface is opaque, so force to an alpha format in 844 // that case. 845 if (!surfPortion.IsEqualEdges(mPictureRect) && isOpaque) { 846 format = SurfaceFormat::B8G8R8A8; 847 } 848 849 // If we need to pre-multiply the alpha, then we need to be able to 850 // read/write the pixel data, and as such, we want to avoid creating a 851 // SourceSurfaceRecording for the same reasons earlier in this method. 852 RefPtr<DrawTarget> cropped; 853 if (mustPremultiply && aTarget->IsRecording()) { 854 cropped = Factory::CreateDrawTarget(BackendType::SKIA, 855 mPictureRect.Size(), format); 856 } else { 857 if (aTarget->CanCreateSimilarDrawTarget(mPictureRect.Size(), format)) { 858 cropped = aTarget->CreateSimilarDrawTarget(mPictureRect.Size(), format); 859 } 860 } 861 862 if (NS_WARN_IF(!cropped)) { 863 return nullptr; 864 } 865 866 cropped->CopySurface(surface, surfPortion, dest); 867 surface = cropped->GetBackingSurface(); 868 hasCopied = true; 869 if (NS_WARN_IF(!surface)) { 870 return nullptr; 871 } 872 } 873 874 // Pre-multiply alpha here. 875 // Ignore this step if the source surface does not have alpha channel; this 876 // kind of source surfaces might come form layers::PlanarYCbCrImage. If the 877 // crop rect imputed transparency, and the original surface was opaque, we 878 // can skip doing the pre-multiply here as the only transparent pixels are 879 // already transparent black. 880 if (mustPremultiply) { 881 MOZ_ASSERT(surface->GetFormat() == SurfaceFormat::R8G8B8A8 || 882 surface->GetFormat() == SurfaceFormat::B8G8R8A8 || 883 surface->GetFormat() == SurfaceFormat::A8R8G8B8); 884 885 RefPtr<DataSourceSurface> srcSurface = surface->GetDataSurface(); 886 if (NS_WARN_IF(!srcSurface)) { 887 return nullptr; 888 } 889 890 if (hasCopied) { 891 // If we are using our own local copy, then we can safely premultiply in 892 // place without an additional allocation. 893 DataSourceSurface::ScopedMap map(srcSurface, 894 DataSourceSurface::READ_WRITE); 895 if (!map.IsMapped()) { 896 gfxCriticalError() << "Failed to map surface for premultiplying alpha."; 897 return nullptr; 898 } 899 900 PremultiplyData(map.GetData(), map.GetStride(), srcSurface->GetFormat(), 901 map.GetData(), map.GetStride(), srcSurface->GetFormat(), 902 surface->GetSize()); 903 } else { 904 RefPtr<DataSourceSurface> dstSurface = Factory::CreateDataSourceSurface( 905 srcSurface->GetSize(), srcSurface->GetFormat()); 906 if (NS_WARN_IF(!dstSurface)) { 907 return nullptr; 908 } 909 910 DataSourceSurface::ScopedMap srcMap(srcSurface, DataSourceSurface::READ); 911 if (!srcMap.IsMapped()) { 912 gfxCriticalError() 913 << "Failed to map source surface for premultiplying alpha."; 914 return nullptr; 915 } 916 917 DataSourceSurface::ScopedMap dstMap(dstSurface, DataSourceSurface::WRITE); 918 if (!dstMap.IsMapped()) { 919 gfxCriticalError() 920 << "Failed to map destination surface for premultiplying alpha."; 921 return nullptr; 922 } 923 924 PremultiplyData(srcMap.GetData(), srcMap.GetStride(), 925 srcSurface->GetFormat(), dstMap.GetData(), 926 dstMap.GetStride(), dstSurface->GetFormat(), 927 dstSurface->GetSize()); 928 929 surface = std::move(dstSurface); 930 } 931 } 932 933 // Replace our surface with one optimized for the target we're about to draw 934 // to, under the assumption it'll likely be drawn again to that target. 935 // This call should be a no-op for already-optimized surfaces 936 mSurface = aTarget->OptimizeSourceSurface(surface); 937 if (!mSurface) { 938 mSurface = std::move(surface); 939 } 940 return do_AddRef(mSurface); 941 } 942 943 already_AddRefed<layers::Image> ImageBitmap::TransferAsImage() { 944 RefPtr<layers::Image> image = mData; 945 Close(); 946 return image.forget(); 947 } 948 949 UniquePtr<ImageBitmapCloneData> ImageBitmap::ToCloneData() const { 950 if (!mData) { 951 // A closed image cannot be cloned. 952 return nullptr; 953 } 954 955 RefPtr<SourceSurface> surface = mData->GetAsSourceSurface(); 956 if (!surface) { 957 // It might just not be possible to get/map the surface. (e.g. from another 958 // process) 959 return nullptr; 960 } 961 962 RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface(); 963 if (NS_WARN_IF(!dataSurface)) { 964 // This can reasonably fail in many cases (e.g. canvas state doesn't allow 965 // reading back the snapshot). 966 return nullptr; 967 } 968 969 auto result = MakeUnique<ImageBitmapCloneData>(); 970 result->mPictureRect = mPictureRect; 971 result->mAlphaType = mAlphaType; 972 result->mSurface = std::move(dataSurface); 973 result->mWriteOnly = mWriteOnly; 974 return result; 975 } 976 977 /* static */ 978 already_AddRefed<ImageBitmap> ImageBitmap::CreateFromSourceSurface( 979 nsIGlobalObject* aGlobal, gfx::SourceSurface* aSource, ErrorResult& aRv) { 980 RefPtr<layers::Image> data = CreateImageFromSurface(aSource); 981 RefPtr<ImageBitmap> ret = 982 new ImageBitmap(aGlobal, data, true, false /* writeOnly */); 983 return ret.forget(); 984 } 985 986 /* static */ 987 already_AddRefed<ImageBitmap> ImageBitmap::CreateFromCloneData( 988 nsIGlobalObject* aGlobal, ImageBitmapCloneData* aData) { 989 RefPtr<layers::Image> data = CreateImageFromSurface(aData->mSurface); 990 991 RefPtr<ImageBitmap> ret = new ImageBitmap( 992 aGlobal, data, true, aData->mWriteOnly, aData->mAlphaType); 993 994 ErrorResult rv; 995 ret->SetPictureRect(aData->mPictureRect, rv); 996 return ret.forget(); 997 } 998 999 /* static */ 1000 already_AddRefed<ImageBitmap> ImageBitmap::CreateFromOffscreenCanvas( 1001 nsIGlobalObject* aGlobal, OffscreenCanvas& aOffscreenCanvas, 1002 ErrorResult& aRv) { 1003 // Check write-only mode. 1004 bool writeOnly = aOffscreenCanvas.IsWriteOnly(); 1005 uint32_t flags = nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE | 1006 nsLayoutUtils::SFE_EXACT_SIZE_SURFACE; 1007 1008 SurfaceFromElementResult res = 1009 nsLayoutUtils::SurfaceFromOffscreenCanvas(&aOffscreenCanvas, flags); 1010 1011 RefPtr<SourceSurface> surface = res.GetSourceSurface(); 1012 1013 if (NS_WARN_IF(!surface)) { 1014 aRv.ThrowUnknownError("Failed to create ImageBitmap from OffscreenCanvas"); 1015 return nullptr; 1016 } 1017 1018 RefPtr<layers::Image> data = CreateImageFromSurface(surface); 1019 1020 RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, true, writeOnly); 1021 1022 return ret.forget(); 1023 } 1024 1025 /* static */ 1026 already_AddRefed<ImageBitmap> ImageBitmap::CreateImageBitmapInternal( 1027 nsIGlobalObject* aGlobal, gfx::SourceSurface* aSurface, 1028 const Maybe<IntRect>& aCropRect, const ImageBitmapOptions& aOptions, 1029 const bool aWriteOnly, const bool aAllocatedImageData, const bool aMustCopy, 1030 const gfxAlphaType aAlphaType, ErrorResult& aRv) { 1031 bool needToReportMemoryAllocation = aAllocatedImageData; 1032 const IntSize srcSize = aSurface->GetSize(); 1033 IntRect cropRect = 1034 aCropRect.valueOr(IntRect(0, 0, srcSize.width, srcSize.height)); 1035 1036 RefPtr<SourceSurface> surface = aSurface; 1037 RefPtr<DataSourceSurface> dataSurface; 1038 1039 // handle alpha premultiplication if surface not of correct type 1040 1041 gfxAlphaType alphaType = aAlphaType; 1042 bool requiresPremultiply = false; 1043 bool requiresUnpremultiply = false; 1044 1045 if (!IsOpaque(surface->GetFormat())) { 1046 if (aAlphaType == gfxAlphaType::Premult && 1047 aOptions.mPremultiplyAlpha == PremultiplyAlpha::None) { 1048 requiresUnpremultiply = true; 1049 alphaType = gfxAlphaType::NonPremult; 1050 } else if (aAlphaType == gfxAlphaType::NonPremult && 1051 aOptions.mPremultiplyAlpha == PremultiplyAlpha::Premultiply) { 1052 requiresPremultiply = true; 1053 alphaType = gfxAlphaType::Premult; 1054 } 1055 } 1056 1057 /* 1058 * if we don't own the data and need to modify the buffer. 1059 * or 1060 * we need to crop and flip, where crop must come first. 1061 * or 1062 * the caller demands a copy (WebGL contexts). 1063 */ 1064 bool willModify = aOptions.mImageOrientation == ImageOrientation::FlipY || 1065 requiresPremultiply || requiresUnpremultiply; 1066 if ((willModify && !aAllocatedImageData) || 1067 (aOptions.mImageOrientation == ImageOrientation::FlipY && 1068 aCropRect.isSome()) || 1069 aMustCopy) { 1070 dataSurface = surface->GetDataSurface(); 1071 1072 dataSurface = CropAndCopyDataSourceSurface(dataSurface, cropRect); 1073 if (NS_WARN_IF(!dataSurface)) { 1074 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 1075 return nullptr; 1076 } 1077 1078 surface = dataSurface; 1079 cropRect.SetRect(0, 0, dataSurface->GetSize().width, 1080 dataSurface->GetSize().height); 1081 needToReportMemoryAllocation = true; 1082 } 1083 1084 // flip image in Y direction 1085 if (aOptions.mImageOrientation == ImageOrientation::FlipY) { 1086 if (!dataSurface) { 1087 dataSurface = surface->GetDataSurface(); 1088 } 1089 1090 surface = FlipYDataSourceSurface(dataSurface); 1091 if (NS_WARN_IF(!surface)) { 1092 return nullptr; 1093 } 1094 } 1095 1096 if (requiresPremultiply) { 1097 if (!dataSurface) { 1098 dataSurface = surface->GetDataSurface(); 1099 } 1100 1101 surface = AlphaPremultiplyDataSourceSurface(dataSurface, true); 1102 if (NS_WARN_IF(!surface)) { 1103 return nullptr; 1104 } 1105 } 1106 1107 // resize if required 1108 if (aOptions.mResizeWidth.WasPassed() || aOptions.mResizeHeight.WasPassed()) { 1109 if (!dataSurface) { 1110 dataSurface = surface->GetDataSurface(); 1111 }; 1112 1113 surface = ScaleDataSourceSurface(dataSurface, aOptions); 1114 if (NS_WARN_IF(!surface)) { 1115 aRv.ThrowInvalidStateError("Failed to create resized image"); 1116 return nullptr; 1117 } 1118 1119 needToReportMemoryAllocation = true; 1120 cropRect.SetRect(0, 0, surface->GetSize().width, surface->GetSize().height); 1121 } 1122 1123 if (requiresUnpremultiply) { 1124 if (!dataSurface) { 1125 dataSurface = surface->GetDataSurface(); 1126 } 1127 1128 surface = AlphaPremultiplyDataSourceSurface(dataSurface, false); 1129 if (NS_WARN_IF(!surface)) { 1130 return nullptr; 1131 } 1132 } 1133 1134 // Create an Image from the SourceSurface. 1135 RefPtr<layers::Image> data = CreateImageFromSurface(surface); 1136 RefPtr<ImageBitmap> ret = new ImageBitmap( 1137 aGlobal, data, needToReportMemoryAllocation, aWriteOnly, alphaType); 1138 1139 // Set the picture rectangle. 1140 ret->SetPictureRect(cropRect, aRv); 1141 1142 return ret.forget(); 1143 } 1144 1145 /* static */ 1146 already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal( 1147 nsIGlobalObject* aGlobal, HTMLImageElement& aImageEl, 1148 const Maybe<IntRect>& aCropRect, const ImageBitmapOptions& aOptions, 1149 ErrorResult& aRv) { 1150 // Check if the image element is completely available or not. 1151 if (!aImageEl.Complete()) { 1152 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 1153 return nullptr; 1154 } 1155 1156 bool writeOnly = true; 1157 gfxAlphaType alphaType = gfxAlphaType::NonPremult; 1158 1159 // Get the SourceSurface out from the image element and then do security 1160 // checking. 1161 RefPtr<SourceSurface> surface = GetSurfaceFromElement( 1162 aGlobal, aImageEl, &writeOnly, aOptions, &alphaType, aRv); 1163 1164 if (NS_WARN_IF(aRv.Failed())) { 1165 return nullptr; 1166 } 1167 1168 bool needToReportMemoryAllocation = false; 1169 return CreateImageBitmapInternal(aGlobal, surface, aCropRect, aOptions, 1170 writeOnly, needToReportMemoryAllocation, 1171 false, alphaType, aRv); 1172 } 1173 /* static */ 1174 already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal( 1175 nsIGlobalObject* aGlobal, SVGImageElement& aImageEl, 1176 const Maybe<IntRect>& aCropRect, const ImageBitmapOptions& aOptions, 1177 ErrorResult& aRv) { 1178 bool writeOnly = true; 1179 gfxAlphaType alphaType = gfxAlphaType::NonPremult; 1180 1181 // Get the SourceSurface out from the image element and then do security 1182 // checking. 1183 RefPtr<SourceSurface> surface = GetSurfaceFromElement( 1184 aGlobal, aImageEl, &writeOnly, aOptions, &alphaType, aRv); 1185 1186 if (NS_WARN_IF(aRv.Failed())) { 1187 return nullptr; 1188 } 1189 1190 bool needToReportMemoryAllocation = false; 1191 1192 return CreateImageBitmapInternal(aGlobal, surface, aCropRect, aOptions, 1193 writeOnly, needToReportMemoryAllocation, 1194 false, alphaType, aRv); 1195 } 1196 1197 /* static */ 1198 already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal( 1199 nsIGlobalObject* aGlobal, HTMLVideoElement& aVideoEl, 1200 const Maybe<IntRect>& aCropRect, const ImageBitmapOptions& aOptions, 1201 ErrorResult& aRv) { 1202 aVideoEl.LogVisibility( 1203 mozilla::dom::HTMLVideoElement::CallerAPI::CREATE_IMAGEBITMAP); 1204 1205 // Check network state. 1206 if (aVideoEl.NetworkState() == NETWORK_EMPTY) { 1207 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 1208 return nullptr; 1209 } 1210 1211 // Check ready state. 1212 // Cannot be HTMLMediaElement::HAVE_NOTHING or 1213 // HTMLMediaElement::HAVE_METADATA. 1214 if (aVideoEl.ReadyState() <= HAVE_METADATA) { 1215 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 1216 return nullptr; 1217 } 1218 1219 // Check security. 1220 nsCOMPtr<nsIPrincipal> principal = aVideoEl.GetCurrentVideoPrincipal(); 1221 bool hadCrossOriginRedirects = aVideoEl.HadCrossOriginRedirects(); 1222 bool CORSUsed = aVideoEl.GetCORSMode() != CORS_NONE; 1223 bool writeOnly = CanvasUtils::CheckWriteOnlySecurity(CORSUsed, principal, 1224 hadCrossOriginRedirects); 1225 1226 // Create ImageBitmap. 1227 RefPtr<layers::Image> data = aVideoEl.GetCurrentImage(); 1228 if (!data) { 1229 aRv.Throw(NS_ERROR_NOT_AVAILABLE); 1230 return nullptr; 1231 } 1232 1233 RefPtr<SourceSurface> surface = data->GetAsSourceSurface(); 1234 if (!surface) { 1235 // preserve original behavior in case of unavailble surface 1236 RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, false, writeOnly); 1237 return ret.forget(); 1238 } 1239 1240 bool needToReportMemoryAllocation = false; 1241 1242 return CreateImageBitmapInternal(aGlobal, surface, aCropRect, aOptions, 1243 writeOnly, needToReportMemoryAllocation, 1244 false, gfxAlphaType::Premult, aRv); 1245 } 1246 1247 /* static */ 1248 already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal( 1249 nsIGlobalObject* aGlobal, HTMLCanvasElement& aCanvasEl, 1250 const Maybe<IntRect>& aCropRect, const ImageBitmapOptions& aOptions, 1251 ErrorResult& aRv) { 1252 if (aCanvasEl.Width() == 0 || aCanvasEl.Height() == 0) { 1253 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 1254 return nullptr; 1255 } 1256 1257 bool writeOnly = true; 1258 gfxAlphaType alphaType = gfxAlphaType::NonPremult; 1259 1260 RefPtr<SourceSurface> surface = GetSurfaceFromElement( 1261 aGlobal, aCanvasEl, &writeOnly, aOptions, &alphaType, aRv); 1262 1263 if (NS_WARN_IF(aRv.Failed())) { 1264 return nullptr; 1265 } 1266 1267 if (!writeOnly) { 1268 writeOnly = aCanvasEl.IsWriteOnly(); 1269 } 1270 1271 // If the HTMLCanvasElement's rendering context is WebGL/WebGPU, 1272 // then the snapshot we got from the HTMLCanvasElement is 1273 // a DataSourceSurface which is a copy of the rendering context. 1274 // We handle cropping in this case. 1275 bool needToReportMemoryAllocation = false; 1276 bool mustCopy = false; 1277 1278 if ((aCanvasEl.GetCurrentContextType() == CanvasContextType::WebGL1 || 1279 aCanvasEl.GetCurrentContextType() == CanvasContextType::WebGL2 || 1280 aCanvasEl.GetCurrentContextType() == CanvasContextType::WebGPU) && 1281 aCropRect.isSome()) { 1282 mustCopy = true; 1283 } 1284 1285 return CreateImageBitmapInternal(aGlobal, surface, aCropRect, aOptions, 1286 writeOnly, needToReportMemoryAllocation, 1287 mustCopy, alphaType, aRv); 1288 } 1289 1290 /* static */ 1291 already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal( 1292 nsIGlobalObject* aGlobal, OffscreenCanvas& aOffscreenCanvas, 1293 const Maybe<IntRect>& aCropRect, const ImageBitmapOptions& aOptions, 1294 ErrorResult& aRv) { 1295 if (aOffscreenCanvas.Width() == 0) { 1296 aRv.ThrowInvalidStateError("Passed-in canvas has width 0"); 1297 return nullptr; 1298 } 1299 1300 if (aOffscreenCanvas.Height() == 0) { 1301 aRv.ThrowInvalidStateError("Passed-in canvas has height 0"); 1302 return nullptr; 1303 } 1304 1305 uint32_t flags = nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE | 1306 nsLayoutUtils::SFE_EXACT_SIZE_SURFACE; 1307 1308 // by default surfaces have premultiplied alpha 1309 // attempt to get non premultiplied if required 1310 if (aOptions.mPremultiplyAlpha == PremultiplyAlpha::None) { 1311 flags |= nsLayoutUtils::SFE_ALLOW_NON_PREMULT; 1312 } 1313 1314 SurfaceFromElementResult res = 1315 nsLayoutUtils::SurfaceFromOffscreenCanvas(&aOffscreenCanvas, flags); 1316 1317 RefPtr<SourceSurface> surface = res.GetSourceSurface(); 1318 if (NS_WARN_IF(!surface)) { 1319 aRv.ThrowInvalidStateError("Passed-in canvas failed to create snapshot"); 1320 return nullptr; 1321 } 1322 1323 gfxAlphaType alphaType = res.mAlphaType; 1324 bool writeOnly = res.mIsWriteOnly; 1325 1326 // If the OffscreenCanvas's rendering context is WebGL/WebGPU, then the 1327 // snapshot we got from the OffscreenCanvas is a DataSourceSurface which 1328 // is a copy of the rendering context. We handle cropping in this case. 1329 bool needToReportMemoryAllocation = false; 1330 bool mustCopy = 1331 aCropRect.isSome() && 1332 (aOffscreenCanvas.GetContextType() == CanvasContextType::WebGL1 || 1333 aOffscreenCanvas.GetContextType() == CanvasContextType::WebGL2 || 1334 aOffscreenCanvas.GetContextType() == CanvasContextType::WebGPU); 1335 1336 return CreateImageBitmapInternal(aGlobal, surface, aCropRect, aOptions, 1337 writeOnly, needToReportMemoryAllocation, 1338 mustCopy, alphaType, aRv); 1339 } 1340 1341 /* static */ 1342 already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal( 1343 nsIGlobalObject* aGlobal, ImageData& aImageData, 1344 const Maybe<IntRect>& aCropRect, const ImageBitmapOptions& aOptions, 1345 ErrorResult& aRv) { 1346 // Copy data into SourceSurface. 1347 RootedSpiderMonkeyInterface<Uint8ClampedArray> array(RootingCx()); 1348 if (!array.Init(aImageData.GetDataObject())) { 1349 aRv.ThrowInvalidStateError( 1350 "Failed to extract Uint8ClampedArray from ImageData (security check " 1351 "failed?)"); 1352 return nullptr; 1353 } 1354 const SurfaceFormat FORMAT = SurfaceFormat::R8G8B8A8; 1355 // ImageData's underlying data is not alpha-premultiplied. 1356 auto alphaType = (aOptions.mPremultiplyAlpha == PremultiplyAlpha::Premultiply) 1357 ? gfxAlphaType::Premult 1358 : gfxAlphaType::NonPremult; 1359 1360 const uint32_t BYTES_PER_PIXEL = BytesPerPixel(FORMAT); 1361 const uint32_t imageWidth = aImageData.Width(); 1362 const uint32_t imageHeight = aImageData.Height(); 1363 const uint32_t imageStride = imageWidth * BYTES_PER_PIXEL; 1364 const gfx::IntSize imageSize(imageWidth, imageHeight); 1365 1366 // Check the ImageData is neutered or not. 1367 if (imageWidth == 0 || imageHeight == 0) { 1368 aRv.ThrowInvalidStateError("Passed-in image is empty"); 1369 return nullptr; 1370 } 1371 1372 return array.ProcessFixedData( 1373 [&](const Span<const uint8_t>& aData) -> already_AddRefed<ImageBitmap> { 1374 const uint32_t dataLength = aData.Length(); 1375 if ((imageWidth * imageHeight * BYTES_PER_PIXEL) != dataLength) { 1376 aRv.ThrowInvalidStateError("Data size / image format mismatch"); 1377 return nullptr; 1378 } 1379 1380 // Create and Crop the raw data into a layers::Image 1381 RefPtr<layers::Image> data; 1382 1383 uint8_t* fixedData = const_cast<uint8_t*>(aData.Elements()); 1384 1385 if (NS_IsMainThread()) { 1386 data = 1387 CreateImageFromRawData(imageSize, imageStride, FORMAT, fixedData, 1388 dataLength, aCropRect, aOptions); 1389 } else { 1390 RefPtr<CreateImageFromRawDataInMainThreadSyncTask> task = 1391 new CreateImageFromRawDataInMainThreadSyncTask( 1392 fixedData, dataLength, imageStride, FORMAT, imageSize, 1393 aCropRect, getter_AddRefs(data), aOptions); 1394 task->Dispatch(GetCurrentThreadWorkerPrivate(), Canceling, aRv); 1395 } 1396 1397 if (NS_WARN_IF(!data)) { 1398 aRv.ThrowInvalidStateError("Failed to create internal image"); 1399 return nullptr; 1400 } 1401 1402 // Create an ImageBitmap. 1403 RefPtr<ImageBitmap> ret = new ImageBitmap( 1404 aGlobal, data, true, false /* write-only */, alphaType); 1405 1406 // The cropping information has been handled in the 1407 // CreateImageFromRawData() function. 1408 1409 return ret.forget(); 1410 }); 1411 } 1412 1413 /* static */ 1414 already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal( 1415 nsIGlobalObject* aGlobal, CanvasRenderingContext2D& aCanvasCtx, 1416 const Maybe<IntRect>& aCropRect, const ImageBitmapOptions& aOptions, 1417 ErrorResult& aRv) { 1418 nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(aGlobal); 1419 nsGlobalWindowInner* window = nsGlobalWindowInner::Cast(win); 1420 if (NS_WARN_IF(!window) || !window->GetExtantDoc()) { 1421 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); 1422 return nullptr; 1423 } 1424 1425 window->GetExtantDoc()->WarnOnceAbout( 1426 DeprecatedOperations::eCreateImageBitmapCanvasRenderingContext2D); 1427 1428 // Check write-only mode. 1429 bool writeOnly = 1430 aCanvasCtx.GetCanvas()->IsWriteOnly() || aCanvasCtx.IsWriteOnly(); 1431 1432 RefPtr<SourceSurface> surface = aCanvasCtx.GetSurfaceSnapshot(); 1433 1434 if (NS_WARN_IF(!surface)) { 1435 aRv.Throw(NS_ERROR_NOT_AVAILABLE); 1436 return nullptr; 1437 } 1438 1439 const IntSize surfaceSize = surface->GetSize(); 1440 if (surfaceSize.width == 0 || surfaceSize.height == 0) { 1441 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 1442 return nullptr; 1443 } 1444 1445 bool needToReportMemoryAllocation = false; 1446 1447 return CreateImageBitmapInternal(aGlobal, surface, aCropRect, aOptions, 1448 writeOnly, needToReportMemoryAllocation, 1449 false, gfxAlphaType::Premult, aRv); 1450 } 1451 1452 /* static */ 1453 already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal( 1454 nsIGlobalObject* aGlobal, ImageBitmap& aImageBitmap, 1455 const Maybe<IntRect>& aCropRect, const ImageBitmapOptions& aOptions, 1456 ErrorResult& aRv) { 1457 if (!aImageBitmap.mData) { 1458 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 1459 return nullptr; 1460 } 1461 1462 IntRect cropRect; 1463 RefPtr<SourceSurface> surface; 1464 RefPtr<DataSourceSurface> dataSurface; 1465 gfxAlphaType alphaType; 1466 1467 bool needToReportMemoryAllocation = false; 1468 1469 if (aImageBitmap.mSurface && 1470 (dataSurface = aImageBitmap.mSurface->GetDataSurface())) { 1471 // the source imageBitmap already has a cropped surface, and we can get a 1472 // DataSourceSurface from it, so just use it directly 1473 surface = aImageBitmap.mSurface; 1474 cropRect = aCropRect.valueOr(IntRect(IntPoint(0, 0), surface->GetSize())); 1475 alphaType = IsOpaque(surface->GetFormat()) ? gfxAlphaType::Opaque 1476 : gfxAlphaType::Premult; 1477 } else { 1478 RefPtr<layers::Image> data = aImageBitmap.mData; 1479 surface = data->GetAsSourceSurface(); 1480 if (NS_WARN_IF(!surface)) { 1481 aRv.Throw(NS_ERROR_NOT_AVAILABLE); 1482 return nullptr; 1483 } 1484 1485 cropRect = aImageBitmap.mPictureRect; 1486 alphaType = aImageBitmap.mAlphaType; 1487 if (aCropRect.isSome()) { 1488 // get new crop rect relative to original uncropped surface 1489 IntRect newCropRect = aCropRect.ref(); 1490 newCropRect = FixUpNegativeDimension(newCropRect, aRv); 1491 1492 newCropRect.MoveBy(cropRect.X(), cropRect.Y()); 1493 1494 if (cropRect.Contains(newCropRect)) { 1495 // new crop region within existing surface 1496 // safe to just crop this with new rect 1497 cropRect = newCropRect; 1498 } else { 1499 // crop includes area outside original cropped region 1500 // create new surface cropped by original bitmap crop rect 1501 RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface(); 1502 1503 surface = CropAndCopyDataSourceSurface(dataSurface, cropRect); 1504 if (NS_WARN_IF(!surface)) { 1505 aRv.Throw(NS_ERROR_NOT_AVAILABLE); 1506 return nullptr; 1507 } 1508 needToReportMemoryAllocation = true; 1509 cropRect = aCropRect.ref(); 1510 } 1511 } 1512 } 1513 1514 return CreateImageBitmapInternal( 1515 aGlobal, surface, Some(cropRect), aOptions, aImageBitmap.mWriteOnly, 1516 needToReportMemoryAllocation, false, alphaType, aRv); 1517 } 1518 1519 /* static */ 1520 already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal( 1521 nsIGlobalObject* aGlobal, VideoFrame& aVideoFrame, 1522 const Maybe<IntRect>& aCropRect, const ImageBitmapOptions& aOptions, 1523 ErrorResult& aRv) { 1524 if (aVideoFrame.CodedWidth() == 0) { 1525 aRv.ThrowInvalidStateError("Passed-in video frame has width 0"); 1526 return nullptr; 1527 } 1528 1529 if (aVideoFrame.CodedHeight() == 0) { 1530 aRv.ThrowInvalidStateError("Passed-in video frame has height 0"); 1531 return nullptr; 1532 } 1533 1534 uint32_t flags = nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE; 1535 1536 // by default surfaces have premultiplied alpha 1537 // attempt to get non premultiplied if required 1538 if (aOptions.mPremultiplyAlpha == PremultiplyAlpha::None) { 1539 flags |= nsLayoutUtils::SFE_ALLOW_NON_PREMULT; 1540 } 1541 1542 SurfaceFromElementResult res = 1543 nsLayoutUtils::SurfaceFromVideoFrame(&aVideoFrame, flags); 1544 1545 RefPtr<SourceSurface> surface = res.GetSourceSurface(); 1546 if (NS_WARN_IF(!surface)) { 1547 aRv.ThrowInvalidStateError("Passed-in video frame has no surface data"); 1548 return nullptr; 1549 } 1550 1551 gfxAlphaType alphaType = res.mAlphaType; 1552 bool writeOnly = res.mIsWriteOnly; 1553 bool needToReportMemoryAllocation = false; 1554 bool mustCopy = false; 1555 1556 return CreateImageBitmapInternal(aGlobal, surface, aCropRect, aOptions, 1557 writeOnly, needToReportMemoryAllocation, 1558 mustCopy, alphaType, aRv); 1559 } 1560 1561 class FulfillImageBitmapPromise { 1562 protected: 1563 FulfillImageBitmapPromise(Promise* aPromise, ImageBitmap* aImageBitmap) 1564 : mPromise(aPromise), mImageBitmap(aImageBitmap) { 1565 MOZ_ASSERT(aPromise); 1566 } 1567 1568 void DoFulfillImageBitmapPromise() { mPromise->MaybeResolve(mImageBitmap); } 1569 1570 private: 1571 RefPtr<Promise> mPromise; 1572 RefPtr<ImageBitmap> mImageBitmap; 1573 }; 1574 1575 class FulfillImageBitmapPromiseTask final : public Runnable, 1576 public FulfillImageBitmapPromise { 1577 public: 1578 FulfillImageBitmapPromiseTask(Promise* aPromise, ImageBitmap* aImageBitmap) 1579 : Runnable("dom::FulfillImageBitmapPromiseTask"), 1580 FulfillImageBitmapPromise(aPromise, aImageBitmap) {} 1581 1582 NS_IMETHOD Run() override { 1583 DoFulfillImageBitmapPromise(); 1584 return NS_OK; 1585 } 1586 }; 1587 1588 class FulfillImageBitmapPromiseWorkerTask final 1589 : public WorkerSameThreadRunnable, 1590 public FulfillImageBitmapPromise { 1591 public: 1592 FulfillImageBitmapPromiseWorkerTask(Promise* aPromise, 1593 ImageBitmap* aImageBitmap) 1594 : WorkerSameThreadRunnable("FulfillImageBitmapPromiseWorkerTask"), 1595 FulfillImageBitmapPromise(aPromise, aImageBitmap) {} 1596 1597 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { 1598 DoFulfillImageBitmapPromise(); 1599 return true; 1600 } 1601 }; 1602 1603 static void AsyncFulfillImageBitmapPromise(Promise* aPromise, 1604 ImageBitmap* aImageBitmap) { 1605 if (NS_IsMainThread()) { 1606 nsCOMPtr<nsIRunnable> task = 1607 new FulfillImageBitmapPromiseTask(aPromise, aImageBitmap); 1608 NS_DispatchToCurrentThread(task); // Actually, to the main-thread. 1609 } else { 1610 RefPtr<FulfillImageBitmapPromiseWorkerTask> task = 1611 new FulfillImageBitmapPromiseWorkerTask(aPromise, aImageBitmap); 1612 task->Dispatch(GetCurrentThreadWorkerPrivate()); // Actually, to the 1613 // current worker-thread. 1614 } 1615 } 1616 1617 class CreateImageBitmapFromBlobRunnable; 1618 1619 class CreateImageBitmapFromBlob final : public DiscardableRunnable, 1620 public imgIContainerCallback, 1621 public nsIInputStreamCallback { 1622 friend class CreateImageBitmapFromBlobRunnable; 1623 1624 public: 1625 NS_DECL_ISUPPORTS_INHERITED 1626 NS_DECL_IMGICONTAINERCALLBACK 1627 NS_DECL_NSIINPUTSTREAMCALLBACK 1628 1629 static already_AddRefed<CreateImageBitmapFromBlob> Create( 1630 Promise* aPromise, nsIGlobalObject* aGlobal, Blob& aBlob, 1631 const Maybe<IntRect>& aCropRect, nsIEventTarget* aMainThreadEventTarget, 1632 const ImageBitmapOptions& aOptions); 1633 1634 NS_IMETHOD Run() override { 1635 MOZ_ASSERT(IsCurrentThread()); 1636 1637 nsresult rv = StartMimeTypeAndDecodeAndCropBlob(); 1638 if (NS_WARN_IF(NS_FAILED(rv))) { 1639 MimeTypeAndDecodeAndCropBlobCompletedMainThread(nullptr, rv); 1640 } 1641 1642 return NS_OK; 1643 } 1644 1645 // Called by the WorkerRef. 1646 void WorkerShuttingDown(); 1647 1648 private: 1649 CreateImageBitmapFromBlob(Promise* aPromise, nsIGlobalObject* aGlobal, 1650 already_AddRefed<nsIInputStream> aInputStream, 1651 const Maybe<IntRect>& aCropRect, 1652 nsIEventTarget* aMainThreadEventTarget, 1653 const ImageBitmapOptions& aOptions) 1654 : DiscardableRunnable("dom::CreateImageBitmapFromBlob"), 1655 1656 mMutex("dom::CreateImageBitmapFromBlob::mMutex"), 1657 mPromise(aPromise), 1658 mGlobalObject(aGlobal), 1659 mInputStream(std::move(aInputStream)), 1660 mCropRect(aCropRect), 1661 mMainThreadEventTarget(aMainThreadEventTarget), 1662 mOptions(aOptions), 1663 mThread(PR_GetCurrentThread()) {} 1664 1665 virtual ~CreateImageBitmapFromBlob() = default; 1666 1667 bool IsCurrentThread() const { return mThread == PR_GetCurrentThread(); } 1668 1669 // Called on the owning thread. 1670 nsresult StartMimeTypeAndDecodeAndCropBlob(); 1671 1672 // Will be called when the decoding + cropping is completed on the 1673 // main-thread. This could the not the owning thread! 1674 void MimeTypeAndDecodeAndCropBlobCompletedMainThread(layers::Image* aImage, 1675 nsresult aStatus); 1676 1677 // Will be called when the decoding + cropping is completed on the owning 1678 // thread. 1679 void MimeTypeAndDecodeAndCropBlobCompletedOwningThread(layers::Image* aImage, 1680 nsresult aStatus); 1681 1682 // This is called on the main-thread only. 1683 nsresult MimeTypeAndDecodeAndCropBlob(); 1684 1685 // This is called on the main-thread only. 1686 nsresult DecodeAndCropBlob(const nsACString& aMimeType); 1687 1688 // This is called on the main-thread only. 1689 nsresult GetMimeTypeSync(nsACString& aMimeType); 1690 1691 // This is called on the main-thread only. 1692 nsresult GetMimeTypeAsync(); 1693 1694 Mutex mMutex MOZ_UNANNOTATED; 1695 1696 // The access to this object is protected by mutex but is always nullified on 1697 // the owning thread. 1698 RefPtr<ThreadSafeWorkerRef> mWorkerRef; 1699 1700 // Touched only on the owning thread. 1701 RefPtr<Promise> mPromise; 1702 1703 // Touched only on the owning thread. 1704 nsCOMPtr<nsIGlobalObject> mGlobalObject; 1705 1706 nsCOMPtr<nsIInputStream> mInputStream; 1707 Maybe<IntRect> mCropRect; 1708 nsCOMPtr<nsIEventTarget> mMainThreadEventTarget; 1709 const ImageBitmapOptions mOptions; 1710 void* mThread; 1711 }; 1712 1713 NS_IMPL_ISUPPORTS_INHERITED(CreateImageBitmapFromBlob, DiscardableRunnable, 1714 imgIContainerCallback, nsIInputStreamCallback) 1715 1716 class CreateImageBitmapFromBlobRunnable final : public WorkerThreadRunnable { 1717 public: 1718 explicit CreateImageBitmapFromBlobRunnable(CreateImageBitmapFromBlob* aTask, 1719 layers::Image* aImage, 1720 nsresult aStatus) 1721 : WorkerThreadRunnable("CreateImageBitmapFromBlobRunnable"), 1722 mTask(aTask), 1723 mImage(aImage), 1724 mStatus(aStatus) {} 1725 1726 // Override Predispatch/PostDispatch to remove the noise of assertion for 1727 // nested Worker. 1728 virtual bool PreDispatch(WorkerPrivate*) override { return true; } 1729 virtual void PostDispatch(WorkerPrivate*, bool) override {} 1730 1731 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { 1732 mTask->MimeTypeAndDecodeAndCropBlobCompletedOwningThread(mImage, mStatus); 1733 return true; 1734 } 1735 1736 private: 1737 RefPtr<CreateImageBitmapFromBlob> mTask; 1738 RefPtr<layers::Image> mImage; 1739 nsresult mStatus; 1740 }; 1741 1742 static void AsyncCreateImageBitmapFromBlob(Promise* aPromise, 1743 nsIGlobalObject* aGlobal, 1744 Blob& aBlob, 1745 const Maybe<IntRect>& aCropRect, 1746 const ImageBitmapOptions& aOptions) { 1747 // Let's identify the main-thread event target. 1748 nsCOMPtr<nsIEventTarget> mainThreadEventTarget; 1749 if (NS_IsMainThread()) { 1750 mainThreadEventTarget = aGlobal->SerialEventTarget(); 1751 } else { 1752 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 1753 MOZ_ASSERT(workerPrivate); 1754 mainThreadEventTarget = workerPrivate->MainThreadEventTarget(); 1755 } 1756 1757 RefPtr<CreateImageBitmapFromBlob> task = CreateImageBitmapFromBlob::Create( 1758 aPromise, aGlobal, aBlob, aCropRect, mainThreadEventTarget, aOptions); 1759 if (NS_WARN_IF(!task)) { 1760 aPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR); 1761 return; 1762 } 1763 1764 NS_DispatchToCurrentThread(task); 1765 } 1766 1767 /* static */ 1768 already_AddRefed<Promise> ImageBitmap::Create( 1769 nsIGlobalObject* aGlobal, const ImageBitmapSource& aSrc, 1770 const Maybe<gfx::IntRect>& aCropRect, const ImageBitmapOptions& aOptions, 1771 ErrorResult& aRv) { 1772 MOZ_ASSERT(aGlobal); 1773 1774 RefPtr<Promise> promise = Promise::Create(aGlobal, aRv); 1775 1776 if (NS_WARN_IF(aRv.Failed())) { 1777 return nullptr; 1778 } 1779 1780 if (aCropRect.isSome()) { 1781 if (aCropRect->Width() == 0) { 1782 aRv.ThrowRangeError( 1783 "The crop rect width passed to createImageBitmap must be nonzero"); 1784 return promise.forget(); 1785 } 1786 1787 if (aCropRect->Height() == 0) { 1788 aRv.ThrowRangeError( 1789 "The crop rect height passed to createImageBitmap must be nonzero"); 1790 return promise.forget(); 1791 } 1792 } 1793 1794 if (aOptions.mResizeWidth.WasPassed() && aOptions.mResizeWidth.Value() == 0) { 1795 aRv.ThrowInvalidStateError( 1796 "The resizeWidth passed to createImageBitmap must be nonzero"); 1797 return promise.forget(); 1798 } 1799 1800 if (aOptions.mResizeHeight.WasPassed() && 1801 aOptions.mResizeHeight.Value() == 0) { 1802 aRv.ThrowInvalidStateError( 1803 "The resizeHeight passed to createImageBitmap must be nonzero"); 1804 return promise.forget(); 1805 } 1806 1807 RefPtr<ImageBitmap> imageBitmap; 1808 1809 if (aSrc.IsHTMLImageElement()) { 1810 MOZ_ASSERT( 1811 NS_IsMainThread(), 1812 "Creating ImageBitmap from HTMLImageElement off the main thread."); 1813 imageBitmap = CreateInternal(aGlobal, aSrc.GetAsHTMLImageElement(), 1814 aCropRect, aOptions, aRv); 1815 } else if (aSrc.IsSVGImageElement()) { 1816 MOZ_ASSERT( 1817 NS_IsMainThread(), 1818 "Creating ImageBitmap from SVGImageElement off the main thread."); 1819 imageBitmap = CreateInternal(aGlobal, aSrc.GetAsSVGImageElement(), 1820 aCropRect, aOptions, aRv); 1821 } else if (aSrc.IsHTMLVideoElement()) { 1822 MOZ_ASSERT( 1823 NS_IsMainThread(), 1824 "Creating ImageBitmap from HTMLVideoElement off the main thread."); 1825 imageBitmap = CreateInternal(aGlobal, aSrc.GetAsHTMLVideoElement(), 1826 aCropRect, aOptions, aRv); 1827 } else if (aSrc.IsHTMLCanvasElement()) { 1828 MOZ_ASSERT( 1829 NS_IsMainThread(), 1830 "Creating ImageBitmap from HTMLCanvasElement off the main thread."); 1831 imageBitmap = CreateInternal(aGlobal, aSrc.GetAsHTMLCanvasElement(), 1832 aCropRect, aOptions, aRv); 1833 } else if (aSrc.IsOffscreenCanvas()) { 1834 imageBitmap = CreateInternal(aGlobal, aSrc.GetAsOffscreenCanvas(), 1835 aCropRect, aOptions, aRv); 1836 } else if (aSrc.IsImageData()) { 1837 imageBitmap = CreateInternal(aGlobal, aSrc.GetAsImageData(), aCropRect, 1838 aOptions, aRv); 1839 } else if (aSrc.IsCanvasRenderingContext2D()) { 1840 MOZ_ASSERT(NS_IsMainThread(), 1841 "Creating ImageBitmap from CanvasRenderingContext2D off the " 1842 "main thread."); 1843 imageBitmap = CreateInternal(aGlobal, aSrc.GetAsCanvasRenderingContext2D(), 1844 aCropRect, aOptions, aRv); 1845 } else if (aSrc.IsImageBitmap()) { 1846 imageBitmap = CreateInternal(aGlobal, aSrc.GetAsImageBitmap(), aCropRect, 1847 aOptions, aRv); 1848 } else if (aSrc.IsBlob()) { 1849 AsyncCreateImageBitmapFromBlob(promise, aGlobal, aSrc.GetAsBlob(), 1850 aCropRect, aOptions); 1851 return promise.forget(); 1852 } else if (aSrc.IsVideoFrame()) { 1853 imageBitmap = CreateInternal(aGlobal, aSrc.GetAsVideoFrame(), aCropRect, 1854 aOptions, aRv); 1855 } else { 1856 MOZ_CRASH("Unsupported type!"); 1857 return nullptr; 1858 } 1859 1860 if (!aRv.Failed()) { 1861 AsyncFulfillImageBitmapPromise(promise, imageBitmap); 1862 } 1863 1864 return promise.forget(); 1865 } 1866 1867 /*static*/ 1868 JSObject* ImageBitmap::ReadStructuredClone( 1869 JSContext* aCx, JSStructuredCloneReader* aReader, nsIGlobalObject* aParent, 1870 const nsTArray<RefPtr<DataSourceSurface>>& aClonedSurfaces, 1871 uint32_t aIndex) { 1872 MOZ_ASSERT(aCx); 1873 MOZ_ASSERT(aReader); 1874 // aParent might be null. 1875 1876 uint32_t picRectX_; 1877 uint32_t picRectY_; 1878 uint32_t picRectWidth_; 1879 uint32_t picRectHeight_; 1880 uint32_t alphaType_; 1881 uint32_t writeOnly; 1882 1883 if (!JS_ReadUint32Pair(aReader, &picRectX_, &picRectY_) || 1884 !JS_ReadUint32Pair(aReader, &picRectWidth_, &picRectHeight_) || 1885 !JS_ReadUint32Pair(aReader, &alphaType_, &writeOnly)) { 1886 return nullptr; 1887 } 1888 1889 int32_t picRectX = BitwiseCast<int32_t>(picRectX_); 1890 int32_t picRectY = BitwiseCast<int32_t>(picRectY_); 1891 int32_t picRectWidth = BitwiseCast<int32_t>(picRectWidth_); 1892 int32_t picRectHeight = BitwiseCast<int32_t>(picRectHeight_); 1893 const auto alphaType = BitwiseCast<gfxAlphaType>(alphaType_); 1894 1895 // Create a new ImageBitmap. 1896 MOZ_ASSERT(!aClonedSurfaces.IsEmpty()); 1897 MOZ_ASSERT(aIndex < aClonedSurfaces.Length()); 1898 1899 // RefPtr<ImageBitmap> needs to go out of scope before toObjectOrNull() is 1900 // called because the static analysis thinks dereferencing XPCOM objects 1901 // can GC (because in some cases it can!), and a return statement with a 1902 // JSObject* type means that JSObject* is on the stack as a raw pointer 1903 // while destructors are running. 1904 JS::Rooted<JS::Value> value(aCx); 1905 { 1906 #ifdef FUZZING 1907 if (aIndex >= aClonedSurfaces.Length()) { 1908 return nullptr; 1909 } 1910 #endif 1911 RefPtr<layers::Image> img = CreateImageFromSurface(aClonedSurfaces[aIndex]); 1912 RefPtr<ImageBitmap> imageBitmap = 1913 new ImageBitmap(aParent, img, true, !!writeOnly, alphaType); 1914 1915 ErrorResult error; 1916 imageBitmap->SetPictureRect( 1917 IntRect(picRectX, picRectY, picRectWidth, picRectHeight), error); 1918 if (NS_WARN_IF(error.Failed())) { 1919 error.SuppressException(); 1920 return nullptr; 1921 } 1922 1923 if (!GetOrCreateDOMReflector(aCx, imageBitmap, &value)) { 1924 return nullptr; 1925 } 1926 } 1927 1928 return &(value.toObject()); 1929 } 1930 1931 /*static*/ 1932 void ImageBitmap::WriteStructuredClone( 1933 JSStructuredCloneWriter* aWriter, 1934 nsTArray<RefPtr<DataSourceSurface>>& aClonedSurfaces, 1935 ImageBitmap* aImageBitmap, ErrorResult& aRv) { 1936 MOZ_ASSERT(aWriter); 1937 MOZ_ASSERT(aImageBitmap); 1938 1939 if (aImageBitmap->IsWriteOnly()) { 1940 return aRv.ThrowDataCloneError("Cannot clone ImageBitmap, is write-only"); 1941 } 1942 1943 if (!aImageBitmap->mData) { 1944 // A closed image cannot be cloned. 1945 return aRv.ThrowDataCloneError("Cannot clone ImageBitmap, is closed"); 1946 } 1947 1948 const uint32_t picRectX = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.x); 1949 const uint32_t picRectY = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.y); 1950 const uint32_t picRectWidth = 1951 BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.width); 1952 const uint32_t picRectHeight = 1953 BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.height); 1954 const uint32_t alphaType = BitwiseCast<uint32_t>(aImageBitmap->mAlphaType); 1955 1956 // Indexing the cloned surfaces and send the index to the receiver. 1957 uint32_t index = aClonedSurfaces.Length(); 1958 1959 if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter, SCTAG_DOM_IMAGEBITMAP, index)) || 1960 NS_WARN_IF(!JS_WriteUint32Pair(aWriter, picRectX, picRectY)) || 1961 NS_WARN_IF(!JS_WriteUint32Pair(aWriter, picRectWidth, picRectHeight)) || 1962 NS_WARN_IF( 1963 !JS_WriteUint32Pair(aWriter, alphaType, aImageBitmap->mWriteOnly))) { 1964 return aRv.ThrowDataCloneError( 1965 "Cannot clone ImageBitmap, failed to write params"); 1966 } 1967 1968 RefPtr<SourceSurface> surface = aImageBitmap->mData->GetAsSourceSurface(); 1969 if (NS_WARN_IF(!surface)) { 1970 return aRv.ThrowDataCloneError("Cannot clone ImageBitmap, no surface"); 1971 } 1972 1973 RefPtr<DataSourceSurface> snapshot = surface->GetDataSurface(); 1974 if (NS_WARN_IF(!snapshot)) { 1975 return aRv.ThrowDataCloneError("Cannot clone ImageBitmap, no data surface"); 1976 } 1977 1978 RefPtr<DataSourceSurface> dstDataSurface; 1979 { 1980 // DataSourceSurfaceD2D1::GetStride() will call EnsureMapped implicitly and 1981 // won't Unmap after exiting function. So instead calling GetStride() 1982 // directly, using ScopedMap to get stride. 1983 DataSourceSurface::ScopedMap map(snapshot, DataSourceSurface::READ); 1984 if (NS_WARN_IF(!map.IsMapped())) { 1985 return aRv.ThrowDataCloneError( 1986 "Cannot clone ImageBitmap, cannot map surface"); 1987 } 1988 1989 dstDataSurface = Factory::CreateDataSourceSurfaceWithStride( 1990 snapshot->GetSize(), snapshot->GetFormat(), map.GetStride(), true); 1991 } 1992 if (NS_WARN_IF(!dstDataSurface)) { 1993 return aRv.ThrowDataCloneError("Cannot clone ImageBitmap, out of memory"); 1994 } 1995 Factory::CopyDataSourceSurface(snapshot, dstDataSurface); 1996 aClonedSurfaces.AppendElement(dstDataSurface); 1997 } 1998 1999 size_t ImageBitmap::GetAllocatedSize() const { 2000 if (!mAllocatedImageData || !mData) { 2001 return 0; 2002 } 2003 2004 // Calculate how many bytes are used. 2005 switch (mData->GetFormat()) { 2006 case ImageFormat::PLANAR_YCBCR: 2007 return mData->AsPlanarYCbCrImage()->GetDataSize(); 2008 case ImageFormat::NV_IMAGE: 2009 return mData->AsNVImage()->GetBufferSize(); 2010 default: 2011 break; 2012 } 2013 2014 IntSize size = mData->GetSize(); 2015 CheckedInt<uint32_t> bytes = 2016 CheckedInt<uint32_t>(size.width) * size.height * 4; 2017 return bytes.isValid() ? bytes.value() : 0; 2018 } 2019 2020 size_t BindingJSObjectMallocBytes(ImageBitmap* aBitmap) { 2021 return aBitmap->GetAllocatedSize(); 2022 } 2023 2024 void ImageBitmap::RemoveAssociatedMemory() { 2025 if (!mAllocatedImageData) { 2026 return; 2027 } 2028 if (JSObject* wrapper = GetWrapperMaybeDead()) { 2029 if (size_t bytes = BindingJSObjectMallocBytes(this)) { 2030 JS::RemoveAssociatedMemory(wrapper, bytes, JS::MemoryUse::DOMBinding); 2031 } 2032 } 2033 mAllocatedImageData = false; 2034 } 2035 2036 /* static */ 2037 already_AddRefed<CreateImageBitmapFromBlob> CreateImageBitmapFromBlob::Create( 2038 Promise* aPromise, nsIGlobalObject* aGlobal, Blob& aBlob, 2039 const Maybe<IntRect>& aCropRect, nsIEventTarget* aMainThreadEventTarget, 2040 const ImageBitmapOptions& aOptions) { 2041 // Get the internal stream of the blob. 2042 nsCOMPtr<nsIInputStream> stream; 2043 ErrorResult error; 2044 aBlob.Impl()->CreateInputStream(getter_AddRefs(stream), error); 2045 if (NS_WARN_IF(error.Failed())) { 2046 return nullptr; 2047 } 2048 2049 if (!NS_InputStreamIsBuffered(stream)) { 2050 nsCOMPtr<nsIInputStream> bufferedStream; 2051 nsresult rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), 2052 stream.forget(), 4096); 2053 if (NS_WARN_IF(NS_FAILED(rv))) { 2054 return nullptr; 2055 } 2056 2057 stream = bufferedStream; 2058 } 2059 2060 RefPtr<CreateImageBitmapFromBlob> task = new CreateImageBitmapFromBlob( 2061 aPromise, aGlobal, stream.forget(), aCropRect, aMainThreadEventTarget, 2062 aOptions); 2063 2064 // Nothing to do for the main-thread. 2065 if (NS_IsMainThread()) { 2066 return task.forget(); 2067 } 2068 2069 // Let's use a WorkerRef to keep the worker alive if this is not the 2070 // main-thread. 2071 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 2072 MOZ_ASSERT(workerPrivate); 2073 2074 RefPtr<StrongWorkerRef> workerRef = 2075 StrongWorkerRef::Create(workerPrivate, "CreateImageBitmapFromBlob", 2076 [task]() { task->WorkerShuttingDown(); }); 2077 if (NS_WARN_IF(!workerRef)) { 2078 return nullptr; 2079 } 2080 2081 task->mWorkerRef = new ThreadSafeWorkerRef(workerRef); 2082 return task.forget(); 2083 } 2084 2085 nsresult CreateImageBitmapFromBlob::StartMimeTypeAndDecodeAndCropBlob() { 2086 MOZ_ASSERT(IsCurrentThread()); 2087 2088 // Workers. 2089 if (!NS_IsMainThread()) { 2090 RefPtr<CreateImageBitmapFromBlob> self = this; 2091 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction( 2092 "CreateImageBitmapFromBlob::MimeTypeAndDecodeAndCropBlob", [self]() { 2093 nsresult rv = self->MimeTypeAndDecodeAndCropBlob(); 2094 if (NS_WARN_IF(NS_FAILED(rv))) { 2095 self->MimeTypeAndDecodeAndCropBlobCompletedMainThread(nullptr, rv); 2096 } 2097 }); 2098 2099 return mMainThreadEventTarget->Dispatch(r.forget()); 2100 } 2101 2102 // Main-thread. 2103 return MimeTypeAndDecodeAndCropBlob(); 2104 } 2105 2106 nsresult CreateImageBitmapFromBlob::MimeTypeAndDecodeAndCropBlob() { 2107 MOZ_ASSERT(NS_IsMainThread()); 2108 2109 nsAutoCString mimeType; 2110 nsresult rv = GetMimeTypeSync(mimeType); 2111 if (rv == NS_BASE_STREAM_WOULD_BLOCK) { 2112 return GetMimeTypeAsync(); 2113 } 2114 2115 if (NS_WARN_IF(NS_FAILED(rv))) { 2116 return rv; 2117 } 2118 2119 return DecodeAndCropBlob(mimeType); 2120 } 2121 2122 nsresult CreateImageBitmapFromBlob::DecodeAndCropBlob( 2123 const nsACString& aMimeType) { 2124 // Get the Component object. 2125 nsCOMPtr<imgITools> imgtool = do_GetService(NS_IMGTOOLS_CID); 2126 if (NS_WARN_IF(!imgtool)) { 2127 return NS_ERROR_FAILURE; 2128 } 2129 2130 // Decode image. 2131 nsresult rv = imgtool->DecodeImageAsync(mInputStream, aMimeType, this, 2132 mMainThreadEventTarget); 2133 if (NS_WARN_IF(NS_FAILED(rv))) { 2134 return rv; 2135 } 2136 2137 return NS_OK; 2138 } 2139 2140 static nsresult sniff_cb(nsIInputStream* aInputStream, void* aClosure, 2141 const char* aFromRawSegment, uint32_t aToOffset, 2142 uint32_t aCount, uint32_t* aWriteCount) { 2143 nsACString* mimeType = static_cast<nsACString*>(aClosure); 2144 MOZ_ASSERT(mimeType); 2145 2146 if (aCount > 0) { 2147 imgLoader::GetMimeTypeFromContent(aFromRawSegment, aCount, *mimeType); 2148 } 2149 2150 *aWriteCount = 0; 2151 2152 // We don't want to consume data from the stream. 2153 return NS_ERROR_FAILURE; 2154 } 2155 2156 nsresult CreateImageBitmapFromBlob::GetMimeTypeSync(nsACString& aMimeType) { 2157 uint32_t dummy; 2158 return mInputStream->ReadSegments(sniff_cb, &aMimeType, 128, &dummy); 2159 } 2160 2161 nsresult CreateImageBitmapFromBlob::GetMimeTypeAsync() { 2162 nsCOMPtr<nsIAsyncInputStream> asyncInputStream = 2163 do_QueryInterface(mInputStream); 2164 if (NS_WARN_IF(!asyncInputStream)) { 2165 // If the stream is not async, why are we here? 2166 return NS_ERROR_FAILURE; 2167 } 2168 2169 return asyncInputStream->AsyncWait(this, 0, 128, mMainThreadEventTarget); 2170 } 2171 2172 NS_IMETHODIMP 2173 CreateImageBitmapFromBlob::OnInputStreamReady(nsIAsyncInputStream* aStream) { 2174 // The stream should have data now. Let's start from scratch again. 2175 nsresult rv = MimeTypeAndDecodeAndCropBlob(); 2176 if (NS_WARN_IF(NS_FAILED(rv))) { 2177 MimeTypeAndDecodeAndCropBlobCompletedMainThread(nullptr, rv); 2178 } 2179 2180 return NS_OK; 2181 } 2182 2183 NS_IMETHODIMP 2184 CreateImageBitmapFromBlob::OnImageReady(imgIContainer* aImgContainer, 2185 nsresult aStatus) { 2186 MOZ_ASSERT(NS_IsMainThread()); 2187 2188 if (NS_FAILED(aStatus)) { 2189 MimeTypeAndDecodeAndCropBlobCompletedMainThread(nullptr, aStatus); 2190 return NS_OK; 2191 } 2192 2193 MOZ_ASSERT(aImgContainer); 2194 2195 // Get the surface out. 2196 uint32_t frameFlags = 2197 imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY; 2198 uint32_t whichFrame = imgIContainer::FRAME_FIRST; 2199 2200 if (mOptions.mPremultiplyAlpha == PremultiplyAlpha::None) { 2201 frameFlags |= imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA; 2202 } 2203 2204 if (mOptions.mColorSpaceConversion == ColorSpaceConversion::None) { 2205 frameFlags |= imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION; 2206 } 2207 2208 RefPtr<SourceSurface> surface = 2209 aImgContainer->GetFrame(whichFrame, frameFlags); 2210 2211 if (NS_WARN_IF(!surface)) { 2212 MimeTypeAndDecodeAndCropBlobCompletedMainThread( 2213 nullptr, NS_ERROR_DOM_INVALID_STATE_ERR); 2214 return NS_OK; 2215 } 2216 2217 // Crop the source surface if needed. 2218 RefPtr<SourceSurface> croppedSurface = surface; 2219 RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface(); 2220 2221 // force a copy into unprotected memory as a side effect of 2222 // CropAndCopyDataSourceSurface 2223 bool copyRequired = mCropRect.isSome() || 2224 mOptions.mImageOrientation == ImageOrientation::FlipY; 2225 2226 if (copyRequired) { 2227 // The blob is just decoded into a RasterImage and not optimized yet, so the 2228 // _surface_ we get is a DataSourceSurface which wraps the RasterImage's 2229 // raw buffer. 2230 // 2231 // The _surface_ might already be optimized so that its type is not 2232 // SurfaceType::DATA. However, we could keep using the generic cropping and 2233 // copying since the decoded buffer is only used in this ImageBitmap so we 2234 // should crop it to save memory usage. 2235 // 2236 // TODO: Bug1189632 is going to refactor this create-from-blob part to 2237 // decode the blob off the main thread. Re-check if we should do 2238 // cropping at this moment again there. 2239 2240 IntRect cropRect = 2241 mCropRect.isSome() ? mCropRect.ref() : dataSurface->GetRect(); 2242 2243 croppedSurface = CropAndCopyDataSourceSurface(dataSurface, cropRect); 2244 if (NS_WARN_IF(!croppedSurface)) { 2245 MimeTypeAndDecodeAndCropBlobCompletedMainThread( 2246 nullptr, NS_ERROR_DOM_INVALID_STATE_ERR); 2247 return NS_OK; 2248 } 2249 2250 dataSurface = croppedSurface->GetDataSurface(); 2251 2252 if (mCropRect.isSome()) { 2253 mCropRect->SetRect(0, 0, dataSurface->GetSize().width, 2254 dataSurface->GetSize().height); 2255 } 2256 } 2257 2258 if (mOptions.mImageOrientation == ImageOrientation::FlipY) { 2259 croppedSurface = FlipYDataSourceSurface(dataSurface); 2260 } 2261 2262 if (mOptions.mResizeWidth.WasPassed() || mOptions.mResizeHeight.WasPassed()) { 2263 dataSurface = croppedSurface->GetDataSurface(); 2264 croppedSurface = ScaleDataSourceSurface(dataSurface, mOptions); 2265 if (NS_WARN_IF(!croppedSurface)) { 2266 MimeTypeAndDecodeAndCropBlobCompletedMainThread( 2267 nullptr, NS_ERROR_DOM_INVALID_STATE_ERR); 2268 return NS_OK; 2269 } 2270 if (mCropRect.isSome()) { 2271 mCropRect->SetRect(0, 0, croppedSurface->GetSize().width, 2272 croppedSurface->GetSize().height); 2273 } 2274 } 2275 2276 if (NS_WARN_IF(!croppedSurface)) { 2277 MimeTypeAndDecodeAndCropBlobCompletedMainThread( 2278 nullptr, NS_ERROR_DOM_INVALID_STATE_ERR); 2279 return NS_OK; 2280 } 2281 2282 // Create an Image from the source surface. 2283 RefPtr<layers::Image> image = CreateImageFromSurface(croppedSurface); 2284 2285 if (NS_WARN_IF(!image)) { 2286 MimeTypeAndDecodeAndCropBlobCompletedMainThread( 2287 nullptr, NS_ERROR_DOM_INVALID_STATE_ERR); 2288 return NS_OK; 2289 } 2290 2291 MimeTypeAndDecodeAndCropBlobCompletedMainThread(image, NS_OK); 2292 return NS_OK; 2293 } 2294 2295 void CreateImageBitmapFromBlob::MimeTypeAndDecodeAndCropBlobCompletedMainThread( 2296 layers::Image* aImage, nsresult aStatus) { 2297 MOZ_ASSERT(NS_IsMainThread()); 2298 2299 if (!IsCurrentThread()) { 2300 MutexAutoLock lock(mMutex); 2301 2302 if (!mWorkerRef) { 2303 // The worker is already gone. 2304 return; 2305 } 2306 2307 RefPtr<CreateImageBitmapFromBlobRunnable> r = 2308 new CreateImageBitmapFromBlobRunnable(this, aImage, aStatus); 2309 r->Dispatch(mWorkerRef->Private()); 2310 return; 2311 } 2312 2313 MimeTypeAndDecodeAndCropBlobCompletedOwningThread(aImage, aStatus); 2314 } 2315 2316 void CreateImageBitmapFromBlob:: 2317 MimeTypeAndDecodeAndCropBlobCompletedOwningThread(layers::Image* aImage, 2318 nsresult aStatus) { 2319 MOZ_ASSERT(IsCurrentThread()); 2320 2321 if (!mPromise) { 2322 // The worker is going to be released soon. No needs to continue. 2323 return; 2324 } 2325 2326 // Let's release what has to be released on the owning thread. 2327 auto raii = MakeScopeExit([&] { 2328 // Doing this we also release the worker. 2329 mWorkerRef = nullptr; 2330 2331 mPromise = nullptr; 2332 mGlobalObject = nullptr; 2333 }); 2334 2335 if (NS_WARN_IF(NS_FAILED(aStatus))) { 2336 mPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR); 2337 return; 2338 } 2339 2340 gfxAlphaType alphaType = gfxAlphaType::Premult; 2341 2342 if (mOptions.mPremultiplyAlpha == PremultiplyAlpha::None) { 2343 alphaType = gfxAlphaType::NonPremult; 2344 } 2345 2346 // Create ImageBitmap object. 2347 RefPtr<ImageBitmap> imageBitmap = new ImageBitmap( 2348 mGlobalObject, aImage, true, false /* write-only */, alphaType); 2349 2350 if (mCropRect.isSome()) { 2351 ErrorResult rv; 2352 imageBitmap->SetPictureRect(mCropRect.ref(), rv); 2353 2354 if (rv.Failed()) { 2355 mPromise->MaybeReject(std::move(rv)); 2356 return; 2357 } 2358 } 2359 2360 mPromise->MaybeResolve(imageBitmap); 2361 } 2362 2363 void CreateImageBitmapFromBlob::WorkerShuttingDown() { 2364 MOZ_ASSERT(IsCurrentThread()); 2365 2366 MutexAutoLock lock(mMutex); 2367 2368 // Let's release all the non-thread-safe objects now. 2369 mWorkerRef = nullptr; 2370 mPromise = nullptr; 2371 mGlobalObject = nullptr; 2372 } 2373 2374 } // namespace mozilla::dom