Bug 648610: Implement <canvas>.toBlob. r=smaug
authorKyle Huey <khuey@kylehuey.com>
Mon, 15 Oct 2012 14:15:25 -0700
changeset 110332 227e87a7c628
parent 110331 76dcbb473416
child 110333 6593f27f1898
push id16480
push userkhuey@mozilla.com
push dateMon, 15 Oct 2012 21:15:38 +0000
treeherdermozilla-inbound@227e87a7c628 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs648610
milestone19.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 648610: Implement <canvas>.toBlob. r=smaug
content/canvas/test/Makefile.in
content/canvas/test/test_toBlob.html
content/html/content/src/nsHTMLCanvasElement.cpp
dom/interfaces/html/nsIDOMHTMLCanvasElement.idl
--- a/content/canvas/test/Makefile.in
+++ b/content/canvas/test/Makefile.in
@@ -45,16 +45,17 @@ MOCHITEST_FILES = \
 	test_2d.composite.image.destination-in.html \
 	test_2d.composite.image.source-in.html \
 	test_2d.composite.image.source-out.html \
 	test_2d.composite.uncovered.image.destination-in.html \
 	test_2d.composite.uncovered.image.source-in.html \
 	test_2d.composite.uncovered.image.source-out.html \
 	test_2d.drawImage.zerocanvas.html \
 	test_2d.strokeRect.zero.5.html \
+	test_toBlob.html \
 	test_toDataURL_alpha.html \
 	test_toDataURL_lowercase_ascii.html \
 	test_toDataURL_parameters.html \
 	test_mozGetAsFile.html \
 	test_canvas_strokeStyle_getter.html \
 	test_bug613794.html \
 	test_bug753758.html \
 	test_bug764125.html \
copy from content/canvas/test/test_mozGetAsFile.html
copy to content/canvas/test/test_toBlob.html
--- a/content/canvas/test/test_mozGetAsFile.html
+++ b/content/canvas/test/test_toBlob.html
@@ -2,22 +2,22 @@
 <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>
 
-var gCompares = 0;
+var gCompares = 2;
 
-function compareAsync(file, canvas, type)
+function BlobListener(type, canvas, file)
 {
-  ++gCompares;
-
+  is(file.type, type,
+     "When a valid type is specified that should be returned");
   var reader = new FileReader();
   reader.onload = 
     function(e) {
       is(e.target.result, canvas.toDataURL(type),
  "<canvas>.mozGetAsFile().getAsDataURL() should equal <canvas>.toDataURL()");
       if (--gCompares == 0) {
         SimpleTest.finish();
       }
@@ -28,23 +28,15 @@ function compareAsync(file, canvas, type
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(function () {
 
 var canvas = document.getElementById('c');
 var ctx = canvas.getContext('2d');
 
 ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
 
-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");
+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/src/nsHTMLCanvasElement.cpp
+++ b/content/html/content/src/nsHTMLCanvasElement.cpp
@@ -35,16 +35,42 @@
 
 #define DEFAULT_CANVAS_WIDTH 300
 #define DEFAULT_CANVAS_HEIGHT 150
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 
+namespace {
+
+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
+
 class nsHTMLCanvasPrintState : public nsIDOMMozCanvasPrintState
 {
 public:
   nsHTMLCanvasPrintState(nsHTMLCanvasElement* aCanvas,
                          nsICanvasRenderingContextInternal* aContext,
                          nsITimerCallback* aCallback)
     : mIsDone(false), mPendingNotify(false), mCanvas(aCanvas),
       mContext(aContext), mCallback(aCallback)
@@ -545,16 +571,62 @@ nsHTMLCanvasElement::ToDataURLImpl(const
   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
+nsHTMLCanvasElement::ToBlob(nsIFileCallback* aCallback,
+                            const nsAString& aType,
+                            nsIVariant* aParams,
+                            uint8_t optional_argc)
+{
+  // do a trust check if this is a write-only canvas
+  if (mWriteOnly && !nsContentUtils::IsCallerTrustedForRead()) {
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
+  nsAutoString type;
+  nsresult rv = nsContentUtils::ASCIIToLower(aType, type);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  bool fallbackToPNG = false;
+
+  nsCOMPtr<nsIInputStream> stream;
+  nsresult rv = ExtractData(type, EmptyString(), getter_AddRefs(stream),
+                            fallbackToPNG);
+  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, imgSize);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // The DOMFile takes ownership of the buffer
+  nsRefPtr<nsDOMMemoryFile> blob =
+    new nsDOMMemoryFile(imgData, imgSize, type);
+
+  nsRefPtr<ToBlobRunnable> runnable = new ToBlobRunnable(aCallback, blob);
+  return NS_DispatchToCurrentThread(runnable);
+}
+
 NS_IMETHODIMP
 nsHTMLCanvasElement::MozGetAsFile(const nsAString& aName,
                                   const nsAString& aType,
                                   uint8_t optional_argc,
                                   nsIDOMFile** aResult)
 {
   // do a trust check if this is a write-only canvas
   if ((mWriteOnly) &&
--- a/dom/interfaces/html/nsIDOMHTMLCanvasElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLCanvasElement.idl
@@ -15,16 +15,17 @@
  * <canvas> element.
  *
  * For more information on this interface, please see
  * http://www.whatwg.org/specs/web-apps/current-work/#graphics
  *
  * @status UNDER_DEVELOPMENT
  */
 
+interface nsIDOMBlob;
 interface nsIDOMFile;
 interface nsIVariant;
 interface nsIInputStreamCallback;
 
 [scriptable, builtinclass, uuid(8d5fb8a0-7782-11e1-b0c4-0800200c9a67)]
 interface nsIDOMMozCanvasPrintState : nsISupports
 {
   // A canvas rendering context.
@@ -35,16 +36,21 @@ interface nsIDOMMozCanvasPrintState : ns
 };
 
 [scriptable, function, uuid(8d5fb8a0-7782-11e1-b0c4-0800200c9a66)]
 interface nsIPrintCallback : nsISupports
 {
   void render(in nsIDOMMozCanvasPrintState ctx);
 };
 
+[scriptable, function, uuid(6e9ffb59-2067-4aef-a51c-65e65a3e0d81)]
+interface nsIFileCallback : nsISupports {
+  void receive(in nsIDOMBlob file);
+};
+
 [scriptable, uuid(a7062fca-41c6-4520-b777-3bb30fd77273)]
 interface nsIDOMHTMLCanvasElement : nsIDOMHTMLElement
 {
   attribute unsigned long width;
   attribute unsigned long height;
   attribute boolean mozOpaque;
 
   nsISupports getContext(in DOMString contextId,
@@ -59,16 +65,20 @@ interface nsIDOMHTMLCanvasElement : nsID
                                       [optional] in nsIVariant params);
 
   // Valid calls are
   // mozGetAsFile(name);              -- defaults to image/png
   // mozGetAsFile(name, type);        -- uses given type
   [optional_argc] nsIDOMFile mozGetAsFile(in DOMString name,
                                           [optional] in DOMString type);
 
+  [optional_argc] void toBlob(in nsIFileCallback callback,
+                              [optional] in DOMString type,
+                              [optional] in nsIVariant params);
+
   // A Mozilla-only extension to get a canvas context backed by double-buffered
   // shared memory. Only privileged callers can call this.
   nsISupports MozGetIPCContext(in DOMString contextId);
 
   // A Mozilla-only extension that returns the canvas' image data as a data
   // stream in the desired image format.
   void mozFetchAsStream(in nsIInputStreamCallback callback,
                                         [optional] in DOMString type);