Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/base/ImageEncoder.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
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 "ImageEncoder.h"
8
#include "mozilla/dom/CanvasRenderingContext2D.h"
9
#include "mozilla/dom/WorkerPrivate.h"
10
#include "mozilla/gfx/2D.h"
11
#include "mozilla/gfx/DataSurfaceHelpers.h"
12
#include "mozilla/layers/AsyncCanvasRenderer.h"
13
#include "mozilla/RefPtr.h"
14
#include "mozilla/SyncRunnable.h"
15
#include "mozilla/Unused.h"
16
#include "gfxUtils.h"
17
#include "nsThreadPool.h"
18
#include "nsNetUtil.h"
19
#include "nsXPCOMCIDInternal.h"
20
#include "YCbCrUtils.h"
21
22
using namespace mozilla::gfx;
23
24
namespace mozilla {
25
namespace dom {
26
27
// This class should be placed inside GetBRGADataSourceSurfaceSync(). However,
28
// due to B2G ICS uses old complier (C++98/03) which forbids local class as
29
// template parameter, we need to move this class outside.
30
class SurfaceHelper : public Runnable {
31
public:
32
  explicit SurfaceHelper(already_AddRefed<layers::Image> aImage)
33
    : Runnable("SurfaceHelper")
34
    , mImage(aImage)
35
0
  {}
36
37
  // It retrieves a SourceSurface reference and convert color format on main
38
  // thread and passes DataSourceSurface to caller thread.
39
0
  NS_IMETHOD Run() override {
40
0
    // It guarantees the reference will be released on main thread.
41
0
    nsCountedRef<nsMainThreadSourceSurfaceRef> surface;
42
0
    surface.own(mImage->GetAsSourceSurface().take());
43
0
44
0
    if (surface->GetFormat() == gfx::SurfaceFormat::B8G8R8A8) {
45
0
      mDataSourceSurface = surface->GetDataSurface();
46
0
    } else {
47
0
      mDataSourceSurface = gfxUtils::
48
0
        CopySurfaceToDataSourceSurfaceWithFormat(surface,
49
0
                                                 gfx::SurfaceFormat::B8G8R8A8);
50
0
    }
51
0
    return NS_OK;
52
0
  }
53
54
0
  already_AddRefed<gfx::DataSourceSurface> GetDataSurfaceSafe() {
55
0
    nsCOMPtr<nsIEventTarget> mainTarget = GetMainThreadEventTarget();
56
0
    MOZ_ASSERT(mainTarget);
57
0
    SyncRunnable::DispatchToThread(mainTarget, this, false);
58
0
59
0
    return mDataSourceSurface.forget();
60
0
  }
61
62
private:
63
  RefPtr<layers::Image> mImage;
64
  RefPtr<gfx::DataSourceSurface> mDataSourceSurface;
65
};
66
67
// This function returns a DataSourceSurface in B8G8R8A8 format.
68
// It uses SourceSurface to do format convert. Because most SourceSurface in
69
// image formats should be referenced or dereferenced on main thread, it uses a
70
// sync class SurfaceHelper to retrieve SourceSurface and convert to B8G8R8A8 on
71
// main thread.
72
already_AddRefed<DataSourceSurface>
73
GetBRGADataSourceSurfaceSync(already_AddRefed<layers::Image> aImage)
74
0
{
75
0
  RefPtr<SurfaceHelper> helper = new SurfaceHelper(std::move(aImage));
76
0
  return helper->GetDataSurfaceSafe();
77
0
}
78
79
class EncodingCompleteEvent : public CancelableRunnable
80
{
81
0
  virtual ~EncodingCompleteEvent() {}
82
83
public:
84
  explicit EncodingCompleteEvent(EncodeCompleteCallback* aEncodeCompleteCallback)
85
    : CancelableRunnable("EncodingCompleteEvent")
86
    , mImgSize(0)
87
    , mType()
88
    , mImgData(nullptr)
89
    , mEncodeCompleteCallback(aEncodeCompleteCallback)
90
    , mFailed(false)
91
0
  {
92
0
    if (!NS_IsMainThread() && IsCurrentThreadRunningWorker()) {
93
0
      mCreationEventTarget = GetCurrentThreadEventTarget();
94
0
    } else {
95
0
      mCreationEventTarget = GetMainThreadEventTarget();
96
0
    }
97
0
  }
98
99
  NS_IMETHOD Run() override
100
0
  {
101
0
    nsresult rv = NS_OK;
102
0
103
0
    if (!mFailed) {
104
0
      // The correct parentObject has to be set by the mEncodeCompleteCallback.
105
0
      RefPtr<Blob> blob =
106
0
        Blob::CreateMemoryBlob(nullptr, mImgData, mImgSize, mType);
107
0
      MOZ_ASSERT(blob);
108
0
109
0
      rv = mEncodeCompleteCallback->ReceiveBlob(blob.forget());
110
0
    }
111
0
112
0
    mEncodeCompleteCallback = nullptr;
113
0
114
0
    return rv;
115
0
  }
116
117
  void SetMembers(void* aImgData, uint64_t aImgSize, const nsAutoString& aType)
118
0
  {
119
0
    mImgData = aImgData;
120
0
    mImgSize = aImgSize;
121
0
    mType = aType;
122
0
  }
123
124
  void SetFailed()
125
0
  {
126
0
    mFailed = true;
127
0
  }
128
129
  nsIEventTarget* GetCreationThreadEventTarget()
130
0
  {
131
0
    return mCreationEventTarget;
132
0
  }
133
134
private:
135
  uint64_t mImgSize;
136
  nsAutoString mType;
137
  void* mImgData;
138
  nsCOMPtr<nsIEventTarget> mCreationEventTarget;
139
  RefPtr<EncodeCompleteCallback> mEncodeCompleteCallback;
140
  bool mFailed;
141
};
142
143
class EncodingRunnable : public Runnable
144
{
145
0
  virtual ~EncodingRunnable() {}
146
147
public:
148
  NS_INLINE_DECL_REFCOUNTING_INHERITED(EncodingRunnable, Runnable)
149
150
  EncodingRunnable(const nsAString& aType,
151
                   const nsAString& aOptions,
152
                   UniquePtr<uint8_t[]> aImageBuffer,
153
                   layers::Image* aImage,
154
                   imgIEncoder* aEncoder,
155
                   EncodingCompleteEvent* aEncodingCompleteEvent,
156
                   int32_t aFormat,
157
                   const nsIntSize aSize,
158
                   bool aUsePlaceholder,
159
                   bool aUsingCustomOptions)
160
    : Runnable("EncodingRunnable")
161
    , mType(aType)
162
    , mOptions(aOptions)
163
    , mImageBuffer(std::move(aImageBuffer))
164
    , mImage(aImage)
165
    , mEncoder(aEncoder)
166
    , mEncodingCompleteEvent(aEncodingCompleteEvent)
167
    , mFormat(aFormat)
168
    , mSize(aSize)
169
    , mUsePlaceholder(aUsePlaceholder)
170
    , mUsingCustomOptions(aUsingCustomOptions)
171
0
  {}
172
173
  nsresult ProcessImageData(uint64_t* aImgSize, void** aImgData)
174
0
  {
175
0
    nsCOMPtr<nsIInputStream> stream;
176
0
    nsresult rv = ImageEncoder::ExtractDataInternal(mType,
177
0
                                                    mOptions,
178
0
                                                    mImageBuffer.get(),
179
0
                                                    mFormat,
180
0
                                                    mSize,
181
0
                                                    mUsePlaceholder,
182
0
                                                    mImage,
183
0
                                                    nullptr,
184
0
                                                    nullptr,
185
0
                                                    getter_AddRefs(stream),
186
0
                                                    mEncoder);
187
0
188
0
    // If there are unrecognized custom parse options, we should fall back to
189
0
    // the default values for the encoder without any options at all.
190
0
    if (rv == NS_ERROR_INVALID_ARG && mUsingCustomOptions) {
191
0
      rv = ImageEncoder::ExtractDataInternal(mType,
192
0
                                             EmptyString(),
193
0
                                             mImageBuffer.get(),
194
0
                                             mFormat,
195
0
                                             mSize,
196
0
                                             mUsePlaceholder,
197
0
                                             mImage,
198
0
                                             nullptr,
199
0
                                             nullptr,
200
0
                                             getter_AddRefs(stream),
201
0
                                             mEncoder);
202
0
    }
203
0
    NS_ENSURE_SUCCESS(rv, rv);
204
0
205
0
    rv = NS_ReadInputStreamToBuffer(stream, aImgData, -1, aImgSize);
206
0
    NS_ENSURE_SUCCESS(rv, rv);
207
0
208
0
    return rv;
209
0
  }
210
211
  NS_IMETHOD Run() override
212
0
  {
213
0
    uint64_t imgSize;
214
0
    void* imgData = nullptr;
215
0
216
0
    nsresult rv = ProcessImageData(&imgSize, &imgData);
217
0
    if (NS_FAILED(rv)) {
218
0
      mEncodingCompleteEvent->SetFailed();
219
0
    } else {
220
0
      mEncodingCompleteEvent->SetMembers(imgData, imgSize, mType);
221
0
    }
222
0
    rv = mEncodingCompleteEvent->GetCreationThreadEventTarget()->
223
0
      Dispatch(mEncodingCompleteEvent, nsIThread::DISPATCH_NORMAL);
224
0
    if (NS_FAILED(rv)) {
225
0
      // Better to leak than to crash.
226
0
      Unused << mEncodingCompleteEvent.forget();
227
0
      return rv;
228
0
    }
229
0
230
0
    return rv;
231
0
  }
232
233
private:
234
  nsAutoString mType;
235
  nsAutoString mOptions;
236
  UniquePtr<uint8_t[]> mImageBuffer;
237
  RefPtr<layers::Image> mImage;
238
  nsCOMPtr<imgIEncoder> mEncoder;
239
  RefPtr<EncodingCompleteEvent> mEncodingCompleteEvent;
240
  int32_t mFormat;
241
  const nsIntSize mSize;
242
  bool mUsePlaceholder;
243
  bool mUsingCustomOptions;
244
};
245
246
StaticRefPtr<nsIThreadPool> ImageEncoder::sThreadPool;
247
248
/* static */
249
nsresult
250
ImageEncoder::ExtractData(nsAString& aType,
251
                          const nsAString& aOptions,
252
                          const nsIntSize aSize,
253
                          bool aUsePlaceholder,
254
                          nsICanvasRenderingContextInternal* aContext,
255
                          layers::AsyncCanvasRenderer* aRenderer,
256
                          nsIInputStream** aStream)
257
0
{
258
0
  nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
259
0
  if (!encoder) {
260
0
    return NS_IMAGELIB_ERROR_NO_ENCODER;
261
0
  }
262
0
263
0
  return ExtractDataInternal(aType, aOptions, nullptr, 0, aSize,
264
0
                             aUsePlaceholder, nullptr,
265
0
                             aContext, aRenderer, aStream, encoder);
266
0
}
267
268
/* static */
269
nsresult
270
ImageEncoder::ExtractDataFromLayersImageAsync(nsAString& aType,
271
                                              const nsAString& aOptions,
272
                                              bool aUsingCustomOptions,
273
                                              layers::Image* aImage,
274
                                              bool aUsePlaceholder,
275
                                              EncodeCompleteCallback* aEncodeCallback)
276
0
{
277
0
  nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
278
0
  if (!encoder) {
279
0
    return NS_IMAGELIB_ERROR_NO_ENCODER;
280
0
  }
281
0
282
0
  nsresult rv = EnsureThreadPool();
283
0
  if (NS_FAILED(rv)) {
284
0
    return rv;
285
0
  }
286
0
287
0
  RefPtr<EncodingCompleteEvent> completeEvent =
288
0
    new EncodingCompleteEvent(aEncodeCallback);
289
0
290
0
  nsIntSize size(aImage->GetSize().width, aImage->GetSize().height);
291
0
  nsCOMPtr<nsIRunnable> event = new EncodingRunnable(aType,
292
0
                                                     aOptions,
293
0
                                                     nullptr,
294
0
                                                     aImage,
295
0
                                                     encoder,
296
0
                                                     completeEvent,
297
0
                                                     imgIEncoder::INPUT_FORMAT_HOSTARGB,
298
0
                                                     size,
299
0
                                                     aUsePlaceholder,
300
0
                                                     aUsingCustomOptions);
301
0
  return sThreadPool->Dispatch(event, NS_DISPATCH_NORMAL);
302
0
}
303
304
/* static */
305
nsresult
306
ImageEncoder::ExtractDataAsync(nsAString& aType,
307
                               const nsAString& aOptions,
308
                               bool aUsingCustomOptions,
309
                               UniquePtr<uint8_t[]> aImageBuffer,
310
                               int32_t aFormat,
311
                               const nsIntSize aSize,
312
                               bool aUsePlaceholder,
313
                               EncodeCompleteCallback* aEncodeCallback)
314
0
{
315
0
  nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
316
0
  if (!encoder) {
317
0
    return NS_IMAGELIB_ERROR_NO_ENCODER;
318
0
  }
319
0
320
0
  nsresult rv = EnsureThreadPool();
321
0
  if (NS_FAILED(rv)) {
322
0
    return rv;
323
0
  }
324
0
325
0
  RefPtr<EncodingCompleteEvent> completeEvent =
326
0
    new EncodingCompleteEvent(aEncodeCallback);
327
0
328
0
  nsCOMPtr<nsIRunnable> event = new EncodingRunnable(aType,
329
0
                                                     aOptions,
330
0
                                                     std::move(aImageBuffer),
331
0
                                                     nullptr,
332
0
                                                     encoder,
333
0
                                                     completeEvent,
334
0
                                                     aFormat,
335
0
                                                     aSize,
336
0
                                                     aUsePlaceholder,
337
0
                                                     aUsingCustomOptions);
338
0
  return sThreadPool->Dispatch(event, NS_DISPATCH_NORMAL);
339
0
}
340
341
/*static*/ nsresult
342
ImageEncoder::GetInputStream(int32_t aWidth,
343
                             int32_t aHeight,
344
                             uint8_t* aImageBuffer,
345
                             int32_t aFormat,
346
                             imgIEncoder* aEncoder,
347
                             const char16_t* aEncoderOptions,
348
                             nsIInputStream** aStream)
349
0
{
350
0
  nsresult rv =
351
0
    aEncoder->InitFromData(aImageBuffer,
352
0
                           aWidth * aHeight * 4, aWidth, aHeight, aWidth * 4,
353
0
                           aFormat,
354
0
                           nsDependentString(aEncoderOptions));
355
0
  NS_ENSURE_SUCCESS(rv, rv);
356
0
357
0
  return CallQueryInterface(aEncoder, aStream);
358
0
}
359
360
/* static */
361
nsresult
362
ImageEncoder::ExtractDataInternal(const nsAString& aType,
363
                                  const nsAString& aOptions,
364
                                  uint8_t* aImageBuffer,
365
                                  int32_t aFormat,
366
                                  const nsIntSize aSize,
367
                                  bool aUsePlaceholder,
368
                                  layers::Image* aImage,
369
                                  nsICanvasRenderingContextInternal* aContext,
370
                                  layers::AsyncCanvasRenderer* aRenderer,
371
                                  nsIInputStream** aStream,
372
                                  imgIEncoder* aEncoder)
373
0
{
374
0
  if (aSize.IsEmpty()) {
375
0
    return NS_ERROR_INVALID_ARG;
376
0
  }
377
0
378
0
  nsCOMPtr<nsIInputStream> imgStream;
379
0
380
0
  // get image bytes
381
0
  nsresult rv;
382
0
  if (aImageBuffer && !aUsePlaceholder) {
383
0
    if (BufferSizeFromDimensions(aSize.width, aSize.height, 4) == 0) {
384
0
      return NS_ERROR_INVALID_ARG;
385
0
    }
386
0
387
0
    rv = ImageEncoder::GetInputStream(
388
0
      aSize.width,
389
0
      aSize.height,
390
0
      aImageBuffer,
391
0
      aFormat,
392
0
      aEncoder,
393
0
      nsPromiseFlatString(aOptions).get(),
394
0
      getter_AddRefs(imgStream));
395
0
  } else if (aContext && !aUsePlaceholder) {
396
0
    NS_ConvertUTF16toUTF8 encoderType(aType);
397
0
    rv = aContext->GetInputStream(encoderType.get(),
398
0
                                  nsPromiseFlatString(aOptions).get(),
399
0
                                  getter_AddRefs(imgStream));
400
0
  } else if (aRenderer && !aUsePlaceholder) {
401
0
    NS_ConvertUTF16toUTF8 encoderType(aType);
402
0
    rv = aRenderer->GetInputStream(encoderType.get(),
403
0
                                   nsPromiseFlatString(aOptions).get(),
404
0
                                   getter_AddRefs(imgStream));
405
0
  } else if (aImage && !aUsePlaceholder) {
406
0
    // It is safe to convert PlanarYCbCr format from YUV to RGB off-main-thread.
407
0
    // Other image formats could have problem to convert format off-main-thread.
408
0
    // So here it uses a help function GetBRGADataSourceSurfaceSync() to convert
409
0
    // format on main thread.
410
0
    if (aImage->GetFormat() == ImageFormat::PLANAR_YCBCR) {
411
0
      nsTArray<uint8_t> data;
412
0
      layers::PlanarYCbCrImage* ycbcrImage = static_cast<layers::PlanarYCbCrImage*> (aImage);
413
0
      gfxImageFormat format = SurfaceFormat::A8R8G8B8_UINT32;
414
0
      uint32_t stride = GetAlignedStride<16>(aSize.width, 4);
415
0
      size_t length = BufferSizeFromStrideAndHeight(stride, aSize.height);
416
0
      if (length == 0) {
417
0
        return NS_ERROR_INVALID_ARG;
418
0
      }
419
0
      data.SetCapacity(length);
420
0
421
0
      ConvertYCbCrToRGB(*ycbcrImage->GetData(),
422
0
                        format,
423
0
                        aSize,
424
0
                        data.Elements(),
425
0
                        stride);
426
0
427
0
      rv = aEncoder->InitFromData(data.Elements(),
428
0
                                  aSize.width * aSize.height * 4,
429
0
                                  aSize.width,
430
0
                                  aSize.height,
431
0
                                  aSize.width * 4,
432
0
                                  imgIEncoder::INPUT_FORMAT_HOSTARGB,
433
0
                                  aOptions);
434
0
    } else {
435
0
      if (BufferSizeFromDimensions(aSize.width, aSize.height, 4) == 0) {
436
0
        return NS_ERROR_INVALID_ARG;
437
0
      }
438
0
439
0
      RefPtr<gfx::DataSourceSurface> dataSurface;
440
0
      RefPtr<layers::Image> image(aImage);
441
0
      dataSurface = GetBRGADataSourceSurfaceSync(image.forget());
442
0
443
0
      DataSourceSurface::MappedSurface map;
444
0
      if (!dataSurface->Map(gfx::DataSourceSurface::MapType::READ, &map)) {
445
0
        return NS_ERROR_INVALID_ARG;
446
0
      }
447
0
      rv = aEncoder->InitFromData(map.mData,
448
0
                                  aSize.width * aSize.height * 4,
449
0
                                  aSize.width,
450
0
                                  aSize.height,
451
0
                                  aSize.width * 4,
452
0
                                  imgIEncoder::INPUT_FORMAT_HOSTARGB,
453
0
                                  aOptions);
454
0
      dataSurface->Unmap();
455
0
    }
456
0
457
0
    if (NS_SUCCEEDED(rv)) {
458
0
      imgStream = do_QueryInterface(aEncoder);
459
0
    }
460
0
  } else {
461
0
    if (BufferSizeFromDimensions(aSize.width, aSize.height, 4) == 0) {
462
0
      return NS_ERROR_INVALID_ARG;
463
0
    }
464
0
465
0
    // no context, so we have to encode an empty image
466
0
    // note that if we didn't have a current context, the spec says we're
467
0
    // supposed to just return transparent black pixels of the canvas
468
0
    // dimensions.
469
0
    RefPtr<DataSourceSurface> emptyCanvas =
470
0
      Factory::CreateDataSourceSurfaceWithStride(IntSize(aSize.width, aSize.height),
471
0
                                                 SurfaceFormat::B8G8R8A8,
472
0
                                                 4 * aSize.width, true);
473
0
    if (NS_WARN_IF(!emptyCanvas)) {
474
0
      return NS_ERROR_INVALID_ARG;
475
0
    }
476
0
477
0
    DataSourceSurface::MappedSurface map;
478
0
    if (!emptyCanvas->Map(DataSourceSurface::MapType::WRITE, &map)) {
479
0
      return NS_ERROR_INVALID_ARG;
480
0
    }
481
0
    if (aUsePlaceholder) {
482
0
      // If placeholder data was requested, return all-white, opaque image data.
483
0
      memset(map.mData, 0xFF, 4 * aSize.width * aSize.height);
484
0
    }
485
0
    rv = aEncoder->InitFromData(map.mData,
486
0
                                aSize.width * aSize.height * 4,
487
0
                                aSize.width,
488
0
                                aSize.height,
489
0
                                aSize.width * 4,
490
0
                                imgIEncoder::INPUT_FORMAT_HOSTARGB,
491
0
                                aOptions);
492
0
    emptyCanvas->Unmap();
493
0
    if (NS_SUCCEEDED(rv)) {
494
0
      imgStream = do_QueryInterface(aEncoder);
495
0
    }
496
0
  }
497
0
  NS_ENSURE_SUCCESS(rv, rv);
498
0
499
0
  imgStream.forget(aStream);
500
0
  return rv;
501
0
}
502
503
/* static */
504
already_AddRefed<imgIEncoder>
505
ImageEncoder::GetImageEncoder(nsAString& aType)
506
0
{
507
0
  // Get an image encoder for the media type.
508
0
  nsCString encoderCID("@mozilla.org/image/encoder;2?type=");
509
0
  NS_ConvertUTF16toUTF8 encoderType(aType);
510
0
  encoderCID += encoderType;
511
0
  nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(encoderCID.get());
512
0
513
0
  if (!encoder && aType != NS_LITERAL_STRING("image/png")) {
514
0
    // Unable to create an encoder instance of the specified type. Falling back
515
0
    // to PNG.
516
0
    aType.AssignLiteral("image/png");
517
0
    nsCString PNGEncoderCID("@mozilla.org/image/encoder;2?type=image/png");
518
0
    encoder = do_CreateInstance(PNGEncoderCID.get());
519
0
  }
520
0
521
0
  return encoder.forget();
522
0
}
523
524
class EncoderThreadPoolTerminator final : public nsIObserver
525
{
526
  public:
527
    NS_DECL_ISUPPORTS
528
529
    NS_IMETHOD Observe(nsISupports *, const char *topic, const char16_t *) override
530
0
    {
531
0
      NS_ASSERTION(!strcmp(topic, "xpcom-shutdown-threads"),
532
0
                   "Unexpected topic");
533
0
      if (ImageEncoder::sThreadPool) {
534
0
        ImageEncoder::sThreadPool->Shutdown();
535
0
        ImageEncoder::sThreadPool = nullptr;
536
0
      }
537
0
      return NS_OK;
538
0
    }
539
  private:
540
0
    ~EncoderThreadPoolTerminator() {}
541
};
542
543
NS_IMPL_ISUPPORTS(EncoderThreadPoolTerminator, nsIObserver)
544
545
static void
546
RegisterEncoderThreadPoolTerminatorObserver()
547
0
{
548
0
  MOZ_ASSERT(NS_IsMainThread());
549
0
  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
550
0
  NS_ASSERTION(os, "do_GetService failed");
551
0
  os->AddObserver(new EncoderThreadPoolTerminator(),
552
0
                  "xpcom-shutdown-threads",
553
0
                  false);
554
0
}
555
556
/* static */
557
nsresult
558
ImageEncoder::EnsureThreadPool()
559
0
{
560
0
  if (!sThreadPool) {
561
0
    nsCOMPtr<nsIThreadPool> threadPool = new nsThreadPool();
562
0
    sThreadPool = threadPool;
563
0
564
0
    if (!NS_IsMainThread()) {
565
0
      NS_DispatchToMainThread(NS_NewRunnableFunction(
566
0
        "dom::ImageEncoder::EnsureThreadPool",
567
0
        []() -> void { RegisterEncoderThreadPoolTerminatorObserver(); }));
568
0
    } else {
569
0
      RegisterEncoderThreadPoolTerminatorObserver();
570
0
    }
571
0
572
0
    const uint32_t kThreadLimit = 2;
573
0
    const uint32_t kIdleThreadLimit = 1;
574
0
    const uint32_t kIdleThreadTimeoutMs = 30000;
575
0
576
0
    nsresult rv = sThreadPool->SetName(NS_LITERAL_CSTRING("EncodingRunnable"));
577
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
578
0
      return rv;
579
0
    }
580
0
581
0
    rv = sThreadPool->SetThreadLimit(kThreadLimit);
582
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
583
0
      return rv;
584
0
    }
585
0
586
0
    rv = sThreadPool->SetIdleThreadLimit(kIdleThreadLimit);
587
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
588
0
      return rv;
589
0
    }
590
0
591
0
    rv = sThreadPool->SetIdleThreadTimeout(kIdleThreadTimeoutMs);
592
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
593
0
      return rv;
594
0
    }
595
0
  }
596
0
597
0
  return NS_OK;
598
0
}
599
600
} // namespace dom
601
} // namespace mozilla