2009-09-07 Fumitoshi Ukai <ukai@chromium.org>
authoreric@webkit.org <eric@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 7 Sep 2009 17:02:20 +0000 (17:02 +0000)
committereric@webkit.org <eric@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 7 Sep 2009 17:02:20 +0000 (17:02 +0000)
        Reviewed by Alexey Proskuryakov.

        Export WTF::tryFastMalloc used in WebSocketChannel.
        https://bugs.webkit.org/show_bug.cgi?id=28038

        * JavaScriptCore.exp:
2009-09-07  Fumitoshi Ukai  <ukai@chromium.org>

        Reviewed by Alexey Proskuryakov.

        Add WebSocket API.
        https://bugs.webkit.org/show_bug.cgi?id=28038

        WebSocket constructor is available in window object.

        * fast/dom/Window/window-properties-expected.txt:
        * fast/dom/prototype-inheritance-2-expected.txt:
        * fast/dom/prototype-inheritance-expected.txt:
        * fast/js/global-constructors-expected.txt:
2009-09-07  Fumitoshi Ukai  <ukai@chromium.org>

        Reviewed by Alexey Proskuryakov.

        WebSocket API implementation.
        https://bugs.webkit.org/show_bug.cgi?id=28038

        Add build systems only for GNUmakefile.am and WebCore.xcodeproj now.
        Other build systems will be updated once the code is functional.
        Tests will be landed once the code is completed and functional.

        * DerivedSources.cpp:
        * GNUmakefile.am:
        * WebCore.xcodeproj/project.pbxproj:
        * bindings/js/JSDOMWindowCustom.cpp:
        (WebCore::JSDOMWindow::webSocket):
        * bindings/js/JSEventTarget.cpp:
        (WebCore::toJS):
        (WebCore::toEventTarget):
        * dom/EventTarget.cpp:
        (WebCore::EventTarget::toWebSocket):
        * dom/EventTarget.h:
        * page/DOMWindow.idl:
        * websockets/WebSocket.cpp:
        (WebCore::ProcessWebSocketEventTask::create):
        (WebCore::ProcessWebSocketEventTask::performTask):
        (WebCore::ProcessWebSocketEventTask::ProcessWebSocketEventTask):
        (WebCore::isValidProtocolString):
        (WebCore::WebSocket::connect):
        (WebCore::WebSocket::send):
        (WebCore::WebSocket::close):
        (WebCore::WebSocket::bufferedAmount):
        (WebCore::WebSocket::addEventListener):
        (WebCore::WebSocket::removeEventListener):
        (WebCore::WebSocket::dispatchEvent):
        (WebCore::WebSocket::didConnect):
        (WebCore::WebSocket::didReceiveMessage):
        (WebCore::WebSocket::didClose):
        (WebCore::WebSocket::dispatchOpenEvent):
        (WebCore::WebSocket::dispatchMessageEvent):
        (WebCore::WebSocket::dispatchCloseEvent):
        * websockets/WebSocket.h:
        (WebCore::WebSocket::create):
        (WebCore::WebSocket::):
        (WebCore::WebSocket::setOnopen):
        (WebCore::WebSocket::onopen):
        (WebCore::WebSocket::setOnmessage):
        (WebCore::WebSocket::onmessage):
        (WebCore::WebSocket::setOnclose):
        (WebCore::WebSocket::onclose):
        (WebCore::WebSocket::toWebSocket):
        (WebCore::WebSocket::eventListeners):
        (WebCore::WebSocket::refEventTarget):
        (WebCore::WebSocket::derefEventTarget):
        * websockets/WebSocketChannel.cpp: Added.
        * websockets/WebSocketChannel.h: Added.
        * websockets/WebSocketChannelClient.h: Added.
        * websockets/WebSocketHandshake.cpp: Added.
        * websockets/WebSocketHandshake.h: Added.

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

23 files changed:
JavaScriptCore/ChangeLog
JavaScriptCore/JavaScriptCore.exp
LayoutTests/ChangeLog
LayoutTests/fast/dom/Window/window-properties-expected.txt
LayoutTests/fast/dom/prototype-inheritance-2-expected.txt
LayoutTests/fast/dom/prototype-inheritance-expected.txt
LayoutTests/fast/js/global-constructors-expected.txt
WebCore/ChangeLog
WebCore/DerivedSources.cpp
WebCore/GNUmakefile.am
WebCore/WebCore.xcodeproj/project.pbxproj
WebCore/bindings/js/JSDOMWindowCustom.cpp
WebCore/bindings/js/JSEventTarget.cpp
WebCore/dom/EventTarget.cpp
WebCore/dom/EventTarget.h
WebCore/page/DOMWindow.idl
WebCore/websockets/WebSocket.cpp
WebCore/websockets/WebSocket.h
WebCore/websockets/WebSocketChannel.cpp [new file with mode: 0644]
WebCore/websockets/WebSocketChannel.h [new file with mode: 0644]
WebCore/websockets/WebSocketChannelClient.h [new file with mode: 0644]
WebCore/websockets/WebSocketHandshake.cpp [new file with mode: 0644]
WebCore/websockets/WebSocketHandshake.h [new file with mode: 0644]

index 224aca7..1bbcbd4 100644 (file)
@@ -1,3 +1,12 @@
+2009-09-07  Fumitoshi Ukai  <ukai@chromium.org>
+
+        Reviewed by Alexey Proskuryakov.
+
+        Export WTF::tryFastMalloc used in WebSocketChannel.
+        https://bugs.webkit.org/show_bug.cgi?id=28038
+
+        * JavaScriptCore.exp:
+
 2009-09-04  Oliver Hunt  <oliver@apple.com>
 
         Reviewed by NOBODY (Build fix).
index a029724..cd80d74 100644 (file)
@@ -294,6 +294,7 @@ __ZN3WTF12detachThreadEj
 __ZN3WTF12isMainThreadEv
 __ZN3WTF12randomNumberEv
 __ZN3WTF13currentThreadEv
+__ZN3WTF13tryFastMallocEm
 __ZN3WTF13tryFastCallocEmm
 __ZN3WTF13tryFastMallocEm
 __ZN3WTF15ThreadCondition4waitERNS_5MutexE
index f39f08b..a2425b7 100644 (file)
@@ -1,3 +1,17 @@
+2009-09-07  Fumitoshi Ukai  <ukai@chromium.org>
+
+        Reviewed by Alexey Proskuryakov.
+
+        Add WebSocket API.
+        https://bugs.webkit.org/show_bug.cgi?id=28038
+        
+        WebSocket constructor is available in window object.
+
+        * fast/dom/Window/window-properties-expected.txt:
+        * fast/dom/prototype-inheritance-2-expected.txt:
+        * fast/dom/prototype-inheritance-expected.txt:
+        * fast/js/global-constructors-expected.txt:
+
 2009-09-07 Andras Becsi <becsi.andras@stud.u-szeged.hu>
 
         Reviewed by Tor Arne Vestbø
index 23b08b7..37f3ad0 100644 (file)
@@ -1509,6 +1509,13 @@ window.WebKitPoint [object WebKitPointConstructor]
 window.WebKitPoint.prototype [object WebKitPointPrototype]
 window.WebKitTransitionEvent [object WebKitTransitionEventConstructor]
 window.WebKitTransitionEvent.prototype [printed above as window.Event.prototype]
+window.WebSocket [object WebSocketConstructor]
+window.WebSocket.prototype [object WebSocketPrototype]
+window.WebSocket.prototype.CLOSED [number]
+window.WebSocket.prototype.CONNECTING [number]
+window.WebSocket.prototype.OPEN [number]
+window.WebSocket.prototype.close [function]
+window.WebSocket.prototype.send [function]
 window.WheelEvent [object WheelEventConstructor]
 window.WheelEvent.prototype [printed above as window.Event.prototype]
 window.Worker [object WorkerConstructor]
index e1fecad..1e99ebd 100644 (file)
@@ -362,6 +362,7 @@ Never found TextMetrics
 Never found WebKitCSSMatrix
 Never found WebKitCSSTransformValue
 Never found WebKitPoint
+Never found WebSocket
 Never found Worker
 Never found XMLHttpRequest
 Never found XMLHttpRequestUpload
index 2650c59..395c2c6 100644 (file)
@@ -335,6 +335,8 @@ PASS inner.WebKitPoint.isInner is true
 PASS inner.WebKitPoint.constructor.isInner is true
 PASS inner.WebKitTransitionEvent.isInner is true
 PASS inner.WebKitTransitionEvent.constructor.isInner is true
+PASS inner.WebSocket.isInner is true
+PASS inner.WebSocket.constructor.isInner is true
 PASS inner.WheelEvent.isInner is true
 PASS inner.WheelEvent.constructor.isInner is true
 PASS inner.Worker.isInner is true
index b2c1c6e..e7b5eb7 100644 (file)
@@ -162,6 +162,7 @@ PASS WebKitCSSMatrix.toString() is '[object WebKitCSSMatrixConstructor]'
 PASS WebKitCSSTransformValue.toString() is '[object WebKitCSSTransformValueConstructor]'
 PASS WebKitPoint.toString() is '[object WebKitPointConstructor]'
 PASS WebKitTransitionEvent.toString() is '[object WebKitTransitionEventConstructor]'
+PASS WebSocket.toString() is '[object WebSocketConstructor]'
 PASS WheelEvent.toString() is '[object WheelEventConstructor]'
 PASS Worker.toString() is '[object WorkerConstructor]'
 PASS XMLDocument.toString() is '[object DocumentConstructor]'
index 76a0612..3291d36 100644 (file)
@@ -1,3 +1,63 @@
+2009-09-07  Fumitoshi Ukai  <ukai@chromium.org>
+
+        Reviewed by Alexey Proskuryakov.
+
+        WebSocket API implementation.
+        https://bugs.webkit.org/show_bug.cgi?id=28038
+        
+        Add build systems only for GNUmakefile.am and WebCore.xcodeproj now.
+        Other build systems will be updated once the code is functional.
+        Tests will be landed once the code is completed and functional.
+
+        * DerivedSources.cpp:
+        * GNUmakefile.am:
+        * WebCore.xcodeproj/project.pbxproj:
+        * bindings/js/JSDOMWindowCustom.cpp:
+        (WebCore::JSDOMWindow::webSocket):
+        * bindings/js/JSEventTarget.cpp:
+        (WebCore::toJS):
+        (WebCore::toEventTarget):
+        * dom/EventTarget.cpp:
+        (WebCore::EventTarget::toWebSocket):
+        * dom/EventTarget.h:
+        * page/DOMWindow.idl:
+        * websockets/WebSocket.cpp:
+        (WebCore::ProcessWebSocketEventTask::create):
+        (WebCore::ProcessWebSocketEventTask::performTask):
+        (WebCore::ProcessWebSocketEventTask::ProcessWebSocketEventTask):
+        (WebCore::isValidProtocolString):
+        (WebCore::WebSocket::connect):
+        (WebCore::WebSocket::send):
+        (WebCore::WebSocket::close):
+        (WebCore::WebSocket::bufferedAmount):
+        (WebCore::WebSocket::addEventListener):
+        (WebCore::WebSocket::removeEventListener):
+        (WebCore::WebSocket::dispatchEvent):
+        (WebCore::WebSocket::didConnect):
+        (WebCore::WebSocket::didReceiveMessage):
+        (WebCore::WebSocket::didClose):
+        (WebCore::WebSocket::dispatchOpenEvent):
+        (WebCore::WebSocket::dispatchMessageEvent):
+        (WebCore::WebSocket::dispatchCloseEvent):
+        * websockets/WebSocket.h:
+        (WebCore::WebSocket::create):
+        (WebCore::WebSocket::):
+        (WebCore::WebSocket::setOnopen):
+        (WebCore::WebSocket::onopen):
+        (WebCore::WebSocket::setOnmessage):
+        (WebCore::WebSocket::onmessage):
+        (WebCore::WebSocket::setOnclose):
+        (WebCore::WebSocket::onclose):
+        (WebCore::WebSocket::toWebSocket):
+        (WebCore::WebSocket::eventListeners):
+        (WebCore::WebSocket::refEventTarget):
+        (WebCore::WebSocket::derefEventTarget):
+        * websockets/WebSocketChannel.cpp: Added.
+        * websockets/WebSocketChannel.h: Added.
+        * websockets/WebSocketChannelClient.h: Added.
+        * websockets/WebSocketHandshake.cpp: Added.
+        * websockets/WebSocketHandshake.h: Added.
+
 2009-09-07  Gustavo Noronha Silva  <gustavo.noronha@collabora.co.uk>
 
         Reviewed by Xan Lopez.
index 9bc63ba..408daba 100644 (file)
 #include "JSWebKitCSSMatrix.cpp"
 #include "JSWebKitPoint.cpp"
 #include "JSWebKitTransitionEvent.cpp"
+#include "JSWebSocket.cpp"
 #include "JSWheelEvent.cpp"
 #include "JSWorker.cpp"
 #include "JSWorkerContext.cpp"
index bc8c579..d8b5b2f 100644 (file)
@@ -3234,7 +3234,12 @@ webcore_sources += \
        WebCore/platform/network/SocketStreamHandleBase.h \
        WebCore/platform/network/SocketStreamHandleClient.h \
        WebCore/websockets/WebSocket.cpp \
-       WebCore/websockets/WebSocket.h
+       WebCore/websockets/WebSocket.h \
+       WebCore/websockets/WebSocketChannel.cpp \
+       WebCore/websockets/WebSocketChannel.h \
+       WebCore/websockets/WebSocketChannelClient.h \
+       WebCore/websockets/WebSocketHandshake.cpp \
+       WebCore/websockets/WebSocketHandshake.h
 
 webcoregtk_sources += \
        WebCore/platform/network/soup/SocketStreamError.h \
index eac4448..976be89 100644 (file)
                510D4A36103165EE0049EA54 /* SocketStreamHandleBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 510D4A30103165EE0049EA54 /* SocketStreamHandleBase.cpp */; };
                510D4A37103165EE0049EA54 /* SocketStreamHandleBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 510D4A31103165EE0049EA54 /* SocketStreamHandleBase.h */; };
                510D4A38103165EE0049EA54 /* SocketStreamHandleClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 510D4A32103165EE0049EA54 /* SocketStreamHandleClient.h */; };
+               510D4A4E103177A20049EA54 /* WebSocketChannel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 510D4A47103177A20049EA54 /* WebSocketChannel.cpp */; };
+               510D4A4F103177A20049EA54 /* WebSocketChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = 510D4A48103177A20049EA54 /* WebSocketChannel.h */; };
+               510D4A50103177A20049EA54 /* WebSocketChannelClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 510D4A49103177A20049EA54 /* WebSocketChannelClient.h */; };
                5116D9770CF177BD00C2B84D /* DatabaseDetails.h in Headers */ = {isa = PBXBuildFile; fileRef = 5116D9750CF177BD00C2B84D /* DatabaseDetails.h */; settings = {ATTRIBUTES = (Private, ); }; };
                511F23170DC160DA004F0032 /* LocalStorageTask.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 511F23130DC160DA004F0032 /* LocalStorageTask.cpp */; };
                511F23180DC160DA004F0032 /* LocalStorageTask.h in Headers */ = {isa = PBXBuildFile; fileRef = 511F23140DC160DA004F0032 /* LocalStorageTask.h */; };
                51ABAE1B103C18FF008C5260 /* SocketStreamError.h in Headers */ = {isa = PBXBuildFile; fileRef = 51ABAE1A103C18FF008C5260 /* SocketStreamError.h */; };
                51ABAE1E103C1913008C5260 /* SocketStreamHandle.h in Headers */ = {isa = PBXBuildFile; fileRef = 51ABAE1C103C1913008C5260 /* SocketStreamHandle.h */; };
                51ABAE1F103C1913008C5260 /* SocketStreamHandleCFNet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 51ABAE1D103C1913008C5260 /* SocketStreamHandleCFNet.cpp */; };
+               51ABAE2E103D4F75008C5260 /* WebSocketHandshaker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 51ABAE2C103D4F75008C5260 /* WebSocketHandshaker.cpp */; };
+               51ABAE2F103D4F75008C5260 /* WebSocketHandshaker.h in Headers */ = {isa = PBXBuildFile; fileRef = 51ABAE2D103D4F75008C5260 /* WebSocketHandshaker.h */; };
+               51ABAE441043AB4A008C5260 /* WebSocketHandshake.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 51ABAE421043AB4A008C5260 /* WebSocketHandshake.cpp */; };
+               51ABAE451043AB4A008C5260 /* WebSocketHandshake.h in Headers */ = {isa = PBXBuildFile; fileRef = 51ABAE431043AB4A008C5260 /* WebSocketHandshake.h */; };
                51B2417B0D931F3F00E83F5C /* LegacyWebArchiveMac.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51B2417A0D931F3F00E83F5C /* LegacyWebArchiveMac.mm */; };
                51BE37E00DAEE00E001085FC /* StorageArea.h in Headers */ = {isa = PBXBuildFile; fileRef = 51BE37DE0DAEE00E001085FC /* StorageArea.h */; };
                51C0AA390F2AA10A001648C2 /* CachedFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 51C0AA380F2AA10A001648C2 /* CachedFrame.h */; settings = {ATTRIBUTES = (Private, ); }; };
                510D4A30103165EE0049EA54 /* SocketStreamHandleBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SocketStreamHandleBase.cpp; sourceTree = "<group>"; };
                510D4A31103165EE0049EA54 /* SocketStreamHandleBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SocketStreamHandleBase.h; sourceTree = "<group>"; };
                510D4A32103165EE0049EA54 /* SocketStreamHandleClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SocketStreamHandleClient.h; sourceTree = "<group>"; };
+               510D4A47103177A20049EA54 /* WebSocketChannel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebSocketChannel.cpp; sourceTree = "<group>"; };
+               510D4A48103177A20049EA54 /* WebSocketChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebSocketChannel.h; sourceTree = "<group>"; };
+               510D4A49103177A20049EA54 /* WebSocketChannelClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebSocketChannelClient.h; sourceTree = "<group>"; };
                5116D9750CF177BD00C2B84D /* DatabaseDetails.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DatabaseDetails.h; sourceTree = "<group>"; };
                511F23130DC160DA004F0032 /* LocalStorageTask.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LocalStorageTask.cpp; sourceTree = "<group>"; };
                511F23140DC160DA004F0032 /* LocalStorageTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LocalStorageTask.h; sourceTree = "<group>"; };
                51ABAE1A103C18FF008C5260 /* SocketStreamError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SocketStreamError.h; path = cf/SocketStreamError.h; sourceTree = "<group>"; };
                51ABAE1C103C1913008C5260 /* SocketStreamHandle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SocketStreamHandle.h; path = cf/SocketStreamHandle.h; sourceTree = "<group>"; };
                51ABAE1D103C1913008C5260 /* SocketStreamHandleCFNet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SocketStreamHandleCFNet.cpp; path = cf/SocketStreamHandleCFNet.cpp; sourceTree = "<group>"; };
+               51ABAE2C103D4F75008C5260 /* WebSocketHandshaker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebSocketHandshaker.cpp; sourceTree = "<group>"; };
+               51ABAE2D103D4F75008C5260 /* WebSocketHandshaker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebSocketHandshaker.h; sourceTree = "<group>"; };
+               51ABAE421043AB4A008C5260 /* WebSocketHandshake.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebSocketHandshake.cpp; sourceTree = "<group>"; };
+               51ABAE431043AB4A008C5260 /* WebSocketHandshake.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebSocketHandshake.h; sourceTree = "<group>"; };
                51B2417A0D931F3F00E83F5C /* LegacyWebArchiveMac.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = LegacyWebArchiveMac.mm; sourceTree = "<group>"; };
                51BE37DE0DAEE00E001085FC /* StorageArea.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StorageArea.h; sourceTree = "<group>"; };
                51C0AA380F2AA10A001648C2 /* CachedFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CachedFrame.h; sourceTree = "<group>"; };
                518A34BD1026C831001B6896 /* websockets */ = {
                        isa = PBXGroup;
                        children = (
+                               51ABAE421043AB4A008C5260 /* WebSocketHandshake.cpp */,
+                               51ABAE431043AB4A008C5260 /* WebSocketHandshake.h */,
+                               510D4A47103177A20049EA54 /* WebSocketChannel.cpp */,
+                               510D4A48103177A20049EA54 /* WebSocketChannel.h */,
+                               510D4A49103177A20049EA54 /* WebSocketChannelClient.h */,
                                518A34BE1026C831001B6896 /* WebSocket.cpp */,
                                518A34BF1026C831001B6896 /* WebSocket.h */,
                                518A34C01026C831001B6896 /* WebSocket.idl */,
                                510D4A38103165EE0049EA54 /* SocketStreamHandleClient.h in Headers */,
                                51ABAE1B103C18FF008C5260 /* SocketStreamError.h in Headers */,
                                51ABAE1E103C1913008C5260 /* SocketStreamHandle.h in Headers */,
+                               510D4A4F103177A20049EA54 /* WebSocketChannel.h in Headers */,
+                               510D4A50103177A20049EA54 /* WebSocketChannelClient.h in Headers */,
+                               51ABAE451043AB4A008C5260 /* WebSocketHandshake.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                510D4A33103165EE0049EA54 /* SocketStreamErrorBase.cpp in Sources */,
                                510D4A36103165EE0049EA54 /* SocketStreamHandleBase.cpp in Sources */,
                                51ABAE1F103C1913008C5260 /* SocketStreamHandleCFNet.cpp in Sources */,
+                               510D4A4E103177A20049EA54 /* WebSocketChannel.cpp in Sources */,
+                               51ABAE441043AB4A008C5260 /* WebSocketHandshake.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
index 005a92f..4ca3f68 100644 (file)
@@ -54,6 +54,9 @@
 
 #include "JSWebKitCSSMatrixConstructor.h"
 #include "JSWebKitPointConstructor.h"
+#if ENABLE(WEB_SOCKETS)
+#include "JSWebSocketConstructor.h"
+#endif
 #include "JSWorkerConstructor.h"
 #include "JSXMLHttpRequestConstructor.h"
 #include "JSXSLTProcessorConstructor.h"
@@ -646,6 +649,13 @@ JSValue JSDOMWindow::sharedWorker(ExecState* exec) const
 }
 #endif
 
+#if ENABLE(WEB_SOCKETS)
+JSValue JSDOMWindow::webSocket(ExecState* exec) const
+{
+    return getDOMConstructor<JSWebSocketConstructor>(exec, this);
+}
+#endif
+
 // Custom functions
 
 // Helper for window.open() and window.showModalDialog()
index ed80d80..0421d10 100644 (file)
 #include "Notification.h"
 #endif
 
+#if ENABLE(WEB_SOCKETS)
+#include "JSWebSocket.h"
+#include "WebSocket.h"
+#endif
+
 using namespace JSC;
 
 namespace WebCore {
@@ -139,6 +144,11 @@ JSValue toJS(ExecState* exec, JSDOMGlobalObject* globalObject, EventTarget* targ
         return toJS(exec, notification);
 #endif
 
+#if ENABLE(WEB_SOCKETS)
+    if (WebSocket* webSocket = target->toWebSocket())
+        return toJS(exec, webSocket);
+#endif
+
     ASSERT_NOT_REACHED();
     return jsNull();
 }
@@ -183,6 +193,10 @@ EventTarget* toEventTarget(JSC::JSValue value)
     CONVERT_TO_EVENT_TARGET(Notification)
 #endif
 
+#if ENABLE(WEB_SOCKETS)
+    CONVERT_TO_EVENT_TARGET(WebSocket)
+#endif
+
     return 0;
 }
 
index 2f13da8..652644f 100644 (file)
@@ -83,6 +83,13 @@ SVGElementInstance* EventTarget::toSVGElementInstance()
 }
 #endif
 
+#if ENABLE(WEB_SOCKETS)
+WebSocket* EventTarget::toWebSocket()
+{
+    return 0;
+}
+#endif
+
 MessagePort* EventTarget::toMessagePort()
 {
     return 0;
index 6d3cb7c..6bcc3fb 100644 (file)
@@ -51,6 +51,7 @@ namespace WebCore {
     class ScriptExecutionContext;
     class SharedWorker;
     class SharedWorkerContext;
+    class WebSocket;
     class Worker;
     class XMLHttpRequest;
     class XMLHttpRequestUpload;
@@ -75,11 +76,13 @@ namespace WebCore {
         virtual Worker* toWorker();
         virtual DedicatedWorkerContext* toDedicatedWorkerContext();
 #endif
-
 #if ENABLE(SHARED_WORKERS)
         virtual SharedWorker* toSharedWorker();
         virtual SharedWorkerContext* toSharedWorkerContext();
 #endif
+#if ENABLE(WEB_SOCKETS)
+        virtual WebSocket* toWebSocket();
+#endif
 
 #if ENABLE(NOTIFICATIONS)
         virtual Notification* toNotification();
index 6ca28ce..5c9ddbc 100644 (file)
@@ -487,6 +487,10 @@ module window {
         attribute [JSCCustomGetter] SharedWorkerConstructor SharedWorker; // Usable with the new operator
 #endif
 
+#if defined(ENABLE_WEB_SOCKETS) && ENABLE_WEB_SOCKETS
+        attribute [JSCCustomGetter] WebSocketConstructor WebSocket; // Usable with the new operator
+#endif
+
         attribute PluginConstructor Plugin;
         attribute PluginArrayConstructor PluginArray;
 
index 999c1ff..8f90b1c 100644 (file)
 
 #include "WebSocket.h"
 
+#include "CString.h"
+#include "DOMWindow.h"
 #include "Event.h"
 #include "EventException.h"
 #include "EventListener.h"
+#include "EventNames.h"
+#include "Logging.h"
+#include "MessageEvent.h"
+#include "ScriptExecutionContext.h"
+#include "SecurityOrigin.h"
+#include "WebSocketChannel.h"
 #include <wtf/StdLibExtras.h>
 
 namespace WebCore {
 
+class ProcessWebSocketEventTask : public ScriptExecutionContext::Task {
+public:
+    typedef void (WebSocket::*Method)(Event*);
+    static PassRefPtr<ProcessWebSocketEventTask> create(PassRefPtr<WebSocket> webSocket, Method method, PassRefPtr<Event> event)
+    {
+        return adoptRef(new ProcessWebSocketEventTask(webSocket, method, event));
+    }
+    virtual void performTask(ScriptExecutionContext*)
+    {
+        (m_webSocket.get()->*m_method)(m_event.get());
+    }
+
+  private:
+    ProcessWebSocketEventTask(PassRefPtr<WebSocket> webSocket, Method method, PassRefPtr<Event> event)
+        : m_webSocket(webSocket)
+        , m_method(method)
+        , m_event(event) { }
+
+    RefPtr<WebSocket> m_webSocket;
+    Method m_method;
+    RefPtr<Event> m_event;
+};
+
+static bool isValidProtocolString(const WebCore::String& protocol)
+{
+    if (protocol.isNull())
+        return true;
+    if (protocol.isEmpty())
+        return false;
+    const UChar* characters = protocol.characters();
+    for (size_t i = 0; i < protocol.length(); i++) {
+        if (characters[i] < 0x21 || characters[i] > 0x7E)
+            return false;
+    }
+    return true;
+}
+
 WebSocket::WebSocket(ScriptExecutionContext* context)
     : ActiveDOMObject(context, this)
     , m_state(CONNECTING)
@@ -59,39 +104,49 @@ void WebSocket::connect(const KURL& url, ExceptionCode& ec)
 
 void WebSocket::connect(const KURL& url, const String& protocol, ExceptionCode& ec)
 {
+    LOG(Network, "WebSocket %p connect to %s protocol=%s", this, url.string().utf8().data(), protocol.utf8().data());
     m_url = url;
     m_protocol = protocol;
 
     if (!m_url.protocolIs("ws") && !m_url.protocolIs("wss")) {
+        LOG_ERROR("Error: wrong url for WebSocket %s", url.string().utf8().data());
         m_state = CLOSED;
         ec = SYNTAX_ERR;
         return;
     }
-    if (!m_protocol.isNull() && m_protocol.isEmpty()) {
+    if (!isValidProtocolString(m_protocol)) {
+        LOG_ERROR("Error: wrong protocol for WebSocket %s", m_protocol.utf8().data());
         m_state = CLOSED;
         ec = SYNTAX_ERR;
         return;
     }
-    // FIXME: Check protocol is valid form.
-    // FIXME: Connect WebSocketChannel.
+    // FIXME: if m_url.port() is blocking port, raise SECURITY_ERR.
+
+    m_channel = WebSocketChannel::create(scriptExecutionContext(), this, m_url, m_protocol);
+    m_channel->connect();
 }
 
-bool WebSocket::send(const String&, ExceptionCode& ec)
+bool WebSocket::send(const String& message, ExceptionCode& ec)
 {
-    if (m_state != OPEN) {
+    LOG(Network, "WebSocket %p send %s", this, message.utf8().data());
+    if (m_state == CONNECTING) {
         ec = INVALID_STATE_ERR;
         return false;
     }
-    // FIXME: send message on WebSocketChannel.
-    return false;
+    // No exception is raised if the connection was once established but has subsequently been closed.
+    if (m_state == CLOSED)
+        return false;
+    // FIXME: check message is valid utf8.
+    return m_channel->send(message);
 }
 
 void WebSocket::close()
 {
+    LOG(Network, "WebSocket %p close", this);
     if (m_state == CLOSED)
         return;
     m_state = CLOSED;
-    // FIXME: close WebSocketChannel.
+    m_channel->close();
 }
 
 const KURL& WebSocket::url() const
@@ -106,7 +161,8 @@ WebSocket::State WebSocket::readyState() const
 
 unsigned long WebSocket::bufferedAmount() const
 {
-    // FIXME: ask platform code to get buffered amount to be sent.
+    if (m_state == OPEN)
+        return m_channel->bufferedAmount();
     return 0;
 }
 
@@ -115,58 +171,120 @@ ScriptExecutionContext* WebSocket::scriptExecutionContext() const
     return ActiveDOMObject::scriptExecutionContext();
 }
 
-void WebSocket::addEventListener(const AtomicString&, PassRefPtr<EventListener>, bool)
+void WebSocket::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> eventListener, bool)
 {
-    // FIXME: implement this.
+    EventListenersMap::iterator iter = m_eventListeners.find(eventType);
+    if (iter == m_eventListeners.end()) {
+        ListenerVector listeners;
+        listeners.append(eventListener);
+        m_eventListeners.add(eventType, listeners);
+    } else {
+        ListenerVector& listeners = iter->second;
+        for (ListenerVector::iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter)
+            if (*listenerIter == eventListener)
+                return;
+
+        listeners.append(eventListener);
+        m_eventListeners.add(eventType, listeners);
+    }
 }
 
-void WebSocket::removeEventListener(const AtomicString&, EventListener*, bool)
+void WebSocket::removeEventListener(const AtomicString& eventType, EventListener* eventListener, bool)
 {
-    // FIXME: implement this.
+    EventListenersMap::iterator iter = m_eventListeners.find(eventType);
+    if (iter == m_eventListeners.end())
+        return;
+
+    ListenerVector& listeners = iter->second;
+    for (ListenerVector::const_iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter)
+        if (*listenerIter == eventListener) {
+            listeners.remove(listenerIter - listeners.begin());
+            return;
+        }
 }
 
-bool WebSocket::dispatchEvent(PassRefPtr<Event>, ExceptionCode&)
+bool WebSocket::dispatchEvent(PassRefPtr<Event> evt, ExceptionCode& ec)
 {
-    // FIXME: implement this.
-    return false;
+    if (!evt || evt->type().isEmpty()) {
+        ec = EventException::UNSPECIFIED_EVENT_TYPE_ERR;
+        return true;
+    }
+
+    ListenerVector listenersCopy = m_eventListeners.get(evt->type());
+    for (ListenerVector::const_iterator listenerIter = listenersCopy.begin(); listenerIter != listenersCopy.end(); ++listenerIter) {
+        evt->setTarget(this);
+        evt->setCurrentTarget(this);
+        listenerIter->get()->handleEvent(evt.get(), false);
+    }
+    return !evt->defaultPrevented();
 }
 
 void WebSocket::didConnect()
 {
+    LOG(Network, "WebSocket %p didConnect", this);
     if (m_state != CONNECTING) {
         didClose();
         return;
     }
     m_state = OPEN;
-    dispatchOpenEvent();
+    scriptExecutionContext()->postTask(ProcessWebSocketEventTask::create(this, &WebSocket::dispatchOpenEvent, Event::create(eventNames().openEvent, false, false)));
 }
 
 void WebSocket::didReceiveMessage(const String& msg)
 {
+    LOG(Network, "WebSocket %p didReceiveMessage %s", this, msg.utf8().data());
     if (m_state != OPEN)
         return;
-    dispatchMessageEvent(msg);
+    RefPtr<MessageEvent> evt = MessageEvent::create();
+    // FIXME: origin, lastEventId, source, messagePort.
+    evt->initMessageEvent(eventNames().messageEvent, false, false, msg, "", "", 0, 0);
+    scriptExecutionContext()->postTask(ProcessWebSocketEventTask::create(this, &WebSocket::dispatchMessageEvent, evt));
 }
 
 void WebSocket::didClose()
 {
+    LOG(Network, "WebSocket %p didClose", this);
     m_state = CLOSED;
-    dispatchCloseEvent();
+    scriptExecutionContext()->postTask(ProcessWebSocketEventTask::create(this, &WebSocket::dispatchCloseEvent, Event::create(eventNames().closeEvent, false, false)));
 }
 
-void WebSocket::dispatchOpenEvent()
+void WebSocket::dispatchOpenEvent(Event* evt)
 {
-    // FIXME: implement this.
+    if (m_onopen) {
+        evt->setTarget(this);
+        evt->setCurrentTarget(this);
+        m_onopen->handleEvent(evt, false);
+    }
+
+    ExceptionCode ec = 0;
+    dispatchEvent(evt, ec);
+    ASSERT(!ec);
 }
 
-void WebSocket::dispatchMessageEvent(const String&)
+void WebSocket::dispatchMessageEvent(Event* evt)
 {
-    // FIXME: implement this.
+    if (m_onmessage) {
+        evt->setTarget(this);
+        evt->setCurrentTarget(this);
+        m_onmessage->handleEvent(evt, false);
+    }
+
+    ExceptionCode ec = 0;
+    dispatchEvent(evt, ec);
+    ASSERT(!ec);
 }
 
-void WebSocket::dispatchCloseEvent()
+void WebSocket::dispatchCloseEvent(Event* evt)
 {
-    // FIXME: implement this.
+    if (m_onclose) {
+        evt->setTarget(this);
+        evt->setCurrentTarget(this);
+        m_onclose->handleEvent(evt, false);
+    }
+
+    ExceptionCode ec = 0;
+    dispatchEvent(evt, ec);
+    ASSERT(!ec);
 }
 
 }  // namespace WebCore
index 485e2cc..2be3445 100644 (file)
 #ifndef WebSocket_h
 #define WebSocket_h
 
+#if ENABLE(WEB_SOCKETS)
+
 #include "ActiveDOMObject.h"
 #include "AtomicStringHash.h"
 #include "EventListener.h"
 #include "EventTarget.h"
 #include "KURL.h"
+#include "WebSocketChannelClient.h"
 #include <wtf/OwnPtr.h>
 #include <wtf/RefCounted.h>
 
 namespace WebCore {
 
-class String;
+    class String;
+    class WebSocketChannel;
 
-class WebSocket : public RefCounted<WebSocket>, public EventTarget, public ActiveDOMObject {
-public:
-    static PassRefPtr<WebSocket> create(ScriptExecutionContext* context) { return adoptRef(new WebSocket(context)); }
-    virtual ~WebSocket();
+    class WebSocket : public RefCounted<WebSocket>, public EventTarget, public ActiveDOMObject, public WebSocketChannelClient {
+    public:
+        static PassRefPtr<WebSocket> create(ScriptExecutionContext* context) { return adoptRef(new WebSocket(context)); }
+        virtual ~WebSocket();
 
-    enum State {
-        CONNECTING = 0,
-        OPEN = 1,
-        CLOSED = 2
-    };
+        enum State {
+            CONNECTING = 0,
+            OPEN = 1,
+            CLOSED = 2
+        };
 
-    void connect(const KURL& url, ExceptionCode&);
-    void connect(const KURL& url, const String& protocol, ExceptionCode&);
+        void connect(const KURL&, ExceptionCode&);
+        void connect(const KURL&, const String& protocol, ExceptionCode&);
 
-    bool send(const String& message, ExceptionCode&);
+        bool send(const String& message, ExceptionCode&);
 
-    void close();
+        void close();
 
-    const KURL& url() const;
-    State readyState() const;
-    unsigned long bufferedAmount() const;
+        const KURL& url() const;
+        State readyState() const;
+        unsigned long bufferedAmount() const;
 
-    void setOnopen(PassRefPtr<EventListener> eventListener) { m_onopen = eventListener; }
-    EventListener* onopen() const { return m_onopen.get(); }
-    void setOnmessage(PassRefPtr<EventListener> eventListener) { m_onmessage = eventListener; }
-    EventListener* onmessage() const { return m_onmessage.get(); }
-    void setOnclose(PassRefPtr<EventListener> eventListener) { m_onclose = eventListener; }
-    EventListener* onclose() const { return m_onclose.get(); }
+        void setOnopen(PassRefPtr<EventListener> eventListener) { m_onopen = eventListener; }
+        EventListener* onopen() const { return m_onopen.get(); }
+        void setOnmessage(PassRefPtr<EventListener> eventListener) { m_onmessage = eventListener; }
+        EventListener* onmessage() const { return m_onmessage.get(); }
+        void setOnclose(PassRefPtr<EventListener> eventListener) { m_onclose = eventListener; }
+        EventListener* onclose() const { return m_onclose.get(); }
 
-    // EventTarget
-    virtual WebSocket* toWebSocket() { return this; }
+        // EventTarget
+        virtual WebSocket* toWebSocket() { return this; }
 
-    virtual ScriptExecutionContext* scriptExecutionContext() const;
+        virtual ScriptExecutionContext* scriptExecutionContext() const;
 
-    virtual void addEventListener(const AtomicString& eventType, PassRefPtr<EventListener>, bool useCapture);
-    virtual void removeEventListener(const AtomicString& eventType, EventListener*, bool useCapture);
-    virtual bool dispatchEvent(PassRefPtr<Event>, ExceptionCode&);
+        typedef Vector<RefPtr<EventListener> > ListenerVector;
+        typedef HashMap<AtomicString, ListenerVector> EventListenersMap;
 
-    // ActiveDOMObject
-    // virtual bool hasPendingActivity() const;
-    // virtual void contextDestroyed();
-    // virtual bool canSuspend() const;
-    // virtual void suspend();
-    // virtual void resume();
-    // virtual void stop();
+        virtual void addEventListener(const AtomicString& eventType, PassRefPtr<EventListener>, bool useCapture);
+        virtual void removeEventListener(const AtomicString& eventType, EventListener*, bool useCapture);
+        virtual bool dispatchEvent(PassRefPtr<Event>, ExceptionCode&);
+        EventListenersMap& eventListeners() { return m_eventListeners; }
 
-    using RefCounted<WebSocket>::ref;
-    using RefCounted<WebSocket>::deref;
+        // ActiveDOMObject
+        //  virtual bool hasPendingActivity() const;
+        // virtual void contextDestroyed();
+        // virtual bool canSuspend() const;
+        // virtual void suspend();
+        // virtual void resume();
+        // virtual void stop();
 
-private:
-    WebSocket(ScriptExecutionContext*);
+        using RefCounted<WebSocket>::ref;
+        using RefCounted<WebSocket>::deref;
 
-    virtual void refEventTarget() { ref(); }
-    virtual void derefEventTarget() { deref(); }
+        // WebSocketChannelClient
+        virtual void didConnect();
+        virtual void didReceiveMessage(const String& msg);
+        virtual void didClose();
 
-    // WebSocketChannelClient
-    void didConnect();
-    void didReceiveMessage(const String& msg);
-    void didClose();
+    private:
+        WebSocket(ScriptExecutionContext*);
 
-    void dispatchOpenEvent();
-    void dispatchMessageEvent(const String& msg);
-    void dispatchCloseEvent();
+        virtual void refEventTarget() { ref(); }
+        virtual void derefEventTarget() { deref(); }
 
-    // FIXME: add WebSocketChannel.
+        void dispatchOpenEvent(Event*);
+        void dispatchMessageEvent(Event*);
+        void dispatchCloseEvent(Event*);
 
-    RefPtr<EventListener> m_onopen;
-    RefPtr<EventListener> m_onmessage;
-    RefPtr<EventListener> m_onclose;
+        RefPtr<WebSocketChannel> m_channel;
 
-    State m_state;
-    KURL m_url;
-    String m_protocol;
-};
+        RefPtr<EventListener> m_onopen;
+        RefPtr<EventListener> m_onmessage;
+        RefPtr<EventListener> m_onclose;
+        EventListenersMap m_eventListeners;
+
+        State m_state;
+        KURL m_url;
+        String m_protocol;
+    };
 
 }  // namespace WebCore
 
+#endif  // ENABLE(WEB_SOCKETS)
+
 #endif  // WebSocket_h
diff --git a/WebCore/websockets/WebSocketChannel.cpp b/WebCore/websockets/WebSocketChannel.cpp
new file mode 100644 (file)
index 0000000..145cd34
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2009 Google Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if ENABLE(WEB_SOCKETS)
+
+#include "WebSocketChannel.h"
+
+#include "CString.h"
+#include "CookieJar.h"
+#include "Document.h"
+#include "Logging.h"
+#include "PlatformString.h"
+#include "ScriptExecutionContext.h"
+#include "SocketStreamError.h"
+#include "SocketStreamHandle.h"
+#include "StringHash.h"
+#include "WebSocketChannelClient.h"
+
+#include <wtf/Deque.h>
+#include <wtf/FastMalloc.h>
+#include <wtf/HashMap.h>
+
+namespace WebCore {
+
+WebSocketChannel::WebSocketChannel(ScriptExecutionContext* context, WebSocketChannelClient* client, const KURL& url, const String& protocol)
+    : m_context(context)
+    , m_client(client)
+    , m_handshake(url, protocol, context)
+    , m_buffer(0)
+    , m_bufferSize(0)
+    , m_unhandledBufferSize(0)
+{
+}
+
+WebSocketChannel::~WebSocketChannel()
+{
+    fastFree(m_buffer);
+}
+
+void WebSocketChannel::connect()
+{
+    LOG(Network, "WebSocketChannel %p connect", this);
+    ASSERT(!m_handle.get());
+    m_handshake.reset();
+    m_handle = SocketStreamHandle::create(m_handshake.url(), this);
+}
+
+bool WebSocketChannel::send(const String& msg)
+{
+    LOG(Network, "WebSocketChannel %p send %s", this, msg.utf8().data());
+    Vector<char> buf;
+    buf.append('\0');  // frame type
+    buf.append(msg.utf8().data(), msg.utf8().length());
+    buf.append('\xff');  // frame end
+    if (!m_handle.get()) {
+        m_unhandledBufferSize += buf.size();
+        return false;
+    }
+    return m_handle->send(buf.data(), buf.size());
+}
+
+unsigned long WebSocketChannel::bufferedAmount() const
+{
+    LOG(Network, "WebSocketChannel %p bufferedAmount", this);
+    if (!m_handle.get())
+        return m_unhandledBufferSize;
+    return m_handle->bufferedAmount();
+}
+
+void WebSocketChannel::close()
+{
+    LOG(Network, "WebSocketChannel %p close", this);
+    if (m_handle.get())
+        m_handle->close();  // will call didClose()
+}
+
+void WebSocketChannel::willOpenStream(SocketStreamHandle*, const KURL&)
+{
+}
+
+void WebSocketChannel::willSendData(SocketStreamHandle*, const char*, int)
+{
+}
+
+void WebSocketChannel::didOpen(SocketStreamHandle* handle)
+{
+    LOG(Network, "WebSocketChannel %p didOpen", this);
+    ASSERT(handle == m_handle.get());
+    const CString& handshakeMessage = m_handshake.clientHandshakeMessage();
+    if (!handle->send(handshakeMessage.data(), handshakeMessage.length())) {
+        LOG(Network, "Error in sending handshake message.");
+        handle->close();
+    }
+}
+
+void WebSocketChannel::didClose(SocketStreamHandle* handle)
+{
+    LOG(Network, "WebSocketChannel %p didClose", this);
+    ASSERT(handle == m_handle.get() || !m_handle.get());
+    if (!m_handle.get())
+        return;
+    m_unhandledBufferSize = handle->bufferedAmount();
+    WebSocketChannelClient* client = m_client;
+    m_client = 0;
+    m_handle = 0;
+    client->didClose();
+}
+
+void WebSocketChannel::didReceiveData(SocketStreamHandle* handle, const char* data, int len)
+{
+    LOG(Network, "WebSocketChannel %p didReceiveData %d", this, len);
+    ASSERT(handle == m_handle.get());
+    if (!appendToBuffer(data, len)) {
+        handle->close();
+        return;
+    }
+    if (m_handshake.mode() != WebSocketHandshake::Connected) {
+        int headerLength = m_handshake.readServerHandshake(m_buffer, m_bufferSize);
+        if (headerLength <= 0)
+            return;
+        switch (m_handshake.mode()) {
+        case WebSocketHandshake::Connected:
+            if (!m_handshake.serverSetCookie().isEmpty()) {
+                if (m_context->isDocument()) {
+                    Document* document = static_cast<Document*>(m_context);
+                    if (cookiesEnabled(document))
+                        document->setCookie(m_handshake.serverSetCookie());
+                }
+            }
+            // FIXME: handle set-cookie2.
+            LOG(Network, "WebSocketChannel %p connected", this);
+            m_client->didConnect();
+            break;
+        default:
+            LOG(Network, "WebSocketChannel %p connection failed", this);
+            handle->close();
+            return;
+        }
+        skipBuffer(headerLength);
+        if (!m_buffer)
+            return;
+        LOG(Network, "remaining in read buf %ul", m_bufferSize);
+    }
+
+    const char* nextFrame = m_buffer;
+    const char* p = m_buffer;
+    const char* end = p + m_bufferSize;
+    while (p < end) {
+        unsigned char frameByte = static_cast<unsigned char>(*p++);
+        if ((frameByte & 0x80) == 0x80) {
+            int length = 0;
+            while (p < end && (*p & 0x80) == 0x80) {
+                if (length > std::numeric_limits<int>::max() / 128) {
+                    LOG(Network, "frame length overflow %d", length);
+                    handle->close();
+                    return;
+                }
+                length = length * 128 + *p & 0x7f;
+                ++p;
+            }
+            if (p + length < end) {
+                p += length;
+                nextFrame = p;
+            }
+        } else {
+            const char* msgStart = p;
+            while (p < end && *p != '\xff')
+                ++p;
+            if (p < end && *p == '\xff') {
+                if (frameByte == 0x00)
+                    m_client->didReceiveMessage(String::fromUTF8(msgStart, p - msgStart));
+                ++p;
+                nextFrame = p;
+            }
+        }
+    }
+    skipBuffer(nextFrame - m_buffer);
+}
+
+void WebSocketChannel::didFail(SocketStreamHandle* handle, const SocketStreamError&)
+{
+    LOG(Network, "WebSocketChannel %p didFail", this);
+    ASSERT(handle == m_handle.get() || !m_handle.get());
+    handle->close();
+}
+
+bool WebSocketChannel::appendToBuffer(const char* data, int len)
+{
+    char* newBuffer = 0;
+    if (tryFastMalloc(m_bufferSize + len).getValue(newBuffer)) {
+        if (m_buffer)
+            memcpy(newBuffer, m_buffer, m_bufferSize);
+        memcpy(newBuffer + m_bufferSize, data, len);
+        fastFree(m_buffer);
+        m_buffer = newBuffer;
+        m_bufferSize += len;
+        return true;
+    }
+    LOG(Network, "Too long WebSocket frame %d", m_bufferSize + len);
+    return false;
+}
+
+void WebSocketChannel::skipBuffer(int len)
+{
+    ASSERT(len <= m_bufferSize);
+    m_bufferSize -= len;
+    if (!m_bufferSize) {
+        fastFree(m_buffer);
+        m_buffer = 0;
+        return;
+    }
+    memmove(m_buffer, m_buffer + len, m_bufferSize);
+}
+
+}  // namespace WebCore
+
+#endif  // ENABLE(WEB_SOCKETS)
diff --git a/WebCore/websockets/WebSocketChannel.h b/WebCore/websockets/WebSocketChannel.h
new file mode 100644 (file)
index 0000000..75f41f6
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2009 Google Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WebSocketChannel_h
+#define WebSocketChannel_h
+
+#if ENABLE(WEB_SOCKETS)
+
+#include "SocketStreamHandleClient.h"
+#include "WebSocketHandshake.h"
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+    class ScriptExecutionContext;
+    class String;
+    class SocketStreamHandle;
+    class SocketStreamError;
+    class WebSocketChannelClient;
+
+    class WebSocketChannel : public RefCounted<WebSocketChannel>, public SocketStreamHandleClient {
+    public:
+        static PassRefPtr<WebSocketChannel> create(ScriptExecutionContext* context, WebSocketChannelClient* client, const KURL& url, const String& protocol) { return new WebSocketChannel(context, client, url, protocol); }
+        virtual ~WebSocketChannel();
+
+        virtual void connect();
+
+        virtual bool send(const String& msg);
+        virtual unsigned long bufferedAmount() const;
+
+        virtual void close();
+
+        virtual void willOpenStream(SocketStreamHandle*, const KURL&);
+        virtual void willSendData(SocketStreamHandle*, const char*, int);
+        virtual void didOpen(SocketStreamHandle*);
+        virtual void didClose(SocketStreamHandle*);
+        virtual void didReceiveData(SocketStreamHandle*, const char*, int);
+        virtual void didFail(SocketStreamHandle*, const SocketStreamError&);
+
+    private:
+        WebSocketChannel(ScriptExecutionContext*, WebSocketChannelClient*, const KURL&, const String&);
+
+        bool appendToBuffer(const char* data, int len);
+        void skipBuffer(int len);
+
+        ScriptExecutionContext* m_context;
+        WebSocketChannelClient* m_client;
+        WebSocketHandshake m_handshake;
+        RefPtr<SocketStreamHandle> m_handle;
+        char* m_buffer;
+        int m_bufferSize;
+        unsigned long m_unhandledBufferSize;
+    };
+
+}  // namespace WebCore
+
+#endif  // ENABLE(WEB_SOCKETS)
+
+#endif  // WebSocketChannel_h
diff --git a/WebCore/websockets/WebSocketChannelClient.h b/WebCore/websockets/WebSocketChannelClient.h
new file mode 100644 (file)
index 0000000..463cada
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2009 Google Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WebSocketChannelClient_h
+#define WebSocketChannelClient_h
+
+#if ENABLE(WEB_SOCKETS)
+
+namespace WebCore {
+
+    class WebSocketChannelClient {
+    public:
+        virtual ~WebSocketChannelClient() { }
+        virtual void didConnect() { }
+        virtual void didReceiveMessage(const String&) { }
+        virtual void didClose() { }
+
+    protected:
+        WebSocketChannelClient() { }
+    };
+
+}  // namespace WebCore
+
+#endif  // ENABLE(WEB_SOCKETS)
+
+#endif  // WebSocketChannelClient_h
diff --git a/WebCore/websockets/WebSocketHandshake.cpp b/WebCore/websockets/WebSocketHandshake.cpp
new file mode 100644 (file)
index 0000000..691fa1c
--- /dev/null
@@ -0,0 +1,462 @@
+/*
+ * Copyright (C) 2009 Google Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if ENABLE(WEB_SOCKETS)
+
+#include "WebSocketHandshake.h"
+
+#include "AtomicString.h"
+#include "CString.h"
+#include "CookieJar.h"
+#include "Document.h"
+#include "HTTPHeaderMap.h"
+#include "KURL.h"
+#include "Logging.h"
+#include "ScriptExecutionContext.h"
+#include "SecurityOrigin.h"
+#include "StringBuilder.h"
+#include <wtf/StringExtras.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+const char webSocketServerHandshakeHeader[] = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n";
+const char webSocketUpgradeHeader[] = "Upgrade: WebSocket\r\n";
+const char webSocketConnectionHeader[] = "Connection: Upgrade\r\n";
+
+static String extractResponseCode(const char* header, int len)
+{
+    const char* space1 = 0;
+    const char* space2 = 0;
+    const char* p;
+    for (p = header; p - header < len; p++) {
+        if (*p == ' ') {
+            if (!space1)
+                space1 = p;
+            else if (!space2)
+                space2 = p;
+        } else if (*p == '\n')
+            break;
+    }
+    if (p - header == len)
+        return String();
+    if (!space1 || !space2)
+        return "";
+    return String(space1 + 1, space2 - space1 - 1);
+}
+
+WebSocketHandshake::WebSocketHandshake(const KURL& url, const String& protocol, ScriptExecutionContext* context)
+    : m_url(url)
+    , m_clientProtocol(protocol)
+    , m_secure(m_url.protocolIs("wss"))
+    , m_context(context)
+    , m_mode(Incomplete)
+{
+}
+
+WebSocketHandshake::~WebSocketHandshake()
+{
+}
+
+const KURL& WebSocketHandshake::url() const
+{
+    return m_url;
+}
+
+void WebSocketHandshake::setURL(const KURL& url)
+{
+    m_url = url.copy();
+}
+
+const String WebSocketHandshake::host() const
+{
+    return m_url.host().lower();
+}
+
+const String& WebSocketHandshake::clientProtocol() const
+{
+    return m_clientProtocol;
+}
+
+void WebSocketHandshake::setClientProtocol(const String& protocol)
+{
+    m_clientProtocol = protocol;
+}
+
+bool WebSocketHandshake::secure() const
+{
+    return m_secure;
+}
+
+void WebSocketHandshake::setSecure(bool secure)
+{
+    m_secure = secure;
+}
+
+String WebSocketHandshake::clientOrigin() const
+{
+    return m_context->securityOrigin()->toString();
+}
+
+String WebSocketHandshake::clientLocation() const
+{
+    StringBuilder builder;
+    builder.append(m_secure ? "wss" : "ws");
+    builder.append("://");
+    builder.append(m_url.host().lower());
+    if (m_url.port()) {
+        if ((!m_secure && m_url.port() != 80) || (m_secure && m_url.port() != 443)) {
+            builder.append(":");
+            builder.append(String::number(m_url.port()));
+        }
+    }
+    builder.append(m_url.path());
+    return builder.toString();
+}
+
+CString WebSocketHandshake::clientHandshakeMessage() const
+{
+    StringBuilder builder;
+
+    builder.append("GET ");
+    builder.append(m_url.path());
+    if (!m_url.query().isEmpty()) {
+        builder.append("?");
+        builder.append(m_url.query());
+    }
+    builder.append(" HTTP/1.1\r\n");
+    builder.append("Upgrade: WebSocket\r\n");
+    builder.append("Connection: Upgrade\r\n");
+    builder.append("Host: ");
+    builder.append(m_url.host().lower());
+    if (m_url.port()) {
+        if ((!m_secure && m_url.port() != 80) || (m_secure && m_url.port() != 443)) {
+            builder.append(":");
+            builder.append(String::number(m_url.port()));
+        }
+    }
+    builder.append("\r\n");
+    builder.append("Origin: ");
+    builder.append(clientOrigin());
+    builder.append("\r\n");
+    if (!m_clientProtocol.isEmpty()) {
+        builder.append("WebSocket-Protocol: ");
+        builder.append(m_clientProtocol);
+        builder.append("\r\n");
+    }
+    KURL url = httpURLForAuthenticationAndCookies();
+    // FIXME: set authentication information or cookies for url.
+    // Set "Authorization: <credentials>" if authentication information exists for url.
+    if (m_context->isDocument()) {
+        Document* document = static_cast<Document*>(m_context);
+        String cookie = cookies(document, url);
+        if (!cookie.isEmpty()) {
+            builder.append("Cookie: ");
+            builder.append(cookie);
+            builder.append("\r\n");
+        }
+        // Set "Cookie2: <cookie>" if cookies 2 exists for url?
+    }
+    builder.append("\r\n");
+    return builder.toString().utf8();
+}
+
+void WebSocketHandshake::reset()
+{
+    m_mode = Incomplete;
+
+    m_wsOrigin = String();
+    m_wsLocation = String();
+    m_wsProtocol = String();
+    m_setCookie = String();
+    m_setCookie2 = String();
+}
+
+int WebSocketHandshake::readServerHandshake(const char* header, size_t len)
+{
+    m_mode = Incomplete;
+    if (len < sizeof(webSocketServerHandshakeHeader) - 1) {
+        // Just hasn't been received fully yet.
+        return -1;
+    }
+    if (!memcmp(header, webSocketServerHandshakeHeader, sizeof(webSocketServerHandshakeHeader) - 1))
+        m_mode = Normal;
+    else {
+        const String& code = extractResponseCode(header, len);
+        if (code.isNull()) {
+            LOG(Network, "short server handshake: %s", header);
+            return -1;
+        }
+        if (code.isEmpty()) {
+            LOG(Network, "no response code found: %s", header);
+            return len;
+        }
+        LOG(Network, "response code: %s", code.utf8().data());
+        if (code == "401") {
+            LOG(Network, "Authentication required");
+            return len;
+        } else {
+            LOG(Network, "Mismatch server handshake: %s", header);
+            return len;
+        }
+    }
+    const char* p = header + sizeof(webSocketServerHandshakeHeader) - 1;
+    const char* end = header + len + 1;
+
+    if (m_mode == Normal) {
+        size_t headerSize = end - p;
+        if (headerSize < sizeof(webSocketUpgradeHeader) - 1)
+            return 0;
+        if (memcmp(p, webSocketUpgradeHeader, sizeof(webSocketUpgradeHeader) - 1)) {
+            LOG(Network, "Bad upgrade header: %s", p);
+            return p - header + sizeof(webSocketUpgradeHeader) - 1;
+        }
+        p += sizeof(webSocketUpgradeHeader) - 1;
+
+        headerSize = end - p;
+        if (headerSize < sizeof(webSocketConnectionHeader) - 1)
+            return -1;
+        if (memcmp(p, webSocketConnectionHeader, sizeof(webSocketConnectionHeader) - 1)) {
+            LOG(Network, "Bad connection header: %s", p);
+            return p - header + sizeof(webSocketConnectionHeader) - 1;
+        }
+        p += sizeof(webSocketConnectionHeader) - 1;
+    }
+
+    if (!strnstr(p, "\r\n\r\n", end - p)) {
+        // Just hasn't been received fully yet.
+        return -1;
+    }
+    HTTPHeaderMap headers;
+    p = readHTTPHeaders(p, end, &headers);
+    if (!p) {
+        LOG(Network, "readHTTPHeaders failed");
+        m_mode = Failed;
+        return len;
+    }
+    if (!processHeaders(headers)) {
+        LOG(Network, "header process failed");
+        m_mode = Failed;
+        return p - header;
+    }
+    switch (m_mode) {
+    case Normal:
+        checkResponseHeaders();
+        break;
+    default:
+        m_mode = Failed;
+        break;
+    }
+    return p - header;
+}
+
+WebSocketHandshake::Mode WebSocketHandshake::mode() const
+{
+    return m_mode;
+}
+
+const String& WebSocketHandshake::serverWebSocketOrigin() const
+{
+    return m_wsOrigin;
+}
+
+void WebSocketHandshake::setServerWebSocketOrigin(const String& webSocketOrigin)
+{
+    m_wsOrigin = webSocketOrigin;
+}
+
+const String& WebSocketHandshake::serverWebSocketLocation() const
+{
+    return m_wsLocation;
+}
+
+void WebSocketHandshake::setServerWebSocketLocation(const String& webSocketLocation)
+{
+    m_wsLocation = webSocketLocation;
+}
+
+const String& WebSocketHandshake::serverWebSocketProtocol() const
+{
+    return m_wsProtocol;
+}
+
+void WebSocketHandshake::setServerWebSocketProtocol(const String& webSocketProtocol)
+{
+    m_wsProtocol = webSocketProtocol;
+}
+
+const String& WebSocketHandshake::serverSetCookie() const
+{
+    return m_setCookie;
+}
+
+void WebSocketHandshake::setServerSetCookie(const String& setCookie)
+{
+    m_setCookie = setCookie;
+}
+
+const String& WebSocketHandshake::serverSetCookie2() const
+{
+    return m_setCookie2;
+}
+
+void WebSocketHandshake::setServerSetCookie2(const String& setCookie2)
+{
+    m_setCookie2 = setCookie2;
+}
+
+KURL WebSocketHandshake::httpURLForAuthenticationAndCookies() const
+{
+    KURL url = m_url.copy();
+    url.setProtocol(m_secure ? "https" : "http");
+    return url;
+}
+
+const char* WebSocketHandshake::readHTTPHeaders(const char* start, const char* end, HTTPHeaderMap* headers)
+{
+    Vector<char> name;
+    Vector<char> value;
+    for (const char* p = start; p < end; p++) {
+        name.clear();
+        value.clear();
+
+        for (; p < end; p++) {
+            switch (*p) {
+            case '\r':
+                if (name.isEmpty()) {
+                    if (p + 1 < end && *(p + 1) == '\n')
+                        return p + 2;
+                    LOG(Network, "CR doesn't follow LF p=%p end=%p", p, end);
+                    return 0;
+                }
+                LOG(Network, "Unexpected CR in name");
+                return 0;
+            case '\n':
+                LOG(Network, "Unexpected LF in name");
+                return 0;
+            case ':':
+                break;
+            default:
+                if (*p >= 0x41 && *p <= 0x5a)
+                    name.append(*p + 0x20);
+                else
+                    name.append(*p);
+                continue;
+            }
+            if (*p == ':') {
+                ++p;
+                break;
+            }
+        }
+
+        for (; p < end && *p == 0x20; p++) { }
+
+        for (; p < end; p++) {
+            switch (*p) {
+            case '\r':
+                break;
+            case '\n':
+                LOG(Network, "Unexpected LF in value");
+                return 0;
+            default:
+                value.append(*p);
+            }
+            if (*p == '\r') {
+                ++p;
+                break;
+            }
+        }
+        if (p >= end || *p != '\n') {
+            LOG(Network, "CR doesn't follow LF after value p=%p end=%p", p, end);
+            return 0;
+        }
+        AtomicString nameStr(String::fromUTF8(name.data(), name.size()));
+        String valueStr = String::fromUTF8(value.data(), value.size());
+        LOG(Network, "name=%s value=%s", nameStr.string().utf8().data(), valueStr.utf8().data());
+        headers->add(nameStr, valueStr);
+    }
+    ASSERT_NOT_REACHED();
+    return 0;
+}
+
+bool WebSocketHandshake::processHeaders(const HTTPHeaderMap& headers)
+{
+    for (HTTPHeaderMap::const_iterator it = headers.begin(); it != headers.end(); ++it) {
+        switch (m_mode) {
+        case Normal:
+            if (it->first == "websocket-origin")
+                m_wsOrigin = it->second;
+            else if (it->first == "websocket-location")
+                m_wsLocation = it->second;
+            else if (it->first == "websocket-protocol")
+                m_wsProtocol = it->second;
+            else if (it->first == "set-cookie")
+                m_setCookie = it->second;
+            else if (it->first == "set-cookie2")
+                m_setCookie2 = it->second;
+            continue;
+        case Incomplete:
+        case Failed:
+        case Connected:
+            ASSERT_NOT_REACHED();
+        }
+        ASSERT_NOT_REACHED();
+    }
+    return true;
+}
+
+void WebSocketHandshake::checkResponseHeaders()
+{
+    ASSERT(m_mode == Normal);
+    m_mode = Failed;
+    if (m_wsOrigin.isNull() || m_wsLocation.isNull())
+        return;
+
+    if (clientOrigin() != m_wsOrigin) {
+        LOG(Network, "Mismatch origin: %s != %s", clientOrigin().utf8().data(), m_wsOrigin.utf8().data());
+        return;
+    }
+    if (clientLocation() != m_wsLocation) {
+        LOG(Network, "Mismatch location: %s != %s", clientLocation().utf8().data(), m_wsLocation.utf8().data());
+        return;
+    }
+    if (!m_clientProtocol.isEmpty() && m_clientProtocol != m_wsProtocol) {
+        LOG(Network, "Mismatch protocol: %s != %s", m_clientProtocol.utf8().data(), m_wsProtocol.utf8().data());
+        return;
+    }
+    m_mode = Connected;
+    return;
+}
+
+}  // namespace WebCore
+
+#endif  // ENABLE(WEB_SOCKETS)
diff --git a/WebCore/websockets/WebSocketHandshake.h b/WebCore/websockets/WebSocketHandshake.h
new file mode 100644 (file)
index 0000000..d5dbe68
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2009 Google Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WebSocketHandshake_h
+#define WebSocketHandshake_h
+
+#if ENABLE(WEB_SOCKETS)
+
+#include "KURL.h"
+#include "PlatformString.h"
+#include <wtf/Noncopyable.h>
+
+namespace WebCore {
+
+    class ScriptExecutionContext;
+    class HTTPHeaderMap;
+
+    class WebSocketHandshake : public Noncopyable {
+    public:
+        enum Mode {
+            Incomplete, Normal, Failed, Connected
+        };
+        WebSocketHandshake(const KURL&, const String& protocol, ScriptExecutionContext*);
+        ~WebSocketHandshake();
+
+        const KURL& url() const;
+        void setURL(const KURL&);
+        const String host() const;
+
+        const String& clientProtocol() const;
+        void setClientProtocol(const String& protocol);
+
+        bool secure() const;
+        void setSecure(bool secure);
+
+        String clientOrigin() const;
+        String clientLocation() const;
+
+        CString clientHandshakeMessage() const;
+
+        void reset();
+
+        int readServerHandshake(const char* header, size_t len);
+        Mode mode() const;
+
+        const String& serverWebSocketOrigin() const;
+        void setServerWebSocketOrigin(const String& webSocketOrigin);
+
+        const String& serverWebSocketLocation() const;
+        void setServerWebSocketLocation(const String& webSocketLocation);
+
+        const String& serverWebSocketProtocol() const;
+        void setServerWebSocketProtocol(const String& webSocketProtocol);
+
+        const String& serverSetCookie() const;
+        void setServerSetCookie(const String& setCookie);
+        const String& serverSetCookie2() const;
+        void setServerSetCookie2(const String& setCookie2);
+
+    private:
+        KURL httpURLForAuthenticationAndCookies() const;
+
+        // Reads all headers except for the two predefined ones.
+        const char* readHTTPHeaders(const char* start, const char* end, HTTPHeaderMap* headers);
+        bool processHeaders(const HTTPHeaderMap& headers);
+        void checkResponseHeaders();
+
+        KURL m_url;
+        String m_clientProtocol;
+        bool m_secure;
+        ScriptExecutionContext* m_context;
+
+        Mode m_mode;
+
+        String m_wsOrigin;
+        String m_wsLocation;
+        String m_wsProtocol;
+        String m_setCookie;
+        String m_setCookie2;
+    };
+
+}  // namespace WebCore
+
+#endif  // ENABLE(WEB_SOCKETS)
+
+#endif  // WebSocketHandshake_h