1 /* 2 * Copyright (C) 2011 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 33 #include "modules/websockets/DOMWebSocket.h" 34 35 #include "bindings/core/v8/ExceptionState.h" 36 #include "bindings/core/v8/ScriptController.h" 37 #include "core/dom/Document.h" 38 #include "core/dom/ExceptionCode.h" 39 #include "core/dom/ExecutionContext.h" 40 #include "core/events/MessageEvent.h" 41 #include "core/fileapi/Blob.h" 42 #include "core/frame/ConsoleTypes.h" 43 #include "core/frame/LocalDOMWindow.h" 44 #include "core/frame/LocalFrame.h" 45 #include "core/frame/csp/ContentSecurityPolicy.h" 46 #include "core/inspector/ConsoleMessage.h" 47 #include "core/inspector/ScriptCallStack.h" 48 #include "modules/websockets/CloseEvent.h" 49 #include "platform/Logging.h" 50 #include "platform/blob/BlobData.h" 51 #include "platform/heap/Handle.h" 52 #include "platform/weborigin/KnownPorts.h" 53 #include "platform/weborigin/SecurityOrigin.h" 54 #include "public/platform/Platform.h" 55 #include "wtf/ArrayBuffer.h" 56 #include "wtf/ArrayBufferView.h" 57 #include "wtf/Assertions.h" 58 #include "wtf/HashSet.h" 59 #include "wtf/PassOwnPtr.h" 60 #include "wtf/StdLibExtras.h" 61 #include "wtf/text/CString.h" 62 #include "wtf/text/StringBuilder.h" 63 #include "wtf/text/WTFString.h" 64 65 namespace blink { 66 67 DOMWebSocket::EventQueue::EventQueue(EventTarget* target) 68 : m_state(Active) 69 , m_target(target) 70 , m_resumeTimer(this, &EventQueue::resumeTimerFired) { } 71 72 DOMWebSocket::EventQueue::~EventQueue() { stop(); } 73 74 void DOMWebSocket::EventQueue::dispatch(PassRefPtrWillBeRawPtr<Event> event) 75 { 76 switch (m_state) { 77 case Active: 78 ASSERT(m_events.isEmpty()); 79 ASSERT(m_target->executionContext()); 80 m_target->dispatchEvent(event); 81 break; 82 case Suspended: 83 m_events.append(event); 84 break; 85 case Stopped: 86 ASSERT(m_events.isEmpty()); 87 // Do nothing. 88 break; 89 } 90 } 91 92 bool DOMWebSocket::EventQueue::isEmpty() const 93 { 94 return m_events.isEmpty(); 95 } 96 97 void DOMWebSocket::EventQueue::suspend() 98 { 99 m_resumeTimer.stop(); 100 if (m_state != Active) 101 return; 102 103 m_state = Suspended; 104 } 105 106 void DOMWebSocket::EventQueue::resume() 107 { 108 if (m_state != Suspended || m_resumeTimer.isActive()) 109 return; 110 111 m_resumeTimer.startOneShot(0, FROM_HERE); 112 } 113 114 void DOMWebSocket::EventQueue::stop() 115 { 116 if (m_state == Stopped) 117 return; 118 119 m_state = Stopped; 120 m_resumeTimer.stop(); 121 m_events.clear(); 122 } 123 124 void DOMWebSocket::EventQueue::dispatchQueuedEvents() 125 { 126 if (m_state != Active) 127 return; 128 129 WillBeHeapDeque<RefPtrWillBeMember<Event> > events; 130 events.swap(m_events); 131 while (!events.isEmpty()) { 132 if (m_state == Stopped || m_state == Suspended) 133 break; 134 ASSERT(m_state == Active); 135 ASSERT(m_target->executionContext()); 136 m_target->dispatchEvent(events.takeFirst()); 137 // |this| can be stopped here. 138 } 139 if (m_state == Suspended) { 140 while (!m_events.isEmpty()) 141 events.append(m_events.takeFirst()); 142 events.swap(m_events); 143 } 144 } 145 146 void DOMWebSocket::EventQueue::resumeTimerFired(Timer<EventQueue>*) 147 { 148 ASSERT(m_state == Suspended); 149 m_state = Active; 150 dispatchQueuedEvents(); 151 } 152 153 void DOMWebSocket::EventQueue::trace(Visitor* visitor) 154 { 155 visitor->trace(m_events); 156 } 157 158 const size_t maxReasonSizeInBytes = 123; 159 160 static inline bool isValidSubprotocolCharacter(UChar character) 161 { 162 const UChar minimumProtocolCharacter = '!'; // U+0021. 163 const UChar maximumProtocolCharacter = '~'; // U+007E. 164 // Set to true if character does not matches "separators" ABNF defined in 165 // RFC2616. SP and HT are excluded since the range check excludes them. 166 bool isNotSeparator = character != '"' && character != '(' && character != ')' && character != ',' && character != '/' 167 && !(character >= ':' && character <= '@') // U+003A - U+0040 (':', ';', '<', '=', '>', '?', '@'). 168 && !(character >= '[' && character <= ']') // U+005B - U+005D ('[', '\\', ']'). 169 && character != '{' && character != '}'; 170 return character >= minimumProtocolCharacter && character <= maximumProtocolCharacter && isNotSeparator; 171 } 172 173 bool DOMWebSocket::isValidSubprotocolString(const String& protocol) 174 { 175 if (protocol.isEmpty()) 176 return false; 177 for (size_t i = 0; i < protocol.length(); ++i) { 178 if (!isValidSubprotocolCharacter(protocol[i])) 179 return false; 180 } 181 return true; 182 } 183 184 static String encodeSubprotocolString(const String& protocol) 185 { 186 StringBuilder builder; 187 for (size_t i = 0; i < protocol.length(); i++) { 188 if (protocol[i] < 0x20 || protocol[i] > 0x7E) 189 builder.append(String::format("\\u%04X", protocol[i])); 190 else if (protocol[i] == 0x5c) 191 builder.appendLiteral("\\\\"); 192 else 193 builder.append(protocol[i]); 194 } 195 return builder.toString(); 196 } 197 198 static String joinStrings(const Vector<String>& strings, const char* separator) 199 { 200 StringBuilder builder; 201 for (size_t i = 0; i < strings.size(); ++i) { 202 if (i) 203 builder.append(separator); 204 builder.append(strings[i]); 205 } 206 return builder.toString(); 207 } 208 209 static unsigned long saturateAdd(unsigned long a, unsigned long b) 210 { 211 if (std::numeric_limits<unsigned long>::max() - a < b) 212 return std::numeric_limits<unsigned long>::max(); 213 return a + b; 214 } 215 216 static void setInvalidStateErrorForSendMethod(ExceptionState& exceptionState) 217 { 218 exceptionState.throwDOMException(InvalidStateError, "Still in CONNECTING state."); 219 } 220 221 const char* DOMWebSocket::subprotocolSeperator() 222 { 223 return ", "; 224 } 225 226 DOMWebSocket::DOMWebSocket(ExecutionContext* context) 227 : ActiveDOMObject(context) 228 , m_state(CONNECTING) 229 , m_bufferedAmount(0) 230 , m_consumedBufferedAmount(0) 231 , m_bufferedAmountAfterClose(0) 232 , m_binaryType(BinaryTypeBlob) 233 , m_subprotocol("") 234 , m_extensions("") 235 , m_eventQueue(EventQueue::create(this)) 236 , m_bufferedAmountConsumeTimer(this, &DOMWebSocket::reflectBufferedAmountConsumption) 237 { 238 } 239 240 DOMWebSocket::~DOMWebSocket() 241 { 242 ASSERT(!m_channel); 243 } 244 245 void DOMWebSocket::logError(const String& message) 246 { 247 executionContext()->addConsoleMessage(ConsoleMessage::create(JSMessageSource, ErrorMessageLevel, message)); 248 } 249 250 DOMWebSocket* DOMWebSocket::create(ExecutionContext* context, const String& url, ExceptionState& exceptionState) 251 { 252 Vector<String> protocols; 253 return create(context, url, protocols, exceptionState); 254 } 255 256 DOMWebSocket* DOMWebSocket::create(ExecutionContext* context, const String& url, const Vector<String>& protocols, ExceptionState& exceptionState) 257 { 258 if (url.isNull()) { 259 exceptionState.throwDOMException(SyntaxError, "Failed to create a WebSocket: the provided URL is invalid."); 260 return nullptr; 261 } 262 263 DOMWebSocket* webSocket(adoptRefCountedGarbageCollectedWillBeNoop(new DOMWebSocket(context))); 264 webSocket->suspendIfNeeded(); 265 266 webSocket->connect(url, protocols, exceptionState); 267 if (exceptionState.hadException()) 268 return nullptr; 269 270 return webSocket; 271 } 272 273 DOMWebSocket* DOMWebSocket::create(ExecutionContext* context, const String& url, const String& protocol, ExceptionState& exceptionState) 274 { 275 Vector<String> protocols; 276 protocols.append(protocol); 277 return create(context, url, protocols, exceptionState); 278 } 279 280 void DOMWebSocket::connect(const String& url, const Vector<String>& protocols, ExceptionState& exceptionState) 281 { 282 WTF_LOG(Network, "WebSocket %p connect() url='%s'", this, url.utf8().data()); 283 m_url = KURL(KURL(), url); 284 285 if (!m_url.isValid()) { 286 m_state = CLOSED; 287 exceptionState.throwDOMException(SyntaxError, "The URL '" + url + "' is invalid."); 288 return; 289 } 290 if (!m_url.protocolIs("ws") && !m_url.protocolIs("wss")) { 291 m_state = CLOSED; 292 exceptionState.throwDOMException(SyntaxError, "The URL's scheme must be either 'ws' or 'wss'. '" + m_url.protocol() + "' is not allowed."); 293 return; 294 } 295 296 if (m_url.hasFragmentIdentifier()) { 297 m_state = CLOSED; 298 exceptionState.throwDOMException(SyntaxError, "The URL contains a fragment identifier ('" + m_url.fragmentIdentifier() + "'). Fragment identifiers are not allowed in WebSocket URLs."); 299 return; 300 } 301 if (!portAllowed(m_url)) { 302 m_state = CLOSED; 303 exceptionState.throwSecurityError("The port " + String::number(m_url.port()) + " is not allowed."); 304 return; 305 } 306 307 // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved. 308 bool shouldBypassMainWorldCSP = false; 309 if (executionContext()->isDocument()) { 310 Document* document = toDocument(executionContext()); 311 shouldBypassMainWorldCSP = document->frame()->script().shouldBypassMainWorldCSP(); 312 } 313 if (!shouldBypassMainWorldCSP && !executionContext()->contentSecurityPolicy()->allowConnectToSource(m_url)) { 314 m_state = CLOSED; 315 // The URL is safe to expose to JavaScript, as this check happens synchronously before redirection. 316 exceptionState.throwSecurityError("Refused to connect to '" + m_url.elidedString() + "' because it violates the document's Content Security Policy."); 317 return; 318 } 319 320 m_channel = createChannel(executionContext(), this); 321 322 for (size_t i = 0; i < protocols.size(); ++i) { 323 if (!isValidSubprotocolString(protocols[i])) { 324 m_state = CLOSED; 325 exceptionState.throwDOMException(SyntaxError, "The subprotocol '" + encodeSubprotocolString(protocols[i]) + "' is invalid."); 326 releaseChannel(); 327 return; 328 } 329 } 330 HashSet<String> visited; 331 for (size_t i = 0; i < protocols.size(); ++i) { 332 if (!visited.add(protocols[i]).isNewEntry) { 333 m_state = CLOSED; 334 exceptionState.throwDOMException(SyntaxError, "The subprotocol '" + encodeSubprotocolString(protocols[i]) + "' is duplicated."); 335 releaseChannel(); 336 return; 337 } 338 } 339 340 String protocolString; 341 if (!protocols.isEmpty()) 342 protocolString = joinStrings(protocols, subprotocolSeperator()); 343 344 if (!m_channel->connect(m_url, protocolString)) { 345 m_state = CLOSED; 346 exceptionState.throwSecurityError("An insecure WebSocket connection may not be initiated from a page loaded over HTTPS."); 347 releaseChannel(); 348 return; 349 } 350 } 351 352 void DOMWebSocket::updateBufferedAmountAfterClose(unsigned long payloadSize) 353 { 354 m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, payloadSize); 355 m_bufferedAmountAfterClose = saturateAdd(m_bufferedAmountAfterClose, getFramingOverhead(payloadSize)); 356 357 logError("WebSocket is already in CLOSING or CLOSED state."); 358 } 359 360 void DOMWebSocket::reflectBufferedAmountConsumption(Timer<DOMWebSocket>*) 361 { 362 ASSERT(m_bufferedAmount >= m_consumedBufferedAmount); 363 WTF_LOG(Network, "WebSocket %p reflectBufferedAmountConsumption() %lu => %lu", this, m_bufferedAmount, m_bufferedAmount - m_consumedBufferedAmount); 364 365 m_bufferedAmount -= m_consumedBufferedAmount; 366 m_consumedBufferedAmount = 0; 367 } 368 369 void DOMWebSocket::releaseChannel() 370 { 371 ASSERT(m_channel); 372 m_channel->disconnect(); 373 m_channel = nullptr; 374 } 375 376 void DOMWebSocket::send(const String& message, ExceptionState& exceptionState) 377 { 378 WTF_LOG(Network, "WebSocket %p send() Sending String '%s'", this, message.utf8().data()); 379 if (m_state == CONNECTING) { 380 setInvalidStateErrorForSendMethod(exceptionState); 381 return; 382 } 383 // No exception is raised if the connection was once established but has subsequently been closed. 384 if (m_state == CLOSING || m_state == CLOSED) { 385 updateBufferedAmountAfterClose(message.utf8().length()); 386 return; 387 } 388 Platform::current()->histogramEnumeration("WebCore.WebSocket.SendType", WebSocketSendTypeString, WebSocketSendTypeMax); 389 ASSERT(m_channel); 390 m_bufferedAmount += message.utf8().length(); 391 m_channel->send(message); 392 } 393 394 void DOMWebSocket::send(ArrayBuffer* binaryData, ExceptionState& exceptionState) 395 { 396 WTF_LOG(Network, "WebSocket %p send() Sending ArrayBuffer %p", this, binaryData); 397 ASSERT(binaryData); 398 if (m_state == CONNECTING) { 399 setInvalidStateErrorForSendMethod(exceptionState); 400 return; 401 } 402 if (m_state == CLOSING || m_state == CLOSED) { 403 updateBufferedAmountAfterClose(binaryData->byteLength()); 404 return; 405 } 406 Platform::current()->histogramEnumeration("WebCore.WebSocket.SendType", WebSocketSendTypeArrayBuffer, WebSocketSendTypeMax); 407 ASSERT(m_channel); 408 m_bufferedAmount += binaryData->byteLength(); 409 m_channel->send(*binaryData, 0, binaryData->byteLength()); 410 } 411 412 void DOMWebSocket::send(ArrayBufferView* arrayBufferView, ExceptionState& exceptionState) 413 { 414 WTF_LOG(Network, "WebSocket %p send() Sending ArrayBufferView %p", this, arrayBufferView); 415 ASSERT(arrayBufferView); 416 if (m_state == CONNECTING) { 417 setInvalidStateErrorForSendMethod(exceptionState); 418 return; 419 } 420 if (m_state == CLOSING || m_state == CLOSED) { 421 updateBufferedAmountAfterClose(arrayBufferView->byteLength()); 422 return; 423 } 424 Platform::current()->histogramEnumeration("WebCore.WebSocket.SendType", WebSocketSendTypeArrayBufferView, WebSocketSendTypeMax); 425 ASSERT(m_channel); 426 m_bufferedAmount += arrayBufferView->byteLength(); 427 RefPtr<ArrayBuffer> arrayBuffer(arrayBufferView->buffer()); 428 m_channel->send(*arrayBuffer, arrayBufferView->byteOffset(), arrayBufferView->byteLength()); 429 } 430 431 void DOMWebSocket::send(Blob* binaryData, ExceptionState& exceptionState) 432 { 433 WTF_LOG(Network, "WebSocket %p send() Sending Blob '%s'", this, binaryData->uuid().utf8().data()); 434 ASSERT(binaryData); 435 if (m_state == CONNECTING) { 436 setInvalidStateErrorForSendMethod(exceptionState); 437 return; 438 } 439 if (m_state == CLOSING || m_state == CLOSED) { 440 updateBufferedAmountAfterClose(static_cast<unsigned long>(binaryData->size())); 441 return; 442 } 443 Platform::current()->histogramEnumeration("WebCore.WebSocket.SendType", WebSocketSendTypeBlob, WebSocketSendTypeMax); 444 m_bufferedAmount += binaryData->size(); 445 ASSERT(m_channel); 446 m_channel->send(binaryData->blobDataHandle()); 447 } 448 449 void DOMWebSocket::close(unsigned short code, const String& reason, ExceptionState& exceptionState) 450 { 451 closeInternal(code, reason, exceptionState); 452 } 453 454 void DOMWebSocket::close(ExceptionState& exceptionState) 455 { 456 closeInternal(WebSocketChannel::CloseEventCodeNotSpecified, String(), exceptionState); 457 } 458 459 void DOMWebSocket::close(unsigned short code, ExceptionState& exceptionState) 460 { 461 closeInternal(code, String(), exceptionState); 462 } 463 464 void DOMWebSocket::closeInternal(int code, const String& reason, ExceptionState& exceptionState) 465 { 466 if (code == WebSocketChannel::CloseEventCodeNotSpecified) { 467 WTF_LOG(Network, "WebSocket %p close() without code and reason", this); 468 } else { 469 WTF_LOG(Network, "WebSocket %p close() code=%d reason='%s'", this, code, reason.utf8().data()); 470 if (!(code == WebSocketChannel::CloseEventCodeNormalClosure || (WebSocketChannel::CloseEventCodeMinimumUserDefined <= code && code <= WebSocketChannel::CloseEventCodeMaximumUserDefined))) { 471 exceptionState.throwDOMException(InvalidAccessError, "The code must be either 1000, or between 3000 and 4999. " + String::number(code) + " is neither."); 472 return; 473 } 474 CString utf8 = reason.utf8(StrictUTF8ConversionReplacingUnpairedSurrogatesWithFFFD); 475 if (utf8.length() > maxReasonSizeInBytes) { 476 exceptionState.throwDOMException(SyntaxError, "The message must not be greater than " + String::number(maxReasonSizeInBytes) + " bytes."); 477 return; 478 } 479 } 480 481 if (m_state == CLOSING || m_state == CLOSED) 482 return; 483 if (m_state == CONNECTING) { 484 m_state = CLOSING; 485 m_channel->fail("WebSocket is closed before the connection is established.", WarningMessageLevel, String(), 0); 486 return; 487 } 488 m_state = CLOSING; 489 if (m_channel) 490 m_channel->close(code, reason); 491 } 492 493 const KURL& DOMWebSocket::url() const 494 { 495 return m_url; 496 } 497 498 DOMWebSocket::State DOMWebSocket::readyState() const 499 { 500 return m_state; 501 } 502 503 unsigned long DOMWebSocket::bufferedAmount() const 504 { 505 return saturateAdd(m_bufferedAmount, m_bufferedAmountAfterClose); 506 } 507 508 String DOMWebSocket::protocol() const 509 { 510 return m_subprotocol; 511 } 512 513 String DOMWebSocket::extensions() const 514 { 515 return m_extensions; 516 } 517 518 String DOMWebSocket::binaryType() const 519 { 520 switch (m_binaryType) { 521 case BinaryTypeBlob: 522 return "blob"; 523 case BinaryTypeArrayBuffer: 524 return "arraybuffer"; 525 } 526 ASSERT_NOT_REACHED(); 527 return String(); 528 } 529 530 void DOMWebSocket::setBinaryType(const String& binaryType) 531 { 532 if (binaryType == "blob") { 533 m_binaryType = BinaryTypeBlob; 534 return; 535 } 536 if (binaryType == "arraybuffer") { 537 m_binaryType = BinaryTypeArrayBuffer; 538 return; 539 } 540 logError("'" + binaryType + "' is not a valid value for binaryType; binaryType remains unchanged."); 541 } 542 543 const AtomicString& DOMWebSocket::interfaceName() const 544 { 545 return EventTargetNames::DOMWebSocket; 546 } 547 548 ExecutionContext* DOMWebSocket::executionContext() const 549 { 550 return ActiveDOMObject::executionContext(); 551 } 552 553 void DOMWebSocket::contextDestroyed() 554 { 555 WTF_LOG(Network, "WebSocket %p contextDestroyed()", this); 556 ASSERT(!m_channel); 557 ASSERT(m_state == CLOSED); 558 ActiveDOMObject::contextDestroyed(); 559 } 560 561 bool DOMWebSocket::hasPendingActivity() const 562 { 563 return m_channel || !m_eventQueue->isEmpty(); 564 } 565 566 void DOMWebSocket::suspend() 567 { 568 if (m_channel) 569 m_channel->suspend(); 570 m_eventQueue->suspend(); 571 } 572 573 void DOMWebSocket::resume() 574 { 575 if (m_channel) 576 m_channel->resume(); 577 m_eventQueue->resume(); 578 } 579 580 void DOMWebSocket::stop() 581 { 582 m_eventQueue->stop(); 583 if (m_channel) { 584 m_channel->close(WebSocketChannel::CloseEventCodeGoingAway, String()); 585 releaseChannel(); 586 } 587 m_state = CLOSED; 588 } 589 590 void DOMWebSocket::didConnect(const String& subprotocol, const String& extensions) 591 { 592 WTF_LOG(Network, "WebSocket %p didConnect()", this); 593 if (m_state != CONNECTING) 594 return; 595 m_state = OPEN; 596 m_subprotocol = subprotocol; 597 m_extensions = extensions; 598 m_eventQueue->dispatch(Event::create(EventTypeNames::open)); 599 } 600 601 void DOMWebSocket::didReceiveMessage(const String& msg) 602 { 603 WTF_LOG(Network, "WebSocket %p didReceiveMessage() Text message '%s'", this, msg.utf8().data()); 604 if (m_state != OPEN) 605 return; 606 m_eventQueue->dispatch(MessageEvent::create(msg, SecurityOrigin::create(m_url)->toString())); 607 } 608 609 void DOMWebSocket::didReceiveBinaryData(PassOwnPtr<Vector<char> > binaryData) 610 { 611 WTF_LOG(Network, "WebSocket %p didReceiveBinaryData() %lu byte binary message", this, static_cast<unsigned long>(binaryData->size())); 612 switch (m_binaryType) { 613 case BinaryTypeBlob: { 614 size_t size = binaryData->size(); 615 RefPtr<RawData> rawData = RawData::create(); 616 binaryData->swap(*rawData->mutableData()); 617 OwnPtr<BlobData> blobData = BlobData::create(); 618 blobData->appendData(rawData.release(), 0, BlobDataItem::toEndOfFile); 619 RefPtrWillBeRawPtr<Blob> blob = Blob::create(BlobDataHandle::create(blobData.release(), size)); 620 m_eventQueue->dispatch(MessageEvent::create(blob.release(), SecurityOrigin::create(m_url)->toString())); 621 break; 622 } 623 624 case BinaryTypeArrayBuffer: 625 RefPtr<ArrayBuffer> arrayBuffer = ArrayBuffer::create(binaryData->data(), binaryData->size()); 626 if (!arrayBuffer) { 627 // Failed to allocate an ArrayBuffer. We need to crash the renderer 628 // since there's no way defined in the spec to tell this to the 629 // user. 630 CRASH(); 631 } 632 m_eventQueue->dispatch(MessageEvent::create(arrayBuffer.release(), SecurityOrigin::create(m_url)->toString())); 633 break; 634 } 635 } 636 637 void DOMWebSocket::didReceiveMessageError() 638 { 639 WTF_LOG(Network, "WebSocket %p didReceiveMessageError()", this); 640 m_state = CLOSED; 641 m_eventQueue->dispatch(Event::create(EventTypeNames::error)); 642 } 643 644 void DOMWebSocket::didConsumeBufferedAmount(unsigned long consumed) 645 { 646 ASSERT(m_bufferedAmount >= consumed); 647 WTF_LOG(Network, "WebSocket %p didConsumeBufferedAmount(%lu)", this, consumed); 648 if (m_state == CLOSED) 649 return; 650 m_consumedBufferedAmount += consumed; 651 if (!m_bufferedAmountConsumeTimer.isActive()) 652 m_bufferedAmountConsumeTimer.startOneShot(0, FROM_HERE); 653 } 654 655 void DOMWebSocket::didStartClosingHandshake() 656 { 657 WTF_LOG(Network, "WebSocket %p didStartClosingHandshake()", this); 658 m_state = CLOSING; 659 } 660 661 void DOMWebSocket::didClose(ClosingHandshakeCompletionStatus closingHandshakeCompletion, unsigned short code, const String& reason) 662 { 663 WTF_LOG(Network, "WebSocket %p didClose()", this); 664 if (!m_channel) 665 return; 666 bool hasAllDataConsumed = m_bufferedAmount == m_consumedBufferedAmount; 667 bool wasClean = m_state == CLOSING && hasAllDataConsumed && closingHandshakeCompletion == ClosingHandshakeComplete && code != WebSocketChannel::CloseEventCodeAbnormalClosure; 668 m_state = CLOSED; 669 670 m_eventQueue->dispatch(CloseEvent::create(wasClean, code, reason)); 671 releaseChannel(); 672 } 673 674 size_t DOMWebSocket::getFramingOverhead(size_t payloadSize) 675 { 676 static const size_t hybiBaseFramingOverhead = 2; // Every frame has at least two-byte header. 677 static const size_t hybiMaskingKeyLength = 4; // Every frame from client must have masking key. 678 static const size_t minimumPayloadSizeWithTwoByteExtendedPayloadLength = 126; 679 static const size_t minimumPayloadSizeWithEightByteExtendedPayloadLength = 0x10000; 680 size_t overhead = hybiBaseFramingOverhead + hybiMaskingKeyLength; 681 if (payloadSize >= minimumPayloadSizeWithEightByteExtendedPayloadLength) 682 overhead += 8; 683 else if (payloadSize >= minimumPayloadSizeWithTwoByteExtendedPayloadLength) 684 overhead += 2; 685 return overhead; 686 } 687 688 void DOMWebSocket::trace(Visitor* visitor) 689 { 690 visitor->trace(m_channel); 691 visitor->trace(m_eventQueue); 692 WebSocketChannelClient::trace(visitor); 693 EventTargetWithInlineData::trace(visitor); 694 } 695 696 } // namespace blink 697