LCOV - code coverage report
Current view: top level - dom/base - ImageEncoder.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 1 243 0.4 %
Date: 2017-07-14 16:53:18 Functions: 0 36 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13