tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

imgTools.cpp (21987B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 *
      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 "imgTools.h"
      8 
      9 #include "DecodePool.h"
     10 #include "gfxUtils.h"
     11 #include "mozilla/gfx/2D.h"
     12 #include "mozilla/gfx/Logging.h"
     13 #include "mozilla/RefPtr.h"
     14 #include "nsCOMPtr.h"
     15 #include "mozilla/dom/Document.h"
     16 #include "nsError.h"
     17 #include "imgLoader.h"
     18 #include "imgICache.h"
     19 #include "imgIContainer.h"
     20 #include "imgIEncoder.h"
     21 #include "nsComponentManagerUtils.h"
     22 #include "nsNetUtil.h"  // for NS_NewBufferedInputStream
     23 #include "nsStreamUtils.h"
     24 #include "nsStringStream.h"
     25 #include "nsContentUtils.h"
     26 #include "nsProxyRelease.h"
     27 #include "nsIStreamListener.h"
     28 #include "ImageFactory.h"
     29 #include "Image.h"
     30 #include "IProgressObserver.h"
     31 #include "ScriptedNotificationObserver.h"
     32 #include "imgIScriptedNotificationObserver.h"
     33 #include "gfxPlatform.h"
     34 #include "js/ArrayBuffer.h"
     35 #include "js/RootingAPI.h"  // JS::{Handle,Rooted}
     36 #include "js/Value.h"       // JS::Value
     37 #include "Orientation.h"
     38 
     39 using namespace mozilla::gfx;
     40 
     41 namespace mozilla {
     42 namespace image {
     43 
     44 namespace {
     45 
     46 static nsresult sniff_mimetype_callback(nsIInputStream* in, void* data,
     47                                        const char* fromRawSegment,
     48                                        uint32_t toOffset, uint32_t count,
     49                                        uint32_t* writeCount) {
     50  nsCString* mimeType = static_cast<nsCString*>(data);
     51  MOZ_ASSERT(mimeType, "mimeType is null!");
     52 
     53  if (count > 0) {
     54    imgLoader::GetMimeTypeFromContent(fromRawSegment, count, *mimeType);
     55  }
     56 
     57  *writeCount = 0;
     58  return NS_ERROR_FAILURE;
     59 }
     60 
     61 class ImageDecoderListener final : public nsIStreamListener,
     62                                   public IProgressObserver,
     63                                   public imgIContainer {
     64 public:
     65  NS_DECL_ISUPPORTS
     66 
     67  ImageDecoderListener(nsIURI* aURI, imgIContainerCallback* aCallback,
     68                       imgINotificationObserver* aObserver)
     69      : mURI(aURI),
     70        mImage(nullptr),
     71        mCallback(aCallback),
     72        mObserver(aObserver) {
     73    MOZ_ASSERT(NS_IsMainThread());
     74  }
     75 
     76  NS_IMETHOD
     77  OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aInputStream,
     78                  uint64_t aOffset, uint32_t aCount) override {
     79    if (!mImage) {
     80      nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
     81 
     82      nsCString mimeType;
     83      channel->GetContentType(mimeType);
     84 
     85      if (aInputStream) {
     86        // Look at the first few bytes and see if we can tell what the data is
     87        // from that since servers tend to lie. :(
     88        uint32_t unused;
     89        aInputStream->ReadSegments(sniff_mimetype_callback, &mimeType, aCount,
     90                                   &unused);
     91      }
     92 
     93      RefPtr<ProgressTracker> tracker = new ProgressTracker();
     94      if (mObserver) {
     95        tracker->AddObserver(this);
     96      }
     97 
     98      mImage = ImageFactory::CreateImage(channel, tracker, mimeType, mURI,
     99                                         /* aIsMultiPart */ false, 0);
    100 
    101      if (mImage->HasError()) {
    102        return NS_ERROR_FAILURE;
    103      }
    104    }
    105 
    106    return mImage->OnImageDataAvailable(aRequest, aInputStream, aOffset,
    107                                        aCount);
    108  }
    109 
    110  NS_IMETHOD
    111  OnStartRequest(nsIRequest* aRequest) override { return NS_OK; }
    112 
    113  NS_IMETHOD
    114  OnStopRequest(nsIRequest* aRequest, nsresult aStatus) override {
    115    // Encouter a fetch error, or no data could be fetched.
    116    if (!mImage || NS_FAILED(aStatus)) {
    117      mCallback->OnImageReady(nullptr, mImage ? aStatus : NS_ERROR_FAILURE);
    118      return NS_OK;
    119    }
    120 
    121    mImage->OnImageDataComplete(aRequest, aStatus, true);
    122    nsCOMPtr<imgIContainer> container = this;
    123    mCallback->OnImageReady(container, aStatus);
    124    return NS_OK;
    125  }
    126 
    127  virtual void Notify(int32_t aType,
    128                      const nsIntRect* aRect = nullptr) override {
    129    if (mObserver) {
    130      mObserver->Notify(nullptr, aType, aRect);
    131    }
    132  }
    133 
    134  virtual void OnLoadComplete(bool aLastPart) override {
    135    // ProgressTracker dispatches LOAD_COMPLETE as OnLoadComplete, but *our*
    136    // observers need the Notify invocation for it.
    137    if (mObserver) {
    138      mObserver->Notify(nullptr, imgINotificationObserver::LOAD_COMPLETE,
    139                        nullptr);
    140    }
    141  }
    142 
    143  // Other notifications are ignored.
    144  virtual void SetHasImage() override {}
    145  virtual bool NotificationsDeferred() const override { return false; }
    146  virtual void MarkPendingNotify() override {}
    147  virtual void ClearPendingNotify() override {}
    148 
    149  // imgIContainer
    150  NS_FORWARD_IMGICONTAINER(mImage->)
    151 
    152 private:
    153  virtual ~ImageDecoderListener() = default;
    154 
    155  nsCOMPtr<nsIURI> mURI;
    156  RefPtr<image::Image> mImage;
    157  nsCOMPtr<imgIContainerCallback> mCallback;
    158  nsCOMPtr<imgINotificationObserver> mObserver;
    159 };
    160 
    161 NS_IMPL_ISUPPORTS(ImageDecoderListener, nsIStreamListener, imgIContainer)
    162 
    163 class ImageDecoderHelper final : public Runnable,
    164                                 public nsIInputStreamCallback {
    165 public:
    166  NS_DECL_ISUPPORTS_INHERITED
    167 
    168  ImageDecoderHelper(already_AddRefed<image::Image> aImage,
    169                     already_AddRefed<nsIInputStream> aInputStream,
    170                     nsIEventTarget* aEventTarget,
    171                     imgIContainerCallback* aCallback,
    172                     nsIEventTarget* aCallbackEventTarget)
    173      : Runnable("ImageDecoderHelper"),
    174        mImage(std::move(aImage)),
    175        mInputStream(std::move(aInputStream)),
    176        mEventTarget(aEventTarget),
    177        mCallback(aCallback),
    178        mCallbackEventTarget(aCallbackEventTarget),
    179        mStatus(NS_OK) {
    180    MOZ_ASSERT(NS_IsMainThread());
    181  }
    182 
    183  NS_IMETHOD
    184  Run() override {
    185    // This runnable is dispatched on the Image thread when reading data, but
    186    // at the end, it goes back to the main-thread in order to complete the
    187    // operation.
    188    if (NS_IsMainThread()) {
    189      // Let the Image know we've sent all the data.
    190      mImage->OnImageDataComplete(nullptr, mStatus, true);
    191 
    192      RefPtr<ProgressTracker> tracker = mImage->GetProgressTracker();
    193      tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
    194 
    195      nsCOMPtr<imgIContainer> container;
    196      if (NS_SUCCEEDED(mStatus)) {
    197        container = mImage;
    198      }
    199 
    200      mCallback->OnImageReady(container, mStatus);
    201      return NS_OK;
    202    }
    203 
    204    uint64_t length;
    205    nsresult rv = mInputStream->Available(&length);
    206    if (rv == NS_BASE_STREAM_CLOSED) {
    207      return OperationCompleted(NS_OK);
    208    }
    209 
    210    if (NS_WARN_IF(NS_FAILED(rv))) {
    211      return OperationCompleted(rv);
    212    }
    213 
    214    // Nothing else to read, but maybe we just need to wait.
    215    if (length == 0) {
    216      nsCOMPtr<nsIAsyncInputStream> asyncInputStream =
    217          do_QueryInterface(mInputStream);
    218      if (asyncInputStream) {
    219        rv = asyncInputStream->AsyncWait(this, 0, 0, mEventTarget);
    220        if (NS_WARN_IF(NS_FAILED(rv))) {
    221          return OperationCompleted(rv);
    222        }
    223        return NS_OK;
    224      }
    225 
    226      // We really have nothing else to read.
    227      if (length == 0) {
    228        return OperationCompleted(NS_OK);
    229      }
    230    }
    231 
    232    // Send the source data to the Image.
    233    rv = mImage->OnImageDataAvailable(nullptr, mInputStream, 0,
    234                                      uint32_t(length));
    235    if (NS_WARN_IF(NS_FAILED(rv))) {
    236      return OperationCompleted(rv);
    237    }
    238 
    239    rv = mEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
    240    if (NS_WARN_IF(NS_FAILED(rv))) {
    241      return OperationCompleted(rv);
    242    }
    243 
    244    return NS_OK;
    245  }
    246 
    247  NS_IMETHOD
    248  OnInputStreamReady(nsIAsyncInputStream* aAsyncInputStream) override {
    249    MOZ_ASSERT(!NS_IsMainThread());
    250    return Run();
    251  }
    252 
    253  nsresult OperationCompleted(nsresult aStatus) {
    254    MOZ_ASSERT(!NS_IsMainThread());
    255 
    256    mStatus = aStatus;
    257    mCallbackEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
    258    return NS_OK;
    259  }
    260 
    261 private:
    262  ~ImageDecoderHelper() {
    263    SurfaceCache::ReleaseImageOnMainThread(mImage.forget());
    264    NS_ReleaseOnMainThread("ImageDecoderHelper::mCallback", mCallback.forget());
    265  }
    266 
    267  RefPtr<image::Image> mImage;
    268 
    269  nsCOMPtr<nsIInputStream> mInputStream;
    270  nsCOMPtr<nsIEventTarget> mEventTarget;
    271  nsCOMPtr<imgIContainerCallback> mCallback;
    272  nsCOMPtr<nsIEventTarget> mCallbackEventTarget;
    273 
    274  nsresult mStatus;
    275 };
    276 
    277 NS_IMPL_ISUPPORTS_INHERITED(ImageDecoderHelper, Runnable,
    278                            nsIInputStreamCallback)
    279 
    280 }  // namespace
    281 
    282 /* ========== imgITools implementation ========== */
    283 
    284 NS_IMPL_ISUPPORTS(imgTools, imgITools)
    285 
    286 imgTools::imgTools() { /* member initializers and constructor code */ }
    287 
    288 imgTools::~imgTools() { /* destructor code */ }
    289 
    290 NS_IMETHODIMP
    291 imgTools::DecodeImageFromArrayBuffer(JS::Handle<JS::Value> aArrayBuffer,
    292                                     const nsACString& aMimeType,
    293                                     JSContext* aCx,
    294                                     imgIContainer** aContainer) {
    295  if (!aArrayBuffer.isObject()) {
    296    return NS_ERROR_FAILURE;
    297  }
    298 
    299  JS::Rooted<JSObject*> obj(aCx,
    300                            JS::UnwrapArrayBuffer(&aArrayBuffer.toObject()));
    301  if (!obj) {
    302    return NS_ERROR_FAILURE;
    303  }
    304 
    305  uint8_t* bufferData = nullptr;
    306  size_t bufferLength = 0;
    307  bool isSharedMemory = false;
    308 
    309  JS::GetArrayBufferLengthAndData(obj, &bufferLength, &isSharedMemory,
    310                                  &bufferData);
    311 
    312  // Throw for large ArrayBuffers to prevent truncation.
    313  if (bufferLength > INT32_MAX) {
    314    return NS_ERROR_ILLEGAL_VALUE;
    315  }
    316 
    317  return DecodeImageFromBuffer((char*)bufferData, bufferLength, aMimeType,
    318                               aContainer);
    319 }
    320 
    321 NS_IMETHODIMP
    322 imgTools::DecodeImageFromBuffer(const char* aBuffer, uint32_t aSize,
    323                                const nsACString& aMimeType,
    324                                imgIContainer** aContainer) {
    325  MOZ_ASSERT(NS_IsMainThread());
    326 
    327  NS_ENSURE_ARG_POINTER(aBuffer);
    328 
    329  // Create a new image container to hold the decoded data.
    330  nsAutoCString mimeType(aMimeType);
    331  RefPtr<image::Image> image =
    332      ImageFactory::CreateAnonymousImage(mimeType, aSize);
    333  RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
    334 
    335  if (image->HasError()) {
    336    return NS_ERROR_FAILURE;
    337  }
    338 
    339  // Let's create a temporary inputStream.
    340  nsCOMPtr<nsIInputStream> stream;
    341  nsresult rv = NS_NewByteInputStream(
    342      getter_AddRefs(stream), Span(aBuffer, aSize), NS_ASSIGNMENT_DEPEND);
    343  NS_ENSURE_SUCCESS(rv, rv);
    344  MOZ_ASSERT(stream);
    345  MOZ_ASSERT(NS_InputStreamIsBuffered(stream));
    346 
    347  rv = image->OnImageDataAvailable(nullptr, stream, 0, aSize);
    348  NS_ENSURE_SUCCESS(rv, rv);
    349 
    350  // Let the Image know we've sent all the data.
    351  rv = image->OnImageDataComplete(nullptr, NS_OK, true);
    352  tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
    353  NS_ENSURE_SUCCESS(rv, rv);
    354 
    355  // All done.
    356  image.forget(aContainer);
    357  return NS_OK;
    358 }
    359 
    360 NS_IMETHODIMP
    361 imgTools::DecodeImageFromChannelAsync(nsIURI* aURI, nsIChannel* aChannel,
    362                                      imgIContainerCallback* aCallback,
    363                                      imgINotificationObserver* aObserver) {
    364  MOZ_ASSERT(NS_IsMainThread());
    365 
    366  NS_ENSURE_ARG_POINTER(aURI);
    367  NS_ENSURE_ARG_POINTER(aChannel);
    368  NS_ENSURE_ARG_POINTER(aCallback);
    369 
    370  RefPtr<ImageDecoderListener> listener =
    371      new ImageDecoderListener(aURI, aCallback, aObserver);
    372 
    373  return aChannel->AsyncOpen(listener);
    374 }
    375 
    376 NS_IMETHODIMP
    377 imgTools::DecodeImageAsync(nsIInputStream* aInStr, const nsACString& aMimeType,
    378                           imgIContainerCallback* aCallback,
    379                           nsIEventTarget* aEventTarget) {
    380  MOZ_ASSERT(NS_IsMainThread());
    381 
    382  NS_ENSURE_ARG_POINTER(aInStr);
    383  NS_ENSURE_ARG_POINTER(aCallback);
    384  NS_ENSURE_ARG_POINTER(aEventTarget);
    385 
    386  nsresult rv;
    387 
    388  // Let's continuing the reading on a separate thread.
    389  DecodePool* decodePool = DecodePool::Singleton();
    390  MOZ_ASSERT(decodePool);
    391 
    392  RefPtr<nsIEventTarget> target = decodePool->GetIOEventTarget();
    393  NS_ENSURE_TRUE(target, NS_ERROR_FAILURE);
    394 
    395  // Prepare the input stream.
    396  nsCOMPtr<nsIInputStream> stream = aInStr;
    397  if (!NS_InputStreamIsBuffered(aInStr)) {
    398    nsCOMPtr<nsIInputStream> bufStream;
    399    rv = NS_NewBufferedInputStream(getter_AddRefs(bufStream), stream.forget(),
    400                                   1024);
    401    NS_ENSURE_SUCCESS(rv, rv);
    402    stream = std::move(bufStream);
    403  }
    404 
    405  // Create a new image container to hold the decoded data.
    406  nsAutoCString mimeType(aMimeType);
    407  RefPtr<image::Image> image = ImageFactory::CreateAnonymousImage(mimeType, 0);
    408 
    409  // Already an error?
    410  if (image->HasError()) {
    411    return NS_ERROR_FAILURE;
    412  }
    413 
    414  RefPtr<ImageDecoderHelper> helper = new ImageDecoderHelper(
    415      image.forget(), stream.forget(), target, aCallback, aEventTarget);
    416  rv = target->Dispatch(helper.forget(), NS_DISPATCH_NORMAL);
    417  NS_ENSURE_SUCCESS(rv, rv);
    418 
    419  return NS_OK;
    420 }
    421 
    422 /**
    423 * This takes a DataSourceSurface rather than a SourceSurface because some
    424 * of the callers have a DataSourceSurface and we don't want to call
    425 * GetDataSurface on such surfaces since that may incur a conversion to
    426 * SurfaceType::DATA which we don't need.
    427 */
    428 static nsresult EncodeImageData(DataSourceSurface* aDataSurface,
    429                                DataSourceSurface::ScopedMap& aMap,
    430                                const nsACString& aMimeType,
    431                                const nsAString& aOutputOptions,
    432                                nsIInputStream** aStream) {
    433  MOZ_ASSERT(aDataSurface->GetFormat() == SurfaceFormat::B8G8R8A8 ||
    434                 aDataSurface->GetFormat() == SurfaceFormat::B8G8R8X8,
    435             "We're assuming B8G8R8A8/X8");
    436 
    437  // Get an image encoder for the media type
    438  nsAutoCString encoderCID("@mozilla.org/image/encoder;2?type="_ns + aMimeType);
    439 
    440  nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(encoderCID.get());
    441  if (!encoder) {
    442    return NS_IMAGELIB_ERROR_NO_ENCODER;
    443  }
    444 
    445  IntSize size = aDataSurface->GetSize();
    446  uint32_t dataLength = aMap.GetStride() * size.height;
    447 
    448  // Encode the bitmap
    449  nsresult rv = encoder->InitFromData(
    450      aMap.GetData(), dataLength, size.width, size.height, aMap.GetStride(),
    451      imgIEncoder::INPUT_FORMAT_HOSTARGB, aOutputOptions, VoidCString());
    452  NS_ENSURE_SUCCESS(rv, rv);
    453 
    454  encoder.forget(aStream);
    455  return NS_OK;
    456 }
    457 
    458 static nsresult EncodeImageData(DataSourceSurface* aDataSurface,
    459                                const nsACString& aMimeType,
    460                                const nsAString& aOutputOptions,
    461                                nsIInputStream** aStream) {
    462  DataSourceSurface::ScopedMap map(aDataSurface, DataSourceSurface::READ);
    463  if (!map.IsMapped()) {
    464    return NS_ERROR_FAILURE;
    465  }
    466 
    467  return EncodeImageData(aDataSurface, map, aMimeType, aOutputOptions, aStream);
    468 }
    469 
    470 NS_IMETHODIMP
    471 imgTools::EncodeImage(imgIContainer* aContainer, const nsACString& aMimeType,
    472                      const nsAString& aOutputOptions,
    473                      nsIInputStream** aStream) {
    474  // Use frame 0 from the image container.
    475  RefPtr<SourceSurface> frame = aContainer->GetFrame(
    476      imgIContainer::FRAME_FIRST,
    477      imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY);
    478  NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
    479 
    480  RefPtr<DataSourceSurface> dataSurface;
    481 
    482  if (frame->GetFormat() == SurfaceFormat::B8G8R8A8 ||
    483      frame->GetFormat() == SurfaceFormat::B8G8R8X8) {
    484    dataSurface = frame->GetDataSurface();
    485  } else {
    486    // Convert format to SurfaceFormat::B8G8R8A8
    487    dataSurface = gfxUtils::CopySurfaceToDataSourceSurfaceWithFormat(
    488        frame, SurfaceFormat::B8G8R8A8);
    489  }
    490 
    491  NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
    492 
    493  return EncodeImageData(dataSurface, aMimeType, aOutputOptions, aStream);
    494 }
    495 
    496 NS_IMETHODIMP
    497 imgTools::EncodeScaledImage(imgIContainer* aContainer,
    498                            const nsACString& aMimeType, int32_t aScaledWidth,
    499                            int32_t aScaledHeight,
    500                            const nsAString& aOutputOptions,
    501                            nsIInputStream** aStream) {
    502  NS_ENSURE_ARG(aScaledWidth >= 0 && aScaledHeight >= 0);
    503 
    504  // If no scaled size is specified, we'll just encode the image at its
    505  // original size (no scaling).
    506  if (aScaledWidth == 0 && aScaledHeight == 0) {
    507    return EncodeImage(aContainer, aMimeType, aOutputOptions, aStream);
    508  }
    509 
    510  // Retrieve the image's size.
    511  int32_t imageWidth = 0;
    512  int32_t imageHeight = 0;
    513  aContainer->GetWidth(&imageWidth);
    514  aContainer->GetHeight(&imageHeight);
    515 
    516  // If the given width or height is zero we'll replace it with the image's
    517  // original dimensions.
    518  IntSize scaledSize(aScaledWidth == 0 ? imageWidth : aScaledWidth,
    519                     aScaledHeight == 0 ? imageHeight : aScaledHeight);
    520 
    521  // Use frame 0 from the image container.
    522  RefPtr<SourceSurface> frame = aContainer->GetFrameAtSize(
    523      scaledSize, imgIContainer::FRAME_FIRST,
    524      imgIContainer::FLAG_HIGH_QUALITY_SCALING |
    525          imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY);
    526  NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
    527 
    528  // If the given surface is the right size/format, we can encode it directly.
    529  if (scaledSize == frame->GetSize() &&
    530      (frame->GetFormat() == SurfaceFormat::B8G8R8A8 ||
    531       frame->GetFormat() == SurfaceFormat::B8G8R8X8)) {
    532    RefPtr<DataSourceSurface> dataSurface = frame->GetDataSurface();
    533    if (dataSurface) {
    534      return EncodeImageData(dataSurface, aMimeType, aOutputOptions, aStream);
    535    }
    536  }
    537 
    538  // Otherwise we need to scale it using a draw target.
    539  // Ensure the surface is initialized to clear in case we need to blend to it.
    540  RefPtr<DataSourceSurface> dataSurface = Factory::CreateDataSourceSurface(
    541      scaledSize, SurfaceFormat::B8G8R8A8, true);
    542  if (NS_WARN_IF(!dataSurface)) {
    543    return NS_ERROR_FAILURE;
    544  }
    545 
    546  DataSourceSurface::ScopedMap map(dataSurface, DataSourceSurface::READ_WRITE);
    547  if (!map.IsMapped()) {
    548    return NS_ERROR_FAILURE;
    549  }
    550 
    551  RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData(
    552      BackendType::SKIA, map.GetData(), dataSurface->GetSize(), map.GetStride(),
    553      SurfaceFormat::B8G8R8A8);
    554  if (!dt) {
    555    gfxWarning() << "imgTools::EncodeImage failed in CreateDrawTargetForData";
    556    return NS_ERROR_OUT_OF_MEMORY;
    557  }
    558 
    559  // Prefer using OP_OVER to scale the surface instead of OP_SOURCE, as both
    560  // D2D and Skia have specific fast-paths for these, and may give divergent
    561  // and slower results when using OP_SOURCE.
    562  IntSize frameSize = frame->GetSize();
    563  dt->DrawSurface(frame, Rect(0, 0, scaledSize.width, scaledSize.height),
    564                  Rect(0, 0, frameSize.width, frameSize.height),
    565                  DrawSurfaceOptions(),
    566                  DrawOptions(1.0f, CompositionOp::OP_OVER));
    567 
    568  return EncodeImageData(dataSurface, map, aMimeType, aOutputOptions, aStream);
    569 }
    570 
    571 NS_IMETHODIMP
    572 imgTools::EncodeCroppedImage(imgIContainer* aContainer,
    573                             const nsACString& aMimeType, int32_t aOffsetX,
    574                             int32_t aOffsetY, int32_t aWidth, int32_t aHeight,
    575                             const nsAString& aOutputOptions,
    576                             nsIInputStream** aStream) {
    577  NS_ENSURE_ARG(aOffsetX >= 0 && aOffsetY >= 0 && aWidth >= 0 && aHeight >= 0);
    578 
    579  // Offsets must be zero when no width and height are given or else we're out
    580  // of bounds.
    581  NS_ENSURE_ARG(aWidth + aHeight > 0 || aOffsetX + aOffsetY == 0);
    582 
    583  // If no size is specified then we'll preserve the image's original dimensions
    584  // and don't need to crop.
    585  if (aWidth == 0 && aHeight == 0) {
    586    return EncodeImage(aContainer, aMimeType, aOutputOptions, aStream);
    587  }
    588 
    589  // Use frame 0 from the image container.
    590  RefPtr<SourceSurface> frame = aContainer->GetFrame(
    591      imgIContainer::FRAME_FIRST,
    592      imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY);
    593  NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
    594 
    595  int32_t frameWidth = frame->GetSize().width;
    596  int32_t frameHeight = frame->GetSize().height;
    597 
    598  // If the given width or height is zero we'll replace it with the image's
    599  // original dimensions.
    600  if (aWidth == 0) {
    601    aWidth = frameWidth;
    602  } else if (aHeight == 0) {
    603    aHeight = frameHeight;
    604  }
    605 
    606  // Check that the given crop rectangle is within image bounds.
    607  NS_ENSURE_ARG(frameWidth >= aOffsetX + aWidth &&
    608                frameHeight >= aOffsetY + aHeight);
    609 
    610  RefPtr<DataSourceSurface> dataSurface = Factory::CreateDataSourceSurface(
    611      IntSize(aWidth, aHeight), SurfaceFormat::B8G8R8A8,
    612      /* aZero = */ true);
    613  if (NS_WARN_IF(!dataSurface)) {
    614    return NS_ERROR_FAILURE;
    615  }
    616 
    617  DataSourceSurface::ScopedMap map(dataSurface, DataSourceSurface::READ_WRITE);
    618  if (!map.IsMapped()) {
    619    return NS_ERROR_FAILURE;
    620  }
    621 
    622  RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData(
    623      BackendType::SKIA, map.GetData(), dataSurface->GetSize(), map.GetStride(),
    624      SurfaceFormat::B8G8R8A8);
    625  if (!dt) {
    626    gfxWarning()
    627        << "imgTools::EncodeCroppedImage failed in CreateDrawTargetForData";
    628    return NS_ERROR_OUT_OF_MEMORY;
    629  }
    630  dt->CopySurface(frame, IntRect(aOffsetX, aOffsetY, aWidth, aHeight),
    631                  IntPoint(0, 0));
    632 
    633  return EncodeImageData(dataSurface, map, aMimeType, aOutputOptions, aStream);
    634 }
    635 
    636 NS_IMETHODIMP
    637 imgTools::CreateScriptedObserver(imgIScriptedNotificationObserver* aInner,
    638                                 imgINotificationObserver** aObserver) {
    639  NS_ADDREF(*aObserver = new ScriptedNotificationObserver(aInner));
    640  return NS_OK;
    641 }
    642 
    643 NS_IMETHODIMP
    644 imgTools::GetImgLoaderForDocument(dom::Document* aDoc, imgILoader** aLoader) {
    645  NS_IF_ADDREF(*aLoader = nsContentUtils::GetImgLoaderForDocument(aDoc));
    646  return NS_OK;
    647 }
    648 
    649 NS_IMETHODIMP
    650 imgTools::GetImgCacheForDocument(dom::Document* aDoc, imgICache** aCache) {
    651  nsCOMPtr<imgILoader> loader;
    652  nsresult rv = GetImgLoaderForDocument(aDoc, getter_AddRefs(loader));
    653  NS_ENSURE_SUCCESS(rv, rv);
    654  return CallQueryInterface(loader, aCache);
    655 }
    656 
    657 }  // namespace image
    658 }  // namespace mozilla