Commit 531e9e74 authored by yutak@chromium.org's avatar yutak@chromium.org

WebSocket.send() should accept ArrayBufferView

https://bugs.webkit.org/show_bug.cgi?id=90877

Reviewed by Kent Tamura.

Source/WebCore:

Accept ArrayBufferView as an argument of WebSocket.send() to comply with recent change
in specification: <http://html5.org/tools/web-apps-tracker?from=7084&to=7085>.

Once WebSocket.send(ArrayBuffer) was removed from the specification, but added back
in <http://html5.org/tools/web-apps-tracker?from=7202&to=7203>. Thus the functionality
of send(ArrayBuffer) is kept as-is.

Tests: http/tests/websocket/tests/hybi/send-arraybufferview.html
       http/tests/websocket/tests/hybi/workers/send-arraybufferview.html

* Modules/websockets/ThreadableWebSocketChannel.h:
Change the signature of send() to receive offset and length so the clients can send
subrange of an ArrayBuffer.
* Modules/websockets/WebSocket.cpp:
(WebCore::WebSocket::send):
WebSocket.send(ArrayBufferView) is added, which puts the sub region of the given
ArrayBuffer into the outgoing frame queue.
* Modules/websockets/WebSocket.h:
* Modules/websockets/WebSocket.idl:
* Modules/websockets/WebSocketChannel.cpp:
(WebCore::WebSocketChannel::send):
* Modules/websockets/WebSocketChannel.h:
(WebSocketChannel):
* Modules/websockets/WorkerThreadableWebSocketChannel.cpp:
(WebCore::WorkerThreadableWebSocketChannel::send):
(WebCore::WorkerThreadableWebSocketChannel::Peer::send):
(WebCore::WorkerThreadableWebSocketChannel::Bridge::send):
Send only necessary part of data to the main thread.
* Modules/websockets/WorkerThreadableWebSocketChannel.h:
(WorkerThreadableWebSocketChannel):
(Bridge):
* bindings/js/JSWebSocketCustom.cpp:
(WebCore::JSWebSocket::send): Accept ArrayBufferView in send().
* bindings/v8/custom/V8WebSocketCustom.cpp:
(WebCore::V8WebSocket::sendCallback): Ditto.

Source/WebKit/chromium:

* src/WebSocketImpl.cpp:
(WebKit::WebSocketImpl::sendArrayBuffer):
Apply change in function signature of WebSocketChannel::send().

LayoutTests:

Two tests are added to check whether WebSocket.send(ArrayBufferView) works in the
main thread and in a worker.

* http/tests/websocket/tests/hybi/send-arraybufferview-expected.txt: Added.
* http/tests/websocket/tests/hybi/send-arraybufferview.html: Added.
* http/tests/websocket/tests/hybi/workers/resources/send-arraybufferview.js: Added.
* http/tests/websocket/tests/hybi/workers/send-arraybufferview-expected.txt: Added.
* http/tests/websocket/tests/hybi/workers/send-arraybufferview.html: Added.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@124846 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent f8de83c1
2012-08-06 Yuta Kitamura <yutak@chromium.org>
WebSocket.send() should accept ArrayBufferView
https://bugs.webkit.org/show_bug.cgi?id=90877
Reviewed by Kent Tamura.
Two tests are added to check whether WebSocket.send(ArrayBufferView) works in the
main thread and in a worker.
* http/tests/websocket/tests/hybi/send-arraybufferview-expected.txt: Added.
* http/tests/websocket/tests/hybi/send-arraybufferview.html: Added.
* http/tests/websocket/tests/hybi/workers/resources/send-arraybufferview.js: Added.
* http/tests/websocket/tests/hybi/workers/send-arraybufferview-expected.txt: Added.
* http/tests/websocket/tests/hybi/workers/send-arraybufferview.html: Added.
2012-08-06 Roger Fong <roger_fong@apple.com>
LayoutTest: fast/forms/input-set-composition-scroll.html is failing on Windows.
WebSocket: Send ArrayBufferViews.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS PASS: Message #0.
PASS PASS: Message #1.
PASS PASS: Message #2.
PASS closeEvent.wasClean is true
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE html>
<html>
<head>
<script src="../../../../js-test-resources/js-test-pre.js"></script>
</head>
<body>
<div id="description"></div>
<div id="console"></div>
<script type="text/javascript">
description("WebSocket: Send ArrayBufferViews.");
window.jsTestIsAsync = true;
function startsWith(target, prefix)
{
return target.indexOf(prefix) === 0;
}
function createArrayBufferViewContainingHelloWorld()
{
var hello = "Hello, world!";
var array = new Uint8Array(hello.length);
for (var i = 0; i < hello.length; ++i)
array[i] = hello.charCodeAt(i);
return array;
}
function createEmptyArrayBufferView()
{
return new Uint8Array(0);
}
function createArrayBufferViewContainingAllDistinctBytes()
{
// Return a slice of ArrayBuffer.
var buffer = new ArrayBuffer(1000);
var array = new Uint8Array(buffer, 123, 256);
for (var i = 0; i < 256; ++i)
array[i] = i;
return array;
}
var url = "ws://127.0.0.1:8880/websocket/tests/hybi/check-binary-messages";
var ws = new WebSocket(url);
var closeEvent;
ws.onopen = function()
{
ws.send(createArrayBufferViewContainingHelloWorld());
ws.send(createEmptyArrayBufferView());
ws.send(createArrayBufferViewContainingAllDistinctBytes());
};
ws.onmessage = function(event)
{
var message = event.data;
if (startsWith(message, "PASS"))
testPassed(message);
else
testFailed(message);
};
ws.onclose = function(event)
{
closeEvent = event;
shouldBeTrue("closeEvent.wasClean");
finishJSTest();
};
</script>
<script src="../../../../js-test-resources/js-test-post.js"></script>
</body>
</html>
function createArrayBufferViewContainingHelloWorld()
{
var hello = "Hello, world!";
var array = new Uint8Array(hello.length);
for (var i = 0; i < hello.length; ++i)
array[i] = hello.charCodeAt(i);
return array;
}
function createEmptyArrayBufferView()
{
return new Uint8Array(0);
}
function createArrayBufferViewContainingAllDistinctBytes()
{
// Return a slice of ArrayBuffer.
var buffer = new ArrayBuffer(1000);
var array = new Uint8Array(buffer, 123, 256);
for (var i = 0; i < 256; ++i)
array[i] = i;
return array;
}
var url = "ws://127.0.0.1:8880/websocket/tests/hybi/workers/resources/check-binary-messages";
var ws = new WebSocket(url);
ws.onopen = function()
{
ws.send(createArrayBufferViewContainingHelloWorld());
ws.send(createEmptyArrayBufferView());
ws.send(createArrayBufferViewContainingAllDistinctBytes());
};
ws.onmessage = function(event)
{
postMessage(event.data);
};
ws.onclose = function(closeEvent)
{
if (closeEvent.wasClean === true)
postMessage("PASS: closeEvent.wasClean is true.");
else
postMessage("FAIL: closeEvent.wasClean should be true, but was: " + closeEvent.wasClean);
postMessage("DONE");
};
WebSocket: Send ArrayBufferViews in Web Workers.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS PASS: Message #0.
PASS PASS: Message #1.
PASS PASS: Message #2.
PASS PASS: closeEvent.wasClean is true.
DONE
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE html>
<html>
<head>
<script src="../../../../../js-test-resources/js-test-pre.js"></script>
</head>
<body>
<div id="description"></div>
<div id="console"></div>
<script type="text/javascript">
description("WebSocket: Send ArrayBufferViews in Web Workers.");
window.jsTestIsAsync = true;
function startsWith(str, prefix)
{
return str.indexOf(prefix) == 0;
}
var worker = new Worker("resources/send-arraybufferview.js");
worker.onmessage = function (event)
{
var message = event.data;
if (startsWith(message, "PASS"))
testPassed(message);
else if (startsWith(message, "FAIL"))
testFailed(message)
else
debug(message);
if (message === "DONE")
finishJSTest();
};
</script>
<script src="../../../../../js-test-resources/js-test-post.js"></script>
</body>
</html>
2012-08-06 Yuta Kitamura <yutak@chromium.org>
WebSocket.send() should accept ArrayBufferView
https://bugs.webkit.org/show_bug.cgi?id=90877
Reviewed by Kent Tamura.
Accept ArrayBufferView as an argument of WebSocket.send() to comply with recent change
in specification: <http://html5.org/tools/web-apps-tracker?from=7084&to=7085>.
Once WebSocket.send(ArrayBuffer) was removed from the specification, but added back
in <http://html5.org/tools/web-apps-tracker?from=7202&to=7203>. Thus the functionality
of send(ArrayBuffer) is kept as-is.
Tests: http/tests/websocket/tests/hybi/send-arraybufferview.html
http/tests/websocket/tests/hybi/workers/send-arraybufferview.html
* Modules/websockets/ThreadableWebSocketChannel.h:
Change the signature of send() to receive offset and length so the clients can send
subrange of an ArrayBuffer.
* Modules/websockets/WebSocket.cpp:
(WebCore::WebSocket::send):
WebSocket.send(ArrayBufferView) is added, which puts the sub region of the given
ArrayBuffer into the outgoing frame queue.
* Modules/websockets/WebSocket.h:
* Modules/websockets/WebSocket.idl:
* Modules/websockets/WebSocketChannel.cpp:
(WebCore::WebSocketChannel::send):
* Modules/websockets/WebSocketChannel.h:
(WebSocketChannel):
* Modules/websockets/WorkerThreadableWebSocketChannel.cpp:
(WebCore::WorkerThreadableWebSocketChannel::send):
(WebCore::WorkerThreadableWebSocketChannel::Peer::send):
(WebCore::WorkerThreadableWebSocketChannel::Bridge::send):
Send only necessary part of data to the main thread.
* Modules/websockets/WorkerThreadableWebSocketChannel.h:
(WorkerThreadableWebSocketChannel):
(Bridge):
* bindings/js/JSWebSocketCustom.cpp:
(WebCore::JSWebSocket::send): Accept ArrayBufferView in send().
* bindings/v8/custom/V8WebSocketCustom.cpp:
(WebCore::V8WebSocket::sendCallback): Ditto.
2012-08-06 James Robinson <jamesr@chromium.org>
[chromium] Add gyp variable to control which compositor target is depended on
......@@ -60,7 +60,7 @@ public:
virtual String subprotocol() = 0; // Will be available after didConnect() callback is invoked.
virtual String extensions() = 0; // Will be available after didConnect() callback is invoked.
virtual SendResult send(const String& message) = 0;
virtual SendResult send(const ArrayBuffer&) = 0;
virtual SendResult send(const ArrayBuffer&, unsigned byteOffset, unsigned byteLength) = 0;
virtual SendResult send(const Blob&) = 0;
virtual unsigned long bufferedAmount() const = 0;
virtual void close(int code, const String& reason) = 0;
......
......@@ -51,6 +51,8 @@
#include "SecurityOrigin.h"
#include "ThreadableWebSocketChannel.h"
#include "WebSocketChannel.h"
#include <wtf/ArrayBuffer.h>
#include <wtf/ArrayBufferView.h>
#include <wtf/HashSet.h>
#include <wtf/OwnPtr.h>
#include <wtf/PassOwnPtr.h>
......@@ -289,7 +291,26 @@ bool WebSocket::send(ArrayBuffer* binaryData, ExceptionCode& ec)
return false;
}
ASSERT(m_channel);
return m_channel->send(*binaryData) == ThreadableWebSocketChannel::SendSuccess;
return m_channel->send(*binaryData, 0, binaryData->byteLength()) == ThreadableWebSocketChannel::SendSuccess;
}
bool WebSocket::send(ArrayBufferView* arrayBufferView, ExceptionCode& ec)
{
LOG(Network, "WebSocket %p send arraybufferview %p", this, arrayBufferView);
ASSERT(arrayBufferView);
if (m_state == CONNECTING) {
ec = INVALID_STATE_ERR;
return false;
}
if (m_state == CLOSING || m_state == CLOSED) {
unsigned payloadSize = arrayBufferView->byteLength();
m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, payloadSize);
m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, getFramingOverhead(payloadSize));
return false;
}
ASSERT(m_channel);
RefPtr<ArrayBuffer> arrayBuffer(arrayBufferView->buffer());
return m_channel->send(*arrayBuffer, arrayBufferView->byteOffset(), arrayBufferView->byteLength()) == ThreadableWebSocketChannel::SendSuccess;
}
bool WebSocket::send(Blob* binaryData, ExceptionCode& ec)
......
......@@ -71,6 +71,7 @@ public:
bool send(const String& message, ExceptionCode&);
bool send(ArrayBuffer*, ExceptionCode&);
bool send(ArrayBufferView*, ExceptionCode&);
bool send(Blob*, ExceptionCode&);
void close(int code, const String& reason, ExceptionCode&);
......
......@@ -69,6 +69,7 @@ module websockets {
// booleans, null, undefined and objects except ArrayBuffer and Blob). Current code
// generator does not handle this rule correctly.
// boolean send(in ArrayBuffer data) raises(DOMException);
// boolean send(in ArrayBufferView data) raises(DOMException);
// boolean send(in Blob data) raises(DOMException);
// boolean send(in DOMString data) raises(DOMException);
[Custom] boolean send(in DOMString data) raises(DOMException);
......
......@@ -153,10 +153,10 @@ ThreadableWebSocketChannel::SendResult WebSocketChannel::send(const String& mess
return ThreadableWebSocketChannel::SendSuccess;
}
ThreadableWebSocketChannel::SendResult WebSocketChannel::send(const ArrayBuffer& binaryData)
ThreadableWebSocketChannel::SendResult WebSocketChannel::send(const ArrayBuffer& binaryData, unsigned byteOffset, unsigned byteLength)
{
LOG(Network, "WebSocketChannel %p send arraybuffer %p", this, &binaryData);
enqueueRawFrame(WebSocketFrame::OpCodeBinary, static_cast<const char*>(binaryData.data()), binaryData.byteLength());
LOG(Network, "WebSocketChannel %p send arraybuffer %p %u %u", this, &binaryData, byteOffset, byteLength);
enqueueRawFrame(WebSocketFrame::OpCodeBinary, static_cast<const char*>(binaryData.data()) + byteOffset, byteLength);
return ThreadableWebSocketChannel::SendSuccess;
}
......
......@@ -72,7 +72,7 @@ public:
virtual String subprotocol() OVERRIDE;
virtual String extensions() OVERRIDE;
virtual ThreadableWebSocketChannel::SendResult send(const String& message) OVERRIDE;
virtual ThreadableWebSocketChannel::SendResult send(const ArrayBuffer&) OVERRIDE;
virtual ThreadableWebSocketChannel::SendResult send(const ArrayBuffer&, unsigned byteOffset, unsigned byteLength) OVERRIDE;
virtual ThreadableWebSocketChannel::SendResult send(const Blob&) OVERRIDE;
virtual unsigned long bufferedAmount() const OVERRIDE;
virtual void close(int code, const String& reason) OVERRIDE; // Start closing handshake.
......
......@@ -91,11 +91,11 @@ ThreadableWebSocketChannel::SendResult WorkerThreadableWebSocketChannel::send(co
return m_bridge->send(message);
}
ThreadableWebSocketChannel::SendResult WorkerThreadableWebSocketChannel::send(const ArrayBuffer& binaryData)
ThreadableWebSocketChannel::SendResult WorkerThreadableWebSocketChannel::send(const ArrayBuffer& binaryData, unsigned byteOffset, unsigned byteLength)
{
if (!m_bridge)
return ThreadableWebSocketChannel::SendFail;
return m_bridge->send(binaryData);
return m_bridge->send(binaryData, byteOffset, byteLength);
}
ThreadableWebSocketChannel::SendResult WorkerThreadableWebSocketChannel::send(const Blob& binaryData)
......@@ -188,7 +188,7 @@ void WorkerThreadableWebSocketChannel::Peer::send(const ArrayBuffer& binaryData)
ASSERT(isMainThread());
if (!m_mainWebSocketChannel || !m_workerClientWrapper)
return;
ThreadableWebSocketChannel::SendResult sendRequestResult = m_mainWebSocketChannel->send(binaryData);
ThreadableWebSocketChannel::SendResult sendRequestResult = m_mainWebSocketChannel->send(binaryData, 0, binaryData.byteLength());
m_loaderProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidSend, m_workerClientWrapper, sendRequestResult), m_taskMode);
}
......@@ -488,14 +488,14 @@ ThreadableWebSocketChannel::SendResult WorkerThreadableWebSocketChannel::Bridge:
return clientWrapper->sendRequestResult();
}
ThreadableWebSocketChannel::SendResult WorkerThreadableWebSocketChannel::Bridge::send(const ArrayBuffer& binaryData)
ThreadableWebSocketChannel::SendResult WorkerThreadableWebSocketChannel::Bridge::send(const ArrayBuffer& binaryData, unsigned byteOffset, unsigned byteLength)
{
if (!m_workerClientWrapper || !m_peer)
return ThreadableWebSocketChannel::SendFail;
// ArrayBuffer isn't thread-safe, hence the content of ArrayBuffer is copied into Vector<char>.
OwnPtr<Vector<char> > data = adoptPtr(new Vector<char>(binaryData.byteLength()));
OwnPtr<Vector<char> > data = adoptPtr(new Vector<char>(byteLength));
if (binaryData.byteLength())
memcpy(data->data(), binaryData.data(), binaryData.byteLength());
memcpy(data->data(), static_cast<const char*>(binaryData.data()) + byteOffset, byteLength);
setMethodNotCompleted();
m_loaderProxy.postTaskToLoader(createCallbackTask(&WorkerThreadableWebSocketChannel::mainThreadSendArrayBuffer, AllowCrossThreadAccess(m_peer), data.release()));
RefPtr<Bridge> protect(this);
......
......@@ -66,7 +66,7 @@ public:
virtual String subprotocol() OVERRIDE;
virtual String extensions() OVERRIDE;
virtual ThreadableWebSocketChannel::SendResult send(const String& message) OVERRIDE;
virtual ThreadableWebSocketChannel::SendResult send(const ArrayBuffer&) OVERRIDE;
virtual ThreadableWebSocketChannel::SendResult send(const ArrayBuffer&, unsigned byteOffset, unsigned byteLength) OVERRIDE;
virtual ThreadableWebSocketChannel::SendResult send(const Blob&) OVERRIDE;
virtual unsigned long bufferedAmount() const OVERRIDE;
virtual void close(int code, const String& reason) OVERRIDE;
......@@ -134,7 +134,7 @@ private:
void initialize();
void connect(const KURL&, const String& protocol);
ThreadableWebSocketChannel::SendResult send(const String& message);
ThreadableWebSocketChannel::SendResult send(const ArrayBuffer&);
ThreadableWebSocketChannel::SendResult send(const ArrayBuffer&, unsigned byteOffset, unsigned byteLength);
ThreadableWebSocketChannel::SendResult send(const Blob&);
unsigned long bufferedAmount();
void close(int code, const String& reason);
......
......@@ -37,6 +37,7 @@
#include "ExceptionCode.h"
#include "JSArrayBuffer.h"
#include "JSArrayBufferView.h"
#include "JSBlob.h"
#include "JSEventListener.h"
#include "KURL.h"
......@@ -100,6 +101,8 @@ JSValue JSWebSocket::send(ExecState* exec)
bool result;
if (message.inherits(&JSArrayBuffer::s_info))
result = impl()->send(toArrayBuffer(message), ec);
else if (message.inherits(&JSArrayBufferView::s_info))
result = impl()->send(toArrayBufferView(message), ec);
else if (message.inherits(&JSBlob::s_info))
result = impl()->send(toBlob(message), ec);
else {
......
......@@ -38,6 +38,7 @@
#include "Frame.h"
#include "Settings.h"
#include "V8ArrayBuffer.h"
#include "V8ArrayBufferView.h"
#include "V8Binding.h"
#include "V8Blob.h"
#include "V8Proxy.h"
......@@ -127,6 +128,10 @@ v8::Handle<v8::Value> V8WebSocket::sendCallback(const v8::Arguments& args)
ArrayBuffer* arrayBuffer = V8ArrayBuffer::toNative(v8::Handle<v8::Object>::Cast(message));
ASSERT(arrayBuffer);
result = webSocket->send(arrayBuffer, ec);
} else if (V8ArrayBufferView::HasInstance(message)) {
ArrayBufferView* arrayBufferView = V8ArrayBufferView::toNative(v8::Handle<v8::Object>::Cast(message));
ASSERT(arrayBufferView);
result = webSocket->send(arrayBufferView, ec);
} else if (V8Blob::HasInstance(message)) {
Blob* blob = V8Blob::toNative(v8::Handle<v8::Object>::Cast(message));
ASSERT(blob);
......
2012-08-06 Yuta Kitamura <yutak@chromium.org>
WebSocket.send() should accept ArrayBufferView
https://bugs.webkit.org/show_bug.cgi?id=90877
Reviewed by Kent Tamura.
* src/WebSocketImpl.cpp:
(WebKit::WebSocketImpl::sendArrayBuffer):
Apply change in function signature of WebSocketChannel::send().
2012-08-06 James Robinson <jamesr@chromium.org>
[chromium] Remove lingering unwrap<>() calls in GraphicsLayerChromium.cpp
......
......@@ -126,7 +126,7 @@ bool WebSocketImpl::sendText(const WebString& message)
bool WebSocketImpl::sendArrayBuffer(const WebArrayBuffer& webArrayBuffer)
{
#if ENABLE(WEB_SOCKETS)
return m_private->send(*PassRefPtr<ArrayBuffer>(webArrayBuffer)) == ThreadableWebSocketChannel::SendSuccess;
return m_private->send(*PassRefPtr<ArrayBuffer>(webArrayBuffer), 0, webArrayBuffer.byteLength()) == ThreadableWebSocketChannel::SendSuccess;
#else
ASSERT_NOT_REACHED();
#endif
......
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