зеркало из https://github.com/mozilla/gecko-dev.git
Bug 648997: Implement BlobBuilder spec as mozBlobBuilder. r=sicking.
Note that there is one key difference between this implementation and the spec. In this patch mozBlobBuilder.getBlob("content/type"); returns a Blob and clears the mozBlobBuilder. In the spec the BlobBuilder is not cleared. Thus, let bb = new mozBlobBuilder(); mozBlobBuilder.append("foo"); let blob1 = mozBlobBuilder.getBlob("content/type"); // blob1 contains "foo" mozBlobBuilder.append("bar"); let blob2 = mozBlobBuilder.getBlob("content/type"); // blob2 contains "bar", the spec says it should contain "foobar". IMO, the spec behavior optimizes for the wrong case. BlobBuilder will probably be used mostly as a one-shot API. Additionally, the spec requires the BlobBuilder to hang on to potentially large amounts of memory between the getBlob() call and when the BlobBuilder is GCd. These issues have been raised on the listserv.
This commit is contained in:
Родитель
ba36dac2c6
Коммит
c8f3d0d068
|
@ -40,6 +40,7 @@
|
|||
#define nsDOMFile_h__
|
||||
|
||||
#include "nsICharsetDetectionObserver.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsIDOMFile.h"
|
||||
#include "nsIDOMFileList.h"
|
||||
#include "nsIDOMFileError.h"
|
||||
|
@ -56,9 +57,12 @@
|
|||
class nsIFile;
|
||||
class nsIInputStream;
|
||||
class nsIClassInfo;
|
||||
class nsIBlobBuilder;
|
||||
|
||||
nsresult NS_NewBlobBuilder(nsISupports* *aSupports);
|
||||
void ParseSize(PRInt64 aSize, PRInt64& aStart, PRInt64& aEnd);
|
||||
|
||||
class nsDOMFile : public nsIDOMFile,
|
||||
public nsIDOMBlob_MOZILLA_2_0_BRANCH,
|
||||
public nsIXHRSendable,
|
||||
public nsICharsetDetectionObserver,
|
||||
public nsIJSNativeInitializer
|
||||
|
@ -67,7 +71,6 @@ public:
|
|||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIDOMBLOB
|
||||
NS_DECL_NSIDOMFILE
|
||||
NS_DECL_NSIDOMBLOB_MOZILLA_2_0_BRANCH
|
||||
NS_DECL_NSIXHRSENDABLE
|
||||
|
||||
nsDOMFile(nsIFile *aFile, const nsAString& aContentType)
|
||||
|
|
|
@ -37,37 +37,33 @@
|
|||
|
||||
#include "domstubs.idl"
|
||||
|
||||
%{C++
|
||||
#include "jsapi.h"
|
||||
%}
|
||||
|
||||
interface nsIDOMFileError;
|
||||
interface nsIInputStream;
|
||||
interface nsIURI;
|
||||
interface nsIPrincipal;
|
||||
interface nsIDOMBlob;
|
||||
|
||||
[scriptable, uuid(5822776a-049c-4de7-adb6-dd9efc39d082)]
|
||||
[scriptable, uuid(d5237f31-443a-460b-9e42-449a135346f0)]
|
||||
interface nsIDOMBlob : nsISupports
|
||||
{
|
||||
readonly attribute unsigned long long size;
|
||||
readonly attribute DOMString type;
|
||||
|
||||
[noscript] nsIDOMBlob slice(in unsigned long long start,
|
||||
in unsigned long long length,
|
||||
[optional] in DOMString contentType);
|
||||
|
||||
[noscript] readonly attribute nsIInputStream internalStream;
|
||||
// The caller is responsible for releasing the internalUrl from the
|
||||
// moz-filedata: protocol handler
|
||||
[noscript] DOMString getInternalUrl(in nsIPrincipal principal);
|
||||
};
|
||||
|
||||
[scriptable, uuid(cb5b4191-a555-4e57-b8d2-88091184b59f)]
|
||||
interface nsIDOMBlob_MOZILLA_2_0_BRANCH : nsISupports
|
||||
{
|
||||
[optional_argc] nsIDOMBlob mozSlice(in long long start,
|
||||
[optional] in long long end,
|
||||
[optional] in DOMString contentType);
|
||||
};
|
||||
|
||||
[scriptable, uuid(ae1405b0-e411-481e-9606-b29ec7982687)]
|
||||
[scriptable, uuid(91c9ebd9-2a4a-4a38-9412-ef492a2799be)]
|
||||
interface nsIDOMFile : nsIDOMBlob
|
||||
{
|
||||
readonly attribute DOMString name;
|
||||
|
@ -84,3 +80,10 @@ interface nsIDOMFile : nsIDOMBlob
|
|||
DOMString getAsDataURL(); // raises(FileException) on retrieval
|
||||
DOMString getAsBinary(); // raises(FileException) on retrieval
|
||||
};
|
||||
|
||||
[scriptable, uuid(c4a77171-039b-4f84-97f9-820fb51626af)]
|
||||
interface nsIDOMBlobBuilder : nsISupports
|
||||
{
|
||||
nsIDOMBlob getBlob([optional] in DOMString contentType);
|
||||
[implicit_jscontext] void append(in jsval data);
|
||||
};
|
||||
|
|
|
@ -92,6 +92,7 @@ CPPSRCS = \
|
|||
nsDataDocumentContentPolicy.cpp \
|
||||
nsDOMAttribute.cpp \
|
||||
nsDOMAttributeMap.cpp \
|
||||
nsDOMBlobBuilder.cpp \
|
||||
nsDOMDocumentType.cpp \
|
||||
nsDOMEventTargetWrapperCache.cpp \
|
||||
nsDOMFile.cpp \
|
||||
|
|
|
@ -0,0 +1,399 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla File API.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Kyle Huey <me@kylehuey.com>
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "jstypedarray.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsDOMClassInfo.h"
|
||||
#include "nsDOMFile.h"
|
||||
#include "nsIMultiplexInputStream.h"
|
||||
#include "nsStringStream.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "CheckedInt.h"
|
||||
|
||||
// XXXkhuey shamelessly stolen from VideoUtils.h. We should patch NSPR.
|
||||
#define PR_INT64_MAX (~((PRInt64)(1) << 63))
|
||||
#define PR_INT64_MIN (-PR_INT64_MAX - 1)
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
class nsDOMMultipartBlob : public nsDOMFile
|
||||
{
|
||||
public:
|
||||
nsDOMMultipartBlob(nsTArray<nsCOMPtr<nsIDOMBlob> > aBlobs,
|
||||
const nsAString& aContentType)
|
||||
: nsDOMFile(nsnull, aContentType),
|
||||
mBlobs(aBlobs)
|
||||
{
|
||||
mIsFullFile = false;
|
||||
mStart = 0;
|
||||
mLength = 0;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetSize(PRUint64*);
|
||||
NS_IMETHOD GetInternalStream(nsIInputStream**);
|
||||
NS_IMETHOD MozSlice(PRInt64 aStart, PRInt64 aEnd,
|
||||
const nsAString& aContentType, PRUint8 optional_argc,
|
||||
nsIDOMBlob **aBlob);
|
||||
|
||||
protected:
|
||||
nsTArray<nsCOMPtr<nsIDOMBlob> > mBlobs;
|
||||
};
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMMultipartBlob::GetSize(PRUint64* aLength)
|
||||
{
|
||||
nsresult rv;
|
||||
*aLength = 0;
|
||||
|
||||
if (mLength) {
|
||||
*aLength = mLength;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
CheckedUint64 length = 0;
|
||||
|
||||
PRUint32 i;
|
||||
PRUint32 len = mBlobs.Length();
|
||||
for (i = 0; i < len; i++) {
|
||||
nsIDOMBlob* blob = mBlobs.ElementAt(i).get();
|
||||
PRUint64 l = 0;
|
||||
|
||||
rv = blob->GetSize(&l);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
length += l;
|
||||
}
|
||||
|
||||
if (!length.valid())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
mLength = length.value();
|
||||
*aLength = mLength;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMMultipartBlob::GetInternalStream(nsIInputStream** aStream)
|
||||
{
|
||||
nsresult rv;
|
||||
*aStream = nsnull;
|
||||
|
||||
nsCOMPtr<nsIMultiplexInputStream> stream =
|
||||
do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
|
||||
NS_ENSURE_TRUE(stream, NS_ERROR_FAILURE);
|
||||
|
||||
PRUint32 i;
|
||||
for (i = 0; i < mBlobs.Length(); i++) {
|
||||
nsCOMPtr<nsIInputStream> scratchStream;
|
||||
nsIDOMBlob* blob = mBlobs.ElementAt(i).get();
|
||||
|
||||
rv = blob->GetInternalStream(getter_AddRefs(scratchStream));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = stream->AppendStream(scratchStream);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
return CallQueryInterface(stream, aStream);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMMultipartBlob::MozSlice(PRInt64 aStart, PRInt64 aEnd,
|
||||
const nsAString& aContentType,
|
||||
PRUint8 optional_argc,
|
||||
nsIDOMBlob **aBlob)
|
||||
{
|
||||
nsresult rv;
|
||||
*aBlob = nsnull;
|
||||
|
||||
// Truncate aStart and aEnd so that we stay within this file.
|
||||
PRUint64 thisLength;
|
||||
rv = GetSize(&thisLength);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!optional_argc) {
|
||||
aEnd = (PRInt64)thisLength;
|
||||
}
|
||||
|
||||
ParseSize((PRInt64)thisLength, aStart, aEnd);
|
||||
|
||||
// If we clamped to nothing we create an empty blob
|
||||
nsTArray<nsCOMPtr<nsIDOMBlob> > blobs;
|
||||
|
||||
PRInt64 length = aEnd - aStart;
|
||||
PRUint64 finalLength = length;
|
||||
PRUint64 skipStart = aStart;
|
||||
|
||||
NS_ABORT_IF_FALSE(aStart + length <= thisLength, "Er, what?");
|
||||
|
||||
// Prune the list of blobs if we can
|
||||
PRUint32 i;
|
||||
for (i = 0; length && skipStart && i < mBlobs.Length(); i++) {
|
||||
nsIDOMBlob* blob = mBlobs[i].get();
|
||||
|
||||
PRUint64 l;
|
||||
rv = blob->GetSize(&l);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (skipStart < l) {
|
||||
PRInt64 upperBound = NS_MIN<PRInt64>(l - skipStart, length);
|
||||
|
||||
nsCOMPtr<nsIDOMBlob> firstBlob;
|
||||
rv = mBlobs.ElementAt(i)->MozSlice(skipStart, skipStart + upperBound,
|
||||
aContentType, 2,
|
||||
getter_AddRefs(firstBlob));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Avoid wrapping a single blob inside an nsDOMMultipartBlob
|
||||
if (length == upperBound) {
|
||||
firstBlob.forget(aBlob);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
blobs.AppendElement(firstBlob);
|
||||
length -= upperBound;
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
skipStart -= l;
|
||||
}
|
||||
|
||||
// Now append enough blobs until we're done
|
||||
for (; length && i < mBlobs.Length(); i++) {
|
||||
nsIDOMBlob* blob = mBlobs[i].get();
|
||||
|
||||
PRUint64 l;
|
||||
rv = blob->GetSize(&l);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (length < l) {
|
||||
nsCOMPtr<nsIDOMBlob> lastBlob;
|
||||
rv = mBlobs.ElementAt(i)->MozSlice(0, length, aContentType, 2,
|
||||
getter_AddRefs(lastBlob));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
blobs.AppendElement(lastBlob);
|
||||
} else {
|
||||
blobs.AppendElement(blob);
|
||||
}
|
||||
length -= NS_MIN<PRInt64>(l, length);
|
||||
}
|
||||
|
||||
// we can create our blob now
|
||||
nsCOMPtr<nsIDOMBlob> blob = new nsDOMMultipartBlob(blobs, aContentType);
|
||||
blob.forget(aBlob);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
class nsDOMBlobBuilder : public nsIDOMBlobBuilder
|
||||
{
|
||||
public:
|
||||
nsDOMBlobBuilder()
|
||||
: mData(nsnull), mDataLen(0), mDataBufferLen(0)
|
||||
{}
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIDOMBLOBBUILDER
|
||||
protected:
|
||||
nsresult AppendVoidPtr(void* aData, PRUint32 aLength);
|
||||
nsresult AppendString(JSString* aString, JSContext* aCx);
|
||||
nsresult AppendBlob(nsIDOMBlob* aBlob);
|
||||
nsresult AppendArrayBuffer(js::ArrayBuffer* aBuffer);
|
||||
|
||||
bool ExpandBufferSize(PRUint64 aSize)
|
||||
{
|
||||
if (mDataBufferLen >= mDataLen + aSize) {
|
||||
mDataLen += aSize;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Start at 1 or we'll loop forever.
|
||||
CheckedUint32 bufferLen = NS_MAX<PRUint32>(mDataBufferLen, 1);
|
||||
while (bufferLen.valid() && bufferLen.value() < mDataLen + aSize)
|
||||
bufferLen *= 2;
|
||||
|
||||
if (!bufferLen.valid())
|
||||
return false;
|
||||
|
||||
// PR_ memory functions are still fallible
|
||||
void* data = PR_Realloc(mData, bufferLen.value());
|
||||
if (!data)
|
||||
return false;
|
||||
|
||||
mData = data;
|
||||
mDataBufferLen = bufferLen.value();
|
||||
mDataLen += aSize;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Flush() {
|
||||
if (mData) {
|
||||
// If we have some data, create a blob for it
|
||||
// and put it on the stack
|
||||
|
||||
nsCOMPtr<nsIDOMBlob> blob =
|
||||
new nsDOMMemoryFile(mData, mDataLen, EmptyString(), EmptyString());
|
||||
mBlobs.AppendElement(blob);
|
||||
mData = nsnull; // The nsDOMMemoryFile takes ownership of the buffer
|
||||
mDataLen = 0;
|
||||
mDataBufferLen = 0;
|
||||
}
|
||||
}
|
||||
|
||||
nsTArray<nsCOMPtr<nsIDOMBlob> > mBlobs;
|
||||
void* mData;
|
||||
PRUint64 mDataLen;
|
||||
PRUint64 mDataBufferLen;
|
||||
};
|
||||
|
||||
DOMCI_DATA(MozBlobBuilder, nsDOMBlobBuilder)
|
||||
|
||||
NS_IMPL_ADDREF(nsDOMBlobBuilder)
|
||||
NS_IMPL_RELEASE(nsDOMBlobBuilder)
|
||||
NS_INTERFACE_MAP_BEGIN(nsDOMBlobBuilder)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMBlobBuilder)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozBlobBuilder)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
nsresult
|
||||
nsDOMBlobBuilder::AppendVoidPtr(void* aData, PRUint32 aLength)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aData);
|
||||
|
||||
PRUint64 offset = mDataLen;
|
||||
|
||||
if (!ExpandBufferSize(aLength))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
memcpy((char*)mData + offset, aData, aLength);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMBlobBuilder::AppendString(JSString* aString, JSContext* aCx)
|
||||
{
|
||||
nsDependentJSString xpcomStr;
|
||||
if (!xpcomStr.init(aCx, aString)) {
|
||||
return NS_ERROR_XPC_BAD_CONVERT_JS;
|
||||
}
|
||||
|
||||
NS_ConvertUTF16toUTF8 utf8Str(xpcomStr);
|
||||
|
||||
return AppendVoidPtr((void*)utf8Str.Data(),
|
||||
utf8Str.Length());
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMBlobBuilder::AppendBlob(nsIDOMBlob* aBlob)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aBlob);
|
||||
|
||||
Flush();
|
||||
mBlobs.AppendElement(aBlob);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMBlobBuilder::AppendArrayBuffer(js::ArrayBuffer* aBuffer)
|
||||
{
|
||||
return AppendVoidPtr(aBuffer->data, aBuffer->byteLength);
|
||||
}
|
||||
|
||||
/* nsIDOMBlob getBlob ([optional] in DOMString contentType); */
|
||||
NS_IMETHODIMP
|
||||
nsDOMBlobBuilder::GetBlob(const nsAString& aContentType,
|
||||
nsIDOMBlob** aBlob)
|
||||
{
|
||||
NS_ENSURE_ARG(aBlob);
|
||||
|
||||
Flush();
|
||||
|
||||
nsCOMPtr<nsIDOMBlob> blob = new nsDOMMultipartBlob(mBlobs,
|
||||
aContentType);
|
||||
blob.forget(aBlob);
|
||||
|
||||
// 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)
|
||||
{
|
||||
// We need to figure out what our jsval is
|
||||
|
||||
// Is it an object?
|
||||
if (JSVAL_IS_OBJECT(aData)) {
|
||||
JSObject* obj = JSVAL_TO_OBJECT(aData);
|
||||
NS_ASSERTION(obj, "Er, what?");
|
||||
|
||||
// Is it a Blob?
|
||||
nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(
|
||||
nsContentUtils::XPConnect()->
|
||||
GetNativeOfWrapper(aCx, obj));
|
||||
if (blob)
|
||||
return AppendBlob(blob);
|
||||
|
||||
// Is it an array buffer?
|
||||
if (js_IsArrayBuffer(obj)) {
|
||||
js::ArrayBuffer* buffer = js::ArrayBuffer::fromJSObject(obj);
|
||||
if (buffer)
|
||||
return AppendArrayBuffer(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
// If it's not a Blob or an ArrayBuffer, coerce it to a string
|
||||
JSString* str = JS_ValueToString(aCx, aData);
|
||||
NS_ENSURE_TRUE(str, NS_ERROR_FAILURE);
|
||||
|
||||
return AppendString(str, aCx);
|
||||
}
|
||||
|
||||
nsresult NS_NewBlobBuilder(nsISupports* *aSupports)
|
||||
{
|
||||
nsDOMBlobBuilder* builder = new nsDOMBlobBuilder();
|
||||
return CallQueryInterface(builder, aSupports);
|
||||
}
|
|
@ -49,7 +49,6 @@
|
|||
#include "nsIConverterInputStream.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIDOMDocument.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsIFileStreams.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsIIPCSerializable.h"
|
||||
|
@ -140,7 +139,6 @@ DOMCI_DATA(Blob, nsDOMFile)
|
|||
NS_INTERFACE_MAP_BEGIN(nsDOMFile)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFile)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMBlob)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDOMBlob_MOZILLA_2_0_BRANCH)
|
||||
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMFile, mIsFullFile)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIXHRSendable)
|
||||
NS_INTERFACE_MAP_ENTRY(nsICharsetDetectionObserver)
|
||||
|
@ -293,13 +291,6 @@ ParseSize(PRInt64 aSize, PRInt64& aStart, PRInt64& aEnd)
|
|||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMFile::Slice(PRUint64 aStart, PRUint64 aLength,
|
||||
const nsAString& aContentType, nsIDOMBlob **aBlob)
|
||||
{
|
||||
return MozSlice(aStart, aStart + aLength, aContentType, 2, aBlob);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMFile::MozSlice(PRInt64 aStart, PRInt64 aEnd,
|
||||
const nsAString& aContentType, PRUint8 optional_argc,
|
||||
|
@ -307,7 +298,7 @@ nsDOMFile::MozSlice(PRInt64 aStart, PRInt64 aEnd,
|
|||
{
|
||||
*aBlob = nsnull;
|
||||
|
||||
// Truncate aLength and aStart so that we stay within this file.
|
||||
// Truncate aStart and aEnd so that we stay within this file.
|
||||
PRUint64 thisLength;
|
||||
nsresult rv = GetSize(&thisLength);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
|
|
@ -481,6 +481,8 @@ _TEST_FILES2 = \
|
|||
bug638112-response.txt \
|
||||
bug638112.sjs \
|
||||
test_bug656283.html \
|
||||
test_blobbuilder.html \
|
||||
fileutils.js \
|
||||
$(NULL)
|
||||
|
||||
# This test fails on the Mac for some reason
|
||||
|
|
|
@ -0,0 +1,272 @@
|
|||
// Utility functions
|
||||
var testRanCounter = 0;
|
||||
var expectedTestCount = 0;
|
||||
|
||||
function testHasRun() {
|
||||
//alert(testRanCounter);
|
||||
++testRanCounter;
|
||||
if (testRanCounter == expectedTestCount) {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function testFile(file, contents, test) {
|
||||
SimpleTest.requestLongerTimeout(2);
|
||||
|
||||
// Load file using FileReader
|
||||
var r = new FileReader();
|
||||
r.onload = getFileReaderLoadHandler(contents, contents.length, "FileReader.readAsBinaryString of " + test);
|
||||
r.readAsBinaryString(file);
|
||||
expectedTestCount++;
|
||||
|
||||
// Load file using URL.createObjectURL and XMLHttpRequest
|
||||
var xhr = new XMLHttpRequest;
|
||||
xhr.open("GET", URL.createObjectURL(file));
|
||||
xhr.onload = getXHRLoadHandler(contents, contents.length, false,
|
||||
"XMLHttpRequest load of " + test);
|
||||
xhr.overrideMimeType('text/plain; charset=x-user-defined');
|
||||
xhr.send();
|
||||
expectedTestCount++;
|
||||
|
||||
// Send file to server using FormData and XMLHttpRequest
|
||||
xhr = new XMLHttpRequest();
|
||||
xhr.onload = function(event) {
|
||||
checkMPSubmission(JSON.parse(event.target.responseText),
|
||||
[{ name: "hello", value: "world"},
|
||||
{ name: "myfile",
|
||||
value: contents,
|
||||
fileName: file.name || "",
|
||||
contentType: file.type || "application/octet-stream" }]);
|
||||
testHasRun();
|
||||
}
|
||||
xhr.open("POST", "../../html/content/test/form_submit_server.sjs");
|
||||
var fd = new FormData;
|
||||
fd.append("hello", "world");
|
||||
fd.append("myfile", file);
|
||||
xhr.send(fd);
|
||||
expectedTestCount++;
|
||||
|
||||
// Send file to server using plain XMLHttpRequest
|
||||
var xhr = new XMLHttpRequest;
|
||||
xhr.open("POST", "file_XHRSendData.sjs");
|
||||
xhr.onload = function (event) {
|
||||
is(event.target.getResponseHeader("Result-Content-Type"),
|
||||
file.type ? file.type : null,
|
||||
"request content-type in XMLHttpRequest send of " + test);
|
||||
is(event.target.getResponseHeader("Result-Content-Length"),
|
||||
file.size,
|
||||
"request content-length in XMLHttpRequest send of " + test);
|
||||
};
|
||||
xhr.addEventListener("load",
|
||||
getXHRLoadHandler(contents, contents.length, true,
|
||||
"XMLHttpRequest send of " + test),
|
||||
false);
|
||||
xhr.overrideMimeType('text/plain; charset=x-user-defined');
|
||||
xhr.send(file);
|
||||
expectedTestCount++;
|
||||
}
|
||||
|
||||
function getFileReaderLoadHandler(expectedResult, expectedLength, testName) {
|
||||
return function (event) {
|
||||
is(event.target.readyState, FileReader.DONE,
|
||||
"[FileReader] readyState in test " + testName);
|
||||
is(event.target.error, null,
|
||||
"[FileReader] no error in test " + testName);
|
||||
// Do not use |is(event.target.result, expectedResult, "...");| that may output raw binary data.
|
||||
is(event.target.result.length, expectedResult.length,
|
||||
"[FileReader] Length of result in test " + testName);
|
||||
ok(event.target.result == expectedResult,
|
||||
"[FileReader] Content of result in test " + testName);
|
||||
is(event.lengthComputable, true,
|
||||
"[FileReader] lengthComputable in test " + testName);
|
||||
is(event.loaded, expectedLength,
|
||||
"[FileReader] Loaded length in test " + testName);
|
||||
is(event.total, expectedLength,
|
||||
"[FileReader] Total length in test " + testName);
|
||||
testHasRun();
|
||||
}
|
||||
}
|
||||
|
||||
function getXHRLoadHandler(expectedResult, expectedLength, statusWorking, testName) {
|
||||
return function (event) {
|
||||
is(event.target.readyState, 4,
|
||||
"[XHR] readyState in test " + testName);
|
||||
if (statusWorking) {
|
||||
is(event.target.status, 200,
|
||||
"[XHR] no error in test " + testName);
|
||||
}
|
||||
else {
|
||||
todo(event.target.status, 200,
|
||||
"[XHR] no error in test " + testName);
|
||||
}
|
||||
// Do not use |is(convertXHRBinary(event.target.responseText), expectedResult, "...");| that may output raw binary data.
|
||||
var convertedData = convertXHRBinary(event.target.responseText);
|
||||
is(convertedData.length, expectedResult.length,
|
||||
"[XHR] Length of result in test " + testName);
|
||||
ok(convertedData == expectedResult,
|
||||
"[XHR] Content of result in test " + testName);
|
||||
is(event.lengthComputable, true,
|
||||
"[XHR] lengthComputable in test " + testName);
|
||||
is(event.loaded, expectedLength,
|
||||
"[XHR] Loaded length in test " + testName);
|
||||
is(event.total, expectedLength,
|
||||
"[XHR] Total length in test " + testName);
|
||||
|
||||
testHasRun();
|
||||
}
|
||||
}
|
||||
|
||||
function convertXHRBinary(s) {
|
||||
var res = "";
|
||||
for (var i = 0; i < s.length; ++i) {
|
||||
res += String.fromCharCode(s.charCodeAt(i) & 255);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function testHasRun() {
|
||||
//alert(testRanCounter);
|
||||
++testRanCounter;
|
||||
if (testRanCounter == expectedTestCount) {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
function createFileWithData(fileData) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
var dirSvc = Components.classes["@mozilla.org/file/directory_service;1"].getService(Components.interfaces.nsIProperties);
|
||||
var testFile = dirSvc.get("ProfD", Components.interfaces.nsIFile);
|
||||
testFile.append("fileAPItestfile2-" + fileNum);
|
||||
fileNum++;
|
||||
var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
|
||||
outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
|
||||
0666, 0);
|
||||
outStream.write(fileData, fileData.length);
|
||||
outStream.close();
|
||||
|
||||
var fileList = document.getElementById('fileList');
|
||||
fileList.value = testFile.path;
|
||||
|
||||
return fileList.files[0];
|
||||
}
|
||||
|
||||
function gc() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindowUtils)
|
||||
.garbageCollect();
|
||||
}
|
||||
|
||||
function checkMPSubmission(sub, expected) {
|
||||
function getPropCount(o) {
|
||||
var x, l = 0;
|
||||
for (x in o) ++l;
|
||||
return l;
|
||||
}
|
||||
|
||||
is(sub.length, expected.length,
|
||||
"Correct number of items");
|
||||
var i;
|
||||
for (i = 0; i < expected.length; ++i) {
|
||||
if (!("fileName" in expected[i])) {
|
||||
is(sub[i].headers["Content-Disposition"],
|
||||
"form-data; name=\"" + expected[i].name + "\"",
|
||||
"Correct name (A)");
|
||||
is (getPropCount(sub[i].headers), 1,
|
||||
"Wrong number of headers (A)");
|
||||
}
|
||||
else {
|
||||
is(sub[i].headers["Content-Disposition"],
|
||||
"form-data; name=\"" + expected[i].name + "\"; filename=\"" +
|
||||
expected[i].fileName + "\"",
|
||||
"Correct name (B)");
|
||||
is(sub[i].headers["Content-Type"],
|
||||
expected[i].contentType,
|
||||
"Correct content type (B)");
|
||||
is (getPropCount(sub[i].headers), 2,
|
||||
"Wrong number of headers (B)");
|
||||
}
|
||||
// Do not use |is(sub[i].body, expected[i].value, "...");| that may output raw binary data.
|
||||
is(sub[i].body.length, expected[i].value.length,
|
||||
"Length of correct value");
|
||||
ok(sub[i].body == expected[i].value,
|
||||
"Content of correct value");
|
||||
}
|
||||
}
|
||||
|
||||
function testSlice(file, size, type, contents, fileType) {
|
||||
is(file.type, type, fileType + " file is correct type");
|
||||
is(file.size, size, fileType + " file is correct size");
|
||||
ok(file instanceof File, fileType + " file is a File");
|
||||
ok(file instanceof Blob, fileType + " file is also a Blob");
|
||||
|
||||
var slice = file.mozSlice(0, size);
|
||||
ok(slice instanceof Blob, fileType + " fullsize slice is a Blob");
|
||||
ok(!(slice instanceof File), fileType + " fullsize slice is not a File");
|
||||
|
||||
slice = file.mozSlice(0, 1234);
|
||||
ok(slice instanceof Blob, fileType + " sized slice is a Blob");
|
||||
ok(!(slice instanceof File), fileType + " sized slice is not a File");
|
||||
|
||||
slice = file.mozSlice(0, size, "foo/bar");
|
||||
is(slice.type, "foo/bar", fileType + " fullsize slice foo/bar type");
|
||||
|
||||
slice = file.mozSlice(0, 5432, "foo/bar");
|
||||
is(slice.type, "foo/bar", fileType + " sized slice foo/bar type");
|
||||
|
||||
is(slice.mozSlice(0, 10).type, "", fileType + " slice-slice type");
|
||||
is(slice.mozSlice(0, 10).size, 10, fileType + " slice-slice size");
|
||||
is(slice.mozSlice(0, 10, "hello/world").type, "hello/world", fileType + " slice-slice hello/world type");
|
||||
is(slice.mozSlice(0, 10, "hello/world").size, 10, fileType + " slice-slice hello/world size");
|
||||
|
||||
var indexes = [[0, size, size],
|
||||
[0, 1234, 1234],
|
||||
[size-500, size, 500],
|
||||
[size-500, size+500, 500],
|
||||
[size+500, size+1500, 0],
|
||||
[0, 0, 0],
|
||||
[1000, 1000, 0],
|
||||
[size, size, 0],
|
||||
[0, undefined, size],
|
||||
[100, undefined, size-100],
|
||||
[-100, undefined, 100],
|
||||
[100, -100, size-200],
|
||||
[-size-100, undefined, size],
|
||||
[-2*size-100, 500, 500],
|
||||
[0, -size-100, 0],
|
||||
[100, -size-100, 0],
|
||||
[50, -size+100, 50],
|
||||
[0, 33000, 33000],
|
||||
[1000, 34000, 33000],
|
||||
];
|
||||
|
||||
for (var i = 0; i < indexes.length; ++i) {
|
||||
var sliceContents;
|
||||
var testName;
|
||||
if (indexes[i][1] == undefined) {
|
||||
slice = file.mozSlice(indexes[i][0]);
|
||||
sliceContents = contents.slice(indexes[i][0]);
|
||||
testName = fileType + " slice(" + indexes[i][0] + ")";
|
||||
}
|
||||
else {
|
||||
slice = file.mozSlice(indexes[i][0], indexes[i][1]);
|
||||
sliceContents = contents.slice(indexes[i][0], indexes[i][1]);
|
||||
testName = fileType + " slice(" + indexes[i][0] + ", " + indexes[i][1] + ")";
|
||||
}
|
||||
is(slice.type, "", testName + " type");
|
||||
is(slice.size, indexes[i][2], testName + " size");
|
||||
is(sliceContents.length, indexes[i][2], testName + " data size");
|
||||
testFile(slice, sliceContents, testName);
|
||||
}
|
||||
|
||||
// Slice of slice
|
||||
var slice = file.mozSlice(0, 40000);
|
||||
testFile(slice.mozSlice(5000, 42000), contents.slice(5000, 40000), "file slice slice");
|
||||
|
||||
// ...of slice of slice
|
||||
slice = slice.mozSlice(5000, 42000).mozSlice(400, 700);
|
||||
SpecialPowers.gc();
|
||||
testFile(slice, contents.slice(5400, 5700), "file slice slice slice");
|
||||
}
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=648997
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 648997</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="fileutils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=648997">Mozilla Bug 648997</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript;version=1.7">
|
||||
|
||||
// We're prefixing still
|
||||
window.BlobBuilder = window.MozBlobBuilder;
|
||||
|
||||
/** Test for Bug 648997 **/
|
||||
var blobBuilder = BlobBuilder();
|
||||
ok(blobBuilder, "BlobBuilder should exist");
|
||||
|
||||
ok(blobBuilder.append, "BlobBuilder should have an append method");
|
||||
ok(blobBuilder.getBlob, "BlobBuilder should have a getBlob method");
|
||||
|
||||
try {
|
||||
blobBuilder.append();
|
||||
ok(false, "NOT REACHED");
|
||||
} catch(e) {
|
||||
ok(true, "an empty argument to append should throw");
|
||||
}
|
||||
|
||||
blobBuilder.append("squiggle");
|
||||
let blob1 = blobBuilder.getBlob();
|
||||
blobBuilder.append("ohai");
|
||||
let blob2 = blobBuilder.getBlob();
|
||||
|
||||
let aB = new ArrayBuffer(16);
|
||||
var int8View = new Int8Array(aB);
|
||||
for (var i = 0; i < 16; i++) {
|
||||
int8View[i] = i+65;
|
||||
}
|
||||
|
||||
let testData =
|
||||
[
|
||||
// Test 3 strings
|
||||
[["foo", "bar", "baz"], [{start: 0, length: 9, contents: "foobarbaz"},
|
||||
{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: 0, length: 9, contents: "foobarbaz"},
|
||||
{start: 0, length: 11, elength: 9, contents: "foobarbaz"},
|
||||
{start: 10, length: 5, elength: 0, contents: ""}]],
|
||||
// Test string, Blob, string
|
||||
[["foo", blob1, "baz"], [{start: 0, length: 3, contents: "foo"},
|
||||
{start: 3, length: 8, contents: "squiggle"},
|
||||
{start: 2, length: 2, contents: "os"},
|
||||
{start: 10, length: 2, contents: "eb"}]],
|
||||
// Test blob, string, blob
|
||||
[[blob1, "foo", blob1], [{start: 0, length: 8, contents: "squiggle"},
|
||||
{start: 7, length: 2, contents: "ef"},
|
||||
{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: ""}]],
|
||||
// Test blobs all the way down
|
||||
[[blob2, blob1, blob2], [{start: 0, length: 4, contents: "ohai"},
|
||||
{start: 4, length: 8, contents: "squiggle"},
|
||||
{start: 12, length: 4, contents: "ohai"},
|
||||
{start: 1, length: 2, contents: "ha"},
|
||||
{start: 5, length: 4, contents: "quig"}]],
|
||||
// Test an array buffer
|
||||
[[aB, blob1, "foo"], [{start: 0, length: 8, contents: "ABCDEFGH"},
|
||||
{start: 8, length:10, contents: "IJKLMNOPsq"},
|
||||
{start: 17, length: 3, contents: "qui"},
|
||||
{start: 4, length: 8, contents: "EFGHIJKL"}]],
|
||||
// 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: 4, length: 8, contents: "DEFGHIJK"}]]
|
||||
];
|
||||
|
||||
let testCounter = 0;
|
||||
|
||||
function doTest(data) {
|
||||
testCounter++;
|
||||
|
||||
[blobs, tests] = data;
|
||||
|
||||
function runTest(test) {
|
||||
|
||||
let bb = new BlobBuilder();
|
||||
ok(bb, "BlobBuilder should exist");
|
||||
|
||||
function doAppend(blob) {
|
||||
bb.append(blob);
|
||||
blob.expando = bb; // Do we leak?
|
||||
}
|
||||
|
||||
blobs.forEach(doAppend);
|
||||
ok(true, "Test " + testCounter + " appended all successfully");
|
||||
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");
|
||||
|
||||
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,
|
||||
"Test " + testCounter + " slice is correct size");
|
||||
|
||||
testFile(slice, test.contents, "Test " + testCounter,
|
||||
"elength" in test ? test.elength : test.length);
|
||||
}
|
||||
tests.forEach(runTest);
|
||||
SpecialPowers.gc();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
testData.forEach(doTest);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -7,6 +7,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=575946
|
|||
<title>Test for Bug 575946</title>
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="fileutils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
|
||||
|
@ -23,8 +24,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=575946
|
|||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
var fileNum = 1;
|
||||
var testRanCounter = 0;
|
||||
var expectedTestCount = 0;
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Create files containing data we'll test with. We'll want long
|
||||
|
@ -69,83 +68,8 @@ ok(size > 65536, "test data sufficiently large");
|
|||
|
||||
|
||||
// Test that basic properties work
|
||||
function testFile(file, size, type, contents, fileType) {
|
||||
is(file.type, type, fileType + " file is correct type");
|
||||
is(file.size, size, fileType + " file is correct size");
|
||||
ok(file instanceof File, fileType + " file is a File");
|
||||
ok(file instanceof Blob, fileType + " file is also a Blob");
|
||||
|
||||
var slice = file.mozSlice(0, size);
|
||||
ok(slice instanceof Blob, fileType + " fullsize slice is a Blob");
|
||||
ok(!(slice instanceof File), fileType + " fullsize slice is not a File");
|
||||
|
||||
slice = file.mozSlice(0, 1234);
|
||||
ok(slice instanceof Blob, fileType + " sized slice is a Blob");
|
||||
ok(!(slice instanceof File), fileType + " sized slice is not a File");
|
||||
|
||||
slice = file.mozSlice(0, size, "foo/bar");
|
||||
is(slice.type, "foo/bar", fileType + " fullsize slice foo/bar type");
|
||||
|
||||
slice = file.mozSlice(0, 5432, "foo/bar");
|
||||
is(slice.type, "foo/bar", fileType + " sized slice foo/bar type");
|
||||
|
||||
is(slice.mozSlice(0, 10).type, "", fileType + " slice-slice type");
|
||||
is(slice.mozSlice(0, 10).size, 10, fileType + " slice-slice size");
|
||||
is(slice.mozSlice(0, 10, "hello/world").type, "hello/world", fileType + " slice-slice hello/world type");
|
||||
is(slice.mozSlice(0, 10, "hello/world").size, 10, fileType + " slice-slice hello/world size");
|
||||
|
||||
var indexes = [[0, size, size],
|
||||
[0, 1234, 1234],
|
||||
[size-500, size, 500],
|
||||
[size-500, size+500, 500],
|
||||
[size+500, size+1500, 0],
|
||||
[0, 0, 0],
|
||||
[1000, 1000, 0],
|
||||
[size, size, 0],
|
||||
[0, undefined, size],
|
||||
[100, undefined, size-100],
|
||||
[-100, undefined, 100],
|
||||
[100, -100, size-200],
|
||||
[-size-100, undefined, size],
|
||||
[-2*size-100, 500, 500],
|
||||
[0, -size-100, 0],
|
||||
[100, -size-100, 0],
|
||||
[50, -size+100, 50],
|
||||
[0, 33000, 33000],
|
||||
[1000, 34000, 33000],
|
||||
];
|
||||
|
||||
for (var i = 0; i < indexes.length; ++i) {
|
||||
var sliceContents;
|
||||
var testName;
|
||||
if (indexes[i][1] == undefined) {
|
||||
slice = file.mozSlice(indexes[i][0]);
|
||||
sliceContents = contents.slice(indexes[i][0]);
|
||||
testName = fileType + " slice(" + indexes[i][0] + ")";
|
||||
}
|
||||
else {
|
||||
slice = file.mozSlice(indexes[i][0], indexes[i][1]);
|
||||
sliceContents = contents.slice(indexes[i][0], indexes[i][1]);
|
||||
testName = fileType + " slice(" + indexes[i][0] + ", " + indexes[i][1] + ")";
|
||||
}
|
||||
is(slice.type, "", testName + " type");
|
||||
is(slice.size, indexes[i][2], testName + " size");
|
||||
is(sliceContents.length, indexes[i][2], testName + " data size");
|
||||
checkFileContents(slice, sliceContents, testName);
|
||||
}
|
||||
|
||||
// Slice of slice
|
||||
var slice = file.mozSlice(0, 40000);
|
||||
checkFileContents(slice.mozSlice(5000, 42000), contents.slice(5000, 40000), "file slice slice");
|
||||
|
||||
// ...of slice of slice
|
||||
slice = slice.mozSlice(5000, 42000).mozSlice(400, 700);
|
||||
gc();
|
||||
checkFileContents(slice, contents.slice(5400, 5700), "file slice slice slice");
|
||||
}
|
||||
|
||||
testFile(memFile, size, "image/png", fileData, "memFile");
|
||||
testFile(fileFile, size, "", fileData, "fileFile");
|
||||
testSlice(memFile, size, "image/png", fileData, "memFile");
|
||||
testSlice(fileFile, size, "", fileData, "fileFile");
|
||||
|
||||
|
||||
// Try loading directly from slice into an image
|
||||
|
@ -209,192 +133,6 @@ img.src = URL.createObjectURL(imgfile.mozSlice(testBinaryData.length, testBinary
|
|||
img.onload = imageLoadHandler;
|
||||
expectedTestCount++;
|
||||
|
||||
|
||||
// Utility functions
|
||||
function checkFileContents(file, contents, test) {
|
||||
SimpleTest.requestLongerTimeout(2);
|
||||
|
||||
// Load file using FileReader
|
||||
var r = new FileReader();
|
||||
r.onload = getFileReaderLoadHandler(contents, contents.length, "FileReader.readAsBinaryString of " + test);
|
||||
r.readAsBinaryString(file);
|
||||
expectedTestCount++;
|
||||
|
||||
// Load file using URL.createObjectURL and XMLHttpRequest
|
||||
var xhr = new XMLHttpRequest;
|
||||
xhr.open("GET", URL.createObjectURL(file));
|
||||
xhr.onload = getXHRLoadHandler(contents, contents.length, false,
|
||||
"XMLHttpRequest load of " + test);
|
||||
xhr.overrideMimeType('text/plain; charset=x-user-defined');
|
||||
xhr.send();
|
||||
expectedTestCount++;
|
||||
|
||||
// Send file to server using FormData and XMLHttpRequest
|
||||
xhr = new XMLHttpRequest();
|
||||
xhr.onload = function(event) {
|
||||
checkMPSubmission(JSON.parse(event.target.responseText),
|
||||
[{ name: "hello", value: "world"},
|
||||
{ name: "myfile",
|
||||
value: contents,
|
||||
fileName: file.name || "",
|
||||
contentType: file.type || "application/octet-stream" }]);
|
||||
testHasRun();
|
||||
}
|
||||
xhr.open("POST", "../../html/content/test/form_submit_server.sjs");
|
||||
var fd = new FormData;
|
||||
fd.append("hello", "world");
|
||||
fd.append("myfile", file);
|
||||
xhr.send(fd);
|
||||
expectedTestCount++;
|
||||
|
||||
// Send file to server using plain XMLHttpRequest
|
||||
var xhr = new XMLHttpRequest;
|
||||
xhr.open("POST", "file_XHRSendData.sjs");
|
||||
xhr.onload = function (event) {
|
||||
is(event.target.getResponseHeader("Result-Content-Type"),
|
||||
file.type ? file.type : null,
|
||||
"request content-type in XMLHttpRequest send of " + test);
|
||||
is(event.target.getResponseHeader("Result-Content-Length"),
|
||||
file.size,
|
||||
"request content-length in XMLHttpRequest send of " + test);
|
||||
};
|
||||
xhr.addEventListener("load",
|
||||
getXHRLoadHandler(contents, contents.length, true,
|
||||
"XMLHttpRequest send of " + test),
|
||||
false);
|
||||
xhr.overrideMimeType('text/plain; charset=x-user-defined');
|
||||
xhr.send(file);
|
||||
expectedTestCount++;
|
||||
}
|
||||
|
||||
function getFileReaderLoadHandler(expectedResult, expectedLength, testName) {
|
||||
return function (event) {
|
||||
is(event.target.readyState, FileReader.DONE,
|
||||
"[FileReader] readyState in test " + testName);
|
||||
is(event.target.error, null,
|
||||
"[FileReader] no error in test " + testName);
|
||||
// Do not use |is(event.target.result, expectedResult, "...");| that may output raw binary data.
|
||||
is(event.target.result.length, expectedResult.length,
|
||||
"[FileReader] Length of result in test " + testName);
|
||||
ok(event.target.result == expectedResult,
|
||||
"[FileReader] Content of result in test " + testName);
|
||||
is(event.lengthComputable, true,
|
||||
"[FileReader] lengthComputable in test " + testName);
|
||||
is(event.loaded, expectedLength,
|
||||
"[FileReader] Loaded length in test " + testName);
|
||||
is(event.total, expectedLength,
|
||||
"[FileReader] Total length in test " + testName);
|
||||
testHasRun();
|
||||
}
|
||||
}
|
||||
|
||||
function getXHRLoadHandler(expectedResult, expectedLength, statusWorking, testName) {
|
||||
return function (event) {
|
||||
is(event.target.readyState, 4,
|
||||
"[XHR] readyState in test " + testName);
|
||||
if (statusWorking) {
|
||||
is(event.target.status, 200,
|
||||
"[XHR] no error in test " + testName);
|
||||
}
|
||||
else {
|
||||
todo(event.target.status, 200,
|
||||
"[XHR] no error in test " + testName);
|
||||
}
|
||||
// Do not use |is(convertXHRBinary(event.target.responseText), expectedResult, "...");| that may output raw binary data.
|
||||
var convertedData = convertXHRBinary(event.target.responseText);
|
||||
is(convertedData.length, expectedResult.length,
|
||||
"[XHR] Length of result in test " + testName);
|
||||
ok(convertedData == expectedResult,
|
||||
"[XHR] Content of result in test " + testName);
|
||||
is(event.lengthComputable, true,
|
||||
"[XHR] lengthComputable in test " + testName);
|
||||
is(event.loaded, expectedLength,
|
||||
"[XHR] Loaded length in test " + testName);
|
||||
is(event.total, expectedLength,
|
||||
"[XHR] Total length in test " + testName);
|
||||
|
||||
testHasRun();
|
||||
}
|
||||
}
|
||||
|
||||
function convertXHRBinary(s) {
|
||||
var res = "";
|
||||
for (var i = 0; i < s.length; ++i) {
|
||||
res += String.fromCharCode(s.charCodeAt(i) & 255);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function testHasRun() {
|
||||
//alert(testRanCounter);
|
||||
++testRanCounter;
|
||||
if (testRanCounter == expectedTestCount) {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
function createFileWithData(fileData) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
var dirSvc = Components.classes["@mozilla.org/file/directory_service;1"].getService(Components.interfaces.nsIProperties);
|
||||
var testFile = dirSvc.get("ProfD", Components.interfaces.nsIFile);
|
||||
testFile.append("fileAPItestfile2-" + fileNum);
|
||||
fileNum++;
|
||||
var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
|
||||
outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
|
||||
0666, 0);
|
||||
outStream.write(fileData, fileData.length);
|
||||
outStream.close();
|
||||
|
||||
var fileList = document.getElementById('fileList');
|
||||
fileList.value = testFile.path;
|
||||
|
||||
return fileList.files[0];
|
||||
}
|
||||
|
||||
function gc() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindowUtils)
|
||||
.garbageCollect();
|
||||
}
|
||||
|
||||
function checkMPSubmission(sub, expected) {
|
||||
function getPropCount(o) {
|
||||
var x, l = 0;
|
||||
for (x in o) ++l;
|
||||
return l;
|
||||
}
|
||||
|
||||
is(sub.length, expected.length,
|
||||
"Correct number of items");
|
||||
var i;
|
||||
for (i = 0; i < expected.length; ++i) {
|
||||
if (!("fileName" in expected[i])) {
|
||||
is(sub[i].headers["Content-Disposition"],
|
||||
"form-data; name=\"" + expected[i].name + "\"",
|
||||
"Correct name (A)");
|
||||
is (getPropCount(sub[i].headers), 1,
|
||||
"Wrong number of headers (A)");
|
||||
}
|
||||
else {
|
||||
is(sub[i].headers["Content-Disposition"],
|
||||
"form-data; name=\"" + expected[i].name + "\"; filename=\"" +
|
||||
expected[i].fileName + "\"",
|
||||
"Correct name (B)");
|
||||
is(sub[i].headers["Content-Type"],
|
||||
expected[i].contentType,
|
||||
"Correct content type (B)");
|
||||
is (getPropCount(sub[i].headers), 2,
|
||||
"Wrong number of headers (B)");
|
||||
}
|
||||
// Do not use |is(sub[i].body, expected[i].value, "...");| that may output raw binary data.
|
||||
is(sub[i].body.length, expected[i].value.length,
|
||||
"Length of correct value");
|
||||
ok(sub[i].body == expected[i].value,
|
||||
"Content of correct value");
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body> </html>
|
||||
|
|
|
@ -1331,6 +1331,8 @@ static nsDOMClassInfoData sClassInfoData[] = {
|
|||
|
||||
NS_DEFINE_CLASSINFO_DATA(XULCommandEvent, nsDOMGenericSH,
|
||||
DOM_DEFAULT_SCRIPTABLE_FLAGS)
|
||||
NS_DEFINE_CLASSINFO_DATA(MozBlobBuilder, nsDOMGenericSH,
|
||||
DOM_DEFAULT_SCRIPTABLE_FLAGS)
|
||||
|
||||
NS_DEFINE_CLASSINFO_DATA(CommandEvent, nsDOMGenericSH,
|
||||
DOM_DEFAULT_SCRIPTABLE_FLAGS)
|
||||
|
@ -1573,6 +1575,7 @@ static const nsConstructorFuncMapData kConstructorFuncMap[] =
|
|||
NS_DEFINE_CONSTRUCTOR_FUNC_DATA(Worker, nsDOMWorker::NewWorker)
|
||||
NS_DEFINE_CONSTRUCTOR_FUNC_DATA(ChromeWorker, nsDOMWorker::NewChromeWorker)
|
||||
NS_DEFINE_CONSTRUCTOR_FUNC_DATA(File, nsDOMFile::NewFile)
|
||||
NS_DEFINE_CONSTRUCTOR_FUNC_DATA(MozBlobBuilder, NS_NewBlobBuilder)
|
||||
};
|
||||
|
||||
nsIXPConnect *nsDOMClassInfo::sXPConnect = nsnull;
|
||||
|
@ -3999,12 +4002,10 @@ nsDOMClassInfo::Init()
|
|||
|
||||
DOM_CLASSINFO_MAP_BEGIN(Blob, nsIDOMBlob)
|
||||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMBlob)
|
||||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMBlob_MOZILLA_2_0_BRANCH)
|
||||
DOM_CLASSINFO_MAP_END
|
||||
|
||||
DOM_CLASSINFO_MAP_BEGIN(File, nsIDOMFile)
|
||||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMBlob)
|
||||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMBlob_MOZILLA_2_0_BRANCH)
|
||||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMFile)
|
||||
DOM_CLASSINFO_MAP_END
|
||||
|
||||
|
@ -4029,6 +4030,10 @@ nsDOMClassInfo::Init()
|
|||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMDOMStringMap)
|
||||
DOM_CLASSINFO_MAP_END
|
||||
|
||||
DOM_CLASSINFO_MAP_BEGIN(MozBlobBuilder, nsIDOMBlobBuilder)
|
||||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMBlobBuilder)
|
||||
DOM_CLASSINFO_MAP_END
|
||||
|
||||
DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(ModalContentWindow, nsIDOMWindow)
|
||||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindow)
|
||||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMJSWindow)
|
||||
|
|
|
@ -406,6 +406,7 @@ DOMCI_CLASS(FileException)
|
|||
DOMCI_CLASS(FileError)
|
||||
DOMCI_CLASS(FileReader)
|
||||
DOMCI_CLASS(MozURLProperty)
|
||||
DOMCI_CLASS(MozBlobBuilder)
|
||||
|
||||
DOMCI_CLASS(DOMStringMap)
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче