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 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
62 const unsigned long long EventSource::defaultReconnectDelay = 3000; | 62 const unsigned long long EventSource::defaultReconnectDelay = 3000; |
63 | 63 |
64 inline EventSource::EventSource(ExecutionContext* context, const KURL& url, cons
t EventSourceInit& eventSourceInit) | 64 inline EventSource::EventSource(ExecutionContext* context, const KURL& url, cons
t EventSourceInit& eventSourceInit) |
65 : ActiveDOMObject(context) | 65 : ActiveDOMObject(context) |
66 , m_url(url) | 66 , m_url(url) |
67 , m_withCredentials(eventSourceInit.withCredentials()) | 67 , m_withCredentials(eventSourceInit.withCredentials()) |
68 , m_state(CONNECTING) | 68 , m_state(CONNECTING) |
69 , m_decoder(TextResourceDecoder::create("text/plain", "UTF-8")) | 69 , m_decoder(TextResourceDecoder::create("text/plain", "UTF-8")) |
70 , m_connectTimer(this, &EventSource::connectTimerFired) | 70 , m_connectTimer(this, &EventSource::connectTimerFired) |
71 , m_discardTrailingNewline(false) | 71 , m_discardTrailingNewline(false) |
72 , m_requestInFlight(false) | |
73 , m_reconnectDelay(defaultReconnectDelay) | 72 , m_reconnectDelay(defaultReconnectDelay) |
74 { | 73 { |
75 } | 74 } |
76 | 75 |
77 EventSource* EventSource::create(ExecutionContext* context, const String& url, c
onst EventSourceInit& eventSourceInit, ExceptionState& exceptionState) | 76 EventSource* EventSource::create(ExecutionContext* context, const String& url, c
onst EventSourceInit& eventSourceInit, ExceptionState& exceptionState) |
78 { | 77 { |
79 if (url.isEmpty()) { | 78 if (url.isEmpty()) { |
80 exceptionState.throwDOMException(SyntaxError, "Cannot open an EventSourc
e to an empty URL."); | 79 exceptionState.throwDOMException(SyntaxError, "Cannot open an EventSourc
e to an empty URL."); |
81 return nullptr; | 80 return nullptr; |
82 } | 81 } |
(...skipping 14 matching lines...) Expand all Loading... |
97 EventSource* source = new EventSource(context, fullURL, eventSourceInit); | 96 EventSource* source = new EventSource(context, fullURL, eventSourceInit); |
98 | 97 |
99 source->scheduleInitialConnect(); | 98 source->scheduleInitialConnect(); |
100 source->suspendIfNeeded(); | 99 source->suspendIfNeeded(); |
101 return source; | 100 return source; |
102 } | 101 } |
103 | 102 |
104 EventSource::~EventSource() | 103 EventSource::~EventSource() |
105 { | 104 { |
106 ASSERT(m_state == CLOSED); | 105 ASSERT(m_state == CLOSED); |
107 ASSERT(!m_requestInFlight); | 106 ASSERT(!m_loader); |
108 } | 107 } |
109 | 108 |
110 void EventSource::scheduleInitialConnect() | 109 void EventSource::scheduleInitialConnect() |
111 { | 110 { |
112 ASSERT(m_state == CONNECTING); | 111 ASSERT(m_state == CONNECTING); |
113 ASSERT(!m_requestInFlight); | 112 ASSERT(!m_loader); |
114 | 113 |
115 m_connectTimer.startOneShot(0, FROM_HERE); | 114 m_connectTimer.startOneShot(0, FROM_HERE); |
116 } | 115 } |
117 | 116 |
118 void EventSource::connect() | 117 void EventSource::connect() |
119 { | 118 { |
120 ASSERT(m_state == CONNECTING); | 119 ASSERT(m_state == CONNECTING); |
121 ASSERT(!m_requestInFlight); | 120 ASSERT(!m_loader); |
122 ASSERT(executionContext()); | 121 ASSERT(executionContext()); |
123 | 122 |
124 ExecutionContext& executionContext = *this->executionContext(); | 123 ExecutionContext& executionContext = *this->executionContext(); |
125 ResourceRequest request(m_url); | 124 ResourceRequest request(m_url); |
126 request.setHTTPMethod("GET"); | 125 request.setHTTPMethod("GET"); |
127 request.setHTTPHeaderField("Accept", "text/event-stream"); | 126 request.setHTTPHeaderField("Accept", "text/event-stream"); |
128 request.setHTTPHeaderField("Cache-Control", "no-cache"); | 127 request.setHTTPHeaderField("Cache-Control", "no-cache"); |
129 request.setRequestContext(WebURLRequest::RequestContextEventSource); | 128 request.setRequestContext(WebURLRequest::RequestContextEventSource); |
130 if (!m_lastEventId.isEmpty()) | 129 if (!m_lastEventId.isEmpty()) |
131 request.setHTTPHeaderField("Last-Event-ID", m_lastEventId); | 130 request.setHTTPHeaderField("Last-Event-ID", m_lastEventId); |
132 | 131 |
133 SecurityOrigin* origin = executionContext.securityOrigin(); | 132 SecurityOrigin* origin = executionContext.securityOrigin(); |
134 | 133 |
135 ThreadableLoaderOptions options; | 134 ThreadableLoaderOptions options; |
136 options.preflightPolicy = PreventPreflight; | 135 options.preflightPolicy = PreventPreflight; |
137 options.crossOriginRequestPolicy = UseAccessControl; | 136 options.crossOriginRequestPolicy = UseAccessControl; |
138 options.contentSecurityPolicyEnforcement = ContentSecurityPolicy::shouldBypa
ssMainWorld(&executionContext) ? DoNotEnforceContentSecurityPolicy : EnforceConn
ectSrcDirective; | 137 options.contentSecurityPolicyEnforcement = ContentSecurityPolicy::shouldBypa
ssMainWorld(&executionContext) ? DoNotEnforceContentSecurityPolicy : EnforceConn
ectSrcDirective; |
139 | 138 |
140 ResourceLoaderOptions resourceLoaderOptions; | 139 ResourceLoaderOptions resourceLoaderOptions; |
141 resourceLoaderOptions.allowCredentials = (origin->canRequest(m_url) || m_wit
hCredentials) ? AllowStoredCredentials : DoNotAllowStoredCredentials; | 140 resourceLoaderOptions.allowCredentials = (origin->canRequest(m_url) || m_wit
hCredentials) ? AllowStoredCredentials : DoNotAllowStoredCredentials; |
142 resourceLoaderOptions.credentialsRequested = m_withCredentials ? ClientReque
stedCredentials : ClientDidNotRequestCredentials; | 141 resourceLoaderOptions.credentialsRequested = m_withCredentials ? ClientReque
stedCredentials : ClientDidNotRequestCredentials; |
143 resourceLoaderOptions.dataBufferingPolicy = DoNotBufferData; | 142 resourceLoaderOptions.dataBufferingPolicy = DoNotBufferData; |
144 resourceLoaderOptions.securityOrigin = origin; | 143 resourceLoaderOptions.securityOrigin = origin; |
145 | 144 |
146 InspectorInstrumentation::willSendEventSourceRequest(&executionContext, this
); | 145 InspectorInstrumentation::willSendEventSourceRequest(&executionContext, this
); |
147 // InspectorInstrumentation::documentThreadableLoaderStartedLoadingForClient
will be called synchronously. | 146 // InspectorInstrumentation::documentThreadableLoaderStartedLoadingForClient
will be called synchronously. |
148 m_loader = ThreadableLoader::create(executionContext, this, request, options
, resourceLoaderOptions); | 147 m_loader = ThreadableLoader::create(executionContext, this, options, resourc
eLoaderOptions); |
149 | 148 m_loader->start(request); |
150 if (m_loader) | |
151 m_requestInFlight = true; | |
152 } | 149 } |
153 | 150 |
154 void EventSource::networkRequestEnded() | 151 void EventSource::networkRequestEnded() |
155 { | 152 { |
156 if (!m_requestInFlight) | |
157 return; | |
158 | |
159 InspectorInstrumentation::didFinishEventSourceRequest(executionContext(), th
is); | 153 InspectorInstrumentation::didFinishEventSourceRequest(executionContext(), th
is); |
160 | 154 |
161 m_requestInFlight = false; | 155 m_loader = nullptr; |
162 | 156 |
163 if (m_state != CLOSED) | 157 if (m_state != CLOSED) |
164 scheduleReconnect(); | 158 scheduleReconnect(); |
165 } | 159 } |
166 | 160 |
167 void EventSource::scheduleReconnect() | 161 void EventSource::scheduleReconnect() |
168 { | 162 { |
169 m_state = CONNECTING; | 163 m_state = CONNECTING; |
170 m_connectTimer.startOneShot(m_reconnectDelay / 1000.0, FROM_HERE); | 164 m_connectTimer.startOneShot(m_reconnectDelay / 1000.0, FROM_HERE); |
171 dispatchEvent(Event::create(EventTypeNames::error)); | 165 dispatchEvent(Event::create(EventTypeNames::error)); |
(...skipping 15 matching lines...) Expand all Loading... |
187 } | 181 } |
188 | 182 |
189 EventSource::State EventSource::readyState() const | 183 EventSource::State EventSource::readyState() const |
190 { | 184 { |
191 return m_state; | 185 return m_state; |
192 } | 186 } |
193 | 187 |
194 void EventSource::close() | 188 void EventSource::close() |
195 { | 189 { |
196 if (m_state == CLOSED) { | 190 if (m_state == CLOSED) { |
197 ASSERT(!m_requestInFlight); | 191 ASSERT(!m_loader); |
198 return; | 192 return; |
199 } | 193 } |
200 | 194 |
201 // Stop trying to reconnect if EventSource was explicitly closed or if Activ
eDOMObject::stop() was called. | 195 // Stop trying to reconnect if EventSource was explicitly closed or if Activ
eDOMObject::stop() was called. |
202 if (m_connectTimer.isActive()) { | 196 if (m_connectTimer.isActive()) { |
203 m_connectTimer.stop(); | 197 m_connectTimer.stop(); |
204 } | 198 } |
205 | 199 |
206 if (m_requestInFlight) | 200 if (m_loader) { |
207 m_loader->cancel(); | 201 m_loader->cancel(); |
| 202 m_loader = nullptr; |
| 203 } |
208 | 204 |
209 m_state = CLOSED; | 205 m_state = CLOSED; |
210 } | 206 } |
211 | 207 |
212 const AtomicString& EventSource::interfaceName() const | 208 const AtomicString& EventSource::interfaceName() const |
213 { | 209 { |
214 return EventTargetNames::EventSource; | 210 return EventTargetNames::EventSource; |
215 } | 211 } |
216 | 212 |
217 ExecutionContext* EventSource::executionContext() const | 213 ExecutionContext* EventSource::executionContext() const |
218 { | 214 { |
219 return ActiveDOMObject::executionContext(); | 215 return ActiveDOMObject::executionContext(); |
220 } | 216 } |
221 | 217 |
222 void EventSource::didReceiveResponse(unsigned long, const ResourceResponse& resp
onse, PassOwnPtr<WebDataConsumerHandle> handle) | 218 void EventSource::didReceiveResponse(unsigned long, const ResourceResponse& resp
onse, PassOwnPtr<WebDataConsumerHandle> handle) |
223 { | 219 { |
224 ASSERT_UNUSED(handle, !handle); | 220 ASSERT_UNUSED(handle, !handle); |
225 ASSERT(m_state == CONNECTING); | 221 ASSERT(m_state == CONNECTING); |
226 ASSERT(m_requestInFlight); | 222 ASSERT(m_loader); |
227 | 223 |
228 m_eventStreamOrigin = SecurityOrigin::create(response.url())->toString(); | 224 m_eventStreamOrigin = SecurityOrigin::create(response.url())->toString(); |
229 int statusCode = response.httpStatusCode(); | 225 int statusCode = response.httpStatusCode(); |
230 bool mimeTypeIsValid = response.mimeType() == "text/event-stream"; | 226 bool mimeTypeIsValid = response.mimeType() == "text/event-stream"; |
231 bool responseIsValid = statusCode == 200 && mimeTypeIsValid; | 227 bool responseIsValid = statusCode == 200 && mimeTypeIsValid; |
232 if (responseIsValid) { | 228 if (responseIsValid) { |
233 const String& charset = response.textEncodingName(); | 229 const String& charset = response.textEncodingName(); |
234 // If we have a charset, the only allowed value is UTF-8 (case-insensiti
ve). | 230 // If we have a charset, the only allowed value is UTF-8 (case-insensiti
ve). |
235 responseIsValid = charset.isEmpty() || equalIgnoringCase(charset, "UTF-8
"); | 231 responseIsValid = charset.isEmpty() || equalIgnoringCase(charset, "UTF-8
"); |
236 if (!responseIsValid) { | 232 if (!responseIsValid) { |
(...skipping 21 matching lines...) Expand all Loading... |
258 dispatchEvent(Event::create(EventTypeNames::open)); | 254 dispatchEvent(Event::create(EventTypeNames::open)); |
259 } else { | 255 } else { |
260 m_loader->cancel(); | 256 m_loader->cancel(); |
261 dispatchEvent(Event::create(EventTypeNames::error)); | 257 dispatchEvent(Event::create(EventTypeNames::error)); |
262 } | 258 } |
263 } | 259 } |
264 | 260 |
265 void EventSource::didReceiveData(const char* data, unsigned length) | 261 void EventSource::didReceiveData(const char* data, unsigned length) |
266 { | 262 { |
267 ASSERT(m_state == OPEN); | 263 ASSERT(m_state == OPEN); |
268 ASSERT(m_requestInFlight); | 264 ASSERT(m_loader); |
269 | 265 |
270 append(m_receiveBuf, m_decoder->decode(data, length)); | 266 append(m_receiveBuf, m_decoder->decode(data, length)); |
271 parseEventStream(); | 267 parseEventStream(); |
272 } | 268 } |
273 | 269 |
274 void EventSource::didFinishLoading(unsigned long, double) | 270 void EventSource::didFinishLoading(unsigned long, double) |
275 { | 271 { |
276 ASSERT(m_state == OPEN); | 272 ASSERT(m_state == OPEN); |
277 ASSERT(m_requestInFlight); | 273 ASSERT(m_loader); |
278 | 274 |
279 if (m_receiveBuf.size() > 0 || m_data.size() > 0) { | 275 if (m_receiveBuf.size() > 0 || m_data.size() > 0) { |
280 parseEventStream(); | 276 parseEventStream(); |
281 | 277 |
282 // Discard everything that has not been dispatched by now. | 278 // Discard everything that has not been dispatched by now. |
283 m_receiveBuf.clear(); | 279 m_receiveBuf.clear(); |
284 m_data.clear(); | 280 m_data.clear(); |
285 m_eventName = emptyAtom; | 281 m_eventName = emptyAtom; |
286 m_currentlyParsedEventId = nullAtom; | 282 m_currentlyParsedEventId = nullAtom; |
287 } | 283 } |
288 networkRequestEnded(); | 284 networkRequestEnded(); |
289 } | 285 } |
290 | 286 |
291 void EventSource::didFail(const ResourceError& error) | 287 void EventSource::didFail(const ResourceError& error) |
292 { | 288 { |
293 ASSERT(m_state != CLOSED); | 289 ASSERT(m_state != CLOSED); |
294 ASSERT(m_requestInFlight); | 290 ASSERT(m_loader); |
295 | 291 |
296 if (error.isCancellation()) | 292 if (error.isCancellation()) |
297 m_state = CLOSED; | 293 m_state = CLOSED; |
298 networkRequestEnded(); | 294 networkRequestEnded(); |
299 } | 295 } |
300 | 296 |
301 void EventSource::didFailAccessControlCheck(const ResourceError& error) | 297 void EventSource::didFailAccessControlCheck(const ResourceError& error) |
302 { | 298 { |
303 String message = "EventSource cannot load " + error.failingURL() + ". " + er
ror.localizedDescription(); | 299 String message = "EventSource cannot load " + error.failingURL() + ". " + er
ror.localizedDescription(); |
304 executionContext()->addConsoleMessage(ConsoleMessage::create(JSMessageSource
, ErrorMessageLevel, message)); | 300 executionContext()->addConsoleMessage(ConsoleMessage::create(JSMessageSource
, ErrorMessageLevel, message)); |
305 | 301 |
306 abortConnectionAttempt(); | 302 abortConnectionAttempt(); |
307 } | 303 } |
308 | 304 |
309 void EventSource::didFailRedirectCheck() | 305 void EventSource::didFailRedirectCheck() |
310 { | 306 { |
311 abortConnectionAttempt(); | 307 abortConnectionAttempt(); |
312 } | 308 } |
313 | 309 |
314 void EventSource::abortConnectionAttempt() | 310 void EventSource::abortConnectionAttempt() |
315 { | 311 { |
316 ASSERT(m_state == CONNECTING); | 312 ASSERT(m_state == CONNECTING); |
317 | 313 |
318 if (m_requestInFlight) { | 314 m_loader = nullptr; |
319 m_loader->cancel(); | 315 m_state = CLOSED; |
320 } else { | 316 networkRequestEnded(); |
321 m_state = CLOSED; | |
322 } | |
323 | |
324 ASSERT(m_state == CLOSED); | |
325 dispatchEvent(Event::create(EventTypeNames::error)); | 317 dispatchEvent(Event::create(EventTypeNames::error)); |
326 } | 318 } |
327 | 319 |
328 void EventSource::parseEventStream() | 320 void EventSource::parseEventStream() |
329 { | 321 { |
330 unsigned bufPos = 0; | 322 unsigned bufPos = 0; |
331 unsigned bufSize = m_receiveBuf.size(); | 323 unsigned bufSize = m_receiveBuf.size(); |
332 while (bufPos < bufSize) { | 324 while (bufPos < bufSize) { |
333 if (m_discardTrailingNewline) { | 325 if (m_discardTrailingNewline) { |
334 if (m_receiveBuf[bufPos] == '\n') | 326 if (m_receiveBuf[bufPos] == '\n') |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
416 if (ok) | 408 if (ok) |
417 m_reconnectDelay = retry; | 409 m_reconnectDelay = retry; |
418 } | 410 } |
419 } | 411 } |
420 } | 412 } |
421 } | 413 } |
422 | 414 |
423 void EventSource::stop() | 415 void EventSource::stop() |
424 { | 416 { |
425 close(); | 417 close(); |
426 | |
427 // (Non)Oilpan: In order to make Worker shutdowns clean, | |
428 // deref the loader. This will in turn deref its | |
429 // RefPtr<WorkerGlobalScope>. | |
430 // | |
431 // Worth doing regardless, it is no longer of use. | |
432 m_loader = nullptr; | |
433 } | 418 } |
434 | 419 |
435 bool EventSource::hasPendingActivity() const | 420 bool EventSource::hasPendingActivity() const |
436 { | 421 { |
437 return m_state != CLOSED; | 422 return m_state != CLOSED; |
438 } | 423 } |
439 | 424 |
440 PassRefPtrWillBeRawPtr<MessageEvent> EventSource::createMessageEvent() | 425 PassRefPtrWillBeRawPtr<MessageEvent> EventSource::createMessageEvent() |
441 { | 426 { |
442 RefPtrWillBeRawPtr<MessageEvent> event = MessageEvent::create(); | 427 RefPtrWillBeRawPtr<MessageEvent> event = MessageEvent::create(); |
443 event->initMessageEvent(m_eventName.isEmpty() ? EventTypeNames::message : m_
eventName, false, false, SerializedScriptValueFactory::instance().create(String(
m_data)), m_eventStreamOrigin, m_lastEventId, 0, nullptr); | 428 event->initMessageEvent(m_eventName.isEmpty() ? EventTypeNames::message : m_
eventName, false, false, SerializedScriptValueFactory::instance().create(String(
m_data)), m_eventStreamOrigin, m_lastEventId, 0, nullptr); |
444 m_data.clear(); | 429 m_data.clear(); |
445 return event.release(); | 430 return event.release(); |
446 } | 431 } |
447 | 432 |
448 DEFINE_TRACE(EventSource) | 433 DEFINE_TRACE(EventSource) |
449 { | 434 { |
450 RefCountedGarbageCollectedEventTargetWithInlineData::trace(visitor); | 435 RefCountedGarbageCollectedEventTargetWithInlineData::trace(visitor); |
451 ActiveDOMObject::trace(visitor); | 436 ActiveDOMObject::trace(visitor); |
452 } | 437 } |
453 | 438 |
454 } // namespace blink | 439 } // namespace blink |
OLD | NEW |