Source/WebCore: Blob constructor accepts a sequence (array-like object) as first...
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 14 Nov 2013 05:46:10 +0000 (05:46 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 14 Nov 2013 05:46:10 +0000 (05:46 +0000)
https://bugs.webkit.org/show_bug.cgi?id=124175

Patch by Victor Costan <costan@gmail.com> on 2013-11-13
Reviewed by Christophe Dumez.

Added test cases to fast/files/script-tests/blob-constructor.js.

* bindings/js/JSBlobCustom.cpp: Make the constructor work with sequences.
(WebCore::JSBlobConstructor::constructJSBlob):
* bindings/js/JSDOMBinding.h:
(WebCore::toJSSequence): Slightly better error message when conversion fails.
(WebCore::toJS): Whitespace.
(WebCore::jsArray): Whitespace.

LayoutTests: Blob constructor accepts a sequence (array-like object) as first arg.
https://bugs.webkit.org/show_bug.cgi?id=124175

Patch by Victor Costan <costan@gmail.com> on 2013-11-13
Reviewed by Christophe Dumez.

* crypto/subtle/argument-conversion-expected.txt: Updated sequence error expectations.
* fast/dom/Window/window-postmessage-args-expected.txt: Updated sequence error expectations.
* fast/events/constructors/message-event-constructor-expected.txt: Updated sequence error expectations.
* fast/events/message-port-multi-expected.txt: Updated sequence error expectations.
* fast/files/blob-constructor-expected.txt: Updated error text and added expectations.
* fast/files/script-tests/blob-constructor.js: Added sequence test cases.
* fast/workers/worker-context-multi-port-expected.txt: Updated sequence error expectations.
* fast/workers/worker-multi-port-expected.txt: Updated sequence error expectations.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@159275 268f45cc-cd09-0410-ab3c-d52691b4dbfc

12 files changed:
LayoutTests/ChangeLog
LayoutTests/crypto/subtle/argument-conversion-expected.txt
LayoutTests/fast/dom/Window/window-postmessage-args-expected.txt
LayoutTests/fast/events/constructors/message-event-constructor-expected.txt
LayoutTests/fast/events/message-port-multi-expected.txt
LayoutTests/fast/files/blob-constructor-expected.txt
LayoutTests/fast/files/script-tests/blob-constructor.js
LayoutTests/fast/workers/worker-context-multi-port-expected.txt
LayoutTests/fast/workers/worker-multi-port-expected.txt
Source/WebCore/ChangeLog
Source/WebCore/bindings/js/JSBlobCustom.cpp
Source/WebCore/bindings/js/JSDOMBinding.h

index d45bd0e..9507dac 100644 (file)
@@ -1,3 +1,20 @@
+2013-11-13  Victor Costan  <costan@gmail.com>
+
+        Blob constructor accepts a sequence (array-like object) as first arg.
+        https://bugs.webkit.org/show_bug.cgi?id=124175
+
+        Reviewed by Christophe Dumez.
+
+
+        * crypto/subtle/argument-conversion-expected.txt: Updated sequence error expectations.
+        * fast/dom/Window/window-postmessage-args-expected.txt: Updated sequence error expectations.
+        * fast/events/constructors/message-event-constructor-expected.txt: Updated sequence error expectations.
+        * fast/events/message-port-multi-expected.txt: Updated sequence error expectations.
+        * fast/files/blob-constructor-expected.txt: Updated error text and added expectations.
+        * fast/files/script-tests/blob-constructor.js: Added sequence test cases.
+        * fast/workers/worker-context-multi-port-expected.txt: Updated sequence error expectations.
+        * fast/workers/worker-multi-port-expected.txt: Updated sequence error expectations.
+
 2013-11-13  Sun-woo Nam  <sunny.nam@samsung.com>
 
         [EFL] Layout tests with css3 selectors3 properties need to be rebaselined.
index 6163e1b..aa618f7 100644 (file)
@@ -17,8 +17,8 @@ PASS ...succeeded
 
 Passing invalid data to digest()
 PASS crypto.subtle.digest({name: 'sha-1'}) threw exception TypeError: Not enough arguments.
-PASS crypto.subtle.digest({name: 'sha-1'}, null) threw exception TypeError: Type error.
-PASS crypto.subtle.digest({name: 'sha-1'}, 10) threw exception TypeError: Type error.
+PASS crypto.subtle.digest({name: 'sha-1'}, null) threw exception TypeError: Value is not a sequence.
+PASS crypto.subtle.digest({name: 'sha-1'}, 10) threw exception TypeError: Value is not a sequence.
 PASS crypto.subtle.digest({name: 'sha-1'}, [10]) threw exception TypeError: Only ArrayBuffer and ArrayBufferView objects can be part of CryptoOperationData sequence.
 PASS crypto.subtle.digest({name: 'sha-1'}, new Uint8Array([0])) threw exception TypeError: Only ArrayBuffer and ArrayBufferView objects can be part of CryptoOperationData sequence.
 
index 5d99d1a..040d628 100644 (file)
@@ -1,8 +1,8 @@
 Test that the second argument of window.postMessage is ignored or triggers an error if it is not a message port. You should see PASS message '1' through '7', followed by 'done', with messages 4-7 received below.
 
-PASS: Posting message ('1', 1): threw exception TypeError: Type error
-PASS: Posting message ('2', c): threw exception TypeError: Type error
-PASS: Posting message ('3', [object Object]): threw exception TypeError: Type error
+PASS: Posting message ('1', 1): threw exception TypeError: Value is not a sequence
+PASS: Posting message ('2', c): threw exception TypeError: Value is not a sequence
+PASS: Posting message ('3', [object Object]): threw exception TypeError: Value is not a sequence
 PASS: Posting message ('4', [object Window]) did not throw an exception
 PASS: Posting message ('4a', *) did not throw an exception
 PASS: Posting message ('5', null) did not throw an exception
index d38aa8f..97d4102 100644 (file)
@@ -83,18 +83,18 @@ PASS new MessageEvent('eventType', { ports: [] }).ports is []
 PASS new MessageEvent('eventType', { ports: undefined }).ports is []
 PASS new MessageEvent('eventType', { ports: null }).ports is []
 PASS new MessageEvent('eventType', { ports: [1, 2, 3] }).ports[2] threw exception TypeError: Type error.
-PASS new MessageEvent('eventType', { ports: test_object }).ports threw exception TypeError: Type error.
-PASS new MessageEvent('eventType', { ports: document }).ports threw exception TypeError: Type error.
-PASS new MessageEvent('eventType', { ports: false }).ports threw exception TypeError: Type error.
-PASS new MessageEvent('eventType', { ports: true }).ports threw exception TypeError: Type error.
-PASS new MessageEvent('eventType', { ports: '' }).ports threw exception TypeError: Type error.
-PASS new MessageEvent('eventType', { ports: 'chocolate' }).ports threw exception TypeError: Type error.
-PASS new MessageEvent('eventType', { ports: 12345 }).ports threw exception TypeError: Type error.
-PASS new MessageEvent('eventType', { ports: 18446744073709551615 }).ports threw exception TypeError: Type error.
-PASS new MessageEvent('eventType', { ports: NaN }).ports threw exception TypeError: Type error.
-PASS new MessageEvent('eventType', { get ports() { return 123; } }).ports threw exception TypeError: Type error.
+PASS new MessageEvent('eventType', { ports: test_object }).ports threw exception TypeError: Value is not a sequence.
+PASS new MessageEvent('eventType', { ports: document }).ports threw exception TypeError: Value is not a sequence.
+PASS new MessageEvent('eventType', { ports: false }).ports threw exception TypeError: Value is not a sequence.
+PASS new MessageEvent('eventType', { ports: true }).ports threw exception TypeError: Value is not a sequence.
+PASS new MessageEvent('eventType', { ports: '' }).ports threw exception TypeError: Value is not a sequence.
+PASS new MessageEvent('eventType', { ports: 'chocolate' }).ports threw exception TypeError: Value is not a sequence.
+PASS new MessageEvent('eventType', { ports: 12345 }).ports threw exception TypeError: Value is not a sequence.
+PASS new MessageEvent('eventType', { ports: 18446744073709551615 }).ports threw exception TypeError: Value is not a sequence.
+PASS new MessageEvent('eventType', { ports: NaN }).ports threw exception TypeError: Value is not a sequence.
+PASS new MessageEvent('eventType', { get ports() { return 123; } }).ports threw exception TypeError: Value is not a sequence.
 PASS new MessageEvent('eventType', { get ports() { throw 'MessageEvent Error'; } }) threw exception MessageEvent Error.
-PASS new MessageEvent('eventType', { ports: {valueOf: function () { return [channel.port1, channel.port2, channel.port2]; } } }).ports[0] threw exception TypeError: Type error.
+PASS new MessageEvent('eventType', { ports: {valueOf: function () { return [channel.port1, channel.port2, channel.port2]; } } }).ports[0] threw exception TypeError: Value is not a sequence.
 PASS new MessageEvent('eventType', { bubbles: true, cancelable: true, data: test_object, origin: 'wonderful', lastEventId: 'excellent', source: window, ports: [channel.port1, channel.port2, channel2.port1] }).bubbles is true
 PASS new MessageEvent('eventType', { bubbles: true, cancelable: true, data: test_object, origin: 'wonderful', lastEventId: 'excellent', source: window, ports: [channel.port1, channel.port2, channel2.port1] }).cancelable is true
 PASS new MessageEvent('eventType', { bubbles: true, cancelable: true, data: test_object, origin: 'wonderful', lastEventId: 'excellent', source: window, ports: [channel.port1, channel.port2, channel2.port1] }).data is test_object
index f7b5d5a..2115051 100644 (file)
@@ -8,7 +8,7 @@ PASS channel.port1.postMessage("entangled port", [channel.port2]) threw exceptio
 PASS channel.port1.postMessage("null port", [channel3.port1, null, channel3.port2]) threw exception Error: InvalidStateError: DOM Exception 11.
 PASS channel.port1.postMessage("notAPort", [channel3.port1, {}, channel3.port2]) threw exception TypeError: Type error.
 PASS channel.port1.postMessage("duplicate port", [channel3.port1, channel3.port1]) threw exception Error: InvalidStateError: DOM Exception 11.
-PASS channel.port1.postMessage("notAnArray", channel3.port1) threw exception TypeError: Type error.
+PASS channel.port1.postMessage("notAnArray", channel3.port1) threw exception TypeError: Value is not a sequence.
 PASS channel.port1.postMessage("notASequence", [{length: 3}]) threw exception TypeError: Type error.
 PASS channel.port1.postMessage("largeSequence", largePortArray) threw exception Error: InvalidStateError: DOM Exception 11.
 PASS event.ports is non-null and zero length when no port sent
index 6797413..8743503 100644 (file)
@@ -10,8 +10,8 @@ PASS (new Blob(['hello'], {})) instanceof window.Blob is true
 PASS (new Blob(['hello'], {type:'text/html'})) instanceof window.Blob is true
 PASS (new Blob(['hello'], {type:'text/html', endings:'native'})) instanceof window.Blob is true
 PASS (new Blob(['hello'], {type:'text/html', endings:'transparent'})) instanceof window.Blob is true
-PASS new Blob('hello') threw exception TypeError: First argument of the constructor is not of type Array.
-PASS new Blob(0) threw exception TypeError: First argument of the constructor is not of type Array.
+PASS new Blob('hello') threw exception TypeError: Value is not a sequence.
+PASS new Blob(0) threw exception TypeError: Value is not a sequence.
 PASS (new Blob([])) instanceof window.Blob is true
 PASS (new Blob(['stringPrimitive'])) instanceof window.Blob is true
 PASS (new Blob([String('stringObject')])) instanceof window.Blob is true
@@ -72,6 +72,12 @@ PASS new Blob([(new Float32Array(100)).buffer]).size is 400
 PASS new Blob([(new Float64Array(100)).buffer]).size is 800
 PASS new Blob([(new Float64Array(100)).buffer, (new Int32Array(100)).buffer, (new Uint8Array(100)).buffer, (new DataView(new ArrayBuffer(100))).buffer]).size is 1400
 PASS new Blob([new Blob([(new Int32Array(100)).buffer]), (new Uint8Array(100)).buffer, (new Float32Array(100)).buffer, (new DataView(new ArrayBuffer(100))).buffer]).size is 1000
+PASS new Blob({length: 0}) instanceof window.Blob is true
+PASS new Blob({length: 0}).size is 0
+PASS new Blob({length: 1, 0: 'string'}).size is 6
+PASS new Blob({length: 2, 0: new Uint8Array(100), 1: new Int16Array(100)}).size is 300
+PASS new Blob({length: 1, 0: 'string'}, {type: 'text/html'}).type is 'text/html'
+PASS new Blob({length: 0}, {endings:'illegal'}) threw exception TypeError: The endings property must be either "transparent" or "native".
 PASS successfullyParsed is true
 
 TEST COMPLETE
index cf2f598..5a329ae 100644 (file)
@@ -10,8 +10,8 @@ shouldBeTrue("(new Blob(['hello'], {type:'text/html', endings:'native'})) instan
 shouldBeTrue("(new Blob(['hello'], {type:'text/html', endings:'transparent'})) instanceof window.Blob");
 
 // Test invalid blob parts
-shouldThrow("new Blob('hello')", "'TypeError: First argument of the constructor is not of type Array'");
-shouldThrow("new Blob(0)", "'TypeError: First argument of the constructor is not of type Array'");
+shouldThrow("new Blob('hello')", "'TypeError: Value is not a sequence'");
+shouldThrow("new Blob(0)", "'TypeError: Value is not a sequence'");
 
 // Test valid blob parts.
 shouldBeTrue("(new Blob([])) instanceof window.Blob");
@@ -98,3 +98,11 @@ shouldBe("new Blob([(new Float32Array(100)).buffer]).size", "400");
 shouldBe("new Blob([(new Float64Array(100)).buffer]).size", "800");
 shouldBe("new Blob([(new Float64Array(100)).buffer, (new Int32Array(100)).buffer, (new Uint8Array(100)).buffer, (new DataView(new ArrayBuffer(100))).buffer]).size", "1400");
 shouldBe("new Blob([new Blob([(new Int32Array(100)).buffer]), (new Uint8Array(100)).buffer, (new Float32Array(100)).buffer, (new DataView(new ArrayBuffer(100))).buffer]).size", "1000");
+
+// Test passing blob parts in sequences.
+shouldBeTrue("new Blob({length: 0}) instanceof window.Blob");
+shouldBe("new Blob({length: 0}).size", "0");
+shouldBe("new Blob({length: 1, 0: 'string'}).size", "6");
+shouldBe("new Blob({length: 2, 0: new Uint8Array(100), 1: new Int16Array(100)}).size", "300");
+shouldBe("new Blob({length: 1, 0: 'string'}, {type: 'text/html'}).type", "'text/html'");
+shouldThrow("new Blob({length: 0}, {endings:'illegal'})", "'TypeError: The endings property must be either \"transparent\" or \"native\"'");
index a74f9c9..25fe772 100644 (file)
@@ -9,7 +9,7 @@ PASS event.ports contains two ports when two ports sent
 PASS posting a null port did throw: Error: InvalidStateError: DOM Exception 11
 PASS posting a non-port did throw: TypeError: Type error
 PASS event.ports contains two ports when two ports re-sent after error
-PASS posting a non-array did throw: TypeError: Type error
+PASS posting a non-array did throw: TypeError: Value is not a sequence
 PASS posting a non-sequence did throw: TypeError: Type error
 
 TEST COMPLETE
index b20111c..92e191a 100644 (file)
@@ -5,7 +5,7 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 
 PASS worker.postMessage("null port", [channel3.port1, null, channel3.port2]) threw exception Error: InvalidStateError: DOM Exception 11.
 PASS worker.postMessage("notAPort", [channel3.port1, {}, channel3.port2]) threw exception TypeError: Type error.
-PASS worker.postMessage("notAnArray", channel3.port1) threw exception TypeError: Type error.
+PASS worker.postMessage("notAnArray", channel3.port1) threw exception TypeError: Value is not a sequence.
 PASS worker.postMessage("notASequence", [{length: 3}]) threw exception TypeError: Type error.
 PASS event.ports is non-null and zero length when no port sent
 PASS event.ports is non-null and zero length when empty array sent
index 4826759..6ac5874 100644 (file)
@@ -1,3 +1,19 @@
+2013-11-13  Victor Costan  <costan@gmail.com>
+
+        Blob constructor accepts a sequence (array-like object) as first arg. 
+        https://bugs.webkit.org/show_bug.cgi?id=124175
+
+        Reviewed by Christophe Dumez.
+
+        Added test cases to fast/files/script-tests/blob-constructor.js.
+
+        * bindings/js/JSBlobCustom.cpp: Make the constructor work with sequences.
+        (WebCore::JSBlobConstructor::constructJSBlob):
+        * bindings/js/JSDOMBinding.h:
+        (WebCore::toJSSequence): Slightly better error message when conversion fails.
+        (WebCore::toJS): Whitespace.
+        (WebCore::jsArray): Whitespace.
+
 2013-11-13  Joseph Pecoraro  <pecoraro@apple.com>
 
         Web Inspector: InspectorBackendDispatcher improvements
index 6076849..72ad961 100644 (file)
@@ -71,9 +71,11 @@ EncodedJSValue JSC_HOST_CALL JSBlobConstructor::constructJSBlob(ExecState* exec)
         return JSValue::encode(CREATE_DOM_WRAPPER(exec, jsConstructor->globalObject(), Blob, blob.get()));
     }
 
-    JSValue firstArg = exec->argument(0);
-    if (!isJSArray(firstArg))
-        return throwVMError(exec, createTypeError(exec, "First argument of the constructor is not of type Array"));
+    unsigned blobPartsLength = 0;
+    JSObject* blobParts = toJSSequence(exec, exec->argument(0), blobPartsLength);
+    if (exec->hadException())
+        return JSValue::encode(jsUndefined());
+    ASSERT(blobParts);
 
     String type;
     String endings = ASCIILiteral("transparent");
@@ -110,11 +112,8 @@ EncodedJSValue JSC_HOST_CALL JSBlobConstructor::constructJSBlob(ExecState* exec)
 
     BlobBuilder blobBuilder;
 
-    JSArray* array = asArray(firstArg);
-    unsigned length = array->length();
-
-    for (unsigned i = 0; i < length; ++i) {
-        JSValue item = array->getIndex(exec, i);
+    for (unsigned i = 0; i < blobPartsLength; ++i) {
+        JSValue item = blobParts->get(exec, i);
 #if ENABLE(BLOB)
         if (item.inherits(JSArrayBuffer::info()))
             blobBuilder.append(toArrayBuffer(item));
index 984fc92..6db8004 100644 (file)
@@ -119,7 +119,7 @@ inline JSC::WeakHandleOwner* wrapperOwner(DOMWrapperWorld& world, JSC::ArrayBuff
 {
     return static_cast<WebCoreTypedArrayController*>(world.vm()->m_typedArrayController.get())->wrapperOwner();
 }
-    
+
 inline void* wrapperContext(DOMWrapperWorld& world, JSC::ArrayBuffer*)
 {
     return &world;
@@ -197,7 +197,7 @@ template <typename DOMClass, typename WrapperClass> inline void uncacheWrapper(D
         return;
     weakRemove(world.m_wrappers, (void*)domObject, wrapper);
 }
-    
+
 #define CREATE_DOM_WRAPPER(exec, globalObject, className, object) createWrapper<JS##className>(exec, globalObject, static_cast<className*>(object))
 template<class WrapperClass, class DOMClass> inline JSDOMWrapper* createWrapper(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, DOMClass* node)
 {
@@ -251,7 +251,7 @@ inline JSC::JSValue jsStringWithCache(JSC::ExecState* exec, const AtomicString&
 {
     return jsStringWithCache(exec, s.string());
 }
-        
+
 JSC::JSValue jsStringOrNull(JSC::ExecState*, const String&); // null if the string is null
 JSC::JSValue jsStringOrNull(JSC::ExecState*, const URL&); // null if the URL is null
 
@@ -325,7 +325,7 @@ inline JSC::JSObject* toJSSequence(JSC::ExecState* exec, JSC::JSValue value, uns
 {
     JSC::JSObject* object = value.getObject();
     if (!object) {
-        throwTypeError(exec);
+        throwVMError(exec, createTypeError(exec, "Value is not a sequence"));
         return 0;
     }
 
@@ -334,7 +334,7 @@ inline JSC::JSObject* toJSSequence(JSC::ExecState* exec, JSC::JSValue value, uns
         return 0;
 
     if (lengthValue.isUndefinedOrNull()) {
-        throwTypeError(exec);
+        throwVMError(exec, createTypeError(exec, "Value is not a sequence"));
         return 0;
     }
 
@@ -374,10 +374,10 @@ template <typename T>
 inline JSC::JSValue toJS(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, Vector<T> vector)
 {
     JSC::JSArray* array = constructEmptyArray(exec, 0, vector.size());
-    
+
     for (size_t i = 0; i < vector.size(); ++i)
         array->putDirectIndex(exec, i, toJS(exec, globalObject, vector[i]));
-    
+
     return array;
 }
 
@@ -385,10 +385,10 @@ template <typename T>
 inline JSC::JSValue toJS(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, Vector<RefPtr<T>> vector)
 {
     JSC::JSArray* array = constructEmptyArray(exec, 0, vector.size());
-    
+
     for (size_t i = 0; i < vector.size(); ++i)
         array->putDirectIndex(exec, i, toJS(exec, globalObject, vector[i].get()));
-    
+
     return array;
 }
 
@@ -428,7 +428,7 @@ template <typename T, size_t inlineCapacity>
 JSC::JSValue jsArray(JSC::ExecState* exec, JSDOMGlobalObject* globalObject, const Vector<T, inlineCapacity>& iterator)
 {
     JSC::MarkedArgumentBuffer list;
-    typename Vector<T, inlineCapacity>::const_iterator end = iterator.end();        
+    typename Vector<T, inlineCapacity>::const_iterator end = iterator.end();
     typedef JSValueTraits<T> TraitsType;
 
     for (typename Vector<T, inlineCapacity>::const_iterator iter = iterator.begin(); iter != end; ++iter)