Source code

Revision control

Other Tools

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
#include "mozilla/CheckedInt.h"
9
#include "mozilla/dom/HTMLMediaElementBinding.h"
10
#include "mozilla/dom/ImageBitmapBinding.h"
11
#include "mozilla/dom/Promise.h"
12
#include "mozilla/dom/StructuredCloneTags.h"
13
#include "mozilla/dom/WorkerPrivate.h"
14
#include "mozilla/dom/WorkerRef.h"
15
#include "mozilla/dom/WorkerRunnable.h"
16
#include "mozilla/gfx/2D.h"
17
#include "mozilla/gfx/Swizzle.h"
18
#include "mozilla/Mutex.h"
19
#include "mozilla/ScopeExit.h"
20
#include "nsNetUtil.h"
21
#include "nsStreamUtils.h"
22
#include "ImageUtils.h"
23
#include "imgLoader.h"
24
#include "imgTools.h"
25
26
using namespace mozilla::gfx;
27
using namespace mozilla::layers;
28
using mozilla::dom::HTMLMediaElement_Binding::HAVE_METADATA;
29
using mozilla::dom::HTMLMediaElement_Binding::NETWORK_EMPTY;
30
31
namespace mozilla {
32
namespace dom {
33
34
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ImageBitmap, mParent)
35
NS_IMPL_CYCLE_COLLECTING_ADDREF(ImageBitmap)
36
NS_IMPL_CYCLE_COLLECTING_RELEASE(ImageBitmap)
37
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ImageBitmap)
38
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
39
NS_INTERFACE_MAP_ENTRY(nsISupports)
40
NS_INTERFACE_MAP_END
41
42
/* This class observes shutdown notifications and sends that notification
43
* to the worker thread if the image bitmap is on a worker thread.
44
*/
45
class ImageBitmapShutdownObserver final : public nsIObserver {
46
public:
47
explicit ImageBitmapShutdownObserver(ImageBitmap* aImageBitmap)
48
: mImageBitmap(nullptr) {
49
if (NS_IsMainThread()) {
50
mImageBitmap = aImageBitmap;
51
} else {
52
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
53
MOZ_ASSERT(workerPrivate);
54
mMainThreadEventTarget = workerPrivate->MainThreadEventTarget();
55
mSendToWorkerTask = new SendShutdownToWorkerThread(aImageBitmap);
56
}
57
}
58
59
void RegisterObserver() {
60
if (NS_IsMainThread()) {
61
nsContentUtils::RegisterShutdownObserver(this);
62
return;
63
}
64
65
MOZ_ASSERT(mMainThreadEventTarget);
66
RefPtr<ImageBitmapShutdownObserver> self = this;
67
nsCOMPtr<nsIRunnable> r =
68
NS_NewRunnableFunction("ImageBitmapShutdownObserver::RegisterObserver",
69
[self]() { self->RegisterObserver(); });
70
71
mMainThreadEventTarget->Dispatch(r.forget());
72
}
73
74
void UnregisterObserver() {
75
if (NS_IsMainThread()) {
76
nsContentUtils::UnregisterShutdownObserver(this);
77
return;
78
}
79
80
MOZ_ASSERT(mMainThreadEventTarget);
81
RefPtr<ImageBitmapShutdownObserver> self = this;
82
nsCOMPtr<nsIRunnable> r =
83
NS_NewRunnableFunction("ImageBitmapShutdownObserver::RegisterObserver",
84
[self]() { self->UnregisterObserver(); });
85
86
mMainThreadEventTarget->Dispatch(r.forget());
87
}
88
89
void Clear() {
90
mImageBitmap = nullptr;
91
if (mSendToWorkerTask) {
92
mSendToWorkerTask->mImageBitmap = nullptr;
93
}
94
}
95
96
NS_DECL_THREADSAFE_ISUPPORTS
97
NS_DECL_NSIOBSERVER
98
private:
99
~ImageBitmapShutdownObserver() {}
100
101
class SendShutdownToWorkerThread : public MainThreadWorkerControlRunnable {
102
public:
103
explicit SendShutdownToWorkerThread(ImageBitmap* aImageBitmap)
104
: MainThreadWorkerControlRunnable(GetCurrentThreadWorkerPrivate()),
105
mImageBitmap(aImageBitmap) {}
106
107
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
108
if (mImageBitmap) {
109
mImageBitmap->OnShutdown();
110
mImageBitmap = nullptr;
111
}
112
return true;
113
}
114
115
ImageBitmap* mImageBitmap;
116
};
117
118
ImageBitmap* mImageBitmap;
119
nsCOMPtr<nsIEventTarget> mMainThreadEventTarget;
120
RefPtr<SendShutdownToWorkerThread> mSendToWorkerTask;
121
};
122
123
NS_IMPL_ISUPPORTS(ImageBitmapShutdownObserver, nsIObserver)
124
125
NS_IMETHODIMP
126
ImageBitmapShutdownObserver::Observe(nsISupports* aSubject, const char* aTopic,
127
const char16_t* aData) {
128
if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
129
if (mSendToWorkerTask) {
130
mSendToWorkerTask->Dispatch();
131
} else {
132
if (mImageBitmap) {
133
mImageBitmap->OnShutdown();
134
mImageBitmap = nullptr;
135
}
136
}
137
nsContentUtils::UnregisterShutdownObserver(this);
138
}
139
140
return NS_OK;
141
}
142
143
/*
144
* If either aRect.width or aRect.height are negative, then return a new IntRect
145
* which represents the same rectangle as the aRect does but with positive width
146
* and height.
147
*/
148
static IntRect FixUpNegativeDimension(const IntRect& aRect, ErrorResult& aRv) {
149
gfx::IntRect rect = aRect;
150
151
// fix up negative dimensions
152
if (rect.width < 0) {
153
CheckedInt32 checkedX = CheckedInt32(rect.x) + rect.width;
154
155
if (!checkedX.isValid()) {
156
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
157
return rect;
158
}
159
160
rect.x = checkedX.value();
161
rect.width = -(rect.width);
162
}
163
164
if (rect.height < 0) {
165
CheckedInt32 checkedY = CheckedInt32(rect.y) + rect.height;
166
167
if (!checkedY.isValid()) {
168
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
169
return rect;
170
}
171
172
rect.y = checkedY.value();
173
rect.height = -(rect.height);
174
}
175
176
return rect;
177
}
178
179
/*
180
* This helper function copies the data of the given DataSourceSurface,
181
* _aSurface_, in the given area, _aCropRect_, into a new DataSourceSurface.
182
* This might return null if it can not create a new SourceSurface or it cannot
183
* read data from the given _aSurface_.
184
*
185
* Warning: Even though the area of _aCropRect_ is just the same as the size of
186
* _aSurface_, this function still copy data into a new
187
* DataSourceSurface.
188
*/
189
static already_AddRefed<DataSourceSurface> CropAndCopyDataSourceSurface(
190
DataSourceSurface* aSurface, const IntRect& aCropRect) {
191
MOZ_ASSERT(aSurface);
192
193
// Check the aCropRect
194
ErrorResult error;
195
const IntRect positiveCropRect = FixUpNegativeDimension(aCropRect, error);
196
if (NS_WARN_IF(error.Failed())) {
197
error.SuppressException();
198
return nullptr;
199
}
200
201
// Calculate the size of the new SourceSurface.
202
// We cannot keep using aSurface->GetFormat() to create new DataSourceSurface,
203
// since it might be SurfaceFormat::B8G8R8X8 which does not handle opacity,
204
// however the specification explicitly define that "If any of the pixels on
205
// this rectangle are outside the area where the input bitmap was placed, then
206
// they will be transparent black in output."
207
// So, instead, we force the output format to be SurfaceFormat::B8G8R8A8.
208
const SurfaceFormat format = SurfaceFormat::B8G8R8A8;
209
const int bytesPerPixel = BytesPerPixel(format);
210
const IntSize dstSize =
211
IntSize(positiveCropRect.width, positiveCropRect.height);
212
const uint32_t dstStride = dstSize.width * bytesPerPixel;
213
214
// Create a new SourceSurface.
215
RefPtr<DataSourceSurface> dstDataSurface =
216
Factory::CreateDataSourceSurfaceWithStride(dstSize, format, dstStride,
217
true);
218
219
if (NS_WARN_IF(!dstDataSurface)) {
220
return nullptr;
221
}
222
223
// Only do copying and cropping when the positiveCropRect intersects with
224
// the size of aSurface.
225
const IntRect surfRect(IntPoint(0, 0), aSurface->GetSize());
226
if (surfRect.Intersects(positiveCropRect)) {
227
const IntRect surfPortion = surfRect.Intersect(positiveCropRect);
228
const IntPoint dest(std::max(0, surfPortion.X() - positiveCropRect.X()),
229
std::max(0, surfPortion.Y() - positiveCropRect.Y()));
230
231
// Copy the raw data into the newly created DataSourceSurface.
232
DataSourceSurface::ScopedMap srcMap(aSurface, DataSourceSurface::READ);
233
DataSourceSurface::ScopedMap dstMap(dstDataSurface,
234
DataSourceSurface::WRITE);
235
if (NS_WARN_IF(!srcMap.IsMapped()) || NS_WARN_IF(!dstMap.IsMapped())) {
236
return nullptr;
237
}
238
239
uint8_t* srcBufferPtr = srcMap.GetData() +
240
surfPortion.y * srcMap.GetStride() +
241
surfPortion.x * bytesPerPixel;
242
uint8_t* dstBufferPtr =
243
dstMap.GetData() + dest.y * dstMap.GetStride() + dest.x * bytesPerPixel;
244
CheckedInt<uint32_t> copiedBytesPerRaw =
245
CheckedInt<uint32_t>(surfPortion.width) * bytesPerPixel;
246
if (!copiedBytesPerRaw.isValid()) {
247
return nullptr;
248
}
249
250
for (int i = 0; i < surfPortion.height; ++i) {
251
memcpy(dstBufferPtr, srcBufferPtr, copiedBytesPerRaw.value());
252
srcBufferPtr += srcMap.GetStride();
253
dstBufferPtr += dstMap.GetStride();
254
}
255
}
256
257
return dstDataSurface.forget();
258
}
259
260
/*
261
* Encapsulate the given _aSurface_ into a layers::SourceSurfaceImage.
262
*/
263
static already_AddRefed<layers::Image> CreateImageFromSurface(
264
SourceSurface* aSurface) {
265
MOZ_ASSERT(aSurface);
266
RefPtr<layers::SourceSurfaceImage> image =
267
new layers::SourceSurfaceImage(aSurface->GetSize(), aSurface);
268
return image.forget();
269
}
270
271
/*
272
* CreateImageFromRawData(), CreateSurfaceFromRawData() and
273
* CreateImageFromRawDataInMainThreadSyncTask are helpers for
274
* create-from-ImageData case
275
*/
276
static already_AddRefed<SourceSurface> CreateSurfaceFromRawData(
277
const gfx::IntSize& aSize, uint32_t aStride, gfx::SurfaceFormat aFormat,
278
uint8_t* aBuffer, uint32_t aBufferLength, const Maybe<IntRect>& aCropRect) {
279
MOZ_ASSERT(!aSize.IsEmpty());
280
MOZ_ASSERT(aBuffer);
281
282
// Wrap the source buffer into a SourceSurface.
283
RefPtr<DataSourceSurface> dataSurface =
284
Factory::CreateWrappingDataSourceSurface(aBuffer, aStride, aSize,
285
aFormat);
286
287
if (NS_WARN_IF(!dataSurface)) {
288
return nullptr;
289
}
290
291
// The temporary cropRect variable is equal to the size of source buffer if we
292
// do not need to crop, or it equals to the given cropping size.
293
const IntRect cropRect =
294
aCropRect.valueOr(IntRect(0, 0, aSize.width, aSize.height));
295
296
// Copy the source buffer in the _cropRect_ area into a new SourceSurface.
297
RefPtr<DataSourceSurface> result =
298
CropAndCopyDataSourceSurface(dataSurface, cropRect);
299
300
if (NS_WARN_IF(!result)) {
301
return nullptr;
302
}
303
304
return result.forget();
305
}
306
307
static already_AddRefed<layers::Image> CreateImageFromRawData(
308
const gfx::IntSize& aSize, uint32_t aStride, gfx::SurfaceFormat aFormat,
309
uint8_t* aBuffer, uint32_t aBufferLength, const Maybe<IntRect>& aCropRect) {
310
MOZ_ASSERT(NS_IsMainThread());
311
312
// Copy and crop the source buffer into a SourceSurface.
313
RefPtr<SourceSurface> rgbaSurface = CreateSurfaceFromRawData(
314
aSize, aStride, aFormat, aBuffer, aBufferLength, aCropRect);
315
316
if (NS_WARN_IF(!rgbaSurface)) {
317
return nullptr;
318
}
319
320
// Convert RGBA to BGRA
321
RefPtr<DataSourceSurface> rgbaDataSurface = rgbaSurface->GetDataSurface();
322
DataSourceSurface::ScopedMap rgbaMap(rgbaDataSurface,
323
DataSourceSurface::READ);
324
if (NS_WARN_IF(!rgbaMap.IsMapped())) {
325
return nullptr;
326
}
327
328
RefPtr<DataSourceSurface> bgraDataSurface =
329
Factory::CreateDataSourceSurfaceWithStride(rgbaDataSurface->GetSize(),
330
SurfaceFormat::B8G8R8A8,
331
rgbaMap.GetStride());
332
if (NS_WARN_IF(!bgraDataSurface)) {
333
return nullptr;
334
}
335
336
DataSourceSurface::ScopedMap bgraMap(bgraDataSurface,
337
DataSourceSurface::WRITE);
338
if (NS_WARN_IF(!bgraMap.IsMapped())) {
339
return nullptr;
340
}
341
342
SwizzleData(rgbaMap.GetData(), rgbaMap.GetStride(), SurfaceFormat::R8G8B8A8,
343
bgraMap.GetData(), bgraMap.GetStride(), SurfaceFormat::B8G8R8A8,
344
bgraDataSurface->GetSize());
345
346
// Create an Image from the BGRA SourceSurface.
347
RefPtr<layers::Image> image = CreateImageFromSurface(bgraDataSurface);
348
349
if (NS_WARN_IF(!image)) {
350
return nullptr;
351
}
352
353
return image.forget();
354
}
355
356
/*
357
* This is a synchronous task.
358
* This class is used to create a layers::SourceSurfaceImage from raw data in
359
* the main thread. While creating an ImageBitmap from an ImageData, we need to
360
* create a SouceSurface from the ImageData's raw data and then set the
361
* SourceSurface into a layers::SourceSurfaceImage. However, the
362
* layers::SourceSurfaceImage asserts the setting operation in the main thread,
363
* so if we are going to create an ImageBitmap from an ImageData off the main
364
* thread, we post an event to the main thread to create a
365
* layers::SourceSurfaceImage from an ImageData's raw data.
366
*/
367
class CreateImageFromRawDataInMainThreadSyncTask final
368
: public WorkerMainThreadRunnable {
369
public:
370
CreateImageFromRawDataInMainThreadSyncTask(
371
uint8_t* aBuffer, uint32_t aBufferLength, uint32_t aStride,
372
gfx::SurfaceFormat aFormat, const gfx::IntSize& aSize,
373
const Maybe<IntRect>& aCropRect, layers::Image** aImage)
374
: WorkerMainThreadRunnable(
375
GetCurrentThreadWorkerPrivate(),
376
NS_LITERAL_CSTRING("ImageBitmap :: Create Image from Raw Data")),
377
mImage(aImage),
378
mBuffer(aBuffer),
379
mBufferLength(aBufferLength),
380
mStride(aStride),
381
mFormat(aFormat),
382
mSize(aSize),
383
mCropRect(aCropRect) {
384
MOZ_ASSERT(!(*aImage),
385
"Don't pass an existing Image into "
386
"CreateImageFromRawDataInMainThreadSyncTask.");
387
}
388
389
bool MainThreadRun() override {
390
RefPtr<layers::Image> image = CreateImageFromRawData(
391
mSize, mStride, mFormat, mBuffer, mBufferLength, mCropRect);
392
393
if (NS_WARN_IF(!image)) {
394
return false;
395
}
396
397
image.forget(mImage);
398
399
return true;
400
}
401
402
private:
403
layers::Image** mImage;
404
uint8_t* mBuffer;
405
uint32_t mBufferLength;
406
uint32_t mStride;
407
gfx::SurfaceFormat mFormat;
408
gfx::IntSize mSize;
409
const Maybe<IntRect>& mCropRect;
410
};
411
412
/*
413
* A wrapper to the nsLayoutUtils::SurfaceFromElement() function followed by the
414
* security checking.
415
*/
416
template <class ElementType>
417
static already_AddRefed<SourceSurface> GetSurfaceFromElement(
418
nsIGlobalObject* aGlobal, ElementType& aElement, bool* aWriteOnly,
419
ErrorResult& aRv) {
420
nsLayoutUtils::SurfaceFromElementResult res =
421
nsLayoutUtils::SurfaceFromElement(
422
&aElement, nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE);
423
424
RefPtr<SourceSurface> surface = res.GetSourceSurface();
425
if (NS_WARN_IF(!surface)) {
426
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
427
return nullptr;
428
}
429
430
*aWriteOnly = res.mIsWriteOnly;
431
432
return surface.forget();
433
}
434
435
ImageBitmap::ImageBitmap(nsIGlobalObject* aGlobal, layers::Image* aData,
436
bool aWriteOnly, gfxAlphaType aAlphaType)
437
: mParent(aGlobal),
438
mData(aData),
439
mSurface(nullptr),
440
mDataWrapper(new ImageUtils(mData)),
441
mPictureRect(0, 0, aData->GetSize().width, aData->GetSize().height),
442
mAlphaType(aAlphaType),
443
mAllocatedImageData(false),
444
mWriteOnly(aWriteOnly) {
445
MOZ_ASSERT(aData, "aData is null in ImageBitmap constructor.");
446
447
mShutdownObserver = new ImageBitmapShutdownObserver(this);
448
mShutdownObserver->RegisterObserver();
449
}
450
451
ImageBitmap::~ImageBitmap() {
452
if (mShutdownObserver) {
453
mShutdownObserver->Clear();
454
mShutdownObserver->UnregisterObserver();
455
mShutdownObserver = nullptr;
456
}
457
}
458
459
JSObject* ImageBitmap::WrapObject(JSContext* aCx,
460
JS::Handle<JSObject*> aGivenProto) {
461
return ImageBitmap_Binding::Wrap(aCx, this, aGivenProto);
462
}
463
464
void ImageBitmap::Close() {
465
mData = nullptr;
466
mSurface = nullptr;
467
mDataWrapper = nullptr;
468
mPictureRect.SetEmpty();
469
}
470
471
void ImageBitmap::OnShutdown() {
472
mShutdownObserver = nullptr;
473
474
Close();
475
}
476
477
void ImageBitmap::SetPictureRect(const IntRect& aRect, ErrorResult& aRv) {
478
mPictureRect = FixUpNegativeDimension(aRect, aRv);
479
}
480
481
/*
482
* The functionality of PrepareForDrawTarget method:
483
* (1) Get a SourceSurface from the mData (which is a layers::Image).
484
* (2) Convert the SourceSurface to format B8G8R8A8 if the original format is
485
* R8G8B8, B8G8R8, HSV or Lab.
486
* Note: if the original format is A8 or Depth, then return null directly.
487
* (3) Do cropping if the size of SourceSurface does not equal to the
488
* mPictureRect.
489
* (4) Pre-multiply alpha if needed.
490
*/
491
already_AddRefed<SourceSurface> ImageBitmap::PrepareForDrawTarget(
492
gfx::DrawTarget* aTarget) {
493
MOZ_ASSERT(aTarget);
494
495
if (!mData) {
496
return nullptr;
497
}
498
499
if (!mSurface) {
500
mSurface = mData->GetAsSourceSurface();
501
502
if (!mSurface) {
503
return nullptr;
504
}
505
}
506
507
RefPtr<DrawTarget> target = aTarget;
508
IntRect surfRect(0, 0, mSurface->GetSize().width, mSurface->GetSize().height);
509
510
// Check if we still need to crop our surface
511
if (!mPictureRect.IsEqualEdges(surfRect)) {
512
IntRect surfPortion = surfRect.Intersect(mPictureRect);
513
514
// the crop lies entirely outside the surface area, nothing to draw
515
if (surfPortion.IsEmpty()) {
516
mSurface = nullptr;
517
RefPtr<gfx::SourceSurface> surface(mSurface);
518
return surface.forget();
519
}
520
521
IntPoint dest(std::max(0, surfPortion.X() - mPictureRect.X()),
522
std::max(0, surfPortion.Y() - mPictureRect.Y()));
523
524
// We must initialize this target with mPictureRect.Size() because the
525
// specification states that if the cropping area is given, then return an
526
// ImageBitmap with the size equals to the cropping area.
527
target = target->CreateSimilarDrawTarget(mPictureRect.Size(),
528
target->GetFormat());
529
530
if (!target) {
531
mSurface = nullptr;
532
RefPtr<gfx::SourceSurface> surface(mSurface);
533
return surface.forget();
534
}
535
536
target->CopySurface(mSurface, surfPortion, dest);
537
mSurface = target->Snapshot();
538
539
// Make mCropRect match new surface we've cropped to
540
mPictureRect.MoveTo(0, 0);
541
}
542
543
// Pre-multiply alpha here.
544
// Ignore this step if the source surface does not have alpha channel; this
545
// kind of source surfaces might come form layers::PlanarYCbCrImage.
546
if (mAlphaType == gfxAlphaType::NonPremult &&
547
!IsOpaque(mSurface->GetFormat())) {
548
MOZ_ASSERT(mSurface->GetFormat() == SurfaceFormat::R8G8B8A8 ||
549
mSurface->GetFormat() == SurfaceFormat::B8G8R8A8 ||
550
mSurface->GetFormat() == SurfaceFormat::A8R8G8B8);
551
552
RefPtr<DataSourceSurface> dstSurface = mSurface->GetDataSurface();
553
MOZ_ASSERT(dstSurface);
554
555
RefPtr<DataSourceSurface> srcSurface;
556
DataSourceSurface::MappedSurface srcMap;
557
DataSourceSurface::MappedSurface dstMap;
558
559
if (dstSurface->Map(DataSourceSurface::MapType::READ_WRITE, &dstMap)) {
560
srcMap = dstMap;
561
} else {
562
srcSurface = dstSurface;
563
if (!srcSurface->Map(DataSourceSurface::READ, &srcMap)) {
564
gfxCriticalError()
565
<< "Failed to map source surface for premultiplying alpha.";
566
return nullptr;
567
}
568
569
dstSurface = Factory::CreateDataSourceSurface(srcSurface->GetSize(),
570
srcSurface->GetFormat());
571
572
if (!dstSurface ||
573
!dstSurface->Map(DataSourceSurface::MapType::WRITE, &dstMap)) {
574
gfxCriticalError()
575
<< "Failed to map destination surface for premultiplying alpha.";
576
srcSurface->Unmap();
577
return nullptr;
578
}
579
}
580
581
PremultiplyData(srcMap.mData, srcMap.mStride, mSurface->GetFormat(),
582
dstMap.mData, dstMap.mStride, mSurface->GetFormat(),
583
dstSurface->GetSize());
584
585
dstSurface->Unmap();
586
if (srcSurface) {
587
srcSurface->Unmap();
588
}
589
590
mSurface = dstSurface;
591
}
592
593
// Replace our surface with one optimized for the target we're about to draw
594
// to, under the assumption it'll likely be drawn again to that target.
595
// This call should be a no-op for already-optimized surfaces
596
mSurface = target->OptimizeSourceSurface(mSurface);
597
598
RefPtr<gfx::SourceSurface> surface(mSurface);
599
return surface.forget();
600
}
601
602
already_AddRefed<layers::Image> ImageBitmap::TransferAsImage() {
603
RefPtr<layers::Image> image = mData;
604
Close();
605
return image.forget();
606
}
607
608
UniquePtr<ImageBitmapCloneData> ImageBitmap::ToCloneData() const {
609
if (!mData) {
610
// A closed image cannot be cloned.
611
return nullptr;
612
}
613
614
UniquePtr<ImageBitmapCloneData> result(new ImageBitmapCloneData());
615
result->mPictureRect = mPictureRect;
616
result->mAlphaType = mAlphaType;
617
RefPtr<SourceSurface> surface = mData->GetAsSourceSurface();
618
result->mSurface = surface->GetDataSurface();
619
MOZ_ASSERT(result->mSurface);
620
result->mWriteOnly = mWriteOnly;
621
622
return result;
623
}
624
625
/* static */
626
already_AddRefed<ImageBitmap> ImageBitmap::CreateFromSourceSurface(
627
nsIGlobalObject* aGlobal, gfx::SourceSurface* aSource, ErrorResult& aRv) {
628
RefPtr<layers::Image> data = CreateImageFromSurface(aSource);
629
RefPtr<ImageBitmap> ret =
630
new ImageBitmap(aGlobal, data, false /* writeOnly */);
631
ret->mAllocatedImageData = true;
632
return ret.forget();
633
}
634
635
/* static */
636
already_AddRefed<ImageBitmap> ImageBitmap::CreateFromCloneData(
637
nsIGlobalObject* aGlobal, ImageBitmapCloneData* aData) {
638
RefPtr<layers::Image> data = CreateImageFromSurface(aData->mSurface);
639
640
RefPtr<ImageBitmap> ret =
641
new ImageBitmap(aGlobal, data, aData->mWriteOnly, aData->mAlphaType);
642
643
ret->mAllocatedImageData = true;
644
645
ErrorResult rv;
646
ret->SetPictureRect(aData->mPictureRect, rv);
647
return ret.forget();
648
}
649
650
/* static */
651
already_AddRefed<ImageBitmap> ImageBitmap::CreateFromOffscreenCanvas(
652
nsIGlobalObject* aGlobal, OffscreenCanvas& aOffscreenCanvas,
653
ErrorResult& aRv) {
654
// Check write-only mode.
655
bool writeOnly = aOffscreenCanvas.IsWriteOnly();
656
657
nsLayoutUtils::SurfaceFromElementResult res =
658
nsLayoutUtils::SurfaceFromOffscreenCanvas(
659
&aOffscreenCanvas, nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE);
660
661
RefPtr<SourceSurface> surface = res.GetSourceSurface();
662
663
if (NS_WARN_IF(!surface)) {
664
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
665
return nullptr;
666
}
667
668
RefPtr<layers::Image> data = CreateImageFromSurface(surface);
669
670
RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, writeOnly);
671
672
ret->mAllocatedImageData = true;
673
674
return ret.forget();
675
}
676
677
/* static */
678
already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal(
679
nsIGlobalObject* aGlobal, HTMLImageElement& aImageEl,
680
const Maybe<IntRect>& aCropRect, ErrorResult& aRv) {
681
// Check if the image element is completely available or not.
682
if (!aImageEl.Complete()) {
683
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
684
return nullptr;
685
}
686
687
bool writeOnly = true;
688
689
// Get the SourceSurface out from the image element and then do security
690
// checking.
691
RefPtr<SourceSurface> surface =
692
GetSurfaceFromElement(aGlobal, aImageEl, &writeOnly, aRv);
693
694
if (NS_WARN_IF(aRv.Failed())) {
695
return nullptr;
696
}
697
698
// Create ImageBitmap.
699
RefPtr<layers::Image> data = CreateImageFromSurface(surface);
700
701
if (NS_WARN_IF(!data)) {
702
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
703
return nullptr;
704
}
705
706
RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, writeOnly);
707
708
// Set the picture rectangle.
709
if (ret && aCropRect.isSome()) {
710
ret->SetPictureRect(aCropRect.ref(), aRv);
711
}
712
713
return ret.forget();
714
}
715
716
/* static */
717
already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal(
718
nsIGlobalObject* aGlobal, SVGImageElement& aImageEl,
719
const Maybe<IntRect>& aCropRect, ErrorResult& aRv) {
720
bool writeOnly = true;
721
722
// Get the SourceSurface out from the image element and then do security
723
// checking.
724
RefPtr<SourceSurface> surface =
725
GetSurfaceFromElement(aGlobal, aImageEl, &writeOnly, aRv);
726
727
if (NS_WARN_IF(aRv.Failed())) {
728
return nullptr;
729
}
730
731
// Create ImageBitmap.
732
RefPtr<layers::Image> data = CreateImageFromSurface(surface);
733
734
if (NS_WARN_IF(!data)) {
735
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
736
return nullptr;
737
}
738
739
RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, writeOnly);
740
741
// Set the picture rectangle.
742
if (ret && aCropRect.isSome()) {
743
ret->SetPictureRect(aCropRect.ref(), aRv);
744
}
745
746
return ret.forget();
747
}
748
749
/* static */
750
already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal(
751
nsIGlobalObject* aGlobal, HTMLVideoElement& aVideoEl,
752
const Maybe<IntRect>& aCropRect, ErrorResult& aRv) {
753
aVideoEl.MarkAsContentSource(
754
mozilla::dom::HTMLVideoElement::CallerAPI::CREATE_IMAGEBITMAP);
755
756
// Check network state.
757
if (aVideoEl.NetworkState() == NETWORK_EMPTY) {
758
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
759
return nullptr;
760
}
761
762
// Check ready state.
763
// Cannot be HTMLMediaElement::HAVE_NOTHING or
764
// HTMLMediaElement::HAVE_METADATA.
765
if (aVideoEl.ReadyState() <= HAVE_METADATA) {
766
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
767
return nullptr;
768
}
769
770
// Check security.
771
nsCOMPtr<nsIPrincipal> principal = aVideoEl.GetCurrentVideoPrincipal();
772
bool hadCrossOriginRedirects = aVideoEl.HadCrossOriginRedirects();
773
bool CORSUsed = aVideoEl.GetCORSMode() != CORS_NONE;
774
bool writeOnly =
775
CheckWriteOnlySecurity(CORSUsed, principal, hadCrossOriginRedirects);
776
777
// Create ImageBitmap.
778
RefPtr<layers::Image> data = aVideoEl.GetCurrentImage();
779
if (!data) {
780
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
781
return nullptr;
782
}
783
RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, writeOnly);
784
785
// Set the picture rectangle.
786
if (ret && aCropRect.isSome()) {
787
ret->SetPictureRect(aCropRect.ref(), aRv);
788
}
789
790
return ret.forget();
791
}
792
793
/* static */
794
already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal(
795
nsIGlobalObject* aGlobal, HTMLCanvasElement& aCanvasEl,
796
const Maybe<IntRect>& aCropRect, ErrorResult& aRv) {
797
if (aCanvasEl.Width() == 0 || aCanvasEl.Height() == 0) {
798
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
799
return nullptr;
800
}
801
802
bool writeOnly = true;
803
RefPtr<SourceSurface> surface =
804
GetSurfaceFromElement(aGlobal, aCanvasEl, &writeOnly, aRv);
805
806
if (NS_WARN_IF(aRv.Failed())) {
807
return nullptr;
808
}
809
810
if (!writeOnly) {
811
writeOnly = aCanvasEl.IsWriteOnly();
812
}
813
814
// Crop the source surface if needed.
815
RefPtr<SourceSurface> croppedSurface;
816
IntRect cropRect = aCropRect.valueOr(IntRect());
817
818
// If the HTMLCanvasElement's rendering context is WebGL, then the snapshot
819
// we got from the HTMLCanvasElement is a DataSourceSurface which is a copy
820
// of the rendering context. We handle cropping in this case.
821
bool needToReportMemoryAllocation = false;
822
if ((aCanvasEl.GetCurrentContextType() == CanvasContextType::WebGL1 ||
823
aCanvasEl.GetCurrentContextType() == CanvasContextType::WebGL2) &&
824
aCropRect.isSome()) {
825
RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface();
826
croppedSurface = CropAndCopyDataSourceSurface(dataSurface, cropRect);
827
cropRect.MoveTo(0, 0);
828
needToReportMemoryAllocation = true;
829
} else {
830
croppedSurface = surface;
831
}
832
833
if (NS_WARN_IF(!croppedSurface)) {
834
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
835
return nullptr;
836
}
837
838
// Create an Image from the SourceSurface.
839
RefPtr<layers::Image> data = CreateImageFromSurface(croppedSurface);
840
841
if (NS_WARN_IF(!data)) {
842
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
843
return nullptr;
844
}
845
846
RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, writeOnly);
847
848
if (needToReportMemoryAllocation) {
849
ret->mAllocatedImageData = true;
850
}
851
852
// Set the picture rectangle.
853
if (ret && aCropRect.isSome()) {
854
ret->SetPictureRect(cropRect, aRv);
855
}
856
857
return ret.forget();
858
}
859
860
/* static */
861
already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal(
862
nsIGlobalObject* aGlobal, ImageData& aImageData,
863
const Maybe<IntRect>& aCropRect, ErrorResult& aRv) {
864
// Copy data into SourceSurface.
865
dom::Uint8ClampedArray array;
866
DebugOnly<bool> inited = array.Init(aImageData.GetDataObject());
867
MOZ_ASSERT(inited);
868
869
array.ComputeLengthAndData();
870
const SurfaceFormat FORMAT = SurfaceFormat::R8G8B8A8;
871
// ImageData's underlying data is not alpha-premultiplied.
872
const auto alphaType = gfxAlphaType::NonPremult;
873
const uint32_t BYTES_PER_PIXEL = BytesPerPixel(FORMAT);
874
const uint32_t imageWidth = aImageData.Width();
875
const uint32_t imageHeight = aImageData.Height();
876
const uint32_t imageStride = imageWidth * BYTES_PER_PIXEL;
877
const uint32_t dataLength = array.Length();
878
const gfx::IntSize imageSize(imageWidth, imageHeight);
879
880
// Check the ImageData is neutered or not.
881
if (imageWidth == 0 || imageHeight == 0 ||
882
(imageWidth * imageHeight * BYTES_PER_PIXEL) != dataLength) {
883
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
884
return nullptr;
885
}
886
887
// Create and Crop the raw data into a layers::Image
888
RefPtr<layers::Image> data;
889
if (NS_IsMainThread()) {
890
data = CreateImageFromRawData(imageSize, imageStride, FORMAT, array.Data(),
891
dataLength, aCropRect);
892
} else {
893
RefPtr<CreateImageFromRawDataInMainThreadSyncTask> task =
894
new CreateImageFromRawDataInMainThreadSyncTask(
895
array.Data(), dataLength, imageStride, FORMAT, imageSize, aCropRect,
896
getter_AddRefs(data));
897
task->Dispatch(Canceling, aRv);
898
}
899
900
if (NS_WARN_IF(!data)) {
901
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
902
return nullptr;
903
}
904
905
// Create an ImageBimtap.
906
RefPtr<ImageBitmap> ret =
907
new ImageBitmap(aGlobal, data, false /* write-only */, alphaType);
908
909
ret->mAllocatedImageData = true;
910
911
// The cropping information has been handled in the CreateImageFromRawData()
912
// function.
913
914
return ret.forget();
915
}
916
917
/* static */
918
already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal(
919
nsIGlobalObject* aGlobal, CanvasRenderingContext2D& aCanvasCtx,
920
const Maybe<IntRect>& aCropRect, ErrorResult& aRv) {
921
nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(aGlobal);
922
nsGlobalWindowInner* window = nsGlobalWindowInner::Cast(win);
923
if (NS_WARN_IF(!window) || !window->GetExtantDoc()) {
924
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
925
return nullptr;
926
}
927
928
window->GetExtantDoc()->WarnOnceAbout(
929
Document::eCreateImageBitmapCanvasRenderingContext2D);
930
931
// Check write-only mode.
932
bool writeOnly =
933
aCanvasCtx.GetCanvas()->IsWriteOnly() || aCanvasCtx.IsWriteOnly();
934
935
RefPtr<SourceSurface> surface = aCanvasCtx.GetSurfaceSnapshot();
936
937
if (NS_WARN_IF(!surface)) {
938
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
939
return nullptr;
940
}
941
942
const IntSize surfaceSize = surface->GetSize();
943
if (surfaceSize.width == 0 || surfaceSize.height == 0) {
944
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
945
return nullptr;
946
}
947
948
RefPtr<layers::Image> data = CreateImageFromSurface(surface);
949
950
if (NS_WARN_IF(!data)) {
951
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
952
return nullptr;
953
}
954
955
RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data, writeOnly);
956
957
ret->mAllocatedImageData = true;
958
959
// Set the picture rectangle.
960
if (ret && aCropRect.isSome()) {
961
ret->SetPictureRect(aCropRect.ref(), aRv);
962
}
963
964
return ret.forget();
965
}
966
967
/* static */
968
already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal(
969
nsIGlobalObject* aGlobal, ImageBitmap& aImageBitmap,
970
const Maybe<IntRect>& aCropRect, ErrorResult& aRv) {
971
if (!aImageBitmap.mData) {
972
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
973
return nullptr;
974
}
975
976
RefPtr<layers::Image> data = aImageBitmap.mData;
977
RefPtr<ImageBitmap> ret = new ImageBitmap(
978
aGlobal, data, aImageBitmap.mWriteOnly, aImageBitmap.mAlphaType);
979
980
// Set the picture rectangle.
981
if (ret && aCropRect.isSome()) {
982
ret->SetPictureRect(aCropRect.ref(), aRv);
983
}
984
985
return ret.forget();
986
}
987
988
class FulfillImageBitmapPromise {
989
protected:
990
FulfillImageBitmapPromise(Promise* aPromise, ImageBitmap* aImageBitmap)
991
: mPromise(aPromise), mImageBitmap(aImageBitmap) {
992
MOZ_ASSERT(aPromise);
993
}
994
995
void DoFulfillImageBitmapPromise() { mPromise->MaybeResolve(mImageBitmap); }
996
997
private:
998
RefPtr<Promise> mPromise;
999
RefPtr<ImageBitmap> mImageBitmap;
1000
};
1001
1002
class FulfillImageBitmapPromiseTask final : public Runnable,
1003
public FulfillImageBitmapPromise {
1004
public:
1005
FulfillImageBitmapPromiseTask(Promise* aPromise, ImageBitmap* aImageBitmap)
1006
: Runnable("dom::FulfillImageBitmapPromiseTask"),
1007
FulfillImageBitmapPromise(aPromise, aImageBitmap) {}
1008
1009
NS_IMETHOD Run() override {
1010
DoFulfillImageBitmapPromise();
1011
return NS_OK;
1012
}
1013
};
1014
1015
class FulfillImageBitmapPromiseWorkerTask final
1016
: public WorkerSameThreadRunnable,
1017
public FulfillImageBitmapPromise {
1018
public:
1019
FulfillImageBitmapPromiseWorkerTask(Promise* aPromise,
1020
ImageBitmap* aImageBitmap)
1021
: WorkerSameThreadRunnable(GetCurrentThreadWorkerPrivate()),
1022
FulfillImageBitmapPromise(aPromise, aImageBitmap) {}
1023
1024
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
1025
DoFulfillImageBitmapPromise();
1026
return true;
1027
}
1028
};
1029
1030
static void AsyncFulfillImageBitmapPromise(Promise* aPromise,
1031
ImageBitmap* aImageBitmap) {
1032
if (NS_IsMainThread()) {
1033
nsCOMPtr<nsIRunnable> task =
1034
new FulfillImageBitmapPromiseTask(aPromise, aImageBitmap);
1035
NS_DispatchToCurrentThread(task); // Actually, to the main-thread.
1036
} else {
1037
RefPtr<FulfillImageBitmapPromiseWorkerTask> task =
1038
new FulfillImageBitmapPromiseWorkerTask(aPromise, aImageBitmap);
1039
task->Dispatch(); // Actually, to the current worker-thread.
1040
}
1041
}
1042
1043
class CreateImageBitmapFromBlobRunnable;
1044
1045
class CreateImageBitmapFromBlob final : public CancelableRunnable,
1046
public imgIContainerCallback,
1047
public nsIInputStreamCallback {
1048
friend class CreateImageBitmapFromBlobRunnable;
1049
1050
public:
1051
NS_DECL_ISUPPORTS_INHERITED
1052
NS_DECL_IMGICONTAINERCALLBACK
1053
NS_DECL_NSIINPUTSTREAMCALLBACK
1054
1055
static already_AddRefed<CreateImageBitmapFromBlob> Create(
1056
Promise* aPromise, nsIGlobalObject* aGlobal, Blob& aBlob,
1057
const Maybe<IntRect>& aCropRect, nsIEventTarget* aMainThreadEventTarget);
1058
1059
NS_IMETHOD Run() override {
1060
MOZ_ASSERT(IsCurrentThread());
1061
1062
nsresult rv = StartMimeTypeAndDecodeAndCropBlob();
1063
if (NS_WARN_IF(NS_FAILED(rv))) {
1064
MimeTypeAndDecodeAndCropBlobCompletedMainThread(nullptr, rv);
1065
}
1066
1067
return NS_OK;
1068
}
1069
1070
// Called by the WorkerRef.
1071
void WorkerShuttingDown();
1072
1073
private:
1074
CreateImageBitmapFromBlob(Promise* aPromise, nsIGlobalObject* aGlobal,
1075
already_AddRefed<nsIInputStream> aInputStream,
1076
const Maybe<IntRect>& aCropRect,
1077
nsIEventTarget* aMainThreadEventTarget)
1078
: CancelableRunnable("dom::CreateImageBitmapFromBlob"),
1079
mMutex("dom::CreateImageBitmapFromBlob::mMutex"),
1080
mPromise(aPromise),
1081
mGlobalObject(aGlobal),
1082
mInputStream(std::move(aInputStream)),
1083
mCropRect(aCropRect),
1084
mOriginalCropRect(aCropRect),
1085
mMainThreadEventTarget(aMainThreadEventTarget),
1086
mThread(PR_GetCurrentThread()) {}
1087
1088
virtual ~CreateImageBitmapFromBlob() {}
1089
1090
bool IsCurrentThread() const { return mThread == PR_GetCurrentThread(); }
1091
1092
// Called on the owning thread.
1093
nsresult StartMimeTypeAndDecodeAndCropBlob();
1094
1095
// Will be called when the decoding + cropping is completed on the
1096
// main-thread. This could the not the owning thread!
1097
void MimeTypeAndDecodeAndCropBlobCompletedMainThread(layers::Image* aImage,
1098
nsresult aStatus);
1099
1100
// Will be called when the decoding + cropping is completed on the owning
1101
// thread.
1102
void MimeTypeAndDecodeAndCropBlobCompletedOwningThread(layers::Image* aImage,
1103
nsresult aStatus);
1104
1105
// This is called on the main-thread only.
1106
nsresult MimeTypeAndDecodeAndCropBlob();
1107
1108
// This is called on the main-thread only.
1109
nsresult DecodeAndCropBlob(const nsACString& aMimeType);
1110
1111
// This is called on the main-thread only.
1112
nsresult GetMimeTypeSync(nsACString& aMimeType);
1113
1114
// This is called on the main-thread only.
1115
nsresult GetMimeTypeAsync();
1116
1117
Mutex mMutex;
1118
1119
// The access to this object is protected by mutex but is always nullified on
1120
// the owning thread.
1121
RefPtr<ThreadSafeWorkerRef> mWorkerRef;
1122
1123
// Touched only on the owning thread.
1124
RefPtr<Promise> mPromise;
1125
1126
// Touched only on the owning thread.
1127
nsCOMPtr<nsIGlobalObject> mGlobalObject;
1128
1129
nsCOMPtr<nsIInputStream> mInputStream;
1130
Maybe<IntRect> mCropRect;
1131
Maybe<IntRect> mOriginalCropRect;
1132
IntSize mSourceSize;
1133
1134
nsCOMPtr<nsIEventTarget> mMainThreadEventTarget;
1135
void* mThread;
1136
};
1137
1138
NS_IMPL_ISUPPORTS_INHERITED(CreateImageBitmapFromBlob, CancelableRunnable,
1139
imgIContainerCallback, nsIInputStreamCallback)
1140
1141
class CreateImageBitmapFromBlobRunnable : public WorkerRunnable {
1142
public:
1143
explicit CreateImageBitmapFromBlobRunnable(WorkerPrivate* aWorkerPrivate,
1144
CreateImageBitmapFromBlob* aTask,
1145
layers::Image* aImage,
1146
nsresult aStatus)
1147
: WorkerRunnable(aWorkerPrivate),
1148
mTask(aTask),
1149
mImage(aImage),
1150
mStatus(aStatus) {}
1151
1152
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
1153
mTask->MimeTypeAndDecodeAndCropBlobCompletedOwningThread(mImage, mStatus);
1154
return true;
1155
}
1156
1157
private:
1158
RefPtr<CreateImageBitmapFromBlob> mTask;
1159
RefPtr<layers::Image> mImage;
1160
nsresult mStatus;
1161
};
1162
1163
static void AsyncCreateImageBitmapFromBlob(Promise* aPromise,
1164
nsIGlobalObject* aGlobal,
1165
Blob& aBlob,
1166
const Maybe<IntRect>& aCropRect) {
1167
// Let's identify the main-thread event target.
1168
nsCOMPtr<nsIEventTarget> mainThreadEventTarget;
1169
if (NS_IsMainThread()) {
1170
mainThreadEventTarget = aGlobal->EventTargetFor(TaskCategory::Other);
1171
} else {
1172
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
1173
MOZ_ASSERT(workerPrivate);
1174
mainThreadEventTarget = workerPrivate->MainThreadEventTarget();
1175
}
1176
1177
RefPtr<CreateImageBitmapFromBlob> task = CreateImageBitmapFromBlob::Create(
1178
aPromise, aGlobal, aBlob, aCropRect, mainThreadEventTarget);
1179
if (NS_WARN_IF(!task)) {
1180
aPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
1181
return;
1182
}
1183
1184
NS_DispatchToCurrentThread(task);
1185
}
1186
1187
/* static */
1188
already_AddRefed<Promise> ImageBitmap::Create(
1189
nsIGlobalObject* aGlobal, const ImageBitmapSource& aSrc,
1190
const Maybe<gfx::IntRect>& aCropRect, ErrorResult& aRv) {
1191
MOZ_ASSERT(aGlobal);
1192
1193
RefPtr<Promise> promise = Promise::Create(aGlobal, aRv);
1194
1195
if (NS_WARN_IF(aRv.Failed())) {
1196
return nullptr;
1197
}
1198
1199
if (aCropRect.isSome()) {
1200
if (aCropRect->Width() == 0) {
1201
aRv.ThrowRangeError(
1202
u"The crop rect width passed to createImageBitmap must be nonzero");
1203
return promise.forget();
1204
}
1205
1206
if (aCropRect->Height() == 0) {
1207
aRv.ThrowRangeError(
1208
u"The crop rect height passed to createImageBitmap must be nonzero");
1209
return promise.forget();
1210
}
1211
}
1212
1213
RefPtr<ImageBitmap> imageBitmap;
1214
1215
if (aSrc.IsHTMLImageElement()) {
1216
MOZ_ASSERT(
1217
NS_IsMainThread(),
1218
"Creating ImageBitmap from HTMLImageElement off the main thread.");
1219
imageBitmap =
1220
CreateInternal(aGlobal, aSrc.GetAsHTMLImageElement(), aCropRect, aRv);
1221
} else if (aSrc.IsSVGImageElement()) {
1222
MOZ_ASSERT(
1223
NS_IsMainThread(),
1224
"Creating ImageBitmap from SVGImageElement off the main thread.");
1225
imageBitmap =
1226
CreateInternal(aGlobal, aSrc.GetAsSVGImageElement(), aCropRect, aRv);
1227
} else if (aSrc.IsHTMLVideoElement()) {
1228
MOZ_ASSERT(
1229
NS_IsMainThread(),
1230
"Creating ImageBitmap from HTMLVideoElement off the main thread.");
1231
imageBitmap =
1232
CreateInternal(aGlobal, aSrc.GetAsHTMLVideoElement(), aCropRect, aRv);
1233
} else if (aSrc.IsHTMLCanvasElement()) {
1234
MOZ_ASSERT(
1235
NS_IsMainThread(),
1236
"Creating ImageBitmap from HTMLCanvasElement off the main thread.");
1237
imageBitmap =
1238
CreateInternal(aGlobal, aSrc.GetAsHTMLCanvasElement(), aCropRect, aRv);
1239
} else if (aSrc.IsImageData()) {
1240
imageBitmap =
1241
CreateInternal(aGlobal, aSrc.GetAsImageData(), aCropRect, aRv);
1242
} else if (aSrc.IsCanvasRenderingContext2D()) {
1243
MOZ_ASSERT(NS_IsMainThread(),
1244
"Creating ImageBitmap from CanvasRenderingContext2D off the "
1245
"main thread.");
1246
imageBitmap = CreateInternal(aGlobal, aSrc.GetAsCanvasRenderingContext2D(),
1247
aCropRect, aRv);
1248
} else if (aSrc.IsImageBitmap()) {
1249
imageBitmap =
1250
CreateInternal(aGlobal, aSrc.GetAsImageBitmap(), aCropRect, aRv);
1251
} else if (aSrc.IsBlob()) {
1252
AsyncCreateImageBitmapFromBlob(promise, aGlobal, aSrc.GetAsBlob(),
1253
aCropRect);
1254
return promise.forget();
1255
} else {
1256
MOZ_CRASH("Unsupported type!");
1257
return nullptr;
1258
}
1259
1260
if (!aRv.Failed()) {
1261
AsyncFulfillImageBitmapPromise(promise, imageBitmap);
1262
}
1263
1264
return promise.forget();
1265
}
1266
1267
/*static*/
1268
JSObject* ImageBitmap::ReadStructuredClone(
1269
JSContext* aCx, JSStructuredCloneReader* aReader, nsIGlobalObject* aParent,
1270
const nsTArray<RefPtr<DataSourceSurface>>& aClonedSurfaces,
1271
uint32_t aIndex) {
1272
MOZ_ASSERT(aCx);
1273
MOZ_ASSERT(aReader);
1274
// aParent might be null.
1275
1276
uint32_t picRectX_;
1277
uint32_t picRectY_;
1278
uint32_t picRectWidth_;
1279
uint32_t picRectHeight_;
1280
uint32_t alphaType_;
1281
uint32_t writeOnly;
1282
1283
if (!JS_ReadUint32Pair(aReader, &picRectX_, &picRectY_) ||
1284
!JS_ReadUint32Pair(aReader, &picRectWidth_, &picRectHeight_) ||
1285
!JS_ReadUint32Pair(aReader, &alphaType_, &writeOnly)) {
1286
return nullptr;
1287
}
1288
1289
int32_t picRectX = BitwiseCast<int32_t>(picRectX_);
1290
int32_t picRectY = BitwiseCast<int32_t>(picRectY_);
1291
int32_t picRectWidth = BitwiseCast<int32_t>(picRectWidth_);
1292
int32_t picRectHeight = BitwiseCast<int32_t>(picRectHeight_);
1293
const auto alphaType = BitwiseCast<gfxAlphaType>(alphaType_);
1294
1295
// Create a new ImageBitmap.
1296
MOZ_ASSERT(!aClonedSurfaces.IsEmpty());
1297
MOZ_ASSERT(aIndex < aClonedSurfaces.Length());
1298
1299
// RefPtr<ImageBitmap> needs to go out of scope before toObjectOrNull() is
1300
// called because the static analysis thinks dereferencing XPCOM objects
1301
// can GC (because in some cases it can!), and a return statement with a
1302
// JSObject* type means that JSObject* is on the stack as a raw pointer
1303
// while destructors are running.
1304
JS::Rooted<JS::Value> value(aCx);
1305
{
1306
#ifdef FUZZING
1307
if (aIndex >= aClonedSurfaces.Length()) {
1308
return nullptr;
1309
}
1310
#endif
1311
RefPtr<layers::Image> img = CreateImageFromSurface(aClonedSurfaces[aIndex]);
1312
RefPtr<ImageBitmap> imageBitmap =
1313
new ImageBitmap(aParent, img, !!writeOnly, alphaType);
1314
1315
ErrorResult error;
1316
imageBitmap->SetPictureRect(
1317
IntRect(picRectX, picRectY, picRectWidth, picRectHeight), error);
1318
if (NS_WARN_IF(error.Failed())) {
1319
error.SuppressException();
1320
return nullptr;
1321
}
1322
1323
if (!GetOrCreateDOMReflector(aCx, imageBitmap, &value)) {
1324
return nullptr;
1325
}
1326
1327
imageBitmap->mAllocatedImageData = true;
1328
}
1329
1330
return &(value.toObject());
1331
}
1332
1333
/*static*/
1334
bool ImageBitmap::WriteStructuredClone(
1335
JSStructuredCloneWriter* aWriter,
1336
nsTArray<RefPtr<DataSourceSurface>>& aClonedSurfaces,
1337
ImageBitmap* aImageBitmap) {
1338
MOZ_ASSERT(aWriter);
1339
MOZ_ASSERT(aImageBitmap);
1340
1341
if (!aImageBitmap->mData) {
1342
// A closed image cannot be cloned.
1343
return false;
1344
}
1345
1346
const uint32_t picRectX = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.x);
1347
const uint32_t picRectY = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.y);
1348
const uint32_t picRectWidth =
1349
BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.width);
1350
const uint32_t picRectHeight =
1351
BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.height);
1352
const uint32_t alphaType = BitwiseCast<uint32_t>(aImageBitmap->mAlphaType);
1353
1354
// Indexing the cloned surfaces and send the index to the receiver.
1355
uint32_t index = aClonedSurfaces.Length();
1356
1357
if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter, SCTAG_DOM_IMAGEBITMAP, index)) ||
1358
NS_WARN_IF(!JS_WriteUint32Pair(aWriter, picRectX, picRectY)) ||
1359
NS_WARN_IF(!JS_WriteUint32Pair(aWriter, picRectWidth, picRectHeight)) ||
1360
NS_WARN_IF(
1361
!JS_WriteUint32Pair(aWriter, alphaType, aImageBitmap->mWriteOnly))) {
1362
return false;
1363
}
1364
1365
RefPtr<SourceSurface> surface = aImageBitmap->mData->GetAsSourceSurface();
1366
RefPtr<DataSourceSurface> snapshot = surface->GetDataSurface();
1367
RefPtr<DataSourceSurface> dstDataSurface;
1368
{
1369
// DataSourceSurfaceD2D1::GetStride() will call EnsureMapped implicitly and
1370
// won't Unmap after exiting function. So instead calling GetStride()
1371
// directly, using ScopedMap to get stride.
1372
DataSourceSurface::ScopedMap map(snapshot, DataSourceSurface::READ);
1373
dstDataSurface = Factory::CreateDataSourceSurfaceWithStride(
1374
snapshot->GetSize(), snapshot->GetFormat(), map.GetStride(), true);
1375
}
1376
if (NS_WARN_IF(!dstDataSurface)) {
1377
return false;
1378
}
1379
Factory::CopyDataSourceSurface(snapshot, dstDataSurface);
1380
aClonedSurfaces.AppendElement(dstDataSurface);
1381
return true;
1382
}
1383
1384
size_t ImageBitmap::GetAllocatedSize() const {
1385
if (!mAllocatedImageData) {
1386
return 0;
1387
}
1388
1389
// Calculate how many bytes are used.
1390
if (mData->GetFormat() == mozilla::ImageFormat::PLANAR_YCBCR) {
1391
return mData->AsPlanarYCbCrImage()->GetDataSize();
1392
}
1393
1394
if (mData->GetFormat() == mozilla::ImageFormat::NV_IMAGE) {
1395
return mData->AsNVImage()->GetBufferSize();
1396
}
1397
1398
RefPtr<SourceSurface> surface = mData->GetAsSourceSurface();
1399
const int bytesPerPixel = BytesPerPixel(surface->GetFormat());
1400
return surface->GetSize().height * surface->GetSize().width * bytesPerPixel;
1401
}
1402
1403
size_t BindingJSObjectMallocBytes(ImageBitmap* aBitmap) {
1404
return aBitmap->GetAllocatedSize();
1405
}
1406
1407
/* static */
1408
already_AddRefed<CreateImageBitmapFromBlob> CreateImageBitmapFromBlob::Create(
1409
Promise* aPromise, nsIGlobalObject* aGlobal, Blob& aBlob,
1410
const Maybe<IntRect>& aCropRect, nsIEventTarget* aMainThreadEventTarget) {
1411
// Get the internal stream of the blob.
1412
nsCOMPtr<nsIInputStream> stream;
1413
ErrorResult error;
1414
aBlob.Impl()->CreateInputStream(getter_AddRefs(stream), error);
1415
if (NS_WARN_IF(error.Failed())) {
1416
return nullptr;
1417
}
1418
1419
if (!NS_InputStreamIsBuffered(stream)) {
1420
nsCOMPtr<nsIInputStream> bufferedStream;
1421
nsresult rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream),
1422
stream.forget(), 4096);
1423
if (NS_WARN_IF(NS_FAILED(rv))) {
1424
return nullptr;
1425
}
1426
1427
stream = bufferedStream;
1428
}
1429
1430
RefPtr<CreateImageBitmapFromBlob> task = new CreateImageBitmapFromBlob(
1431
aPromise, aGlobal, stream.forget(), aCropRect, aMainThreadEventTarget);
1432
1433
// Nothing to do for the main-thread.
1434
if (NS_IsMainThread()) {
1435
return task.forget();
1436
}
1437
1438
// Let's use a WorkerRef to keep the worker alive if this is not the
1439
// main-thread.
1440
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
1441
MOZ_ASSERT(workerPrivate);
1442
1443
RefPtr<StrongWorkerRef> workerRef =
1444
StrongWorkerRef::Create(workerPrivate, "CreateImageBitmapFromBlob",
1445
[task]() { task->WorkerShuttingDown(); });
1446
if (NS_WARN_IF(!workerRef)) {
1447
return nullptr;
1448
}
1449
1450
task->mWorkerRef = new ThreadSafeWorkerRef(workerRef);
1451
return task.forget();
1452
}
1453
1454
nsresult CreateImageBitmapFromBlob::StartMimeTypeAndDecodeAndCropBlob() {
1455
MOZ_ASSERT(IsCurrentThread());
1456
1457
// Workers.
1458
if (!NS_IsMainThread()) {
1459
RefPtr<CreateImageBitmapFromBlob> self = this;
1460
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
1461
"CreateImageBitmapFromBlob::MimeTypeAndDecodeAndCropBlob", [self]() {
1462
nsresult rv = self->MimeTypeAndDecodeAndCropBlob();
1463
if (NS_WARN_IF(NS_FAILED(rv))) {
1464
self->MimeTypeAndDecodeAndCropBlobCompletedMainThread(nullptr, rv);
1465
}
1466
});
1467
1468
return mMainThreadEventTarget->Dispatch(r.forget());
1469
}
1470
1471
// Main-thread.
1472
return MimeTypeAndDecodeAndCropBlob();
1473
}
1474
1475
nsresult CreateImageBitmapFromBlob::MimeTypeAndDecodeAndCropBlob() {
1476
MOZ_ASSERT(NS_IsMainThread());
1477
1478
nsAutoCString mimeType;
1479
nsresult rv = GetMimeTypeSync(mimeType);
1480
if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
1481
return GetMimeTypeAsync();
1482
}
1483
1484
if (NS_WARN_IF(NS_FAILED(rv))) {
1485
return rv;
1486
}
1487
1488
return DecodeAndCropBlob(mimeType);
1489
}
1490
1491
nsresult CreateImageBitmapFromBlob::DecodeAndCropBlob(
1492
const nsACString& aMimeType) {
1493
// Get the Component object.
1494
nsCOMPtr<imgITools> imgtool = do_GetService(NS_IMGTOOLS_CID);
1495
if (NS_WARN_IF(!imgtool)) {
1496
return NS_ERROR_FAILURE;
1497
}
1498
1499
// Decode image.
1500
nsresult rv = imgtool->DecodeImageAsync(mInputStream, aMimeType, this,
1501
mMainThreadEventTarget);
1502
if (NS_WARN_IF(NS_FAILED(rv))) {
1503
return rv;
1504
}
1505
1506
return NS_OK;
1507
}
1508
1509
static nsresult sniff_cb(nsIInputStream* aInputStream, void* aClosure,
1510
const char* aFromRawSegment, uint32_t aToOffset,
1511
uint32_t aCount, uint32_t* aWriteCount) {
1512
nsACString* mimeType = static_cast<nsACString*>(aClosure);
1513
MOZ_ASSERT(mimeType);
1514
1515
if (aCount > 0) {
1516
imgLoader::GetMimeTypeFromContent(aFromRawSegment, aCount, *mimeType);
1517
}
1518
1519
*aWriteCount = 0;
1520
1521
// We don't want to consume data from the stream.
1522
return NS_ERROR_FAILURE;
1523
}
1524
1525
nsresult CreateImageBitmapFromBlob::GetMimeTypeSync(nsACString& aMimeType) {
1526
uint32_t dummy;
1527
return mInputStream->ReadSegments(sniff_cb, &aMimeType, 128, &dummy);
1528
}
1529
1530
nsresult CreateImageBitmapFromBlob::GetMimeTypeAsync() {
1531
nsCOMPtr<nsIAsyncInputStream> asyncInputStream =
1532
do_QueryInterface(mInputStream);
1533
if (NS_WARN_IF(!asyncInputStream)) {
1534
// If the stream is not async, why are we here?
1535
return NS_ERROR_FAILURE;
1536
}
1537
1538
return asyncInputStream->AsyncWait(this, 0, 128, mMainThreadEventTarget);
1539
}
1540
1541
NS_IMETHODIMP
1542
CreateImageBitmapFromBlob::OnInputStreamReady(nsIAsyncInputStream* aStream) {
1543
// The stream should have data now. Let's start from scratch again.
1544
nsresult rv = MimeTypeAndDecodeAndCropBlob();
1545
if (NS_WARN_IF(NS_FAILED(rv))) {
1546
MimeTypeAndDecodeAndCropBlobCompletedMainThread(nullptr, rv);
1547
}
1548
1549
return NS_OK;
1550
}
1551
1552
NS_IMETHODIMP
1553
CreateImageBitmapFromBlob::OnImageReady(imgIContainer* aImgContainer,
1554
nsresult aStatus) {
1555
MOZ_ASSERT(NS_IsMainThread());
1556
1557
if (NS_FAILED(aStatus)) {
1558
MimeTypeAndDecodeAndCropBlobCompletedMainThread(nullptr, aStatus);
1559
return NS_OK;
1560
}
1561
1562
MOZ_ASSERT(aImgContainer);
1563
1564
// Get the surface out.
1565
uint32_t frameFlags = imgIContainer::FLAG_SYNC_DECODE |
1566
imgIContainer::FLAG_ASYNC_NOTIFY |
1567
imgIContainer::FLAG_WANT_DATA_SURFACE;
1568
uint32_t whichFrame = imgIContainer::FRAME_FIRST;
1569
RefPtr<SourceSurface> surface =
1570
aImgContainer->GetFrame(whichFrame, frameFlags);
1571
1572
if (NS_WARN_IF(!surface)) {
1573
MimeTypeAndDecodeAndCropBlobCompletedMainThread(
1574
nullptr, NS_ERROR_DOM_INVALID_STATE_ERR);
1575
return NS_OK;
1576
}
1577
1578
// Store the sourceSize value for the
1579
// MimeTypeAndDecodeAndCropBlobCompletedMainThread call.
1580
mSourceSize = surface->GetSize();
1581
1582
// Crop the source surface if needed.
1583
RefPtr<SourceSurface> croppedSurface = surface;
1584
1585
if (mCropRect.isSome()) {
1586
// The blob is just decoded into a RasterImage and not optimized yet, so the
1587
// _surface_ we get is a DataSourceSurface which wraps the RasterImage's
1588
// raw buffer.
1589
//
1590
// The _surface_ might already be optimized so that its type is not
1591
// SurfaceType::DATA. However, we could keep using the generic cropping and
1592
// copying since the decoded buffer is only used in this ImageBitmap so we
1593
// should crop it to save memory usage.
1594
//
1595
// TODO: Bug1189632 is going to refactor this create-from-blob part to
1596
// decode the blob off the main thread. Re-check if we should do
1597
// cropping at this moment again there.
1598
RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface();
1599
croppedSurface = CropAndCopyDataSourceSurface(dataSurface, mCropRect.ref());
1600
mCropRect->MoveTo(0, 0);
1601
}
1602
1603
if (NS_WARN_IF(!croppedSurface)) {
1604
MimeTypeAndDecodeAndCropBlobCompletedMainThread(
1605
nullptr, NS_ERROR_DOM_INVALID_STATE_ERR);
1606
return NS_OK;
1607
}
1608
1609
// Create an Image from the source surface.
1610
RefPtr<layers::Image> image = CreateImageFromSurface(croppedSurface);
1611
1612
if (NS_WARN_IF(!image)) {
1613
MimeTypeAndDecodeAndCropBlobCompletedMainThread(
1614
nullptr, NS_ERROR_DOM_INVALID_STATE_ERR);
1615
return NS_OK;
1616
}
1617
1618
MimeTypeAndDecodeAndCropBlobCompletedMainThread(image, NS_OK);
1619
return NS_OK;
1620
}
1621
1622
void CreateImageBitmapFromBlob::MimeTypeAndDecodeAndCropBlobCompletedMainThread(
1623
layers::Image* aImage, nsresult aStatus) {
1624
MOZ_ASSERT(NS_IsMainThread());
1625
1626
if (!IsCurrentThread()) {
1627
MutexAutoLock lock(mMutex);
1628
1629
if (!mWorkerRef) {
1630
// The worker is already gone.
1631
return;
1632
}
1633
1634
RefPtr<CreateImageBitmapFromBlobRunnable> r =
1635
new CreateImageBitmapFromBlobRunnable(mWorkerRef->Private(), this,
1636
aImage, aStatus);
1637
r->Dispatch();
1638
return;
1639
}
1640
1641
MimeTypeAndDecodeAndCropBlobCompletedOwningThread(aImage, aStatus);
1642
}
1643
1644
void CreateImageBitmapFromBlob::
1645
MimeTypeAndDecodeAndCropBlobCompletedOwningThread(layers::Image* aImage,
1646
nsresult aStatus) {
1647
MOZ_ASSERT(IsCurrentThread());
1648
1649
if (!mPromise) {
1650
// The worker is going to be released soon. No needs to continue.
1651
return;
1652
}
1653
1654
// Let's release what has to be released on the owning thread.
1655
auto raii = MakeScopeExit([&] {
1656
// Doing this we also release the worker.
1657
mWorkerRef = nullptr;
1658
1659
mPromise = nullptr;
1660
mGlobalObject = nullptr;
1661
});
1662
1663
if (NS_WARN_IF(NS_FAILED(aStatus))) {
1664
mPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
1665
return;
1666
}
1667
1668
// Create ImageBitmap object.
1669
RefPtr<ImageBitmap> imageBitmap =
1670
new ImageBitmap(mGlobalObject, aImage, false /* write-only */);
1671
1672
if (mCropRect.isSome()) {
1673
ErrorResult rv;
1674
imageBitmap->SetPictureRect(mCropRect.ref(), rv);
1675
1676
if (rv.Failed()) {
1677
mPromise->MaybeReject(rv);
1678
return;
1679
}
1680
}
1681
1682
imageBitmap->mAllocatedImageData = true;
1683
1684
mPromise->MaybeResolve(imageBitmap);
1685
}
1686
1687
void CreateImageBitmapFromBlob::WorkerShuttingDown() {
1688
MOZ_ASSERT(IsCurrentThread());
1689
1690
MutexAutoLock lock(mMutex);
1691
1692
// Let's release all the non-thread-safe objects now.
1693
mWorkerRef = nullptr;
1694
mPromise = nullptr;
1695
mGlobalObject = nullptr;
1696
}
1697
1698
} // namespace dom
1699
} // namespace mozilla