Bug 1530402 - Provide imgTools.decodeFromChannelAsync. r=aosmond,snorp
☠☠ backed out by 61ebc1a22544 ☠ ☠
authorAgi Sferro <agi@sferro.dev>
Fri, 15 Nov 2019 16:33:43 +0000
changeset 502227 72274a5a821b2b8e3d9edaa9a0aaeba3e55de836
parent 502226 3d3366fe476fef1598d90b63133f151b6be5cdd3
child 502228 282b033c7daaab3b9bca0cd3ec6dc504e220ce5e
push id114172
push userdluca@mozilla.com
push dateTue, 19 Nov 2019 11:31:10 +0000
treeherdermozilla-inbound@b5c5ba07d3db [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersaosmond, snorp
bugs1530402
milestone72.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1530402 - Provide imgTools.decodeFromChannelAsync. r=aosmond,snorp This method allows consumers to decode images from a |nsIChannel| instance. This method also supports vector images (e.g. SVGs), which other decode methods don't. Differential Revision: https://phabricator.services.mozilla.com/D49037
image/imgITools.idl
image/imgTools.cpp
--- a/image/imgITools.idl
+++ b/image/imgITools.idl
@@ -1,18 +1,20 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
+interface nsIChannel;
 interface nsIEventTarget;
 interface nsIInputStream;
+interface nsIURI;
 interface imgIContainer;
 interface imgILoader;
 interface imgICache;
 interface imgIScriptedNotificationObserver;
 interface imgINotificationObserver;
 interface imgIContainerCallback;
 
 webidl Document;
@@ -48,16 +50,39 @@ interface imgITools : nsISupports
      * @param aMimeType
      *        Type of image in the stream.
      */
     [implicit_jscontext]
     imgIContainer decodeImageFromArrayBuffer(in jsval aArrayBuffer,
                                              in ACString aMimeType);
 
     /**
+     * decodeImageFromChannelAsync
+     * See decodeImage. The main difference between this method and decodeImage
+     * is that here the operation is done async on a thread from the decode
+     * pool. When the operation is completed, the callback is executed with the
+     * result.
+     *
+     * @param aURI
+     *        The original URI of the image
+     * @param aChannel
+     *        Channel to the image to be decoded.
+     * @param aCallback
+     *        The callback is executed when the imgContainer is fully created.
+     * @param aObserver
+     *        Optional observer for the decoded image, the caller should make
+     *        sure the observer is kept alive as long as necessary, as ImageLib
+     *        does not keep a strong reference to the observer.
+     */
+    void decodeImageFromChannelAsync(in nsIURI aURI,
+                                     in nsIChannel aChannel,
+                                     in imgIContainerCallback aCallback,
+                                     in imgINotificationObserver aObserver);
+
+    /**
      * decodeImageAsync
      * See decodeImage. The main difference between this method and decodeImage
      * is that here the operation is done async on a thread from the decode
      * pool. When the operation is completed, the callback is executed with the
      * result.
      *
      * @param aStream
      *        An input stream for an encoded image file.
--- a/image/imgTools.cpp
+++ b/image/imgTools.cpp
@@ -18,32 +18,173 @@
 #include "imgICache.h"
 #include "imgIContainer.h"
 #include "imgIEncoder.h"
 #include "nsNetUtil.h"  // for NS_NewBufferedInputStream
 #include "nsStreamUtils.h"
 #include "nsStringStream.h"
 #include "nsContentUtils.h"
 #include "nsProxyRelease.h"
+#include "nsIStreamListener.h"
 #include "ImageFactory.h"
 #include "Image.h"
+#include "IProgressObserver.h"
 #include "ScriptedNotificationObserver.h"
 #include "imgIScriptedNotificationObserver.h"
 #include "gfxPlatform.h"
 #include "js/ArrayBuffer.h"
 #include "js/RootingAPI.h"  // JS::{Handle,Rooted}
 #include "js/Value.h"       // JS::Value
 
 using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace image {
 
 namespace {
 
+static nsresult sniff_mimetype_callback(nsIInputStream* in, void* data,
+                                        const char* fromRawSegment,
+                                        uint32_t toOffset, uint32_t count,
+                                        uint32_t* writeCount) {
+  nsCString* mimeType = static_cast<nsCString*>(data);
+  MOZ_ASSERT(mimeType, "mimeType is null!");
+
+  if (count > 0) {
+    imgLoader::GetMimeTypeFromContent(fromRawSegment, count, *mimeType);
+  }
+
+  *writeCount = 0;
+  return NS_ERROR_FAILURE;
+}
+
+// Provides WeakPtr for imgINotificationObserver
+class NotificationObserverWrapper : public imgINotificationObserver,
+                                    public mozilla::SupportsWeakPtr<NotificationObserverWrapper> {
+ public:
+  NS_DECL_ISUPPORTS
+  NS_FORWARD_IMGINOTIFICATIONOBSERVER(mObserver->)
+  MOZ_DECLARE_WEAKREFERENCE_TYPENAME(nsGeolocationRequest)
+
+  explicit NotificationObserverWrapper(imgINotificationObserver* observer) : mObserver(observer) {}
+
+ private:
+  virtual ~NotificationObserverWrapper() = default;
+  nsCOMPtr<imgINotificationObserver> mObserver;
+};
+
+NS_IMPL_ISUPPORTS(NotificationObserverWrapper, imgINotificationObserver)
+
+class ImageDecoderListener final : public nsIStreamListener,
+                                   public IProgressObserver,
+                                   public imgIContainer {
+ public:
+  NS_DECL_ISUPPORTS
+
+  ImageDecoderListener(nsIURI* aURI, imgIContainerCallback* aCallback,
+                       imgINotificationObserver* aObserver)
+      : mURI(aURI),
+        mImage(nullptr),
+        mCallback(aCallback),
+        mObserver(new NotificationObserverWrapper(aObserver)) {
+    MOZ_ASSERT(NS_IsMainThread());
+  }
+
+  NS_IMETHOD
+  OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aInputStream,
+                  uint64_t aOffset, uint32_t aCount) override {
+    if (!mImage) {
+      nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
+
+      nsCString mimeType;
+      channel->GetContentType(mimeType);
+
+      if (aInputStream) {
+        // Look at the first few bytes and see if we can tell what the data is from
+        // that since servers tend to lie. :(
+        uint32_t unused;
+        aInputStream->ReadSegments(sniff_mimetype_callback, &mimeType, aCount, &unused);
+      }
+
+      RefPtr<ProgressTracker> tracker = new ProgressTracker();
+      if (mObserver) {
+        tracker->AddObserver(this);
+      }
+
+      mImage = ImageFactory::CreateImage(channel, tracker, mimeType, mURI,
+                                         /* aIsMultiPart */ false, 0);
+
+      if (mImage->HasError()) {
+        return NS_ERROR_FAILURE;
+      }
+    }
+
+    return mImage->OnImageDataAvailable(aRequest, nullptr, aInputStream,
+                                        aOffset, aCount);
+  }
+
+  NS_IMETHOD
+  OnStartRequest(nsIRequest* aRequest) override {
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  OnStopRequest(nsIRequest* aRequest, nsresult aStatus) override {
+    // Depending on the error, we might not have received any data yet, in which case we would not
+    // have an |mImage|
+    if (mImage) {
+      mImage->OnImageDataComplete(aRequest, nullptr, aStatus, true);
+    }
+
+    nsCOMPtr<imgIContainer> container;
+    if (NS_SUCCEEDED(aStatus)) {
+      container = this;
+    }
+
+    mCallback->OnImageReady(container, aStatus);
+    return NS_OK;
+  }
+
+  virtual void Notify(int32_t aType,
+                      const nsIntRect* aRect = nullptr) override {
+    if (mObserver) {
+      mObserver->Notify(nullptr, aType, aRect);
+    }
+  }
+
+  virtual void OnLoadComplete(bool aLastPart) override {}
+
+  // Other notifications are ignored.
+  virtual void SetHasImage() override {}
+  virtual bool NotificationsDeferred() const override { return false; }
+  virtual void MarkPendingNotify() override {}
+  virtual void ClearPendingNotify() override {}
+
+  // imgIContainer
+  NS_FORWARD_IMGICONTAINER(mImage->)
+
+  nsresult GetNativeSizes(nsTArray<nsIntSize>& aNativeSizes) const override {
+    return mImage->GetNativeSizes(aNativeSizes);
+  }
+
+  size_t GetNativeSizesLength() const override {
+    return mImage->GetNativeSizesLength();
+  }
+
+ private:
+  virtual ~ImageDecoderListener() = default;
+
+  nsCOMPtr<nsIURI> mURI;
+  RefPtr<image::Image> mImage;
+  nsCOMPtr<imgIContainerCallback> mCallback;
+  WeakPtr<NotificationObserverWrapper> mObserver;
+};
+
+NS_IMPL_ISUPPORTS(ImageDecoderListener, nsIStreamListener, imgIContainer)
+
 class ImageDecoderHelper final : public Runnable,
                                  public nsIInputStreamCallback {
  public:
   NS_DECL_ISUPPORTS_INHERITED
 
   ImageDecoderHelper(already_AddRefed<image::Image> aImage,
                      already_AddRefed<nsIInputStream> aInputStream,
                      nsIEventTarget* aEventTarget,
@@ -230,16 +371,32 @@ imgTools::DecodeImageFromBuffer(const ch
   NS_ENSURE_SUCCESS(rv, rv);
 
   // All done.
   image.forget(aContainer);
   return NS_OK;
 }
 
 NS_IMETHODIMP
+imgTools::DecodeImageFromChannelAsync(nsIURI* aURI, nsIChannel* aChannel,
+                                      imgIContainerCallback* aCallback,
+                                      imgINotificationObserver* aObserver) {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  NS_ENSURE_ARG_POINTER(aURI);
+  NS_ENSURE_ARG_POINTER(aChannel);
+  NS_ENSURE_ARG_POINTER(aCallback);
+
+  RefPtr<ImageDecoderListener> listener =
+      new ImageDecoderListener(aURI, aCallback, aObserver);
+
+  return aChannel->AsyncOpen(listener);
+}
+
+NS_IMETHODIMP
 imgTools::DecodeImageAsync(nsIInputStream* aInStr, const nsACString& aMimeType,
                            imgIContainerCallback* aCallback,
                            nsIEventTarget* aEventTarget) {
   MOZ_ASSERT(NS_IsMainThread());
 
   NS_ENSURE_ARG_POINTER(aInStr);
   NS_ENSURE_ARG_POINTER(aCallback);
   NS_ENSURE_ARG_POINTER(aEventTarget);