| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2009, 2012 Ericsson AB. All rights reserved. | 2 * Copyright (C) 2009, 2012 Ericsson AB. All rights reserved. |
| 3 * Copyright (C) 2010 Apple Inc. All rights reserved. | 3 * Copyright (C) 2010 Apple Inc. All rights reserved. |
| 4 * Copyright (C) 2011, Code Aurora Forum. All rights reserved. | 4 * Copyright (C) 2011, Code Aurora Forum. All rights reserved. |
| 5 * | 5 * |
| 6 * Redistribution and use in source and binary forms, with or without | 6 * Redistribution and use in source and binary forms, with or without |
| 7 * modification, are permitted provided that the following conditions | 7 * modification, are permitted provided that the following conditions |
| 8 * are met: | 8 * are met: |
| 9 * | 9 * |
| 10 * 1. Redistributions of source code must retain the above copyright | 10 * 1. Redistributions of source code must retain the above copyright |
| (...skipping 26 matching lines...) Expand all Loading... |
| 37 #include "bindings/core/v8/SerializedScriptValue.h" | 37 #include "bindings/core/v8/SerializedScriptValue.h" |
| 38 #include "bindings/core/v8/SerializedScriptValueFactory.h" | 38 #include "bindings/core/v8/SerializedScriptValueFactory.h" |
| 39 #include "core/dom/Document.h" | 39 #include "core/dom/Document.h" |
| 40 #include "core/dom/ExceptionCode.h" | 40 #include "core/dom/ExceptionCode.h" |
| 41 #include "core/dom/ExecutionContext.h" | 41 #include "core/dom/ExecutionContext.h" |
| 42 #include "core/events/Event.h" | 42 #include "core/events/Event.h" |
| 43 #include "core/events/MessageEvent.h" | 43 #include "core/events/MessageEvent.h" |
| 44 #include "core/frame/LocalDOMWindow.h" | 44 #include "core/frame/LocalDOMWindow.h" |
| 45 #include "core/frame/LocalFrame.h" | 45 #include "core/frame/LocalFrame.h" |
| 46 #include "core/frame/csp/ContentSecurityPolicy.h" | 46 #include "core/frame/csp/ContentSecurityPolicy.h" |
| 47 #include "core/html/parser/TextResourceDecoder.h" | |
| 48 #include "core/inspector/ConsoleMessage.h" | 47 #include "core/inspector/ConsoleMessage.h" |
| 49 #include "core/inspector/InspectorInstrumentation.h" | 48 #include "core/inspector/InspectorInstrumentation.h" |
| 50 #include "core/loader/ThreadableLoader.h" | 49 #include "core/loader/ThreadableLoader.h" |
| 51 #include "core/page/EventSourceInit.h" | 50 #include "core/page/EventSourceInit.h" |
| 52 #include "platform/HTTPNames.h" | 51 #include "platform/HTTPNames.h" |
| 53 #include "platform/network/ResourceError.h" | 52 #include "platform/network/ResourceError.h" |
| 54 #include "platform/network/ResourceRequest.h" | 53 #include "platform/network/ResourceRequest.h" |
| 55 #include "platform/network/ResourceResponse.h" | 54 #include "platform/network/ResourceResponse.h" |
| 56 #include "platform/weborigin/SecurityOrigin.h" | 55 #include "platform/weborigin/SecurityOrigin.h" |
| 57 #include "public/platform/WebURLRequest.h" | 56 #include "public/platform/WebURLRequest.h" |
| 58 #include "wtf/ASCIICType.h" | |
| 59 #include "wtf/text/StringBuilder.h" | 57 #include "wtf/text/StringBuilder.h" |
| 60 | 58 |
| 61 namespace blink { | 59 namespace blink { |
| 62 | 60 |
| 63 const unsigned long long EventSource::defaultReconnectDelay = 3000; | 61 const unsigned long long EventSource::defaultReconnectDelay = 3000; |
| 64 | 62 |
| 65 inline EventSource::EventSource(ExecutionContext* context, const KURL& url, cons
t EventSourceInit& eventSourceInit) | 63 inline EventSource::EventSource(ExecutionContext* context, const KURL& url, cons
t EventSourceInit& eventSourceInit) |
| 66 : ActiveDOMObject(context) | 64 : ActiveDOMObject(context) |
| 67 , m_url(url) | 65 , m_url(url) |
| 68 , m_withCredentials(eventSourceInit.withCredentials()) | 66 , m_withCredentials(eventSourceInit.withCredentials()) |
| 69 , m_state(CONNECTING) | 67 , m_state(CONNECTING) |
| 70 , m_decoder(TextResourceDecoder::create("text/plain", "UTF-8")) | |
| 71 , m_connectTimer(this, &EventSource::connectTimerFired) | 68 , m_connectTimer(this, &EventSource::connectTimerFired) |
| 72 , m_discardTrailingNewline(false) | |
| 73 , m_reconnectDelay(defaultReconnectDelay) | 69 , m_reconnectDelay(defaultReconnectDelay) |
| 74 { | 70 { |
| 75 } | 71 } |
| 76 | 72 |
| 77 EventSource* EventSource::create(ExecutionContext* context, const String& url, c
onst EventSourceInit& eventSourceInit, ExceptionState& exceptionState) | 73 EventSource* EventSource::create(ExecutionContext* context, const String& url, c
onst EventSourceInit& eventSourceInit, ExceptionState& exceptionState) |
| 78 { | 74 { |
| 79 if (url.isEmpty()) { | 75 if (url.isEmpty()) { |
| 80 exceptionState.throwDOMException(SyntaxError, "Cannot open an EventSourc
e to an empty URL."); | 76 exceptionState.throwDOMException(SyntaxError, "Cannot open an EventSourc
e to an empty URL."); |
| 81 return nullptr; | 77 return nullptr; |
| 82 } | 78 } |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 120 ASSERT(m_state == CONNECTING); | 116 ASSERT(m_state == CONNECTING); |
| 121 ASSERT(!m_loader); | 117 ASSERT(!m_loader); |
| 122 ASSERT(executionContext()); | 118 ASSERT(executionContext()); |
| 123 | 119 |
| 124 ExecutionContext& executionContext = *this->executionContext(); | 120 ExecutionContext& executionContext = *this->executionContext(); |
| 125 ResourceRequest request(m_url); | 121 ResourceRequest request(m_url); |
| 126 request.setHTTPMethod(HTTPNames::GET); | 122 request.setHTTPMethod(HTTPNames::GET); |
| 127 request.setHTTPHeaderField(HTTPNames::Accept, "text/event-stream"); | 123 request.setHTTPHeaderField(HTTPNames::Accept, "text/event-stream"); |
| 128 request.setHTTPHeaderField(HTTPNames::Cache_Control, "no-cache"); | 124 request.setHTTPHeaderField(HTTPNames::Cache_Control, "no-cache"); |
| 129 request.setRequestContext(WebURLRequest::RequestContextEventSource); | 125 request.setRequestContext(WebURLRequest::RequestContextEventSource); |
| 130 if (!m_lastEventId.isEmpty()) { | 126 if (m_parser && !m_parser->lastEventId().isEmpty()) { |
| 131 // HTTP headers are Latin-1 byte strings, but the Last-Event-ID header i
s encoded as UTF-8. | 127 // HTTP headers are Latin-1 byte strings, but the Last-Event-ID header i
s encoded as UTF-8. |
| 132 // TODO(davidben): This should be captured in the type of setHTTPHeaderF
ield's arguments. | 128 // TODO(davidben): This should be captured in the type of setHTTPHeaderF
ield's arguments. |
| 133 CString lastEventIdUtf8 = m_lastEventId.utf8(); | 129 CString lastEventIdUtf8 = m_parser->lastEventId().utf8(); |
| 134 request.setHTTPHeaderField(HTTPNames::Last_Event_ID, AtomicString(reinte
rpret_cast<const LChar*>(lastEventIdUtf8.data()), lastEventIdUtf8.length())); | 130 request.setHTTPHeaderField(HTTPNames::Last_Event_ID, AtomicString(reinte
rpret_cast<const LChar*>(lastEventIdUtf8.data()), lastEventIdUtf8.length())); |
| 135 } | 131 } |
| 136 | 132 |
| 137 SecurityOrigin* origin = executionContext.securityOrigin(); | 133 SecurityOrigin* origin = executionContext.securityOrigin(); |
| 138 | 134 |
| 139 ThreadableLoaderOptions options; | 135 ThreadableLoaderOptions options; |
| 140 options.preflightPolicy = PreventPreflight; | 136 options.preflightPolicy = PreventPreflight; |
| 141 options.crossOriginRequestPolicy = UseAccessControl; | 137 options.crossOriginRequestPolicy = UseAccessControl; |
| 142 options.contentSecurityPolicyEnforcement = ContentSecurityPolicy::shouldBypa
ssMainWorld(&executionContext) ? DoNotEnforceContentSecurityPolicy : EnforceConn
ectSrcDirective; | 138 options.contentSecurityPolicyEnforcement = ContentSecurityPolicy::shouldBypa
ssMainWorld(&executionContext) ? DoNotEnforceContentSecurityPolicy : EnforceConn
ectSrcDirective; |
| 143 | 139 |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 189 { | 185 { |
| 190 return m_state; | 186 return m_state; |
| 191 } | 187 } |
| 192 | 188 |
| 193 void EventSource::close() | 189 void EventSource::close() |
| 194 { | 190 { |
| 195 if (m_state == CLOSED) { | 191 if (m_state == CLOSED) { |
| 196 ASSERT(!m_loader); | 192 ASSERT(!m_loader); |
| 197 return; | 193 return; |
| 198 } | 194 } |
| 195 if (m_parser) |
| 196 m_parser->stop(); |
| 199 | 197 |
| 200 // Stop trying to reconnect if EventSource was explicitly closed or if Activ
eDOMObject::stop() was called. | 198 // Stop trying to reconnect if EventSource was explicitly closed or if Activ
eDOMObject::stop() was called. |
| 201 if (m_connectTimer.isActive()) { | 199 if (m_connectTimer.isActive()) { |
| 202 m_connectTimer.stop(); | 200 m_connectTimer.stop(); |
| 203 } | 201 } |
| 204 | 202 |
| 205 if (m_loader) { | 203 if (m_loader) { |
| 206 m_loader->cancel(); | 204 m_loader->cancel(); |
| 207 m_loader = nullptr; | 205 m_loader = nullptr; |
| 208 } | 206 } |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 249 message.appendLiteral("EventSource's response has a MIME type (\""); | 247 message.appendLiteral("EventSource's response has a MIME type (\""); |
| 250 message.append(response.mimeType()); | 248 message.append(response.mimeType()); |
| 251 message.appendLiteral("\") that is not \"text/event-stream\". Aborti
ng the connection."); | 249 message.appendLiteral("\") that is not \"text/event-stream\". Aborti
ng the connection."); |
| 252 // FIXME: We are missing the source line. | 250 // FIXME: We are missing the source line. |
| 253 executionContext()->addConsoleMessage(ConsoleMessage::create(JSMessa
geSource, ErrorMessageLevel, message.toString())); | 251 executionContext()->addConsoleMessage(ConsoleMessage::create(JSMessa
geSource, ErrorMessageLevel, message.toString())); |
| 254 } | 252 } |
| 255 } | 253 } |
| 256 | 254 |
| 257 if (responseIsValid) { | 255 if (responseIsValid) { |
| 258 m_state = OPEN; | 256 m_state = OPEN; |
| 257 AtomicString lastEventId; |
| 258 if (m_parser) { |
| 259 // The new parser takes over the event ID. |
| 260 lastEventId = m_parser->lastEventId(); |
| 261 } |
| 262 m_parser = new EventSourceParser(lastEventId, this); |
| 259 dispatchEvent(Event::create(EventTypeNames::open)); | 263 dispatchEvent(Event::create(EventTypeNames::open)); |
| 260 } else { | 264 } else { |
| 261 m_loader->cancel(); | 265 m_loader->cancel(); |
| 262 dispatchEvent(Event::create(EventTypeNames::error)); | 266 dispatchEvent(Event::create(EventTypeNames::error)); |
| 263 } | 267 } |
| 264 } | 268 } |
| 265 | 269 |
| 266 void EventSource::didReceiveData(const char* data, unsigned length) | 270 void EventSource::didReceiveData(const char* data, unsigned length) |
| 267 { | 271 { |
| 268 ASSERT(m_state == OPEN); | 272 ASSERT(m_state == OPEN); |
| 269 ASSERT(m_loader); | 273 ASSERT(m_loader); |
| 274 ASSERT(m_parser); |
| 270 | 275 |
| 271 append(m_receiveBuf, m_decoder->decode(data, length)); | 276 m_parser->addBytes(data, length); |
| 272 parseEventStream(); | |
| 273 } | 277 } |
| 274 | 278 |
| 275 void EventSource::didFinishLoading(unsigned long, double) | 279 void EventSource::didFinishLoading(unsigned long, double) |
| 276 { | 280 { |
| 277 ASSERT(m_state == OPEN); | 281 ASSERT(m_state == OPEN); |
| 278 ASSERT(m_loader); | 282 ASSERT(m_loader); |
| 279 | 283 |
| 280 if (m_receiveBuf.size() > 0 || m_data.size() > 0) { | |
| 281 parseEventStream(); | |
| 282 | |
| 283 // Discard everything that has not been dispatched by now. | |
| 284 m_receiveBuf.clear(); | |
| 285 m_data.clear(); | |
| 286 m_eventName = emptyAtom; | |
| 287 m_currentlyParsedEventId = nullAtom; | |
| 288 } | |
| 289 networkRequestEnded(); | 284 networkRequestEnded(); |
| 290 } | 285 } |
| 291 | 286 |
| 292 void EventSource::didFail(const ResourceError& error) | 287 void EventSource::didFail(const ResourceError& error) |
| 293 { | 288 { |
| 294 ASSERT(m_state != CLOSED); | 289 ASSERT(m_state != CLOSED); |
| 295 ASSERT(m_loader); | 290 ASSERT(m_loader); |
| 296 | 291 |
| 297 if (error.isCancellation()) | 292 if (error.isCancellation()) |
| 298 m_state = CLOSED; | 293 m_state = CLOSED; |
| (...skipping 10 matching lines...) Expand all Loading... |
| 309 abortConnectionAttempt(); | 304 abortConnectionAttempt(); |
| 310 } | 305 } |
| 311 | 306 |
| 312 void EventSource::didFailRedirectCheck() | 307 void EventSource::didFailRedirectCheck() |
| 313 { | 308 { |
| 314 ASSERT(m_loader); | 309 ASSERT(m_loader); |
| 315 | 310 |
| 316 abortConnectionAttempt(); | 311 abortConnectionAttempt(); |
| 317 } | 312 } |
| 318 | 313 |
| 314 void EventSource::onMessageEvent(const AtomicString& eventType, const String& da
ta, const AtomicString& lastEventId) |
| 315 { |
| 316 RefPtrWillBeRawPtr<MessageEvent> e = MessageEvent::create(); |
| 317 e->initMessageEvent(eventType, false, false, SerializedScriptValueFactory::i
nstance().create(data), m_eventStreamOrigin, lastEventId, 0, nullptr); |
| 318 |
| 319 InspectorInstrumentation::willDispatchEventSourceEvent(executionContext(), t
his, eventType, lastEventId, data); |
| 320 dispatchEvent(e); |
| 321 } |
| 322 |
| 323 void EventSource::onReconnectionTimeSet(unsigned long long reconnectionTime) |
| 324 { |
| 325 m_reconnectDelay = reconnectionTime; |
| 326 } |
| 327 |
| 319 void EventSource::abortConnectionAttempt() | 328 void EventSource::abortConnectionAttempt() |
| 320 { | 329 { |
| 321 ASSERT(m_state == CONNECTING); | 330 ASSERT(m_state == CONNECTING); |
| 322 | 331 |
| 323 m_loader = nullptr; | 332 m_loader = nullptr; |
| 324 m_state = CLOSED; | 333 m_state = CLOSED; |
| 325 networkRequestEnded(); | 334 networkRequestEnded(); |
| 326 | 335 |
| 327 dispatchEvent(Event::create(EventTypeNames::error)); | 336 dispatchEvent(Event::create(EventTypeNames::error)); |
| 328 } | 337 } |
| 329 | 338 |
| 330 void EventSource::parseEventStream() | |
| 331 { | |
| 332 unsigned bufPos = 0; | |
| 333 unsigned bufSize = m_receiveBuf.size(); | |
| 334 while (bufPos < bufSize) { | |
| 335 if (m_discardTrailingNewline) { | |
| 336 if (m_receiveBuf[bufPos] == '\n') | |
| 337 bufPos++; | |
| 338 m_discardTrailingNewline = false; | |
| 339 } | |
| 340 | |
| 341 int lineLength = -1; | |
| 342 int fieldLength = -1; | |
| 343 for (unsigned i = bufPos; lineLength < 0 && i < bufSize; i++) { | |
| 344 switch (m_receiveBuf[i]) { | |
| 345 case ':': | |
| 346 if (fieldLength < 0) | |
| 347 fieldLength = i - bufPos; | |
| 348 break; | |
| 349 case '\r': | |
| 350 m_discardTrailingNewline = true; | |
| 351 case '\n': | |
| 352 lineLength = i - bufPos; | |
| 353 break; | |
| 354 } | |
| 355 } | |
| 356 | |
| 357 if (lineLength < 0) | |
| 358 break; | |
| 359 | |
| 360 parseEventStreamLine(bufPos, fieldLength, lineLength); | |
| 361 bufPos += lineLength + 1; | |
| 362 | |
| 363 // EventSource.close() might've been called by one of the message event
handlers. | |
| 364 // Per spec, no further messages should be fired after that. | |
| 365 if (m_state == CLOSED) | |
| 366 break; | |
| 367 } | |
| 368 | |
| 369 if (bufPos == bufSize) | |
| 370 m_receiveBuf.clear(); | |
| 371 else if (bufPos) | |
| 372 m_receiveBuf.remove(0, bufPos); | |
| 373 } | |
| 374 | |
| 375 void EventSource::parseEventStreamLine(unsigned bufPos, int fieldLength, int lin
eLength) | |
| 376 { | |
| 377 if (!lineLength) { | |
| 378 if (!m_data.isEmpty()) { | |
| 379 m_data.removeLast(); | |
| 380 if (!m_currentlyParsedEventId.isNull()) { | |
| 381 m_lastEventId = m_currentlyParsedEventId; | |
| 382 m_currentlyParsedEventId = nullAtom; | |
| 383 } | |
| 384 InspectorInstrumentation::willDispachEventSourceEvent(executionConte
xt(), this, m_eventName.isEmpty() ? EventTypeNames::message : m_eventName, m_las
tEventId, m_data); | |
| 385 dispatchEvent(createMessageEvent()); | |
| 386 } | |
| 387 if (!m_eventName.isEmpty()) | |
| 388 m_eventName = emptyAtom; | |
| 389 } else if (fieldLength) { | |
| 390 bool noValue = fieldLength < 0; | |
| 391 | |
| 392 String field(&m_receiveBuf[bufPos], noValue ? lineLength : fieldLength); | |
| 393 int step; | |
| 394 if (noValue) | |
| 395 step = lineLength; | |
| 396 else if (m_receiveBuf[bufPos + fieldLength + 1] != ' ') | |
| 397 step = fieldLength + 1; | |
| 398 else | |
| 399 step = fieldLength + 2; | |
| 400 bufPos += step; | |
| 401 int valueLength = lineLength - step; | |
| 402 | |
| 403 if (field == "data") { | |
| 404 if (valueLength) | |
| 405 m_data.append(&m_receiveBuf[bufPos], valueLength); | |
| 406 m_data.append('\n'); | |
| 407 } else if (field == "event") { | |
| 408 m_eventName = valueLength ? AtomicString(&m_receiveBuf[bufPos], valu
eLength) : ""; | |
| 409 } else if (field == "id") { | |
| 410 m_currentlyParsedEventId = valueLength ? AtomicString(&m_receiveBuf[
bufPos], valueLength) : ""; | |
| 411 } else if (field == "retry") { | |
| 412 bool hasOnlyDigits = true; | |
| 413 for (int i = 0; i < valueLength && hasOnlyDigits; ++i) { | |
| 414 hasOnlyDigits = isASCIIDigit(m_receiveBuf[bufPos + i]); | |
| 415 } | |
| 416 if (!valueLength) { | |
| 417 m_reconnectDelay = defaultReconnectDelay; | |
| 418 } else if (hasOnlyDigits) { | |
| 419 String value(&m_receiveBuf[bufPos], valueLength); | |
| 420 bool ok; | |
| 421 unsigned long long retry = value.toUInt64(&ok); | |
| 422 if (ok) | |
| 423 m_reconnectDelay = retry; | |
| 424 } | |
| 425 } | |
| 426 } | |
| 427 } | |
| 428 | |
| 429 void EventSource::stop() | 339 void EventSource::stop() |
| 430 { | 340 { |
| 431 close(); | 341 close(); |
| 432 } | 342 } |
| 433 | 343 |
| 434 bool EventSource::hasPendingActivity() const | 344 bool EventSource::hasPendingActivity() const |
| 435 { | 345 { |
| 436 return m_state != CLOSED; | 346 return m_state != CLOSED; |
| 437 } | 347 } |
| 438 | 348 |
| 439 PassRefPtrWillBeRawPtr<MessageEvent> EventSource::createMessageEvent() | |
| 440 { | |
| 441 RefPtrWillBeRawPtr<MessageEvent> event = MessageEvent::create(); | |
| 442 event->initMessageEvent(m_eventName.isEmpty() ? EventTypeNames::message : m_
eventName, false, false, SerializedScriptValueFactory::instance().create(String(
m_data)), m_eventStreamOrigin, m_lastEventId, 0, nullptr); | |
| 443 m_data.clear(); | |
| 444 return event.release(); | |
| 445 } | |
| 446 | |
| 447 DEFINE_TRACE(EventSource) | 349 DEFINE_TRACE(EventSource) |
| 448 { | 350 { |
| 351 visitor->trace(m_parser); |
| 449 RefCountedGarbageCollectedEventTargetWithInlineData::trace(visitor); | 352 RefCountedGarbageCollectedEventTargetWithInlineData::trace(visitor); |
| 450 ActiveDOMObject::trace(visitor); | 353 ActiveDOMObject::trace(visitor); |
| 354 EventSourceParser::Client::trace(visitor); |
| 451 } | 355 } |
| 452 | 356 |
| 453 } // namespace blink | 357 } // namespace blink |
| OLD | NEW |