Commit 49f88e1f authored by sigbjornf@opera.com's avatar sigbjornf@opera.com

Sync Blob.close() behavior wrt updated spec.

The File API now defines Blobs as having a readability state of
opened/closed, http://dev.w3.org/2006/webapi/FileAPI/#readabilityState
With close() naturally making the Blob enter the 'closed' state,

  http://dev.w3.org/2006/webapi/FileAPI/#close-method

Like the FileReader operations, Blob methods will now throw
InvalidStateError when passed a Blob in such a 'closed' state.

Update the implementation to follow the spec.

R=kinuko@chromium.org
BUG=344820

Review URL: https://codereview.chromium.org/184473002

git-svn-id: svn://svn.chromium.org/blink/trunk@168567 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent 6226a4c1
...@@ -12,6 +12,8 @@ PASS blob.close(); blob.size is 0 ...@@ -12,6 +12,8 @@ PASS blob.close(); blob.size is 0
PASS blob.type is "text/plain" PASS blob.type is "text/plain"
PASS sliced1.size is 3 PASS sliced1.size is 3
PASS sliced2.size is 1 PASS sliced2.size is 1
PASS blob.slice(2) threw exception InvalidStateError: Failed to execute 'slice' on 'Blob': Blob has been closed..
PASS blob.close() threw exception InvalidStateError: Failed to execute 'close' on 'Blob': Blob has been closed..
PASS successfullyParsed is true PASS successfullyParsed is true
TEST COMPLETE TEST COMPLETE
......
...@@ -17,8 +17,7 @@ PASS Error triggered on starting Worker from a closed Blob. ...@@ -17,8 +17,7 @@ PASS Error triggered on starting Worker from a closed Blob.
PASS Error triggered on starting Worker from a closed Blob. PASS Error triggered on starting Worker from a closed Blob.
PASS Error triggered on starting Worker from a closed Blob. PASS Error triggered on starting Worker from a closed Blob.
Test creating object URLs on closed Blobs Test creating object URLs on closed Blobs
PASS blobURL1 !== blobURL2 is true PASS window.URL.createObjectURL(blob); threw exception InvalidStateError: Failed to execute 'createObjectURL' on 'URL': Blob has been closed..
PASS blobURL2.length > 0 is true
PASS successfullyParsed is true PASS successfullyParsed is true
TEST COMPLETE TEST COMPLETE
......
...@@ -91,24 +91,19 @@ function testRevokeAfterClose() ...@@ -91,24 +91,19 @@ function testRevokeAfterClose()
img.src = blobURL1; img.src = blobURL1;
} }
function testRegisterAfterClose() function testCreateAfterClose()
{ {
debug("Test creating object URLs on closed Blobs"); debug("Test creating object URLs on closed Blobs");
blob = new Blob(["body{background: green}"], {type: "text/css"}); blob = new Blob(["body{background: green}"], {type: "text/css"});
blobURL1 = window.URL.createObjectURL(blob);
blob.close(); blob.close();
// Verifies createObjectURL() still works and returns a valid (non-empty) URL shouldThrow("window.URL.createObjectURL(blob);");
// without throwing an error. See http://crbug.com/344820 for this requirement.
blobURL2 = window.URL.createObjectURL(blob);
shouldBeTrue("blobURL1 !== blobURL2");
shouldBeTrue("blobURL2.length > 0");
runNextTest(); runNextTest();
} }
var tests = [ var tests = [
testRevokeAfterClose, testRevokeAfterClose,
testRevokeAfterCloseWorkers, testRevokeAfterCloseWorkers,
testRegisterAfterClose ]; testCreateAfterClose ];
function runNextTest() function runNextTest()
{ {
......
...@@ -19,4 +19,6 @@ shouldBeEqualToString("blob.type", "text/plain"); ...@@ -19,4 +19,6 @@ shouldBeEqualToString("blob.type", "text/plain");
shouldBe("sliced1.size", "3"); shouldBe("sliced1.size", "3");
var sliced2 = sliced1.slice(2); var sliced2 = sliced1.slice(2);
shouldBe("sliced2.size", "1"); shouldBe("sliced2.size", "1");
shouldThrow("blob.slice(2)");
shouldThrow("blob.close()");
</script> </script>
...@@ -63,10 +63,14 @@ void DOMURL::setInput(const String& value) ...@@ -63,10 +63,14 @@ void DOMURL::setInput(const String& value)
} }
} }
String DOMURL::createObjectURL(ExecutionContext* executionContext, Blob* blob) String DOMURL::createObjectURL(ExecutionContext* executionContext, Blob* blob, ExceptionState& exceptionState)
{ {
if (!executionContext || !blob) if (!executionContext || !blob)
return String(); return String();
if (blob->hasBeenClosed()) {
exceptionState.throwDOMException(InvalidStateError, String(blob->isFile() ? "File" : "Blob") + " has been closed.");
return String();
}
return createPublicURL(executionContext, blob, blob->uuid()); return createPublicURL(executionContext, blob, blob->uuid());
} }
......
...@@ -59,7 +59,7 @@ public: ...@@ -59,7 +59,7 @@ public:
return adoptRefWillBeNoop(new DOMURL(url, base->m_url, exceptionState)); return adoptRefWillBeNoop(new DOMURL(url, base->m_url, exceptionState));
} }
static String createObjectURL(ExecutionContext*, Blob*); static String createObjectURL(ExecutionContext*, Blob*, ExceptionState&);
static void revokeObjectURL(ExecutionContext*, const String&); static void revokeObjectURL(ExecutionContext*, const String&);
static String createPublicURL(ExecutionContext*, URLRegistrable*, const String& uuid = String()); static String createPublicURL(ExecutionContext*, URLRegistrable*, const String& uuid = String());
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
ImplementedAs=DOMURL, ImplementedAs=DOMURL,
WillBeGarbageCollected WillBeGarbageCollected
] interface URL { ] interface URL {
[CallWith=ExecutionContext,TreatReturnedNullStringAs=Null] static DOMString createObjectURL(Blob? blob); [RaisesException, CallWith=ExecutionContext,TreatReturnedNullStringAs=Null] static DOMString createObjectURL(Blob? blob);
[CallWith=ExecutionContext] static void revokeObjectURL(DOMString url); [CallWith=ExecutionContext] static void revokeObjectURL(DOMString url);
}; };
......
...@@ -31,7 +31,9 @@ ...@@ -31,7 +31,9 @@
#include "config.h" #include "config.h"
#include "core/fileapi/Blob.h" #include "core/fileapi/Blob.h"
#include "bindings/v8/ExceptionState.h"
#include "core/dom/DOMURL.h" #include "core/dom/DOMURL.h"
#include "core/dom/ExceptionCode.h"
#include "core/dom/ExecutionContext.h" #include "core/dom/ExecutionContext.h"
#include "core/fileapi/File.h" #include "core/fileapi/File.h"
#include "platform/blob/BlobRegistry.h" #include "platform/blob/BlobRegistry.h"
...@@ -104,8 +106,13 @@ void Blob::clampSliceOffsets(long long size, long long& start, long long& end) ...@@ -104,8 +106,13 @@ void Blob::clampSliceOffsets(long long size, long long& start, long long& end)
end = size; end = size;
} }
PassRefPtrWillBeRawPtr<Blob> Blob::slice(long long start, long long end, const String& contentType) const PassRefPtrWillBeRawPtr<Blob> Blob::slice(long long start, long long end, const String& contentType, ExceptionState& exceptionState) const
{ {
if (hasBeenClosed()) {
exceptionState.throwDOMException(InvalidStateError, "Blob has been closed.");
return nullptr;
}
long long size = this->size(); long long size = this->size();
clampSliceOffsets(size, start, end); clampSliceOffsets(size, start, end);
...@@ -116,23 +123,26 @@ PassRefPtrWillBeRawPtr<Blob> Blob::slice(long long start, long long end, const S ...@@ -116,23 +123,26 @@ PassRefPtrWillBeRawPtr<Blob> Blob::slice(long long start, long long end, const S
return Blob::create(BlobDataHandle::create(blobData.release(), length)); return Blob::create(BlobDataHandle::create(blobData.release(), length));
} }
void Blob::close(ExecutionContext* executionContext) void Blob::close(ExecutionContext* executionContext, ExceptionState& exceptionState)
{ {
if (!hasBeenClosed()) { if (hasBeenClosed()) {
// Dereferencing a Blob that has been closed should result in exceptionState.throwDOMException(InvalidStateError, "Blob has been closed.");
// a network error. Revoke URLs registered against it through return;
// its UUID.
DOMURL::revokeObjectUUID(executionContext, uuid());
// A closed Blob should have size zero, which most consumers
// will treat as an empty Blob. The exception being the FileReader
// read operations which will throw.
// FIXME: spec not yet set in stone in this regard, track updates to it (http://crbug.com/344820.)
OwnPtr<BlobData> blobData = BlobData::create();
blobData->setContentType(type());
m_blobDataHandle = BlobDataHandle::create(blobData.release(), 0);
m_hasBeenClosed = true;
} }
// Dereferencing a Blob that has been closed should result in
// a network error. Revoke URLs registered against it through
// its UUID.
DOMURL::revokeObjectUUID(executionContext, uuid());
// A Blob enters a 'readability state' of closed, where it will report its
// size as zero. Blob and FileReader operations now throws on
// being passed a Blob in that state. Downstream uses of closed Blobs
// (e.g., XHR.send()) consider them as empty.
OwnPtr<BlobData> blobData = BlobData::create();
blobData->setContentType(type());
m_blobDataHandle = BlobDataHandle::create(blobData.release(), 0);
m_hasBeenClosed = true;
} }
void Blob::appendTo(BlobData& blobData) const void Blob::appendTo(BlobData& blobData) const
......
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
namespace WebCore { namespace WebCore {
class ExceptionState;
class ExecutionContext; class ExecutionContext;
class Blob : public RefCountedWillBeGarbageCollectedFinalized<Blob>, public ScriptWrappable, public URLRegistrable { class Blob : public RefCountedWillBeGarbageCollectedFinalized<Blob>, public ScriptWrappable, public URLRegistrable {
...@@ -59,8 +60,23 @@ public: ...@@ -59,8 +60,23 @@ public:
virtual ~Blob(); virtual ~Blob();
virtual unsigned long long size() const { return m_blobDataHandle->size(); } virtual unsigned long long size() const { return m_blobDataHandle->size(); }
virtual PassRefPtrWillBeRawPtr<Blob> slice(long long start = 0, long long end = std::numeric_limits<long long>::max(), const String& contentType = String()) const; virtual PassRefPtrWillBeRawPtr<Blob> slice(long long start, long long end, const String& contentType, ExceptionState&) const;
virtual void close(ExecutionContext*);
// To allow ExceptionState to be passed in last, manually enumerate the optional argument overloads.
PassRefPtrWillBeRawPtr<Blob> slice(ExceptionState& exceptionState) const
{
return slice(0, std::numeric_limits<long long>::max(), String(), exceptionState);
}
PassRefPtrWillBeRawPtr<Blob> slice(long long start, ExceptionState& exceptionState) const
{
return slice(start, std::numeric_limits<long long>::max(), String(), exceptionState);
}
PassRefPtrWillBeRawPtr<Blob> slice(long long start, long long end, ExceptionState& exceptionState) const
{
return slice(start, end, String(), exceptionState);
}
virtual void close(ExecutionContext*, ExceptionState&);
String type() const { return m_blobDataHandle->type(); } String type() const { return m_blobDataHandle->type(); }
String uuid() const { return m_blobDataHandle->uuid(); } String uuid() const { return m_blobDataHandle->uuid(); }
......
...@@ -38,7 +38,7 @@ ...@@ -38,7 +38,7 @@
readonly attribute unsigned long long size; readonly attribute unsigned long long size;
readonly attribute DOMString type; readonly attribute DOMString type;
Blob slice(optional long long start, optional long long end, [TreatNullAs=NullString, TreatUndefinedAs=NullString] optional DOMString contentType); [RaisesException] Blob slice(optional long long start, optional long long end, [TreatNullAs=NullString, TreatUndefinedAs=NullString] optional DOMString contentType);
[CallWith=ExecutionContext, RuntimeEnabled=FileAPIBlobClose] void close(); [RaisesException, CallWith=ExecutionContext, RuntimeEnabled=FileAPIBlobClose] void close();
}; };
...@@ -26,6 +26,8 @@ ...@@ -26,6 +26,8 @@
#include "config.h" #include "config.h"
#include "core/fileapi/File.h" #include "core/fileapi/File.h"
#include "bindings/v8/ExceptionState.h"
#include "core/dom/ExceptionCode.h"
#include "platform/FileMetadata.h" #include "platform/FileMetadata.h"
#include "platform/MIMETypeRegistry.h" #include "platform/MIMETypeRegistry.h"
#include "public/platform/Platform.h" #include "public/platform/Platform.h"
...@@ -181,10 +183,15 @@ unsigned long long File::size() const ...@@ -181,10 +183,15 @@ unsigned long long File::size() const
return static_cast<unsigned long long>(size); return static_cast<unsigned long long>(size);
} }
PassRefPtrWillBeRawPtr<Blob> File::slice(long long start, long long end, const String& contentType) const PassRefPtrWillBeRawPtr<Blob> File::slice(long long start, long long end, const String& contentType, ExceptionState& exceptionState) const
{ {
if (hasBeenClosed()) {
exceptionState.throwDOMException(InvalidStateError, "File has been closed.");
return nullptr;
}
if (!m_hasBackingFile) if (!m_hasBackingFile)
return Blob::slice(start, end, contentType); return Blob::slice(start, end, contentType, exceptionState);
// FIXME: This involves synchronous file operation. We need to figure out how to make it asynchronous. // FIXME: This involves synchronous file operation. We need to figure out how to make it asynchronous.
long long size; long long size;
...@@ -225,19 +232,22 @@ void File::captureSnapshot(long long& snapshotSize, double& snapshotModification ...@@ -225,19 +232,22 @@ void File::captureSnapshot(long long& snapshotSize, double& snapshotModification
snapshotModificationTime = metadata.modificationTime; snapshotModificationTime = metadata.modificationTime;
} }
void File::close(ExecutionContext* executionContext) void File::close(ExecutionContext* executionContext, ExceptionState& exceptionState)
{ {
if (!hasBeenClosed()) { if (hasBeenClosed()) {
// Reset the File to its closed representation, an empty exceptionState.throwDOMException(InvalidStateError, "Blob has been closed.");
// Blob. The name isn't cleared, as it should still be return;
// available.
m_hasBackingFile = false;
m_path = String();
m_fileSystemURL = KURL();
invalidateSnapshotMetadata();
m_relativePath = String();
Blob::close(executionContext);
} }
// Reset the File to its closed representation, an empty
// Blob. The name isn't cleared, as it should still be
// available.
m_hasBackingFile = false;
m_path = String();
m_fileSystemURL = KURL();
invalidateSnapshotMetadata();
m_relativePath = String();
Blob::close(executionContext, exceptionState);
} }
void File::appendTo(BlobData& blobData) const void File::appendTo(BlobData& blobData) const
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
namespace WebCore { namespace WebCore {
class ExceptionState;
class ExecutionContext; class ExecutionContext;
struct FileMetadata; struct FileMetadata;
class KURL; class KURL;
...@@ -88,8 +89,8 @@ public: ...@@ -88,8 +89,8 @@ public:
} }
virtual unsigned long long size() const OVERRIDE; virtual unsigned long long size() const OVERRIDE;
virtual PassRefPtrWillBeRawPtr<Blob> slice(long long start = 0, long long end = std::numeric_limits<long long>::max(), const String& contentType = String()) const OVERRIDE; virtual PassRefPtrWillBeRawPtr<Blob> slice(long long start, long long end, const String& contentType, ExceptionState&) const OVERRIDE;
virtual void close(ExecutionContext*) OVERRIDE; virtual void close(ExecutionContext*, ExceptionState&) OVERRIDE;
virtual bool isFile() const OVERRIDE { return true; } virtual bool isFile() const OVERRIDE { return true; }
virtual bool hasBackingFile() const OVERRIDE { return m_hasBackingFile; } virtual bool hasBackingFile() const OVERRIDE { return m_hasBackingFile; }
......
...@@ -471,7 +471,7 @@ bool FileContentRequest::didGetEntry(Entry* entry) ...@@ -471,7 +471,7 @@ bool FileContentRequest::didGetEntry(Entry* entry)
bool FileContentRequest::didGetFile(File* file) bool FileContentRequest::didGetFile(File* file)
{ {
RefPtrWillBeRawPtr<Blob> blob = file->slice(m_start, m_end); RefPtrWillBeRawPtr<Blob> blob = static_cast<Blob*>(file)->slice(m_start, m_end, IGNORE_EXCEPTION);
m_reader->setOnload(this); m_reader->setOnload(this);
m_reader->setOnerror(this); m_reader->setOnerror(this);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment