Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / modules / websockets / NewWebSocketChannelImpl.cpp
1 /*
2  * Copyright (C) 2013 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "modules/websockets/NewWebSocketChannelImpl.h"
33
34 #include "core/dom/Document.h"
35 #include "core/dom/ExecutionContext.h"
36 #include "core/fileapi/FileReaderLoader.h"
37 #include "core/fileapi/FileReaderLoaderClient.h"
38 #include "core/frame/LocalFrame.h"
39 #include "core/inspector/InspectorInstrumentation.h"
40 #include "core/inspector/InspectorTraceEvents.h"
41 #include "core/loader/FrameLoader.h"
42 #include "core/loader/MixedContentChecker.h"
43 #include "core/loader/UniqueIdentifier.h"
44 #include "modules/websockets/WebSocketChannelClient.h"
45 #include "modules/websockets/WebSocketFrame.h"
46 #include "platform/Logging.h"
47 #include "platform/network/WebSocketHandshakeRequest.h"
48 #include "platform/weborigin/SecurityOrigin.h"
49 #include "public/platform/Platform.h"
50 #include "public/platform/WebSerializedOrigin.h"
51 #include "public/platform/WebSocketHandshakeRequestInfo.h"
52 #include "public/platform/WebSocketHandshakeResponseInfo.h"
53 #include "public/platform/WebString.h"
54 #include "public/platform/WebURL.h"
55 #include "public/platform/WebVector.h"
56
57 using blink::WebSocketHandle;
58
59 namespace WebCore {
60
61 class NewWebSocketChannelImpl::BlobLoader FINAL : public NoBaseWillBeGarbageCollectedFinalized<NewWebSocketChannelImpl::BlobLoader>, public FileReaderLoaderClient {
62 public:
63     BlobLoader(PassRefPtr<BlobDataHandle>, NewWebSocketChannelImpl*);
64     virtual ~BlobLoader() { }
65
66     void cancel();
67
68     // FileReaderLoaderClient functions.
69     virtual void didStartLoading() OVERRIDE { }
70     virtual void didReceiveData() OVERRIDE { }
71     virtual void didFinishLoading() OVERRIDE;
72     virtual void didFail(FileError::ErrorCode) OVERRIDE;
73
74     void trace(Visitor* visitor)
75     {
76         visitor->trace(m_channel);
77     }
78
79 private:
80     RawPtrWillBeMember<NewWebSocketChannelImpl> m_channel;
81     FileReaderLoader m_loader;
82 };
83
84 NewWebSocketChannelImpl::BlobLoader::BlobLoader(PassRefPtr<BlobDataHandle> blobDataHandle, NewWebSocketChannelImpl* channel)
85     : m_channel(channel)
86     , m_loader(FileReaderLoader::ReadAsArrayBuffer, this)
87 {
88     m_loader.start(channel->executionContext(), blobDataHandle);
89 }
90
91 void NewWebSocketChannelImpl::BlobLoader::cancel()
92 {
93     m_loader.cancel();
94     // didFail will be called immediately.
95     // |this| is deleted here.
96 }
97
98 void NewWebSocketChannelImpl::BlobLoader::didFinishLoading()
99 {
100     m_channel->didFinishLoadingBlob(m_loader.arrayBufferResult());
101     // |this| is deleted here.
102 }
103
104 void NewWebSocketChannelImpl::BlobLoader::didFail(FileError::ErrorCode errorCode)
105 {
106     m_channel->didFailLoadingBlob(errorCode);
107     // |this| is deleted here.
108 }
109
110 NewWebSocketChannelImpl::NewWebSocketChannelImpl(ExecutionContext* context, WebSocketChannelClient* client, const String& sourceURL, unsigned lineNumber)
111     : ContextLifecycleObserver(context)
112     , m_handle(adoptPtr(blink::Platform::current()->createWebSocketHandle()))
113     , m_client(client)
114     , m_identifier(0)
115     , m_sendingQuota(0)
116     , m_receivedDataSizeForFlowControl(receivedDataSizeForFlowControlHighWaterMark * 2) // initial quota
117     , m_bufferedAmount(0)
118     , m_sentSizeOfTopMessage(0)
119     , m_sourceURLAtConstruction(sourceURL)
120     , m_lineNumberAtConstruction(lineNumber)
121 {
122     if (context->isDocument() && toDocument(context)->page())
123         m_identifier = createUniqueIdentifier();
124 }
125
126 NewWebSocketChannelImpl::~NewWebSocketChannelImpl()
127 {
128     abortAsyncOperations();
129 }
130
131 bool NewWebSocketChannelImpl::connect(const KURL& url, const String& protocol)
132 {
133     WTF_LOG(Network, "NewWebSocketChannelImpl %p connect()", this);
134     if (!m_handle)
135         return false;
136
137     if (executionContext()->isDocument() && document()->frame() && !document()->frame()->loader().mixedContentChecker()->canConnectInsecureWebSocket(document()->securityOrigin(), url))
138         return false;
139     if (MixedContentChecker::isMixedContent(document()->securityOrigin(), url)) {
140         String message = "Connecting to a non-secure WebSocket server from a secure origin is deprecated.";
141         document()->addConsoleMessage(JSMessageSource, WarningMessageLevel, message);
142     }
143
144     m_url = url;
145     Vector<String> protocols;
146     // Avoid placing an empty token in the Vector when the protocol string is
147     // empty.
148     if (!protocol.isEmpty()) {
149         // Since protocol is already verified and escaped, we can simply split
150         // it.
151         protocol.split(", ", true, protocols);
152     }
153     blink::WebVector<blink::WebString> webProtocols(protocols.size());
154     for (size_t i = 0; i < protocols.size(); ++i) {
155         webProtocols[i] = protocols[i];
156     }
157     m_handle->connect(url, webProtocols, *executionContext()->securityOrigin(), this);
158     flowControlIfNecessary();
159     if (m_identifier) {
160         TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "WebSocketCreate", "data", InspectorWebSocketCreateEvent::data(document(), m_identifier, url, protocol));
161         TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"), "CallStack", "stack", InspectorCallStackEvent::currentCallStack());
162         // FIXME(361045): remove InspectorInstrumentation calls once DevTools Timeline migrates to tracing.
163         InspectorInstrumentation::didCreateWebSocket(document(), m_identifier, url, protocol);
164     }
165     return true;
166 }
167
168 String NewWebSocketChannelImpl::subprotocol()
169 {
170     WTF_LOG(Network, "NewWebSocketChannelImpl %p subprotocol()", this);
171     return m_subprotocol;
172 }
173
174 String NewWebSocketChannelImpl::extensions()
175 {
176     WTF_LOG(Network, "NewWebSocketChannelImpl %p extensions()", this);
177     return m_extensions;
178 }
179
180 WebSocketChannel::SendResult NewWebSocketChannelImpl::send(const String& message)
181 {
182     WTF_LOG(Network, "NewWebSocketChannelImpl %p sendText(%s)", this, message.utf8().data());
183     if (m_identifier) {
184         // FIXME: Change the inspector API to show the entire message instead
185         // of individual frames.
186         CString data = message.utf8();
187         InspectorInstrumentation::didSendWebSocketFrame(document(), m_identifier, WebSocketFrame::OpCodeText, true, data.data(), data.length());
188     }
189     m_messages.append(Message(message));
190     sendInternal();
191     return SendSuccess;
192 }
193
194 WebSocketChannel::SendResult NewWebSocketChannelImpl::send(PassRefPtr<BlobDataHandle> blobDataHandle)
195 {
196     WTF_LOG(Network, "NewWebSocketChannelImpl %p sendBlob(%s, %s, %llu)", this, blobDataHandle->uuid().utf8().data(), blobDataHandle->type().utf8().data(), blobDataHandle->size());
197     if (m_identifier) {
198         // FIXME: Change the inspector API to show the entire message instead
199         // of individual frames.
200         // FIXME: We can't access the data here.
201         // Since Binary data are not displayed in Inspector, this does not
202         // affect actual behavior.
203         InspectorInstrumentation::didSendWebSocketFrame(document(), m_identifier, WebSocketFrame::OpCodeBinary, true, "", 0);
204     }
205     m_messages.append(Message(blobDataHandle));
206     sendInternal();
207     return SendSuccess;
208 }
209
210 WebSocketChannel::SendResult NewWebSocketChannelImpl::send(const ArrayBuffer& buffer, unsigned byteOffset, unsigned byteLength)
211 {
212     WTF_LOG(Network, "NewWebSocketChannelImpl %p sendArrayBuffer(%p, %u, %u)", this, buffer.data(), byteOffset, byteLength);
213     if (m_identifier) {
214         // FIXME: Change the inspector API to show the entire message instead
215         // of individual frames.
216         InspectorInstrumentation::didSendWebSocketFrame(document(), m_identifier, WebSocketFrame::OpCodeBinary, true, static_cast<const char*>(buffer.data()) + byteOffset, byteLength);
217     }
218     // buffer.slice copies its contents.
219     m_messages.append(buffer.slice(byteOffset, byteOffset + byteLength));
220     sendInternal();
221     return SendSuccess;
222 }
223
224 unsigned long NewWebSocketChannelImpl::bufferedAmount() const
225 {
226     WTF_LOG(Network, "NewWebSocketChannelImpl %p bufferedAmount()", this);
227     return m_bufferedAmount;
228 }
229
230 void NewWebSocketChannelImpl::close(int code, const String& reason)
231 {
232     WTF_LOG(Network, "NewWebSocketChannelImpl %p close(%d, %s)", this, code, reason.utf8().data());
233     ASSERT(m_handle);
234     unsigned short codeToSend = static_cast<unsigned short>(code == CloseEventCodeNotSpecified ? CloseEventCodeNoStatusRcvd : code);
235     m_handle->close(codeToSend, reason);
236 }
237
238 void NewWebSocketChannelImpl::fail(const String& reason, MessageLevel level, const String& sourceURL, unsigned lineNumber)
239 {
240     WTF_LOG(Network, "NewWebSocketChannelImpl %p fail(%s)", this, reason.utf8().data());
241     // m_handle and m_client can be null here.
242
243     if (m_identifier)
244         InspectorInstrumentation::didReceiveWebSocketFrameError(document(), m_identifier, reason);
245     const String message = "WebSocket connection to '" + m_url.elidedString() + "' failed: " + reason;
246     executionContext()->addConsoleMessage(JSMessageSource, level, message, sourceURL, lineNumber);
247
248     if (m_client)
249         m_client->didReceiveMessageError();
250     // |reason| is only for logging and should not be provided for scripts,
251     // hence close reason must be empty.
252     handleDidClose(false, CloseEventCodeAbnormalClosure, String());
253     // handleDidClose may delete this object.
254 }
255
256 void NewWebSocketChannelImpl::disconnect()
257 {
258     WTF_LOG(Network, "NewWebSocketChannelImpl %p disconnect()", this);
259     if (m_identifier) {
260         TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "WebSocketDestroy", "data", InspectorWebSocketEvent::data(document(), m_identifier));
261         TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"), "CallStack", "stack", InspectorCallStackEvent::currentCallStack());
262         // FIXME(361045): remove InspectorInstrumentation calls once DevTools Timeline migrates to tracing.
263         InspectorInstrumentation::didCloseWebSocket(document(), m_identifier);
264     }
265     abortAsyncOperations();
266     m_handle.clear();
267     m_client = 0;
268     m_identifier = 0;
269 }
270
271 void NewWebSocketChannelImpl::suspend()
272 {
273     WTF_LOG(Network, "NewWebSocketChannelImpl %p suspend()", this);
274 }
275
276 void NewWebSocketChannelImpl::resume()
277 {
278     WTF_LOG(Network, "NewWebSocketChannelImpl %p resume()", this);
279 }
280
281 NewWebSocketChannelImpl::Message::Message(const String& text)
282     : type(MessageTypeText)
283     , text(text.utf8(StrictUTF8ConversionReplacingUnpairedSurrogatesWithFFFD)) { }
284
285 NewWebSocketChannelImpl::Message::Message(PassRefPtr<BlobDataHandle> blobDataHandle)
286     : type(MessageTypeBlob)
287     , blobDataHandle(blobDataHandle) { }
288
289 NewWebSocketChannelImpl::Message::Message(PassRefPtr<ArrayBuffer> arrayBuffer)
290     : type(MessageTypeArrayBuffer)
291     , arrayBuffer(arrayBuffer) { }
292
293 void NewWebSocketChannelImpl::sendInternal()
294 {
295     ASSERT(m_handle);
296     unsigned long bufferedAmount = m_bufferedAmount;
297     while (!m_messages.isEmpty() && m_sendingQuota > 0 && !m_blobLoader) {
298         bool final = false;
299         const Message& message = m_messages.first();
300         switch (message.type) {
301         case MessageTypeText: {
302             WebSocketHandle::MessageType type =
303                 m_sentSizeOfTopMessage ? WebSocketHandle::MessageTypeContinuation : WebSocketHandle::MessageTypeText;
304             size_t size = std::min(static_cast<size_t>(m_sendingQuota), message.text.length() - m_sentSizeOfTopMessage);
305             final = (m_sentSizeOfTopMessage + size == message.text.length());
306             m_handle->send(final, type, message.text.data() + m_sentSizeOfTopMessage, size);
307             m_sentSizeOfTopMessage += size;
308             m_sendingQuota -= size;
309             break;
310         }
311         case MessageTypeBlob:
312             ASSERT(!m_blobLoader);
313             m_blobLoader = adoptPtrWillBeNoop(new BlobLoader(message.blobDataHandle, this));
314             break;
315         case MessageTypeArrayBuffer: {
316             WebSocketHandle::MessageType type =
317                 m_sentSizeOfTopMessage ? WebSocketHandle::MessageTypeContinuation : WebSocketHandle::MessageTypeBinary;
318             size_t size = std::min(static_cast<size_t>(m_sendingQuota), message.arrayBuffer->byteLength() - m_sentSizeOfTopMessage);
319             final = (m_sentSizeOfTopMessage + size == message.arrayBuffer->byteLength());
320             m_handle->send(final, type, static_cast<const char*>(message.arrayBuffer->data()) + m_sentSizeOfTopMessage, size);
321             m_sentSizeOfTopMessage += size;
322             m_sendingQuota -= size;
323             break;
324         }
325         }
326         if (final) {
327             m_messages.removeFirst();
328             m_sentSizeOfTopMessage = 0;
329         }
330     }
331     if (m_client && m_bufferedAmount != bufferedAmount) {
332         m_client->didUpdateBufferedAmount(m_bufferedAmount);
333     }
334 }
335
336 void NewWebSocketChannelImpl::flowControlIfNecessary()
337 {
338     if (!m_handle || m_receivedDataSizeForFlowControl < receivedDataSizeForFlowControlHighWaterMark) {
339         return;
340     }
341     m_handle->flowControl(m_receivedDataSizeForFlowControl);
342     m_receivedDataSizeForFlowControl = 0;
343 }
344
345 void NewWebSocketChannelImpl::abortAsyncOperations()
346 {
347     if (m_blobLoader) {
348         m_blobLoader->cancel();
349         m_blobLoader.clear();
350     }
351 }
352
353 void NewWebSocketChannelImpl::handleDidClose(bool wasClean, unsigned short code, const String& reason)
354 {
355     m_handle.clear();
356     abortAsyncOperations();
357     if (!m_client) {
358         return;
359     }
360     WebSocketChannelClient* client = m_client;
361     m_client = 0;
362     WebSocketChannelClient::ClosingHandshakeCompletionStatus status =
363         wasClean ? WebSocketChannelClient::ClosingHandshakeComplete : WebSocketChannelClient::ClosingHandshakeIncomplete;
364     client->didClose(m_bufferedAmount, status, code, reason);
365     // client->didClose may delete this object.
366 }
367
368 Document* NewWebSocketChannelImpl::document()
369 {
370     ASSERT(m_identifier);
371     ExecutionContext* context = executionContext();
372     ASSERT(context->isDocument());
373     return toDocument(context);
374 }
375
376 void NewWebSocketChannelImpl::didConnect(WebSocketHandle* handle, bool fail, const blink::WebString& selectedProtocol, const blink::WebString& extensions)
377 {
378     WTF_LOG(Network, "NewWebSocketChannelImpl %p didConnect(%p, %d, %s, %s)", this, handle, fail, selectedProtocol.utf8().data(), extensions.utf8().data());
379     ASSERT(m_handle);
380     ASSERT(handle == m_handle);
381     ASSERT(m_client);
382     if (fail) {
383         failAsError("Cannot connect to " + m_url.string() + ".");
384         // failAsError may delete this object.
385         return;
386     }
387     m_subprotocol = selectedProtocol;
388     m_extensions = extensions;
389     m_client->didConnect();
390 }
391
392 void NewWebSocketChannelImpl::didStartOpeningHandshake(WebSocketHandle* handle, const blink::WebSocketHandshakeRequestInfo& request)
393 {
394     WTF_LOG(Network, "NewWebSocketChannelImpl %p didStartOpeningHandshake(%p)", this, handle);
395     if (m_identifier) {
396         TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "WebSocketSendHandshakeRequest", "data", InspectorWebSocketEvent::data(document(), m_identifier));
397         TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"), "CallStack", "stack", InspectorCallStackEvent::currentCallStack());
398         // FIXME(361045): remove InspectorInstrumentation calls once DevTools Timeline migrates to tracing.
399         InspectorInstrumentation::willSendWebSocketHandshakeRequest(document(), m_identifier, &request.toCoreRequest());
400         m_handshakeRequest = WebSocketHandshakeRequest::create(request.toCoreRequest());
401     }
402 }
403
404 void NewWebSocketChannelImpl::didFinishOpeningHandshake(WebSocketHandle* handle, const blink::WebSocketHandshakeResponseInfo& response)
405 {
406     WTF_LOG(Network, "NewWebSocketChannelImpl %p didFinishOpeningHandshake(%p)", this, handle);
407     if (m_identifier) {
408         TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "WebSocketReceiveHandshakeResponse", "data", InspectorWebSocketEvent::data(document(), m_identifier));
409         // FIXME(361045): remove InspectorInstrumentation calls once DevTools Timeline migrates to tracing.
410         InspectorInstrumentation::didReceiveWebSocketHandshakeResponse(document(), m_identifier, m_handshakeRequest.get(), &response.toCoreResponse());
411     }
412     m_handshakeRequest.clear();
413 }
414
415 void NewWebSocketChannelImpl::didFail(WebSocketHandle* handle, const blink::WebString& message)
416 {
417     WTF_LOG(Network, "NewWebSocketChannelImpl %p didFail(%p, %s)", this, handle, message.utf8().data());
418     // This function is called when the browser is required to fail the
419     // WebSocketConnection. Hence we fail this channel by calling
420     // |this->failAsError| function.
421     failAsError(message);
422     // |this| may be deleted.
423 }
424
425 void NewWebSocketChannelImpl::didReceiveData(WebSocketHandle* handle, bool fin, WebSocketHandle::MessageType type, const char* data, size_t size)
426 {
427     WTF_LOG(Network, "NewWebSocketChannelImpl %p didReceiveData(%p, %d, %d, (%p, %zu))", this, handle, fin, type, data, size);
428     ASSERT(m_handle);
429     ASSERT(handle == m_handle);
430     ASSERT(m_client);
431     // Non-final frames cannot be empty.
432     ASSERT(fin || size);
433     switch (type) {
434     case WebSocketHandle::MessageTypeText:
435         ASSERT(m_receivingMessageData.isEmpty());
436         m_receivingMessageTypeIsText = true;
437         break;
438     case WebSocketHandle::MessageTypeBinary:
439         ASSERT(m_receivingMessageData.isEmpty());
440         m_receivingMessageTypeIsText = false;
441         break;
442     case WebSocketHandle::MessageTypeContinuation:
443         ASSERT(!m_receivingMessageData.isEmpty());
444         break;
445     }
446
447     m_receivingMessageData.append(data, size);
448     m_receivedDataSizeForFlowControl += size;
449     flowControlIfNecessary();
450     if (!fin) {
451         return;
452     }
453     if (m_identifier) {
454         // FIXME: Change the inspector API to show the entire message instead
455         // of individual frames.
456         WebSocketFrame::OpCode opcode = m_receivingMessageTypeIsText ? WebSocketFrame::OpCodeText : WebSocketFrame::OpCodeBinary;
457         WebSocketFrame frame(opcode, m_receivingMessageData.data(), m_receivingMessageData.size(), WebSocketFrame::Final);
458         InspectorInstrumentation::didReceiveWebSocketFrame(document(), m_identifier, frame.opCode, frame.masked, frame.payload, frame.payloadLength);
459     }
460     if (m_receivingMessageTypeIsText) {
461         String message = m_receivingMessageData.isEmpty() ? emptyString() : String::fromUTF8(m_receivingMessageData.data(), m_receivingMessageData.size());
462         m_receivingMessageData.clear();
463         if (message.isNull()) {
464             failAsError("Could not decode a text frame as UTF-8.");
465             // failAsError may delete this object.
466         } else {
467             m_client->didReceiveMessage(message);
468         }
469     } else {
470         OwnPtr<Vector<char> > binaryData = adoptPtr(new Vector<char>);
471         binaryData->swap(m_receivingMessageData);
472         m_client->didReceiveBinaryData(binaryData.release());
473     }
474 }
475
476 void NewWebSocketChannelImpl::didClose(WebSocketHandle* handle, bool wasClean, unsigned short code, const blink::WebString& reason)
477 {
478     WTF_LOG(Network, "NewWebSocketChannelImpl %p didClose(%p, %d, %u, %s)", this, handle, wasClean, code, String(reason).utf8().data());
479     ASSERT(m_handle);
480     m_handle.clear();
481     if (m_identifier) {
482         TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "WebSocketDestroy", "data", InspectorWebSocketEvent::data(document(), m_identifier));
483         TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"), "CallStack", "stack", InspectorCallStackEvent::currentCallStack());
484         // FIXME(361045): remove InspectorInstrumentation calls once DevTools Timeline migrates to tracing.
485         InspectorInstrumentation::didCloseWebSocket(document(), m_identifier);
486         m_identifier = 0;
487     }
488
489     handleDidClose(wasClean, code, reason);
490     // handleDidClose may delete this object.
491 }
492
493 void NewWebSocketChannelImpl::didReceiveFlowControl(WebSocketHandle* handle, int64_t quota)
494 {
495     WTF_LOG(Network, "NewWebSocketChannelImpl %p didReceiveFlowControl(%p, %ld)", this, handle, static_cast<long>(quota));
496     ASSERT(m_handle);
497     m_sendingQuota += quota;
498     sendInternal();
499 }
500
501 void NewWebSocketChannelImpl::didStartClosingHandshake(WebSocketHandle* handle)
502 {
503     WTF_LOG(Network, "NewWebSocketChannelImpl %p didStartClosingHandshake(%p)", this, handle);
504     if (m_client)
505         m_client->didStartClosingHandshake();
506 }
507
508 void NewWebSocketChannelImpl::didFinishLoadingBlob(PassRefPtr<ArrayBuffer> buffer)
509 {
510     m_blobLoader.clear();
511     ASSERT(m_handle);
512     // The loaded blob is always placed on m_messages[0].
513     ASSERT(m_messages.size() > 0 && m_messages.first().type == MessageTypeBlob);
514     // We replace it with the loaded blob.
515     m_messages.first() = Message(buffer);
516     sendInternal();
517 }
518
519 void NewWebSocketChannelImpl::didFailLoadingBlob(FileError::ErrorCode errorCode)
520 {
521     m_blobLoader.clear();
522     if (errorCode == FileError::ABORT_ERR) {
523         // The error is caused by cancel().
524         return;
525     }
526     // FIXME: Generate human-friendly reason message.
527     failAsError("Failed to load Blob: error code = " + String::number(errorCode));
528     // |this| can be deleted here.
529 }
530
531 void NewWebSocketChannelImpl::trace(Visitor* visitor)
532 {
533     visitor->trace(m_blobLoader);
534     WebSocketChannel::trace(visitor);
535 }
536
537 } // namespace WebCore