Backout changesets 194d051724a8:89e680f30d31 (bug 817700) for insufficient review.
authorMs2ger <ms2ger@gmail.com>
Mon, 09 Sep 2013 16:56:00 +0200
changeset 146196 a5e8a4e466bc970d850f978bc1cdb47087e9fe9a
parent 145965 89e680f30d319fd927d79527628a4327349c5723
child 146197 3931b9652326cf3ac5ad520d9503444c69643c3a
push id25244
push userryanvm@gmail.com
push dateMon, 09 Sep 2013 20:03:14 +0000
treeherdermozilla-central@f320b8c034bd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs817700
milestone26.0a1
backs out194d051724a80e676eaa47a975827678f885bb02
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
Backout changesets 194d051724a8:89e680f30d31 (bug 817700) for insufficient review.
content/canvas/public/nsICanvasRenderingContextInternal.h
content/canvas/src/CanvasRenderingContext2D.cpp
content/canvas/src/CanvasRenderingContext2D.h
content/canvas/src/ImageEncoder.cpp
content/canvas/src/ImageEncoder.h
content/canvas/src/Makefile.in
content/canvas/src/WebGLContext.cpp
content/canvas/src/WebGLContext.h
content/canvas/src/moz.build
content/canvas/test/test_mozGetAsFile.html
content/canvas/test/test_toBlob.html
content/html/content/public/HTMLCanvasElement.h
content/html/content/src/HTMLCanvasElement.cpp
content/html/content/src/Makefile.in
image/test/mochitest/test_animSVGImage.html
--- a/content/canvas/public/nsICanvasRenderingContextInternal.h
+++ b/content/canvas/public/nsICanvasRenderingContextInternal.h
@@ -9,18 +9,18 @@
 #include "nsISupports.h"
 #include "nsIInputStream.h"
 #include "nsIDocShell.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
 #include "gfxPattern.h"
 #include "mozilla/RefPtr.h"
 
 #define NS_ICANVASRENDERINGCONTEXTINTERNAL_IID \
-{ 0x9a6a5bdf, 0x1261, 0x4057, \
-  { 0x85, 0xcc, 0xaf, 0x97, 0x6c, 0x36, 0x99, 0xa9 } }
+{ 0x8b8da863, 0xd151, 0x4014, \
+  { 0x8b, 0xdc, 0x62, 0xb5, 0x0d, 0xc0, 0x2b, 0x62 } }
 
 class gfxContext;
 class gfxASurface;
 class nsDisplayListBuilder;
 
 namespace mozilla {
 namespace layers {
 class CanvasLayer;
@@ -60,19 +60,16 @@ public:
 
   NS_IMETHOD InitializeWithSurface(nsIDocShell *docShell, gfxASurface *surface, int32_t width, int32_t height) = 0;
 
   // Render the canvas at the origin of the given gfxContext
   NS_IMETHOD Render(gfxContext *ctx,
                     gfxPattern::GraphicsFilter aFilter,
                     uint32_t aFlags = RenderFlagPremultAlpha) = 0;
 
-  // Creates an image buffer. Returns null on failure.
-  virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat) = 0;
-
   // Gives you a stream containing the image represented by this context.
   // The format is given in aMimeTime, for example "image/png".
   //
   // If the image format does not support transparency or aIncludeTransparency
   // is false, alpha will be discarded and the result will be the image
   // composited on black.
   NS_IMETHOD GetInputStream(const char *aMimeType,
                             const PRUnichar *aEncoderOptions,
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -43,17 +43,17 @@
 #include "nsIDocShell.h"
 #include "nsIDOMWindow.h"
 #include "nsPIDOMWindow.h"
 #include "nsIXPConnect.h"
 #include "nsDisplayList.h"
 
 #include "nsTArray.h"
 
-#include "ImageEncoder.h"
+#include "imgIEncoder.h"
 
 #include "gfxContext.h"
 #include "gfxASurface.h"
 #include "gfxImageSurface.h"
 #include "gfxPlatform.h"
 #include "gfxFont.h"
 #include "gfxBlur.h"
 #include "gfxUtils.h"
@@ -1042,81 +1042,81 @@ CanvasRenderingContext2D::Render(gfxCont
       NS_ABORT_IF_FALSE(gis, "If non-premult alpha, must be able to get image surface!");
 
       gfxUtils::UnpremultiplyImageSurface(gis);
   }
 
   return rv;
 }
 
-void
-CanvasRenderingContext2D::GetImageBuffer(uint8_t** aImageBuffer,
-                                         int32_t* aFormat)
+NS_IMETHODIMP
+CanvasRenderingContext2D::GetInputStream(const char *aMimeType,
+                                         const PRUnichar *aEncoderOptions,
+                                         nsIInputStream **aStream)
 {
-  *aImageBuffer = nullptr;
-  *aFormat = 0;
+  EnsureTarget();
+  if (!IsTargetValid()) {
+    return NS_ERROR_FAILURE;
+  }
 
   nsRefPtr<gfxASurface> surface;
-  nsresult rv = GetThebesSurface(getter_AddRefs(surface));
-  if (NS_FAILED(rv)) {
-    return;
+
+  if (NS_FAILED(GetThebesSurface(getter_AddRefs(surface)))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsresult rv;
+  const char encoderPrefix[] = "@mozilla.org/image/encoder;2?type=";
+  static const fallible_t fallible = fallible_t();
+  nsAutoArrayPtr<char> conid(new (fallible) char[strlen(encoderPrefix) + strlen(aMimeType) + 1]);
+
+  if (!conid) {
+    return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  static const fallible_t fallible = fallible_t();
-  uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4];
+  strcpy(conid, encoderPrefix);
+  strcat(conid, aMimeType);
+
+  nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(conid);
+  if (!encoder) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsAutoArrayPtr<uint8_t> imageBuffer(new (fallible) uint8_t[mWidth * mHeight * 4]);
   if (!imageBuffer) {
-    return;
+    return NS_ERROR_OUT_OF_MEMORY;
   }
 
   nsRefPtr<gfxImageSurface> imgsurf =
-    new gfxImageSurface(imageBuffer,
+    new gfxImageSurface(imageBuffer.get(),
                         gfxIntSize(mWidth, mHeight),
                         mWidth * 4,
                         gfxASurface::ImageFormatARGB32);
 
   if (!imgsurf || imgsurf->CairoStatus()) {
-    delete[] imageBuffer;
-    return;
+    return NS_ERROR_FAILURE;
   }
 
   nsRefPtr<gfxContext> ctx = new gfxContext(imgsurf);
+
   if (!ctx || ctx->HasError()) {
-    delete[] imageBuffer;
-    return;
+    return NS_ERROR_FAILURE;
   }
 
   ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
   ctx->SetSource(surface, gfxPoint(0, 0));
   ctx->Paint();
 
-  *aImageBuffer = imageBuffer;
-  *aFormat = imgIEncoder::INPUT_FORMAT_HOSTARGB;
-}
-
-NS_IMETHODIMP
-CanvasRenderingContext2D::GetInputStream(const char *aMimeType,
-                                         const PRUnichar *aEncoderOptions,
-                                         nsIInputStream **aStream)
-{
-  uint8_t* imageBuffer = nullptr;
-  int32_t format = 0;
-  GetImageBuffer(&imageBuffer, &format);
-  if (!imageBuffer) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsCString enccid("@mozilla.org/image/encoder;2?type=");
-  enccid += aMimeType;
-  nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
-  if (!encoder) {
-    return NS_ERROR_FAILURE;
-  }
-
-  return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer, format,
-                                      encoder, aEncoderOptions, aStream);
+  rv = encoder->InitFromData(imageBuffer.get(),
+                              mWidth * mHeight * 4, mWidth, mHeight, mWidth * 4,
+                              imgIEncoder::INPUT_FORMAT_HOSTARGB,
+                              nsDependentString(aEncoderOptions));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return CallQueryInterface(encoder, aStream);
 }
 
 SurfaceFormat
 CanvasRenderingContext2D::GetSurfaceFormat() const
 {
   return mOpaque ? FORMAT_B8G8R8X8 : FORMAT_B8G8R8A8;
 }
 
@@ -3745,19 +3745,16 @@ CanvasRenderingContext2D::PutImageData_e
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 CanvasRenderingContext2D::GetThebesSurface(gfxASurface **surface)
 {
   EnsureTarget();
-  if (!IsTargetValid()) {
-    return NS_ERROR_FAILURE;
-  }
 
   nsRefPtr<gfxASurface> thebesSurface =
       gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mTarget);
 
   if (!thebesSurface) {
     return NS_ERROR_FAILURE;
   }
 
--- a/content/canvas/src/CanvasRenderingContext2D.h
+++ b/content/canvas/src/CanvasRenderingContext2D.h
@@ -15,17 +15,16 @@
 #include "mozilla/dom/HTMLVideoElement.h"
 #include "CanvasUtils.h"
 #include "gfxFont.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/CanvasGradient.h"
 #include "mozilla/dom/CanvasRenderingContext2DBinding.h"
 #include "mozilla/dom/CanvasPattern.h"
 #include "mozilla/gfx/Rect.h"
-#include "imgIEncoder.h"
 
 class nsXULElement;
 
 namespace mozilla {
 namespace gfx {
 class SourceSurface;
 }
 
@@ -444,18 +443,16 @@ public:
       mDSPathBuilder->BezierTo(transform * aCP1,
                                 transform * aCP2,
                                 transform * aCP3);
     }
   }
 
   friend class CanvasRenderingContext2DUserData;
 
-  virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat);
-
 protected:
   nsresult GetImageDataArray(JSContext* aCx, int32_t aX, int32_t aY,
                              uint32_t aWidth, uint32_t aHeight,
                              JSObject** aRetval);
 
   nsresult PutImageData_explicit(int32_t x, int32_t y, uint32_t w, uint32_t h,
                                  unsigned char *aData, uint32_t aDataLen,
                                  bool hasDirtyRect, int32_t dirtyX, int32_t dirtyY,
deleted file mode 100644
--- a/content/canvas/src/ImageEncoder.cpp
+++ /dev/null
@@ -1,296 +0,0 @@
-/* -*- 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 "ImageEncoder.h"
-
-#include "mozilla/dom/CanvasRenderingContext2D.h"
-
-namespace mozilla {
-namespace dom {
-
-class EncodingCompleteEvent : public nsRunnable
-{
-public:
-  NS_DECL_THREADSAFE_ISUPPORTS
-
-  EncodingCompleteEvent(JSContext* aJSContext,
-                        nsIThread* aEncoderThread,
-                        nsIFileCallback* aCallback)
-    : mImgSize(0)
-    , mType()
-    , mImgData(nullptr)
-    , mJSContext(aJSContext)
-    , mEncoderThread(aEncoderThread)
-    , mCallback(aCallback)
-  {}
-  virtual ~EncodingCompleteEvent() {}
-
-  NS_IMETHOD Run()
-  {
-    nsRefPtr<nsDOMMemoryFile> blob =
-      new nsDOMMemoryFile(mImgData, mImgSize, mType);
-
-    if (mJSContext) {
-      JS_updateMallocCounter(mJSContext, mImgSize);
-    }
-    nsresult rv = mCallback->Receive(blob);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    mEncoderThread->Shutdown();
-    return rv;
-  }
-
-  void SetMembers(void* aImgData, uint64_t aImgSize, const nsAutoString& aType)
-  {
-    mImgData = aImgData;
-    mImgSize = aImgSize;
-    mType = aType;
-  }
-
-private:
-  uint64_t mImgSize;
-  nsAutoString mType;
-  void* mImgData;
-  JSContext* mJSContext;
-  nsCOMPtr<nsIThread> mEncoderThread;
-  nsCOMPtr<nsIFileCallback> mCallback;
-};
-
-NS_IMPL_ISUPPORTS1(EncodingCompleteEvent, nsIRunnable);
-
-class EncodingRunnable : public nsRunnable
-{
-public:
-  NS_DECL_THREADSAFE_ISUPPORTS
-
-  EncodingRunnable(const nsAString& aType,
-                   const nsAString& aOptions,
-                   uint8_t* aImageBuffer,
-                   imgIEncoder* aEncoder,
-                   nsIThread* aOriginThread,
-                   EncodingCompleteEvent* aEncodingCompleteEvent,
-                   int32_t aFormat,
-                   const nsIntSize aSize,
-                   bool aUsingCustomOptions)
-    : mType(aType)
-    , mOptions(aOptions)
-    , mImageBuffer(aImageBuffer)
-    , mEncoder(aEncoder)
-    , mOriginThread(aOriginThread)
-    , mEncodingCompleteEvent(aEncodingCompleteEvent)
-    , mFormat(aFormat)
-    , mSize(aSize)
-    , mUsingCustomOptions(aUsingCustomOptions)
-  {}
-  virtual ~EncodingRunnable() {}
-
-  NS_IMETHOD Run()
-  {
-    nsCOMPtr<nsIInputStream> stream;
-    nsresult rv = ImageEncoder::ExtractDataInternal(mType,
-                                                    mOptions,
-                                                    mImageBuffer,
-                                                    mFormat,
-                                                    mSize,
-                                                    nullptr,
-                                                    getter_AddRefs(stream),
-                                                    mEncoder);
-
-    // If there are unrecognized custom parse options, we should fall back to
-    // the default values for the encoder without any options at all.
-    if (rv == NS_ERROR_INVALID_ARG && mUsingCustomOptions) {
-      rv = ImageEncoder::ExtractDataInternal(mType,
-                                             EmptyString(),
-                                             mImageBuffer,
-                                             mFormat,
-                                             mSize,
-                                             nullptr,
-                                             getter_AddRefs(stream),
-                                             mEncoder);
-    }
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    uint64_t imgSize;
-    rv = stream->Available(&imgSize);
-    NS_ENSURE_SUCCESS(rv, rv);
-    NS_ENSURE_TRUE(imgSize <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
-
-    void* imgData = nullptr;
-    rv = NS_ReadInputStreamToBuffer(stream, &imgData, imgSize);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    mEncodingCompleteEvent->SetMembers(imgData, imgSize, mType);
-    rv = mOriginThread->Dispatch(mEncodingCompleteEvent, NS_DISPATCH_NORMAL);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    return rv;
-  }
-
-private:
-  nsAutoString mType;
-  nsAutoString mOptions;
-  nsAutoArrayPtr<uint8_t> mImageBuffer;
-  nsCOMPtr<imgIEncoder> mEncoder;
-  nsCOMPtr<nsIThread> mOriginThread;
-  nsRefPtr<EncodingCompleteEvent> mEncodingCompleteEvent;
-  int32_t mFormat;
-  const nsIntSize mSize;
-  bool mUsingCustomOptions;
-};
-
-NS_IMPL_ISUPPORTS1(EncodingRunnable, nsIRunnable)
-
-/* static */
-nsresult
-ImageEncoder::ExtractData(nsAString& aType,
-                          const nsAString& aOptions,
-                          const nsIntSize aSize,
-                          nsICanvasRenderingContextInternal* aContext,
-                          nsIInputStream** aStream)
-{
-  nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
-  if (!encoder) {
-    return NS_IMAGELIB_ERROR_NO_ENCODER;
-  }
-
-  return ExtractDataInternal(aType, aOptions, nullptr, 0, aSize, aContext,
-                             aStream, encoder);
-}
-
-/* static */
-nsresult
-ImageEncoder::ExtractDataAsync(nsAString& aType,
-                               const nsAString& aOptions,
-                               bool aUsingCustomOptions,
-                               uint8_t* aImageBuffer,
-                               int32_t aFormat,
-                               const nsIntSize aSize,
-                               nsICanvasRenderingContextInternal* aContext,
-                               JSContext* aJSContext,
-                               nsIFileCallback* aCallback)
-{
-  nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
-  if (!encoder) {
-    return NS_IMAGELIB_ERROR_NO_ENCODER;
-  }
-
-  nsCOMPtr<nsIThread> encoderThread;
-  nsresult rv = NS_NewThread(getter_AddRefs(encoderThread), nullptr);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsRefPtr<EncodingCompleteEvent> completeEvent =
-    new EncodingCompleteEvent(aJSContext, encoderThread, aCallback);
-
-  nsCOMPtr<nsIRunnable> event = new EncodingRunnable(aType,
-                                                     aOptions,
-                                                     aImageBuffer,
-                                                     encoder,
-                                                     NS_GetCurrentThread(),
-                                                     completeEvent,
-                                                     aFormat,
-                                                     aSize,
-                                                     aUsingCustomOptions);
-  return encoderThread->Dispatch(event, NS_DISPATCH_NORMAL);
-}
-
-/*static*/ nsresult
-ImageEncoder::GetInputStream(int32_t aWidth,
-                             int32_t aHeight,
-                             uint8_t* aImageBuffer,
-                             int32_t aFormat,
-                             imgIEncoder* aEncoder,
-                             const PRUnichar* aEncoderOptions,
-                             nsIInputStream** aStream)
-{
-  nsresult rv =
-    aEncoder->InitFromData(aImageBuffer,
-                           aWidth * aHeight * 4, aWidth, aHeight, aWidth * 4,
-                           aFormat,
-                           nsDependentString(aEncoderOptions));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return CallQueryInterface(aEncoder, aStream);
-}
-
-/* static */
-nsresult
-ImageEncoder::ExtractDataInternal(const nsAString& aType,
-                                  const nsAString& aOptions,
-                                  uint8_t* aImageBuffer,
-                                  int32_t aFormat,
-                                  const nsIntSize aSize,
-                                  nsICanvasRenderingContextInternal* aContext,
-                                  nsIInputStream** aStream,
-                                  imgIEncoder* aEncoder)
-{
-  nsCOMPtr<nsIInputStream> imgStream;
-
-  // get image bytes
-  nsresult rv;
-  if (aImageBuffer) {
-    rv = ImageEncoder::GetInputStream(
-      aSize.width,
-      aSize.height,
-      aImageBuffer,
-      aFormat,
-      aEncoder,
-      nsPromiseFlatString(aOptions).get(),
-      getter_AddRefs(imgStream));
-  } else if (aContext) {
-    NS_ConvertUTF16toUTF8 encoderType(aType);
-    rv = aContext->GetInputStream(encoderType.get(),
-                                  nsPromiseFlatString(aOptions).get(),
-                                  getter_AddRefs(imgStream));
-  } else {
-    // no context, so we have to encode an empty image
-    // note that if we didn't have a current context, the spec says we're
-    // supposed to just return transparent black pixels of the canvas
-    // dimensions.
-    nsRefPtr<gfxImageSurface> emptyCanvas =
-      new gfxImageSurface(gfxIntSize(aSize.width, aSize.height),
-                          gfxASurface::ImageFormatARGB32);
-    if (emptyCanvas->CairoStatus()) {
-      return NS_ERROR_INVALID_ARG;
-    }
-    rv = aEncoder->InitFromData(emptyCanvas->Data(),
-                                aSize.width * aSize.height * 4,
-                                aSize.width,
-                                aSize.height,
-                                aSize.width * 4,
-                                imgIEncoder::INPUT_FORMAT_HOSTARGB,
-                                aOptions);
-    if (NS_SUCCEEDED(rv)) {
-      imgStream = do_QueryInterface(aEncoder);
-    }
-  }
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  imgStream.forget(aStream);
-  return rv;
-}
-
-/* static */
-already_AddRefed<imgIEncoder>
-ImageEncoder::GetImageEncoder(nsAString& aType)
-{
-  // Get an image encoder for the media type.
-  nsCString encoderCID("@mozilla.org/image/encoder;2?type=");
-  NS_ConvertUTF16toUTF8 encoderType(aType);
-  encoderCID += encoderType;
-  nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(encoderCID.get());
-
-  if (!encoder && aType != NS_LITERAL_STRING("image/png")) {
-    // Unable to create an encoder instance of the specified type. Falling back
-    // to PNG.
-    aType.AssignLiteral("image/png");
-    nsCString PNGEncoderCID("@mozilla.org/image/encoder;2?type=image/png");
-    encoder = do_CreateInstance(PNGEncoderCID.get());
-  }
-
-  return encoder.forget();
-}
-
-} // namespace dom
-} // namespace mozilla
deleted file mode 100644
--- a/content/canvas/src/ImageEncoder.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/* -*- 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/. */
-
-#ifndef ImageEncoder_h
-#define ImageEncoder_h
-
-#include "imgIEncoder.h"
-#include "nsDOMFile.h"
-#include "nsError.h"
-#include "nsIDOMHTMLCanvasElement.h"
-#include "nsLayoutUtils.h"
-#include "nsNetUtil.h"
-#include "nsSize.h"
-
-class nsICanvasRenderingContextInternal;
-
-namespace mozilla {
-namespace dom {
-
-class EncodingRunnable;
-
-class ImageEncoder
-{
-public:
-  // Extracts data synchronously and gives you a stream containing the image
-  // represented by aContext. aType may change to "image/png" if we had to fall
-  // back to a PNG encoder. A return value of NS_OK implies successful data
-  // extraction. If there are any unrecognized custom parse options in
-  // aOptions, NS_ERROR_INVALID_ARG will be returned. When encountering this
-  // error it is usual to call this function again without any options at all.
-  static nsresult ExtractData(nsAString& aType,
-                              const nsAString& aOptions,
-                              const nsIntSize aSize,
-                              nsICanvasRenderingContextInternal* aContext,
-                              nsIInputStream** aStream);
-
-  // Extracts data asynchronously. aType may change to "image/png" if we had to
-  // fall back to a PNG encoder. aOptions are the options to be passed to the
-  // encoder and aUsingCustomOptions specifies whether custom parse options were
-  // used (i.e. by using -moz-parse-options). If there are any unrecognized
-  // custom parse options, we fall back to the default values for the encoder
-  // without any options at all. A return value of NS_OK only implies
-  // successful dispatching of the extraction step to the encoding thread.
-  static nsresult ExtractDataAsync(nsAString& aType,
-                                   const nsAString& aOptions,
-                                   bool aUsingCustomOptions,
-                                   uint8_t* aImageBuffer,
-                                   int32_t aFormat,
-                                   const nsIntSize aSize,
-                                   nsICanvasRenderingContextInternal* aContext,
-                                   JSContext* aJSContext,
-                                   nsIFileCallback* aCallback);
-
-  // Gives you a stream containing the image represented by aImageBuffer.
-  // The format is given in aFormat, for example
-  // imgIEncoder::INPUT_FORMAT_HOSTARGB.
-  static nsresult GetInputStream(int32_t aWidth,
-                                 int32_t aHeight,
-                                 uint8_t* aImageBuffer,
-                                 int32_t aFormat,
-                                 imgIEncoder* aEncoder,
-                                 const PRUnichar* aEncoderOptions,
-                                 nsIInputStream** aStream);
-
-private:
-  // When called asynchronously, aContext is null.
-  static nsresult
-  ExtractDataInternal(const nsAString& aType,
-                      const nsAString& aOptions,
-                      uint8_t* aImageBuffer,
-                      int32_t aFormat,
-                      const nsIntSize aSize,
-                      nsICanvasRenderingContextInternal* aContext,
-                      nsIInputStream** aStream,
-                      imgIEncoder* aEncoder);
-
-  // Creates and returns an encoder instance of the type specified in aType.
-  // aType may change to "image/png" if no instance of the original type could
-  // be created and we had to fall back to a PNG encoder. A return value of
-  // NULL should be interpreted as NS_IMAGELIB_ERROR_NO_ENCODER and aType is
-  // undefined in this case.
-  static already_AddRefed<imgIEncoder> GetImageEncoder(nsAString& aType);
-
-  friend class EncodingRunnable;
-};
-
-} // namespace dom
-} // namespace mozilla
-
-#endif // ImageEncoder_h
\ No newline at end of file
--- a/content/canvas/src/Makefile.in
+++ b/content/canvas/src/Makefile.in
@@ -17,11 +17,10 @@ CXXFLAGS	+= $(MOZ_CAIRO_CFLAGS) $(MOZ_PI
 INCLUDES	+= \
 		-I$(srcdir)/../../../layout/xul/base/src \
 		-I$(srcdir)/../../../layout/style \
 		-I$(srcdir)/../../../layout/generic \
 		-I$(srcdir)/../../base/src \
 		-I$(srcdir)/../../html/content/src \
 		-I$(srcdir)/../../../js/xpconnect/src \
 		-I$(srcdir)/../../../dom/base \
-		-I$(srcdir)/../../../image/src \
 		-I$(topsrcdir)/content/xul/content/src \
 		$(NULL)
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -22,17 +22,17 @@
 #include "nsContentUtils.h"
 #include "nsIXPConnect.h"
 #include "nsError.h"
 #include "nsIGfxInfo.h"
 #include "nsIWidget.h"
 
 #include "nsIVariant.h"
 
-#include "ImageEncoder.h"
+#include "imgIEncoder.h"
 
 #include "gfxContext.h"
 #include "gfxPattern.h"
 #include "gfxUtils.h"
 
 #include "CanvasUtils.h"
 #include "nsDisplayList.h"
 
@@ -716,89 +716,67 @@ void WebGLContext::LoseOldestWebGLContex
     } else if (numContexts > kMaxWebGLContexts) {
         GenerateWarning("Exceeded %d live WebGL contexts, losing the least recently used one.",
                         kMaxWebGLContexts);
         MOZ_ASSERT(oldestContext); // if we reach this point, this can't be null
         const_cast<WebGLContext*>(oldestContext)->LoseContext();
     }
 }
 
-void
-WebGLContext::GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat)
-{
-    *aImageBuffer = nullptr;
-    *aFormat = 0;
-
-    nsRefPtr<gfxImageSurface> imgsurf =
-        new gfxImageSurface(gfxIntSize(mWidth, mHeight),
-                            gfxASurface::ImageFormatARGB32);
-
-    if (!imgsurf || imgsurf->CairoStatus()) {
-        return;
-    }
-
-    nsRefPtr<gfxContext> ctx = new gfxContext(imgsurf);
-    if (!ctx || ctx->HasError()) {
-        return;
-    }
-
-    // Use Render() to make sure that appropriate y-flip gets applied
-    uint32_t flags = mOptions.premultipliedAlpha ? RenderFlagPremultAlpha : 0;
-    nsresult rv = Render(ctx, gfxPattern::FILTER_NEAREST, flags);
-    if (NS_FAILED(rv)) {
-        return;
-    }
-
-    int32_t format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
-    if (!mOptions.premultipliedAlpha) {
-        // We need to convert to INPUT_FORMAT_RGBA, otherwise
-        // we are automatically considered premult, and unpremult'd.
-        // Yes, it is THAT silly.
-        // Except for different lossy conversions by color,
-        // we could probably just change the label, and not change the data.
-        gfxUtils::ConvertBGRAtoRGBA(imgsurf);
-        format = imgIEncoder::INPUT_FORMAT_RGBA;
-    }
-
-    static const fallible_t fallible = fallible_t();
-    uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4];
-    if (!imageBuffer) {
-        return;
-    }
-    memcpy(imageBuffer, imgsurf->Data(), mWidth * mHeight * 4);
-
-    *aImageBuffer = imageBuffer;
-    *aFormat = format;
-}
-
 NS_IMETHODIMP
 WebGLContext::GetInputStream(const char* aMimeType,
                              const PRUnichar* aEncoderOptions,
                              nsIInputStream **aStream)
 {
     NS_ASSERTION(gl, "GetInputStream on invalid context?");
     if (!gl)
         return NS_ERROR_FAILURE;
 
-    uint8_t* imageBuffer = nullptr;
-    int32_t format = 0;
-    GetImageBuffer(&imageBuffer, &format);
-    if (!imageBuffer) {
+    nsRefPtr<gfxImageSurface> surf = new gfxImageSurface(gfxIntSize(mWidth, mHeight),
+                                                         gfxASurface::ImageFormatARGB32);
+    if (surf->CairoStatus() != 0)
         return NS_ERROR_FAILURE;
+
+    nsRefPtr<gfxContext> tmpcx = new gfxContext(surf);
+    // Use Render() to make sure that appropriate y-flip gets applied
+    uint32_t flags = mOptions.premultipliedAlpha ? RenderFlagPremultAlpha : 0;
+    nsresult rv = Render(tmpcx, gfxPattern::FILTER_NEAREST, flags);
+    if (NS_FAILED(rv))
+        return rv;
+
+    const char encoderPrefix[] = "@mozilla.org/image/encoder;2?type=";
+    nsAutoArrayPtr<char> conid(new char[strlen(encoderPrefix) + strlen(aMimeType) + 1]);
+
+    strcpy(conid, encoderPrefix);
+    strcat(conid, aMimeType);
+
+    nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(conid);
+    if (!encoder)
+        return NS_ERROR_FAILURE;
+
+    int format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
+    if (!mOptions.premultipliedAlpha) {
+        // We need to convert to INPUT_FORMAT_RGBA, otherwise
+        // we are automatically considered premult, and unpremult'd.
+        // Yes, it is THAT silly.
+        // Except for different lossy conversions by color,
+        // we could probably just change the label, and not change the data.
+        gfxUtils::ConvertBGRAtoRGBA(surf);
+        format = imgIEncoder::INPUT_FORMAT_RGBA;
     }
 
-    nsCString enccid("@mozilla.org/image/encoder;2?type=");
-    enccid += aMimeType;
-    nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
-    if (!encoder) {
-        return NS_ERROR_FAILURE;
-    }
+    rv = encoder->InitFromData(surf->Data(),
+                               mWidth * mHeight * 4,
+                               mWidth, mHeight,
+                               surf->Stride(),
+                               format,
+                               nsDependentString(aEncoderOptions));
+    NS_ENSURE_SUCCESS(rv, rv);
 
-    return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer, format,
-                                        encoder, aEncoderOptions, aStream);
+    return CallQueryInterface(encoder, aStream);
 }
 
 NS_IMETHODIMP
 WebGLContext::GetThebesSurface(gfxASurface **surface)
 {
     return NS_ERROR_NOT_AVAILABLE;
 }
 
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -155,17 +155,16 @@ public:
     NS_IMETHOD SetDimensions(int32_t width, int32_t height) MOZ_OVERRIDE;
     NS_IMETHOD InitializeWithSurface(nsIDocShell *docShell, gfxASurface *surface, int32_t width, int32_t height) MOZ_OVERRIDE
         { return NS_ERROR_NOT_IMPLEMENTED; }
     NS_IMETHOD Reset() MOZ_OVERRIDE
         { /* (InitializeWithSurface) */ return NS_ERROR_NOT_IMPLEMENTED; }
     NS_IMETHOD Render(gfxContext *ctx,
                       gfxPattern::GraphicsFilter f,
                       uint32_t aFlags = RenderFlagPremultAlpha) MOZ_OVERRIDE;
-    virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat);
     NS_IMETHOD GetInputStream(const char* aMimeType,
                               const PRUnichar* aEncoderOptions,
                               nsIInputStream **aStream) MOZ_OVERRIDE;
     NS_IMETHOD GetThebesSurface(gfxASurface **surface) MOZ_OVERRIDE;
     mozilla::TemporaryRef<mozilla::gfx::SourceSurface> GetSurfaceSnapshot() MOZ_OVERRIDE
         { return nullptr; }
 
     NS_IMETHOD SetIsOpaque(bool b) MOZ_OVERRIDE { return NS_OK; };
--- a/content/canvas/src/moz.build
+++ b/content/canvas/src/moz.build
@@ -17,17 +17,16 @@ EXPORTS.mozilla.dom += [
 
 CPP_SOURCES += [
     'CanvasImageCache.cpp',
     'CanvasRenderingContext2D.cpp',
     'CanvasUtils.cpp',
     'DocumentRendererChild.cpp',
     'DocumentRendererParent.cpp',
     'ImageData.cpp',
-    'ImageEncoder.cpp',
 ]
 
 if CONFIG['MOZ_WEBGL']:
     CPP_SOURCES += [
         'WebGLActiveInfo.cpp',
         'WebGLBuffer.cpp',
         'WebGL1Context.cpp',
         'WebGL2Context.cpp',
--- a/content/canvas/test/test_mozGetAsFile.html
+++ b/content/canvas/test/test_mozGetAsFile.html
@@ -2,50 +2,49 @@
 <title>Canvas test: mozGetAsFile</title>
 <script src="/MochiKit/MochiKit.js"></script>
 <script src="/tests/SimpleTest/SimpleTest.js"></script>
 <link rel="stylesheet" href="/tests/SimpleTest/test.css">
 <body>
 <canvas id="c" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
 <script>
 
-function compareAsync(file, canvas, type, callback)
+var gCompares = 0;
+
+function compareAsync(file, canvas, type)
 {
+  ++gCompares;
+
   var reader = new FileReader();
   reader.onload = 
     function(e) {
       is(e.target.result, canvas.toDataURL(type),
  "<canvas>.mozGetAsFile().getAsDataURL() should equal <canvas>.toDataURL()");
-      callback(canvas);
+      if (--gCompares == 0) {
+        SimpleTest.finish();
+      }
     };
   reader.readAsDataURL(file);
 }
 
-function test1(canvas)
-{
-  var pngfile = canvas.mozGetAsFile("foo.png");
-  is(pngfile.type, "image/png", "Default type for mozGetAsFile should be PNG");
-  compareAsync(pngfile, canvas, "image/png", test2);
-  is(pngfile.name, "foo.png", "File name should be what we passed in");
-}
-
-function test2(canvas)
-{
-  var jpegfile = canvas.mozGetAsFile("bar.jpg", "image/jpeg");
-  is(jpegfile.type, "image/jpeg",
-     "When a valid type is specified that should be returned");
-  compareAsync(jpegfile, canvas, "image/jpeg", SimpleTest.finish);
-  is(jpegfile.name, "bar.jpg", "File name should be what we passed in");
-}
-
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(function () {
 
 var canvas = document.getElementById('c');
 var ctx = canvas.getContext('2d');
+
 ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
 
-test1(canvas);
+var pngfile = canvas.mozGetAsFile("foo.png");
+is(pngfile.type, "image/png", "Default type for mozGetAsFile should be PNG");
+compareAsync(pngfile, canvas, "image/png");
+is(pngfile.name, "foo.png", "File name should be what we passed in");
+
+var jpegfile = canvas.mozGetAsFile("bar.jpg", "image/jpeg");
+is(jpegfile.type, "image/jpeg",
+   "When a valid type is specified that should be returned");
+compareAsync(jpegfile, canvas, "image/jpeg");
+is(jpegfile.name, "bar.jpg", "File name should be what we passed in");
 
 });
 </script>
 <img src="image_yellow75.png" id="yellow75.png" class="resource">
 
--- a/content/canvas/test/test_toBlob.html
+++ b/content/canvas/test/test_toBlob.html
@@ -1,48 +1,42 @@
 <!DOCTYPE HTML>
-<title>Canvas test: toBlob</title>
+<title>Canvas test: mozGetAsFile</title>
 <script src="/MochiKit/MochiKit.js"></script>
 <script src="/tests/SimpleTest/SimpleTest.js"></script>
 <link rel="stylesheet" href="/tests/SimpleTest/test.css">
 <body>
 <canvas id="c" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
 <script>
 
-function BlobListener(type, canvas, callback, file)
+var gCompares = 2;
+
+function BlobListener(type, canvas, file)
 {
   is(file.type, type,
      "When a valid type is specified that should be returned");
   var reader = new FileReader();
-  reader.onload =
+  reader.onload = 
     function(e) {
       is(e.target.result, canvas.toDataURL(type),
-  "<canvas>.mozGetAsFile().getAsDataURL() should equal <canvas>.toDataURL()");
-      callback(canvas);
+ "<canvas>.mozGetAsFile().getAsDataURL() should equal <canvas>.toDataURL()");
+      if (--gCompares == 0) {
+        SimpleTest.finish();
+      }
     };
   reader.readAsDataURL(file);
 }
 
-function test1(canvas)
-{
-  canvas.toBlob(BlobListener.bind(undefined, "image/png", canvas, test2));
-}
-
-function test2(canvas)
-{
-  canvas.toBlob(
-    BlobListener.bind(undefined, "image/jpeg", canvas, SimpleTest.finish),
-    "image/jpeg");
-}
-
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(function () {
 
 var canvas = document.getElementById('c');
 var ctx = canvas.getContext('2d');
+
 ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
 
-test1(canvas);
+canvas.toBlob(BlobListener.bind(undefined, "image/png", canvas));
+canvas.toBlob(BlobListener.bind(undefined, "image/jpeg", canvas), "image/jpeg");
 
 });
 </script>
 <img src="image_yellow75.png" id="yellow75.png" class="resource">
 
--- a/content/html/content/public/HTMLCanvasElement.h
+++ b/content/html/content/public/HTMLCanvasElement.h
@@ -222,19 +222,20 @@ protected:
   nsIntSize GetWidthHeight();
 
   nsresult UpdateContext(JSContext* aCx, JS::Handle<JS::Value> options);
   nsresult ParseParams(JSContext* aCx,
                        const nsAString& aType,
                        const JS::Value& aEncoderOptions,
                        nsAString& aParams,
                        bool* usingCustomParseOptions);
-  nsresult ExtractData(nsAString& aType,
+  nsresult ExtractData(const nsAString& aType,
                        const nsAString& aOptions,
-                       nsIInputStream** aStream);
+                       nsIInputStream** aStream,
+                       bool& aFellBackToPNG);
   nsresult ToDataURLImpl(JSContext* aCx,
                          const nsAString& aMimeType,
                          const JS::Value& aEncoderOptions,
                          nsAString& aDataURL);
   nsresult MozGetAsFileImpl(const nsAString& aName,
                             const nsAString& aType,
                             nsIDOMFile** aResult);
   nsresult GetContextHelper(const nsAString& aContextId,
--- a/content/html/content/src/HTMLCanvasElement.cpp
+++ b/content/html/content/src/HTMLCanvasElement.cpp
@@ -1,19 +1,19 @@
 /* -*- 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 "mozilla/dom/HTMLCanvasElement.h"
 
-#include "ImageEncoder.h"
+#include "Layers.h"
+#include "imgIEncoder.h"
 #include "jsapi.h"
 #include "jsfriendapi.h"
-#include "Layers.h"
 #include "mozilla/Base64.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/dom/CanvasRenderingContext2D.h"
 #include "mozilla/dom/HTMLCanvasElementBinding.h"
 #include "mozilla/dom/UnionTypes.h"
 #include "mozilla/gfx/Rect.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
@@ -40,16 +40,38 @@ using namespace mozilla::layers;
 
 NS_IMPL_NS_NEW_HTML_ELEMENT(Canvas)
 
 namespace {
 
 typedef mozilla::dom::HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement
 HTMLImageOrCanvasOrVideoElement;
 
+class ToBlobRunnable : public nsRunnable
+{
+public:
+  ToBlobRunnable(nsIFileCallback* aCallback,
+                 nsIDOMBlob* aBlob)
+    : mCallback(aCallback),
+      mBlob(aBlob)
+  {
+    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  }
+
+  NS_IMETHOD Run()
+  {
+    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+    mCallback->Receive(mBlob);
+    return NS_OK;
+  }
+private:
+  nsCOMPtr<nsIFileCallback> mCallback;
+  nsCOMPtr<nsIDOMBlob> mBlob;
+};
+
 } // anonymous namespace
 
 namespace mozilla {
 namespace dom {
 
 class HTMLCanvasPrintState : public nsIDOMMozCanvasPrintState
 {
 public:
@@ -340,20 +362,20 @@ HTMLCanvasElement::ToDataURL(const nsASt
 NS_IMETHODIMP
 HTMLCanvasElement::MozFetchAsStream(nsIInputStreamCallback *aCallback,
                                     const nsAString& aType)
 {
   if (!nsContentUtils::IsCallerChrome())
     return NS_ERROR_FAILURE;
 
   nsresult rv;
+  bool fellBackToPNG = false;
   nsCOMPtr<nsIInputStream> inputData;
 
-  nsAutoString type(aType);
-  rv = ExtractData(type, EmptyString(), getter_AddRefs(inputData));
+  rv = ExtractData(aType, EmptyString(), getter_AddRefs(inputData), fellBackToPNG);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIAsyncInputStream> asyncData = do_QueryInterface(inputData, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIThread> mainThread;
   rv = NS_GetMainThread(getter_AddRefs(mainThread));
   NS_ENSURE_SUCCESS(rv, rv);
@@ -383,25 +405,78 @@ HTMLCanvasElement::GetMozPrintCallback()
 NS_IMETHODIMP
 HTMLCanvasElement::GetMozPrintCallback(nsIPrintCallback** aCallback)
 {
   NS_IF_ADDREF(*aCallback = GetMozPrintCallback());
   return NS_OK;
 }
 
 nsresult
-HTMLCanvasElement::ExtractData(nsAString& aType,
+HTMLCanvasElement::ExtractData(const nsAString& aType,
                                const nsAString& aOptions,
-                               nsIInputStream** aStream)
+                               nsIInputStream** aStream,
+                               bool& aFellBackToPNG)
 {
-  return ImageEncoder::ExtractData(aType,
-                                   aOptions,
-                                   GetSize(),
-                                   mCurrentContext,
-                                   aStream);
+  // note that if we don't have a current context, the spec says we're
+  // supposed to just return transparent black pixels of the canvas
+  // dimensions.
+  nsRefPtr<gfxImageSurface> emptyCanvas;
+  nsIntSize size = GetWidthHeight();
+  if (!mCurrentContext) {
+    emptyCanvas = new gfxImageSurface(gfxIntSize(size.width, size.height), gfxASurface::ImageFormatARGB32);
+    if (emptyCanvas->CairoStatus()) {
+      return NS_ERROR_INVALID_ARG;
+    }
+  }
+
+  nsresult rv;
+
+  // get image bytes
+  nsCOMPtr<nsIInputStream> imgStream;
+  NS_ConvertUTF16toUTF8 encoderType(aType);
+
+ try_again:
+  if (mCurrentContext) {
+    rv = mCurrentContext->GetInputStream(encoderType.get(),
+                                         nsPromiseFlatString(aOptions).get(),
+                                         getter_AddRefs(imgStream));
+  } else {
+    // no context, so we have to encode the empty image we created above
+    nsCString enccid("@mozilla.org/image/encoder;2?type=");
+    enccid += encoderType;
+
+    nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get(), &rv);
+    if (NS_SUCCEEDED(rv) && encoder) {
+      rv = encoder->InitFromData(emptyCanvas->Data(),
+                                 size.width * size.height * 4,
+                                 size.width,
+                                 size.height,
+                                 size.width * 4,
+                                 imgIEncoder::INPUT_FORMAT_HOSTARGB,
+                                 aOptions);
+      if (NS_SUCCEEDED(rv)) {
+        imgStream = do_QueryInterface(encoder);
+      }
+    } else {
+      rv = NS_ERROR_FAILURE;
+    }
+  }
+
+  if (NS_FAILED(rv) && !aFellBackToPNG) {
+    // Try image/png instead.
+    // XXX ERRMSG we need to report an error to developers here! (bug 329026)
+    aFellBackToPNG = true;
+    encoderType.AssignLiteral("image/png");
+    goto try_again;
+  }
+
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  imgStream.forget(aStream);
+  return NS_OK;
 }
 
 nsresult
 HTMLCanvasElement::ParseParams(JSContext* aCx,
                                const nsAString& aType,
                                const JS::Value& aEncoderOptions,
                                nsAString& aParams,
                                bool* usingCustomParseOptions)
@@ -442,16 +517,18 @@ HTMLCanvasElement::ParseParams(JSContext
 }
 
 nsresult
 HTMLCanvasElement::ToDataURLImpl(JSContext* aCx,
                                  const nsAString& aMimeType,
                                  const JS::Value& aEncoderOptions,
                                  nsAString& aDataURL)
 {
+  bool fallbackToPNG = false;
+
   nsIntSize size = GetWidthHeight();
   if (size.height == 0 || size.width == 0) {
     aDataURL = NS_LITERAL_STRING("data:,");
     return NS_OK;
   }
 
   nsAutoString type;
   nsresult rv = nsContentUtils::ASCIIToLower(aMimeType, type);
@@ -462,37 +539,43 @@ HTMLCanvasElement::ToDataURLImpl(JSConte
   nsAutoString params;
   bool usingCustomParseOptions;
   rv = ParseParams(aCx, type, aEncoderOptions, params, &usingCustomParseOptions);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   nsCOMPtr<nsIInputStream> stream;
-  rv = ExtractData(type, params, getter_AddRefs(stream));
+  rv = ExtractData(type, params, getter_AddRefs(stream), fallbackToPNG);
 
   // If there are unrecognized custom parse options, we should fall back to
   // the default values for the encoder without any options at all.
   if (rv == NS_ERROR_INVALID_ARG && usingCustomParseOptions) {
-    rv = ExtractData(type, EmptyString(), getter_AddRefs(stream));
+    fallbackToPNG = false;
+    rv = ExtractData(type, EmptyString(), getter_AddRefs(stream), fallbackToPNG);
   }
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   // build data URL string
-  aDataURL = NS_LITERAL_STRING("data:") + type + NS_LITERAL_STRING(";base64,");
+  if (fallbackToPNG)
+    aDataURL = NS_LITERAL_STRING("data:image/png;base64,");
+  else
+    aDataURL = NS_LITERAL_STRING("data:") + type +
+      NS_LITERAL_STRING(";base64,");
 
   uint64_t count;
   rv = stream->Available(&count);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(count <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
 
   return Base64EncodeInputStream(stream, aDataURL, (uint32_t)count, aDataURL.Length());
 }
 
+// XXXkhuey the encoding should be off the main thread, but we're lazy.
 NS_IMETHODIMP
 HTMLCanvasElement::ToBlob(nsIFileCallback* aCallback,
                           const nsAString& aType,
                           const JS::Value& aEncoderOptions,
                           JSContext* aCx)
 {
   // do a trust check if this is a write-only canvas
   if (mWriteOnly && !nsContentUtils::IsCallerChrome()) {
@@ -511,34 +594,53 @@ HTMLCanvasElement::ToBlob(nsIFileCallbac
 
   nsAutoString params;
   bool usingCustomParseOptions;
   rv = ParseParams(aCx, type, aEncoderOptions, params, &usingCustomParseOptions);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  JSContext* cx = nsContentUtils::GetCurrentJSContext();
-  nsCOMPtr<nsIThread> currentThread = NS_GetCurrentThread();
+  bool fallbackToPNG = false;
 
-  uint8_t* imageBuffer = nullptr;
-  int32_t format = 0;
-  if (mCurrentContext) {
-    mCurrentContext->GetImageBuffer(&imageBuffer, &format);
+  nsCOMPtr<nsIInputStream> stream;
+  rv = ExtractData(type, params, getter_AddRefs(stream), fallbackToPNG);
+  // If there are unrecognized custom parse options, we should fall back to
+  // the default values for the encoder without any options at all.
+  if (rv == NS_ERROR_INVALID_ARG && usingCustomParseOptions) {
+    fallbackToPNG = false;
+    rv = ExtractData(type, EmptyString(), getter_AddRefs(stream), fallbackToPNG);
+  }
+
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (fallbackToPNG) {
+    type.AssignLiteral("image/png");
   }
 
-  return ImageEncoder::ExtractDataAsync(type,
-                                        params,
-                                        usingCustomParseOptions,
-                                        imageBuffer,
-                                        format,
-                                        GetSize(),
-                                        mCurrentContext,
-                                        cx,
-                                        aCallback);
+  uint64_t imgSize;
+  rv = stream->Available(&imgSize);
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_TRUE(imgSize <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
+
+  void* imgData = nullptr;
+  rv = NS_ReadInputStreamToBuffer(stream, &imgData, imgSize);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // The DOMFile takes ownership of the buffer
+  nsRefPtr<nsDOMMemoryFile> blob =
+    new nsDOMMemoryFile(imgData, imgSize, type);
+
+  JSContext* cx = nsContentUtils::GetCurrentJSContext();
+  if (cx) {
+    JS_updateMallocCounter(cx, imgSize);
+  }
+
+  nsRefPtr<ToBlobRunnable> runnable = new ToBlobRunnable(aCallback, blob);
+  return NS_DispatchToCurrentThread(runnable);
 }
 
 already_AddRefed<nsIDOMFile>
 HTMLCanvasElement::MozGetAsFile(const nsAString& aName,
                                 const nsAString& aType,
                                 ErrorResult& aRv)
 {
   nsCOMPtr<nsIDOMFile> file;
@@ -562,20 +664,27 @@ HTMLCanvasElement::MozGetAsFile(const ns
   return MozGetAsFileImpl(aName, aType, aResult);
 }
 
 nsresult
 HTMLCanvasElement::MozGetAsFileImpl(const nsAString& aName,
                                     const nsAString& aType,
                                     nsIDOMFile** aResult)
 {
+  bool fallbackToPNG = false;
+
   nsCOMPtr<nsIInputStream> stream;
+  nsresult rv = ExtractData(aType, EmptyString(), getter_AddRefs(stream),
+                            fallbackToPNG);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   nsAutoString type(aType);
-  nsresult rv = ExtractData(type, EmptyString(), getter_AddRefs(stream));
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (fallbackToPNG) {
+    type.AssignLiteral("image/png");
+  }
 
   uint64_t imgSize;
   rv = stream->Available(&imgSize);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(imgSize <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
 
   void* imgData = nullptr;
   rv = NS_ReadInputStreamToBuffer(stream, &imgData, (uint32_t)imgSize);
--- a/content/html/content/src/Makefile.in
+++ b/content/html/content/src/Makefile.in
@@ -17,13 +17,12 @@ INCLUDES	+= \
 		-I$(srcdir)/../../../../layout/tables \
 		-I$(srcdir)/../../../../layout/xul/base/src \
 		-I$(srcdir)/../../../../layout/generic \
 		-I$(srcdir)/../../../../dom/base \
 		-I$(srcdir)/../../../../editor/libeditor/base \
 		-I$(srcdir)/../../../../editor/libeditor/text \
 		-I$(srcdir)/../../../../editor/txmgr/src \
 		-I$(srcdir)/../../../../netwerk/base/src \
-		-I$(srcdir)/../../../../content/canvas/src \
 		-I$(srcdir) \
 		-I$(topsrcdir)/xpcom/ds \
 		-I$(topsrcdir)/content/media/ \
 		$(NULL)
--- a/image/test/mochitest/test_animSVGImage.html
+++ b/image/test/mochitest/test_animSVGImage.html
@@ -49,56 +49,64 @@ function takeReferenceSnapshot() {
 
   // Re-hide reference div, and take another snapshot to be sure it's gone
   referenceDiv.style.display = "none";
   let blankSnapshot2 = snapshotWindow(window, false);
   ok(compareSnapshots(blankSnapshot, blankSnapshot2, true)[0],
      "reference div should disappear when it becomes display:none");
 }
 
-function myOnStopFrame() {
+function myOnStopFrame(aRequest) {
   gOnStopFrameCounter++;
   ok(true, "myOnStopFrame called");
   let currentSnapshot = snapshotWindow(window, false);
   if (compareSnapshots(currentSnapshot, gReferenceSnapshot, true)[0]) {
     // SUCCESS!
     ok(true, "Animated image looks correct, " +
              "at call #" + gOnStopFrameCounter + " to onStopFrame");
     cleanUpAndFinish();
   }
-  else
-    setTimeout(myOnStopFrame, 1);
+  setTimeout(function() { myOnStopFrame(0, 0); }, 1000);
 }
 
 function failTest() {
   ok(false, "timing out after " + FAILURE_TIMEOUT + "ms.  " +
             "Animated image still doesn't look correct, " +
             "after call #" + gOnStopFrameCounter + " to onStopFrame");
   cleanUpAndFinish();
 }
 
 function cleanUpAndFinish() {
   // On the off chance that failTest and myOnStopFrame are triggered
   // back-to-back, use a flag to prevent multiple calls to SimpleTest.finish.
   if (gIsTestFinished) {
     return;
   }
+  let imgLoadingContent = gImg.QueryInterface(Ci.nsIImageLoadingContent);
+  imgLoadingContent.removeObserver(gMyDecoderObserver);
   SimpleTest.finish();
   gIsTestFinished = true;
 }
 
 function main() {
   takeReferenceSnapshot();
 
+  // Create, customize & attach decoder observer
+  observer = new ImageDecoderObserverStub();
+  observer.frameComplete = myOnStopFrame;
+  gMyDecoderObserver =
+    Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
+      .createScriptedObserver(observer);
+  let imgLoadingContent = gImg.QueryInterface(Ci.nsIImageLoadingContent);
+  imgLoadingContent.addObserver(gMyDecoderObserver);
+
   // We want to test the cold loading behavior, so clear cache in case an
   // earlier test got our image in there already.
   clearImageCache();
 
-  setTimeout(myOnStopFrame, 1);
-
   // kick off image-loading! myOnStopFrame handles the rest.
   gImg.setAttribute("src", "lime-anim-100x100.svg");
 
   // In case something goes wrong, fail earlier than mochitest timeout,
   // and with more information.
   setTimeout(failTest, FAILURE_TIMEOUT);
 }