Commit 4d0c313f authored by yutak@chromium.org's avatar yutak@chromium.org

WebSocket: Implement "protocol" attribute

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

Reviewed by Kent Tamura.

Source/WebCore:

Tests: http/tests/websocket/tests/hybi/no-subprotocol.html (added)
       http/tests/websocket/tests/hybi/set-protocol.html (added)
       http/tests/websocket/tests/hybi/workers/no-subprotocol.html (added)
       http/tests/websocket/tests/hixie76/undefined-attributes.html (updated)
       http/tests/websocket/tests/hybi/multiple-subprotocols.html (updated)
       http/tests/websocket/tests/hybi/workers/multiple-subprotocols.html (updated)

* websockets/ThreadableWebSocketChannel.h:
Added subprotocol() function. This function is named differently from the counterpart of
WebSocket class, because the name "protocol" can be confused with the WebSocket protocol.
Added m_useHixie76Protocol and m_subprotocol, because these value may be used after
m_channel has been released. Using bool should be fine, because boolean literals do not
appear in ambiguous context.
* websockets/ThreadableWebSocketChannelClientWrapper.cpp:
(WebCore::ThreadableWebSocketChannelClientWrapper::ThreadableWebSocketChannelClientWrapper):
(WebCore::ThreadableWebSocketChannelClientWrapper::subprotocol):
(WebCore::ThreadableWebSocketChannelClientWrapper::setSubprotocol):
* websockets/ThreadableWebSocketChannelClientWrapper.h:
* websockets/WebSocket.cpp:
(WebCore::WebSocket::WebSocket):
(WebCore::WebSocket::connect):
(WebCore::WebSocket::protocol):
The "protocol" attribute is available only when the hybi-10 protocol is chosen.
(WebCore::WebSocket::binaryType):
(WebCore::WebSocket::setBinaryType):
(WebCore::WebSocket::didConnect):
* websockets/WebSocket.h:
* websockets/WebSocket.idl:
* websockets/WebSocketChannel.cpp:
(WebCore::WebSocketChannel::subprotocol):
* websockets/WebSocketChannel.h:
* websockets/WorkerThreadableWebSocketChannel.cpp:
(WebCore::WorkerThreadableWebSocketChannel::subprotocol):
(WebCore::workerContextDidConnect):
Subprotocol value is saved in the client wrapper object after the WebSocket connection is
established.
(WebCore::WorkerThreadableWebSocketChannel::Peer::didConnect):
* websockets/WorkerThreadableWebSocketChannel.h:

LayoutTests:

* http/tests/websocket/tests/hixie76/undefined-attributes-expected.txt:
* http/tests/websocket/tests/hixie76/undefined-attributes.html:
Added a test to check "protocol" attribute.
* http/tests/websocket/tests/hybi/multiple-subprotocols-expected.txt:
* http/tests/websocket/tests/hybi/multiple-subprotocols.html:
The attribute should return an empty string at first. After the connection is established,
it should return the value of Sec-WebSocket-Protocol header in the server's handshake response.
* http/tests/websocket/tests/hybi/no-subprotocol-expected.txt: Added.
* http/tests/websocket/tests/hybi/no-subprotocol.html: Added.
* http/tests/websocket/tests/hybi/set-protocol-expected.txt: Added.
* http/tests/websocket/tests/hybi/set-protocol.html: Added.
* http/tests/websocket/tests/hybi/workers/multiple-subprotocols-expected.txt:
* http/tests/websocket/tests/hybi/workers/no-subprotocol-expected.txt: Added.
* http/tests/websocket/tests/hybi/workers/no-subprotocol.html: Added.
* http/tests/websocket/tests/hybi/workers/resources/multiple-subprotocols.js:
(ws.onopen):
(ws.onclose):
(checkAfterOnClose):
* http/tests/websocket/tests/hybi/workers/resources/no-subprotocol.js: Added.
(ws.onopen):
(ws.onclose):
(checkAfterOnClose):


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@92946 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 41f15933
2011-08-11 Yuta Kitamura <yutak@chromium.org>
WebSocket: Implement "protocol" attribute
https://bugs.webkit.org/show_bug.cgi?id=65248
Reviewed by Kent Tamura.
* http/tests/websocket/tests/hixie76/undefined-attributes-expected.txt:
* http/tests/websocket/tests/hixie76/undefined-attributes.html:
Added a test to check "protocol" attribute.
* http/tests/websocket/tests/hybi/multiple-subprotocols-expected.txt:
* http/tests/websocket/tests/hybi/multiple-subprotocols.html:
The attribute should return an empty string at first. After the connection is established,
it should return the value of Sec-WebSocket-Protocol header in the server's handshake response.
* http/tests/websocket/tests/hybi/no-subprotocol-expected.txt: Added.
* http/tests/websocket/tests/hybi/no-subprotocol.html: Added.
* http/tests/websocket/tests/hybi/set-protocol-expected.txt: Added.
* http/tests/websocket/tests/hybi/set-protocol.html: Added.
* http/tests/websocket/tests/hybi/workers/multiple-subprotocols-expected.txt:
* http/tests/websocket/tests/hybi/workers/no-subprotocol-expected.txt: Added.
* http/tests/websocket/tests/hybi/workers/no-subprotocol.html: Added.
* http/tests/websocket/tests/hybi/workers/resources/multiple-subprotocols.js:
(ws.onopen):
(ws.onclose):
(checkAfterOnClose):
* http/tests/websocket/tests/hybi/workers/resources/no-subprotocol.js: Added.
(ws.onopen):
(ws.onclose):
(checkAfterOnClose):
2011-08-11 MORITA Hajime <morrita@google.com>
[Chromium][DRT] editing/spelling/spelling-backspace-between-lines.html fails
......@@ -3,6 +3,7 @@ Test WebSocket attributes which should not be defined when hixie-76 protocol is
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS ws.binaryType is undefined.
PASS ws.protocol is undefined.
PASS successfullyParsed is true
TEST COMPLETE
......
......@@ -13,6 +13,7 @@ description("Test WebSocket attributes which should not be defined when hixie-76
var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/hixie76/simple");
shouldBeUndefined("ws.binaryType");
shouldBeUndefined("ws.protocol");
var successfullyParsed = true;
</script>
......
......@@ -2,11 +2,16 @@ Test WebSocket subprotocol negotiation.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS ws.protocol is ""
Connected
PASS ws.protocol is "superchat"
Received: superchat
PASS receivedMessage is "superchat"
Closed
PASS ws.protocol is "superchat"
PASS closeEvent.wasClean is true
Exited onclose handler
PASS ws.protocol is "superchat"
PASS successfullyParsed is true
TEST COMPLETE
......
......@@ -18,14 +18,12 @@ var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/hybi/protocol-test?p
var closeEvent;
var receivedMessage;
// FIXME: Implement WebSocket.protocol.
// shouldBeEqualToString("ws.protocol", "");
shouldBeEqualToString("ws.protocol", "");
ws.onopen = function()
{
debug("Connected");
// FIXME: Ditto.
// shouldBeEqualToString("ws.protocol", "superchat");
shouldBeEqualToString("ws.protocol", "superchat");
};
ws.onmessage = function(event)
......@@ -38,11 +36,19 @@ ws.onmessage = function(event)
ws.onclose = function(event)
{
debug("Closed");
shouldBeEqualToString("ws.protocol", "superchat");
closeEvent = event;
shouldBeTrue("closeEvent.wasClean");
finishJSTest();
setTimeout("checkAfterOnClose()", 0);
};
function checkAfterOnClose()
{
debug("Exited onclose handler");
shouldBeEqualToString("ws.protocol", "superchat");
finishJSTest();
}
var successfullyParsed = true;
</script>
<script src="../../../../js-test-resources/js-test-post.js"></script>
......
WebSocket's protocol attribute should be an empty string if the server did not provide Sec-WebSocket-Protocol header.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS ws.protocol is ""
Connected
PASS ws.protocol is ""
Closed
PASS ws.protocol is ""
PASS closeEvent.wasClean is true
Exited onclose handler
PASS ws.protocol is ""
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="../../../../js-test-resources/js-test-style.css">
<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's protocol attribute should be an empty string if the server did not provide Sec-WebSocket-Protocol header.");
window.jsTestIsAsync = true;
if (window.layoutTestController)
layoutTestController.overridePreference("WebKitHixie76WebSocketProtocolEnabled", 0);
var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/hybi/simple");
var closeEvent;
shouldBeEqualToString("ws.protocol", "");
ws.onopen = function()
{
debug("Connected");
shouldBeEqualToString("ws.protocol", "");
};
ws.onclose = function(event)
{
debug("Closed");
shouldBeEqualToString("ws.protocol", "");
closeEvent = event;
shouldBeTrue("closeEvent.wasClean");
setTimeout("checkAfterOnClose()", 0);
};
function checkAfterOnClose()
{
debug("Exited onclose handler");
shouldBeEqualToString("ws.protocol", "");
finishJSTest();
}
var successfullyParsed = true;
</script>
<script src="../../../../js-test-resources/js-test-post.js"></script>
</body>
</html>
WebSocket.protocol should not be modified.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS ws.protocol is ""
PASS ws.protocol is ""
Connected
PASS ws.protocol is "chat"
PASS ws.protocol is "chat"
Closed
PASS ws.protocol is "chat"
PASS ws.protocol is "chat"
PASS closeEvent.wasClean is true
Exited onclose handler
PASS ws.protocol is "chat"
PASS ws.protocol is "chat"
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="../../../../js-test-resources/js-test-style.css">
<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.protocol should not be modified.");
window.jsTestIsAsync = true;
if (window.layoutTestController)
layoutTestController.overridePreference("WebKitHixie76WebSocketProtocolEnabled", 0);
var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/hybi/accept-first-subprotocol", "chat");
var closeEvent;
shouldBeEqualToString("ws.protocol", "");
ws.protocol = "superchat";
shouldBeEqualToString("ws.protocol", "");
ws.onopen = function()
{
debug("Connected");
shouldBeEqualToString("ws.protocol", "chat");
ws.protocol = "superchat";
shouldBeEqualToString("ws.protocol", "chat");
};
ws.onclose = function(event)
{
debug("Closed");
shouldBeEqualToString("ws.protocol", "chat");
ws.protocol = "superchat";
shouldBeEqualToString("ws.protocol", "chat");
closeEvent = event;
shouldBeTrue("closeEvent.wasClean");
setTimeout("checkAfterOnClose()", 0);
};
function checkAfterOnClose()
{
debug("Exited onclose handler");
shouldBeEqualToString("ws.protocol", "chat");
ws.protocol = "superchat";
shouldBeEqualToString("ws.protocol", "chat");
finishJSTest();
}
var successfullyParsed = true;
</script>
<script src="../../../../js-test-resources/js-test-post.js"></script>
</body>
</html>
......@@ -2,11 +2,16 @@ Test WebSocket subprotocol negotiation in workers.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS PASS: ws.protocol is equal to ""
INFO: Connected
PASS PASS: ws.protocol is equal to "superchat"
INFO: Received: superchat
PASS PASS: receivedMessage is equal to "superchat"
INFO: Closed
PASS PASS: ws.protocol is equal to "superchat"
PASS PASS: closeEvent.wasClean is true
INFO: Exited onclose handler
PASS PASS: ws.protocol is equal to "superchat"
DONE
PASS successfullyParsed is true
......
WebSocket's protocol attribute should be an empty string if the server did not provide Sec-WebSocket-Protocol header.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS PASS: ws.protocol is equal to ""
INFO: Connected
PASS PASS: ws.protocol is equal to ""
INFO: Closed
PASS PASS: ws.protocol is equal to ""
PASS PASS: closeEvent.wasClean is true
INFO: Exited onclose handler
PASS PASS: ws.protocol is equal to ""
DONE
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="../../../../../js-test-resources/js-test-style.css">
<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's protocol attribute should be an empty string if the server did not provide Sec-WebSocket-Protocol header.");
window.jsTestIsAsync = true;
if (window.layoutTestController)
layoutTestController.overridePreference("WebKitHixie76WebSocketProtocolEnabled", 0);
function startsWith(str, prefix)
{
return str.indexOf(prefix) == 0;
}
var worker = new Worker("resources/no-subprotocol.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();
};
var successfullyParsed = true;
</script>
<script src="../../../../../js-test-resources/js-test-post.js"></script>
</body>
</html>
var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/hybi/workers/resources/protocol-test?protocol=superchat", ["chat", "superchat"]);
// FIXME: Implement WebSocket.protocol.
// if (ws.protocol === "")
// postMessage("PASS: ws.protocol is equal to \"\"");
// else
// postMessage("FAIL: ws.protocol should be \"\" but was \"" + ws.protocol + "\"");
if (ws.protocol === "")
postMessage("PASS: ws.protocol is equal to \"\"");
else
postMessage("FAIL: ws.protocol should be \"\" but was \"" + ws.protocol + "\"");
ws.onopen = function()
{
postMessage("INFO: Connected");
// FIXME: Ditto.
// if (ws.protocol === "superchat")
// postMessage("PASS: ws.protocol is equal to \"superchat\"");
// else
// postMessage("FAIL: ws.protocol should be \"superchat\" but was \"" + ws.protocol + "\"");
if (ws.protocol === "superchat")
postMessage("PASS: ws.protocol is equal to \"superchat\"");
else
postMessage("FAIL: ws.protocol should be \"superchat\" but was \"" + ws.protocol + "\"");
};
ws.onmessage = function(event)
......@@ -29,10 +27,28 @@ ws.onmessage = function(event)
ws.onclose = function(closeEvent)
{
postMessage("INFO: Closed");
if (ws.protocol === "superchat")
postMessage("PASS: ws.protocol is equal to \"superchat\"");
else
postMessage("FAIL: ws.protocol should be \"superchat\" but was \"" + ws.protocol + "\"");
if (closeEvent.wasClean === true)
postMessage("PASS: closeEvent.wasClean is true");
else
postMessage("FAIL: closeEvent.wasClean should be true but was \"" + closeEvent.wasClean + "\"");
postMessage("DONE");
setTimeout("checkAfterOnClose()", 0);
};
function checkAfterOnClose()
{
postMessage("INFO: Exited onclose handler");
if (ws.protocol === "superchat")
postMessage("PASS: ws.protocol is equal to \"superchat\"");
else
postMessage("FAIL: ws.protocol should be \"superchat\" but was \"" + ws.protocol + "\"");
postMessage("DONE");
}
var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/hybi/workers/resources/simple");
if (ws.protocol === "")
postMessage("PASS: ws.protocol is equal to \"\"");
else
postMessage("FAIL: ws.protocol should be \"\" but was \"" + ws.protocol + "\"");
ws.onopen = function()
{
postMessage("INFO: Connected");
if (ws.protocol === "")
postMessage("PASS: ws.protocol is equal to \"\"");
else
postMessage("FAIL: ws.protocol should be \"\" but was \"" + ws.protocol + "\"");
};
ws.onclose = function(closeEvent)
{
postMessage("INFO: Closed");
if (ws.protocol === "")
postMessage("PASS: ws.protocol is equal to \"\"");
else
postMessage("FAIL: ws.protocol should be \"\" but was \"" + ws.protocol + "\"");
if (closeEvent.wasClean === true)
postMessage("PASS: closeEvent.wasClean is true");
else
postMessage("FAIL: closeEvent.wasClean should be true but was \"" + closeEvent.wasClean + "\"");
setTimeout("checkAfterOnClose()", 0);
};
function checkAfterOnClose()
{
postMessage("INFO: Exited onclose handler");
if (ws.protocol === "")
postMessage("PASS: ws.protocol is equal to \"\"");
else
postMessage("FAIL: ws.protocol should be \"\" but was \"" + ws.protocol + "\"");
postMessage("DONE");
}
2011-08-11 Yuta Kitamura <yutak@chromium.org>
WebSocket: Implement "protocol" attribute
https://bugs.webkit.org/show_bug.cgi?id=65248
Reviewed by Kent Tamura.
Tests: http/tests/websocket/tests/hybi/no-subprotocol.html (added)
http/tests/websocket/tests/hybi/set-protocol.html (added)
http/tests/websocket/tests/hybi/workers/no-subprotocol.html (added)
http/tests/websocket/tests/hixie76/undefined-attributes.html (updated)
http/tests/websocket/tests/hybi/multiple-subprotocols.html (updated)
http/tests/websocket/tests/hybi/workers/multiple-subprotocols.html (updated)
* websockets/ThreadableWebSocketChannel.h:
Added subprotocol() function. This function is named differently from the counterpart of
WebSocket class, because the name "protocol" can be confused with the WebSocket protocol.
Added m_useHixie76Protocol and m_subprotocol, because these value may be used after
m_channel has been released. Using bool should be fine, because boolean literals do not
appear in ambiguous context.
* websockets/ThreadableWebSocketChannelClientWrapper.cpp:
(WebCore::ThreadableWebSocketChannelClientWrapper::ThreadableWebSocketChannelClientWrapper):
(WebCore::ThreadableWebSocketChannelClientWrapper::subprotocol):
(WebCore::ThreadableWebSocketChannelClientWrapper::setSubprotocol):
* websockets/ThreadableWebSocketChannelClientWrapper.h:
* websockets/WebSocket.cpp:
(WebCore::WebSocket::WebSocket):
(WebCore::WebSocket::connect):
(WebCore::WebSocket::protocol):
The "protocol" attribute is available only when the hybi-10 protocol is chosen.
(WebCore::WebSocket::binaryType):
(WebCore::WebSocket::setBinaryType):
(WebCore::WebSocket::didConnect):
* websockets/WebSocket.h:
* websockets/WebSocket.idl:
* websockets/WebSocketChannel.cpp:
(WebCore::WebSocketChannel::subprotocol):
* websockets/WebSocketChannel.h:
* websockets/WorkerThreadableWebSocketChannel.cpp:
(WebCore::WorkerThreadableWebSocketChannel::subprotocol):
(WebCore::workerContextDidConnect):
Subprotocol value is saved in the client wrapper object after the WebSocket connection is
established.
(WebCore::WorkerThreadableWebSocketChannel::Peer::didConnect):
* websockets/WorkerThreadableWebSocketChannel.h:
2011-08-11 Hayato Ito <hayato@chromium.org>
Implement proper handling of events with a related target in regard to shadow DOM boundaries.
......@@ -51,6 +51,7 @@ public:
virtual bool useHixie76Protocol() = 0;
virtual void connect(const KURL&, const String& protocol) = 0;
virtual String subprotocol() = 0; // Will be available after didConnect() callback is invoked.
virtual bool send(const String& message) = 0;
virtual unsigned long bufferedAmount() const = 0;
virtual void close() = 0;
......
......@@ -44,6 +44,7 @@ ThreadableWebSocketChannelClientWrapper::ThreadableWebSocketChannelClientWrapper
: m_client(client)
, m_syncMethodDone(false)
, m_useHixie76Protocol(true)
, m_subprotocol("")
, m_sent(false)
, m_bufferedAmount(0)
, m_suspended(false)
......@@ -80,6 +81,16 @@ void ThreadableWebSocketChannelClientWrapper::setUseHixie76Protocol(bool useHixi
m_useHixie76Protocol = useHixie76Protocol;
}
String ThreadableWebSocketChannelClientWrapper::subprotocol() const
{
return m_subprotocol;
}
void ThreadableWebSocketChannelClientWrapper::setSubprotocol(const String& subprotocol)
{
m_subprotocol = subprotocol;
}
bool ThreadableWebSocketChannelClientWrapper::sent() const
{
return m_sent;
......
......@@ -58,6 +58,10 @@ public:
bool useHixie76Protocol() const;
void setUseHixie76Protocol(bool);
// Subprotocol is cached too. Will be available when didConnect() callback is invoked.
String subprotocol() const;
void setSubprotocol(const String&);
bool sent() const;
void setSent(bool);
......@@ -86,6 +90,7 @@ protected:
WebSocketChannelClient* m_client;
bool m_syncMethodDone;
bool m_useHixie76Protocol;
String m_subprotocol;
bool m_sent;
unsigned long m_bufferedAmount;
bool m_suspended;
......
......@@ -136,6 +136,8 @@ WebSocket::WebSocket(ScriptExecutionContext* context)
, m_state(CONNECTING)
, m_bufferedAmountAfterClose(0)
, m_binaryType(BinaryTypeBlob)
, m_useHixie76Protocol(true)
, m_subprotocol("")
{
}
......@@ -190,9 +192,10 @@ void WebSocket::connect(const String& url, const Vector<String>& protocols, Exce
}
m_channel = ThreadableWebSocketChannel::create(scriptExecutionContext(), this);
m_useHixie76Protocol = m_channel->useHixie76Protocol();
String protocolString;
if (m_channel->useHixie76Protocol()) {
if (m_useHixie76Protocol) {
if (!protocols.isEmpty()) {
// Emulate JavaScript's Array.toString() behavior.
protocolString = joinStrings(protocols, ",");
......@@ -292,9 +295,16 @@ unsigned long WebSocket::bufferedAmount() const
return m_bufferedAmountAfterClose;
}
String WebSocket::protocol() const
{
if (m_useHixie76Protocol)
return String();
return m_subprotocol;
}
String WebSocket::binaryType() const
{
if (m_channel->useHixie76Protocol())
if (m_useHixie76Protocol)
return String();
switch (m_binaryType) {
case BinaryTypeBlob:
......@@ -308,7 +318,7 @@ String WebSocket::binaryType() const
void WebSocket::setBinaryType(const String& binaryType, ExceptionCode& ec)
{
if (m_channel->useHixie76Protocol())
if (m_useHixie76Protocol)
return;
if (binaryType == "blob") {
m_binaryType = BinaryTypeBlob;
......@@ -373,6 +383,7 @@ void WebSocket::didConnect()
}
ASSERT(scriptExecutionContext());
m_state = OPEN;
m_subprotocol = m_channel->subprotocol();
dispatchEvent(Event::create(eventNames().openEvent, false, false));
}
......
......@@ -74,6 +74,8 @@ namespace WebCore {
State readyState() const;
unsigned long bufferedAmount() const;
String protocol() const;
String binaryType() const;
void setBinaryType(const String& binaryType, ExceptionCode&);
......@@ -122,6 +124,8 @@ namespace WebCore {
EventTargetData m_eventTargetData;
unsigned long m_bufferedAmountAfterClose;
BinaryType m_binaryType;
bool m_useHixie76Protocol;
String m_subprotocol;
};
} // namespace WebCore
......
......@@ -58,6 +58,8 @@ module websockets {
attribute EventListener onerror;
attribute EventListener onclose;