From 41aa9e9689bcebdb62ff582385053aef0529722e Mon Sep 17 00:00:00 2001 From: Jonas Sicking Date: Mon, 11 Jul 2011 19:42:02 -0700 Subject: [PATCH] Bug 669437: Implement BlobBuilder.getFile. r=khuey --- content/base/public/nsIDOMFile.idl | 3 +- content/base/src/nsDOMBlobBuilder.cpp | 49 +++++++++++++++++---- content/base/test/test_blobbuilder.html | 57 ++++++++++++++++++++----- 3 files changed, 89 insertions(+), 20 deletions(-) diff --git a/content/base/public/nsIDOMFile.idl b/content/base/public/nsIDOMFile.idl index 7a03fd9c7a8f..a5604985bfb3 100644 --- a/content/base/public/nsIDOMFile.idl +++ b/content/base/public/nsIDOMFile.idl @@ -73,9 +73,10 @@ interface nsIDOMFile : nsIDOMBlob [noscript] readonly attribute DOMString mozFullPathInternal; }; -[scriptable, uuid(c4a77171-039b-4f84-97f9-820fb51626af)] +[scriptable, builtinclass, uuid(006d2cde-ec18-41d4-acc3-43682dd418e2)] interface nsIDOMMozBlobBuilder : nsISupports { nsIDOMBlob getBlob([optional] in DOMString contentType); + nsIDOMFile getFile(in DOMString name, [optional] in DOMString contentType); [implicit_jscontext] void append(in jsval data); }; diff --git a/content/base/src/nsDOMBlobBuilder.cpp b/content/base/src/nsDOMBlobBuilder.cpp index d5278e55d482..e3d1af47bb1f 100644 --- a/content/base/src/nsDOMBlobBuilder.cpp +++ b/content/base/src/nsDOMBlobBuilder.cpp @@ -52,11 +52,20 @@ using namespace mozilla; -class nsDOMMultipartBlob : public nsDOMFileBase +class nsDOMMultipartFile : public nsDOMFileBase { public: + // Create as a file + nsDOMMultipartFile(nsTArray > aBlobs, + const nsAString& aName, + const nsAString& aContentType) + : nsDOMFileBase(aName, aContentType, PR_UINT64_MAX), + mBlobs(aBlobs) + { + } + // Create as a blob - nsDOMMultipartBlob(nsTArray > aBlobs, + nsDOMMultipartFile(nsTArray > aBlobs, const nsAString& aContentType) : nsDOMFileBase(aContentType, PR_UINT64_MAX), mBlobs(aBlobs) @@ -74,7 +83,7 @@ protected: }; NS_IMETHODIMP -nsDOMMultipartBlob::GetSize(PRUint64* aLength) +nsDOMMultipartFile::GetSize(PRUint64* aLength) { if (mLength == PR_UINT64_MAX) { CheckedUint64 length = 0; @@ -101,7 +110,7 @@ nsDOMMultipartBlob::GetSize(PRUint64* aLength) } NS_IMETHODIMP -nsDOMMultipartBlob::GetInternalStream(nsIInputStream** aStream) +nsDOMMultipartFile::GetInternalStream(nsIInputStream** aStream) { nsresult rv; *aStream = nsnull; @@ -126,7 +135,7 @@ nsDOMMultipartBlob::GetInternalStream(nsIInputStream** aStream) } already_AddRefed -nsDOMMultipartBlob::CreateSlice(PRUint64 aStart, PRUint64 aLength, +nsDOMMultipartFile::CreateSlice(PRUint64 aStart, PRUint64 aLength, const nsAString& aContentType) { // If we clamped to nothing we create an empty blob @@ -153,7 +162,7 @@ nsDOMMultipartBlob::CreateSlice(PRUint64 aStart, PRUint64 aLength, getter_AddRefs(firstBlob)); NS_ENSURE_SUCCESS(rv, nsnull); - // Avoid wrapping a single blob inside an nsDOMMultipartBlob + // Avoid wrapping a single blob inside an nsDOMMultipartFile if (length == upperBound) { return firstBlob.forget(); } @@ -188,7 +197,7 @@ nsDOMMultipartBlob::CreateSlice(PRUint64 aStart, PRUint64 aLength, } // we can create our blob now - nsCOMPtr blob = new nsDOMMultipartBlob(blobs, aContentType); + nsCOMPtr blob = new nsDOMMultipartFile(blobs, aContentType); return blob.forget(); } @@ -317,7 +326,7 @@ nsDOMBlobBuilder::GetBlob(const nsAString& aContentType, Flush(); - nsCOMPtr blob = new nsDOMMultipartBlob(mBlobs, + nsCOMPtr blob = new nsDOMMultipartFile(mBlobs, aContentType); blob.forget(aBlob); @@ -330,6 +339,30 @@ nsDOMBlobBuilder::GetBlob(const nsAString& aContentType, return NS_OK; } +/* nsIDOMBlob getFile (in DOMString name, [optional] in DOMString contentType); */ +NS_IMETHODIMP +nsDOMBlobBuilder::GetFile(const nsAString& aName, + const nsAString& aContentType, + nsIDOMFile** aFile) +{ + NS_ENSURE_ARG(aFile); + + Flush(); + + nsCOMPtr file = new nsDOMMultipartFile(mBlobs, + aName, + aContentType); + file.forget(aFile); + + // NB: This is a willful violation of the spec. The spec says that + // the existing contents of the BlobBuilder should be included + // in the next blob produced. This seems silly and has been raised + // on the WHATWG listserv. + mBlobs.Clear(); + + return NS_OK; +} + /* [implicit_jscontext] void append (in jsval data); */ NS_IMETHODIMP nsDOMBlobBuilder::Append(const jsval& aData, JSContext* aCx) diff --git a/content/base/test/test_blobbuilder.html b/content/base/test/test_blobbuilder.html index 32e8d8c1e0f6..8ead57568412 100644 --- a/content/base/test/test_blobbuilder.html +++ b/content/base/test/test_blobbuilder.html @@ -28,6 +28,7 @@ ok(blobBuilder, "BlobBuilder should exist"); ok(blobBuilder.append, "BlobBuilder should have an append method"); ok(blobBuilder.getBlob, "BlobBuilder should have a getBlob method"); +ok(blobBuilder.getFile, "BlobBuilder should have a getFile method"); try { blobBuilder.append(); @@ -38,8 +39,43 @@ ok(true, "an empty argument to append should throw"); blobBuilder.append("squiggle"); let blob1 = blobBuilder.getBlob(); +ok(blob1 instanceof Blob, "getBlob should produce Blobs"); +ok(!(blob1 instanceof File), "getBlob should not produce Files"); +is(blob1.type, "", "getBlob with no argument should return Blob with empty type"); +is(blob1.size, 8, "getBlob should return Blob with correct size"); + blobBuilder.append("ohai"); -let blob2 = blobBuilder.getBlob(); +let blob2 = blobBuilder.getFile("thefilename"); +ok(blob2 instanceof Blob, "getFile should produce Blobs"); +ok(blob2 instanceof File, "getFile should produce Files"); +is(blob2.name, "thefilename", "getFile should produces Files with correct name"); +is(blob2.type, "", "getFile with no second argument should return File with empty type"); +is(blob2.size, 4, "getFile should return Blob with correct size"); + +blobBuilder.append("steak"); +let blob3 = blobBuilder.getBlob("content/type"); +ok(blob3 instanceof Blob, "getBlob should produce Blobs"); +ok(!(blob3 instanceof File), "getBlob should not produce Files"); +is(blob3.type, "content/type", "getBlob with no argument should return Blob with empty type"); +is(blob3.size, 5, "getBlob should return Blob with correct size"); + +blobBuilder.append("apples"); +let blob4 = blobBuilder.getFile("the other filename", "text/plain"); +ok(blob4 instanceof Blob, "getFile should produce Blobs"); +ok(blob4 instanceof File, "getFile should produce Files"); +is(blob4.name, "the other filename", "getFile should produces Files with correct name"); +is(blob4.type, "text/plain", "getFile with second argument should return File with correct type"); +is(blob4.size, 6, "getFile should return Blob with correct size"); + +blobBuilder.append("boletes"); +let blob5 = blobBuilder.getFile(""); +ok(blob5 instanceof Blob, "getFile should produce Blobs"); +ok(blob5 instanceof File, "getFile should produce Files"); +is(blob5.name, "", "getFile with empty name should produces Files with empty name"); +is(blob5.type, "", "getFile with no second argument should return File with correct type"); +is(blob5.size, 7, "getFile should return Blob with correct size"); +testFile(blob5, "boletes", "Test empty-named File from BlobBuilder.getFile"); + let aB = new ArrayBuffer(16); var int8View = new Int8Array(aB); @@ -54,10 +90,10 @@ let testData = {start: 0, length: 3, contents: "foo"}, {start: 3, length:6, contents: "barbaz"}, {start: 6, length: 3, contents: "baz"}, - {start: 6, length: 6, elength: 3, contents: "baz"}, + {start: 6, length: 6, contents: "baz"}, {start: 0, length: 9, contents: "foobarbaz"}, - {start: 0, length: 11, elength: 9, contents: "foobarbaz"}, - {start: 10, length: 5, elength: 0, contents: ""}]], + {start: 0, length: 11, contents: "foobarbaz"}, + {start: 10, length: 5, contents: ""}]], // Test string, Blob, string [["foo", blob1, "baz"], [{start: 0, length: 3, contents: "foo"}, {start: 3, length: 8, contents: "squiggle"}, @@ -69,7 +105,7 @@ let testData = {start: 10, length: 2, contents: "os"}, {start: 1, length: 3, contents: "qui"}, {start: 12, length: 3, contents: "qui"}, - {start: 40, length: 20, elength: 0, contents: ""}]], + {start: 40, length: 20, contents: ""}]], // Test blobs all the way down [[blob2, blob1, blob2], [{start: 0, length: 4, contents: "ohai"}, {start: 4, length: 8, contents: "squiggle"}, @@ -84,7 +120,7 @@ let testData = // Test type coercion of a number [[3, aB, "foo"], [{start: 0, length: 8, contents: "3ABCDEFG"}, {start: 8, length:10, contents: "HIJKLMNOPf"}, - {start: 17, length: 4, elength: 3, contents: "foo"}, + {start: 17, length: 4, contents: "foo"}, {start: 4, length: 8, contents: "DEFGHIJK"}]] ]; @@ -110,17 +146,16 @@ function doTest(data) { let blob = bb.getBlob(); ok(blob, "Test " + testCounter + " got blob"); ok(blob instanceof Blob, "Test " + testCounter + " blob is a Blob"); - //ok(!(blob instanceof File), "Test " + testCounter + " blob is not a File"); + ok(!(blob instanceof File), "Test " + testCounter + " blob is not a File"); let slice = blob.mozSlice(test.start, test.start + test.length); ok(slice, "Test " + testCounter + " got slice"); ok(slice instanceof Blob, "Test " + testCounter + " slice is a Blob"); - //ok(!(slice instanceof File), "Test " + testCounter + " slice is not a File"); - is(slice.size,"elength" in test ? test.elength : test.length, + ok(!(slice instanceof File), "Test " + testCounter + " slice is not a File"); + is(slice.size, test.contents.length, "Test " + testCounter + " slice is correct size"); - testFile(slice, test.contents, "Test " + testCounter, - "elength" in test ? test.elength : test.length); + testFile(slice, test.contents, "Test " + testCounter); } tests.forEach(runTest); SpecialPowers.gc();