1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Wellington Fernando de Macedo and Clayton Williams.
19 : * Portions created by the Initial Developer are Copyright (C) 2008
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Wellington Fernando de Macedo <wfernandom2004@gmail.com>
24 : * Clayton Williams <claytonw@mit.edu>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "mozilla/Util.h"
41 :
42 : #include "nsEventSource.h"
43 : #include "nsNetUtil.h"
44 : #include "nsMimeTypes.h"
45 : #include "nsDOMMessageEvent.h"
46 : #include "nsIJSContextStack.h"
47 : #include "nsIPromptFactory.h"
48 : #include "nsIWindowWatcher.h"
49 : #include "nsPresContext.h"
50 : #include "nsContentPolicyUtils.h"
51 : #include "nsIStringBundle.h"
52 : #include "nsIConsoleService.h"
53 : #include "nsIObserverService.h"
54 : #include "nsIScriptObjectPrincipal.h"
55 : #include "jsdbgapi.h"
56 : #include "nsJSUtils.h"
57 : #include "nsIAsyncVerifyRedirectCallback.h"
58 : #include "nsIScriptError.h"
59 : #include "nsICharsetConverterManager.h"
60 : #include "nsIChannelPolicy.h"
61 : #include "nsIContentSecurityPolicy.h"
62 : #include "nsContentUtils.h"
63 : #include "mozilla/Preferences.h"
64 : #include "xpcpublic.h"
65 : #include "nsCrossSiteListenerProxy.h"
66 : #include "nsWrapperCacheInlines.h"
67 : #include "nsDOMEventTargetHelper.h"
68 :
69 : using namespace mozilla;
70 :
71 : #define REPLACEMENT_CHAR (PRUnichar)0xFFFD
72 : #define BOM_CHAR (PRUnichar)0xFEFF
73 : #define SPACE_CHAR (PRUnichar)0x0020
74 : #define CR_CHAR (PRUnichar)0x000D
75 : #define LF_CHAR (PRUnichar)0x000A
76 : #define COLON_CHAR (PRUnichar)0x003A
77 :
78 : #define DEFAULT_BUFFER_SIZE 4096
79 :
80 : // Reconnection time related values in milliseconds. The default one is equal
81 : // to the default value of the pref dom.server-events.default-reconnection-time
82 : #define MIN_RECONNECTION_TIME_VALUE 500
83 : #define DEFAULT_RECONNECTION_TIME_VALUE 5000
84 : #define MAX_RECONNECTION_TIME_VALUE PR_IntervalToMilliseconds(DELAY_INTERVAL_LIMIT)
85 :
86 0 : nsEventSource::nsEventSource() :
87 : mStatus(PARSE_STATE_OFF),
88 : mFrozen(false),
89 : mErrorLoadOnRedirect(false),
90 : mGoingToDispatchAllMessages(false),
91 : mWithCredentials(false),
92 : mWaitingForOnStopRequest(false),
93 : mLastConvertionResult(NS_OK),
94 : mReadyState(nsIEventSource::CONNECTING),
95 : mScriptLine(0),
96 0 : mInnerWindowID(0)
97 : {
98 0 : }
99 :
100 0 : nsEventSource::~nsEventSource()
101 : {
102 0 : Close();
103 0 : }
104 :
105 : //-----------------------------------------------------------------------------
106 : // nsEventSource::nsISupports
107 : //-----------------------------------------------------------------------------
108 :
109 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsEventSource)
110 :
111 0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsEventSource)
112 0 : bool isBlack = tmp->IsBlack();
113 0 : if (isBlack || tmp->mWaitingForOnStopRequest) {
114 0 : if (tmp->mListenerManager) {
115 0 : tmp->mListenerManager->UnmarkGrayJSListeners();
116 0 : NS_UNMARK_LISTENER_WRAPPER(Open)
117 0 : NS_UNMARK_LISTENER_WRAPPER(Message)
118 0 : NS_UNMARK_LISTENER_WRAPPER(Error)
119 : }
120 0 : if (!isBlack) {
121 0 : xpc_UnmarkGrayObject(tmp->PreservingWrapper() ?
122 0 : tmp->GetWrapperPreserveColor() :
123 0 : tmp->GetExpandoObjectPreserveColor());
124 : }
125 0 : return true;
126 : }
127 0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
128 :
129 0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsEventSource)
130 0 : return tmp->IsBlack();
131 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
132 :
133 0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsEventSource)
134 0 : return tmp->IsBlack();
135 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
136 :
137 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsEventSource,
138 : nsDOMEventTargetHelper)
139 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
140 :
141 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsEventSource,
142 : nsDOMEventTargetHelper)
143 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mSrc)
144 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mNotificationCallbacks)
145 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mLoadGroup)
146 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mChannelEventSink)
147 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mHttpChannel)
148 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mTimer)
149 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnOpenListener)
150 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnMessageListener)
151 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnErrorListener)
152 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mUnicodeDecoder)
153 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
154 :
155 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsEventSource, nsDOMEventTargetHelper)
156 0 : tmp->Close();
157 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnOpenListener)
158 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnMessageListener)
159 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnErrorListener)
160 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
161 :
162 : DOMCI_DATA(EventSource, nsEventSource)
163 :
164 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsEventSource)
165 0 : NS_INTERFACE_MAP_ENTRY(nsIEventSource)
166 0 : NS_INTERFACE_MAP_ENTRY(nsIJSNativeInitializer)
167 0 : NS_INTERFACE_MAP_ENTRY(nsIObserver)
168 0 : NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
169 0 : NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
170 0 : NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
171 0 : NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
172 0 : NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
173 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(EventSource)
174 0 : NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
175 :
176 0 : NS_IMPL_ADDREF_INHERITED(nsEventSource, nsDOMEventTargetHelper)
177 0 : NS_IMPL_RELEASE_INHERITED(nsEventSource, nsDOMEventTargetHelper)
178 :
179 : void
180 0 : nsEventSource::DisconnectFromOwner()
181 : {
182 0 : nsDOMEventTargetHelper::DisconnectFromOwner();
183 0 : NS_DISCONNECT_EVENT_HANDLER(Open)
184 0 : NS_DISCONNECT_EVENT_HANDLER(Message)
185 0 : NS_DISCONNECT_EVENT_HANDLER(Error)
186 0 : Close();
187 0 : }
188 :
189 : //-----------------------------------------------------------------------------
190 : // nsEventSource::nsIEventSource
191 : //-----------------------------------------------------------------------------
192 :
193 : NS_IMETHODIMP
194 0 : nsEventSource::GetUrl(nsAString& aURL)
195 : {
196 0 : aURL = mOriginalURL;
197 0 : return NS_OK;
198 : }
199 :
200 : NS_IMETHODIMP
201 0 : nsEventSource::GetReadyState(PRInt32 *aReadyState)
202 : {
203 0 : NS_ENSURE_ARG_POINTER(aReadyState);
204 0 : *aReadyState = mReadyState;
205 0 : return NS_OK;
206 : }
207 :
208 : NS_IMETHODIMP
209 0 : nsEventSource::GetWithCredentials(bool *aWithCredentials)
210 : {
211 0 : NS_ENSURE_ARG_POINTER(aWithCredentials);
212 0 : *aWithCredentials = mWithCredentials;
213 0 : return NS_OK;
214 : }
215 :
216 : #define NS_EVENTSRC_IMPL_DOMEVENTLISTENER(_eventlistenername, _eventlistener) \
217 : NS_IMETHODIMP \
218 : nsEventSource::GetOn##_eventlistenername(nsIDOMEventListener * *aListener) \
219 : { \
220 : return GetInnerEventListener(_eventlistener, aListener); \
221 : } \
222 : \
223 : NS_IMETHODIMP \
224 : nsEventSource::SetOn##_eventlistenername(nsIDOMEventListener * aListener) \
225 : { \
226 : return RemoveAddEventListener(NS_LITERAL_STRING(#_eventlistenername), \
227 : _eventlistener, aListener); \
228 : }
229 :
230 0 : NS_EVENTSRC_IMPL_DOMEVENTLISTENER(open, mOnOpenListener)
231 0 : NS_EVENTSRC_IMPL_DOMEVENTLISTENER(error, mOnErrorListener)
232 0 : NS_EVENTSRC_IMPL_DOMEVENTLISTENER(message, mOnMessageListener)
233 :
234 : NS_IMETHODIMP
235 0 : nsEventSource::Close()
236 : {
237 0 : if (mReadyState == nsIEventSource::CLOSED) {
238 0 : return NS_OK;
239 : }
240 :
241 0 : nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
242 0 : if (os) {
243 0 : os->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC);
244 0 : os->RemoveObserver(this, DOM_WINDOW_FROZEN_TOPIC);
245 0 : os->RemoveObserver(this, DOM_WINDOW_THAWED_TOPIC);
246 : }
247 :
248 0 : if (mTimer) {
249 0 : mTimer->Cancel();
250 0 : mTimer = nsnull;
251 : }
252 :
253 0 : ResetConnection();
254 :
255 0 : ClearFields();
256 :
257 0 : while (mMessagesToDispatch.GetSize() != 0) {
258 0 : delete static_cast<Message*>(mMessagesToDispatch.PopFront());
259 : }
260 :
261 0 : mSrc = nsnull;
262 0 : mFrozen = false;
263 :
264 0 : mUnicodeDecoder = nsnull;
265 :
266 0 : mReadyState = nsIEventSource::CLOSED;
267 :
268 0 : return NS_OK;
269 : }
270 :
271 : /**
272 : * This Init method should only be called by C++ consumers.
273 : */
274 : NS_IMETHODIMP
275 0 : nsEventSource::Init(nsIPrincipal* aPrincipal,
276 : nsIScriptContext* aScriptContext,
277 : nsPIDOMWindow* aOwnerWindow,
278 : const nsAString& aURL,
279 : bool aWithCredentials)
280 : {
281 0 : NS_ENSURE_ARG(aPrincipal);
282 :
283 0 : if (mReadyState != nsIEventSource::CONNECTING || !PrefEnabled()) {
284 0 : return NS_ERROR_DOM_SECURITY_ERR;
285 : }
286 :
287 0 : mPrincipal = aPrincipal;
288 0 : mWithCredentials = aWithCredentials;
289 0 : if (aOwnerWindow) {
290 0 : BindToOwner(aOwnerWindow->IsOuterWindow() ?
291 0 : aOwnerWindow->GetCurrentInnerWindow() : aOwnerWindow);
292 : } else {
293 0 : BindToOwner(aOwnerWindow);
294 : }
295 :
296 : nsCOMPtr<nsIJSContextStack> stack =
297 0 : do_GetService("@mozilla.org/js/xpc/ContextStack;1");
298 0 : JSContext* cx = nsnull;
299 0 : if (stack && NS_SUCCEEDED(stack->Peek(&cx)) && cx) {
300 : const char *filename;
301 0 : if (nsJSUtils::GetCallingLocation(cx, &filename, &mScriptLine)) {
302 0 : mScriptFile.AssignASCII(filename);
303 : }
304 :
305 0 : mInnerWindowID = nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx);
306 : }
307 :
308 : // Get the load group for the page. When requesting we'll add ourselves to it.
309 : // This way any pending requests will be automatically aborted if the user
310 : // leaves the page.
311 : nsresult rv;
312 0 : nsIScriptContext* sc = GetContextForEventHandlers(&rv);
313 0 : if (sc) {
314 : nsCOMPtr<nsIDocument> doc =
315 0 : nsContentUtils::GetDocumentFromScriptContext(sc);
316 0 : if (doc) {
317 0 : mLoadGroup = doc->GetDocumentLoadGroup();
318 : }
319 : }
320 :
321 : // get the src
322 0 : nsCOMPtr<nsIURI> baseURI;
323 0 : rv = GetBaseURI(getter_AddRefs(baseURI));
324 0 : NS_ENSURE_SUCCESS(rv, rv);
325 :
326 0 : nsCOMPtr<nsIURI> srcURI;
327 0 : rv = NS_NewURI(getter_AddRefs(srcURI), aURL, nsnull, baseURI);
328 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
329 :
330 : // we observe when the window freezes and thaws
331 0 : nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
332 0 : NS_ENSURE_STATE(os);
333 :
334 0 : rv = os->AddObserver(this, DOM_WINDOW_DESTROYED_TOPIC, true);
335 0 : NS_ENSURE_SUCCESS(rv, rv);
336 0 : rv = os->AddObserver(this, DOM_WINDOW_FROZEN_TOPIC, true);
337 0 : NS_ENSURE_SUCCESS(rv, rv);
338 0 : rv = os->AddObserver(this, DOM_WINDOW_THAWED_TOPIC, true);
339 0 : NS_ENSURE_SUCCESS(rv, rv);
340 :
341 0 : nsAutoString origin;
342 0 : rv = nsContentUtils::GetUTFOrigin(srcURI, origin);
343 0 : NS_ENSURE_SUCCESS(rv, rv);
344 :
345 0 : nsCAutoString spec;
346 0 : rv = srcURI->GetSpec(spec);
347 0 : NS_ENSURE_SUCCESS(rv, rv);
348 :
349 0 : mOriginalURL = NS_ConvertUTF8toUTF16(spec);
350 0 : mSrc = srcURI;
351 0 : mOrigin = origin;
352 :
353 : mReconnectionTime =
354 : Preferences::GetInt("dom.server-events.default-reconnection-time",
355 0 : DEFAULT_RECONNECTION_TIME_VALUE);
356 :
357 : nsCOMPtr<nsICharsetConverterManager> convManager =
358 0 : do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
359 0 : NS_ENSURE_SUCCESS(rv, rv);
360 :
361 0 : rv = convManager->GetUnicodeDecoder("UTF-8", getter_AddRefs(mUnicodeDecoder));
362 0 : NS_ENSURE_SUCCESS(rv, rv);
363 0 : mUnicodeDecoder->SetInputErrorBehavior(nsIUnicodeDecoder::kOnError_Recover);
364 :
365 : // the constructor should throw a SYNTAX_ERROR only if it fails resolving the
366 : // url parameter, so we don't care about the InitChannelAndRequestEventSource
367 : // result.
368 0 : InitChannelAndRequestEventSource();
369 :
370 0 : return NS_OK;
371 : }
372 :
373 : //-----------------------------------------------------------------------------
374 : // nsEventSource::nsIJSNativeInitializer methods:
375 : //-----------------------------------------------------------------------------
376 :
377 : /**
378 : * This Initialize method is called from XPConnect via nsIJSNativeInitializer.
379 : * It is used for constructing our nsEventSource from javascript. It expects a
380 : * URL string parameter. Also, initializes the principal, the script context
381 : * and the window owner.
382 : */
383 : NS_IMETHODIMP
384 0 : nsEventSource::Initialize(nsISupports* aOwner,
385 : JSContext* aContext,
386 : JSObject* aObject,
387 : PRUint32 aArgc,
388 : jsval* aArgv)
389 : {
390 0 : if (mReadyState != nsIEventSource::CONNECTING || !PrefEnabled() ||
391 : aArgc < 1) {
392 0 : return NS_ERROR_FAILURE;
393 : }
394 :
395 0 : JSAutoRequest ar(aContext);
396 :
397 0 : JSString* jsstr = JS_ValueToString(aContext, aArgv[0]);
398 0 : if (!jsstr) {
399 0 : return NS_ERROR_DOM_SYNTAX_ERR;
400 : }
401 :
402 0 : JS::Anchor<JSString *> deleteProtector(jsstr);
403 : size_t length;
404 0 : const jschar *chars = JS_GetStringCharsAndLength(aContext, jsstr, &length);
405 0 : if (!chars) {
406 0 : return NS_ERROR_OUT_OF_MEMORY;
407 : }
408 :
409 0 : nsAutoString urlParam;
410 :
411 0 : urlParam.Assign(chars, length);
412 :
413 0 : nsCOMPtr<nsPIDOMWindow> ownerWindow = do_QueryInterface(aOwner);
414 0 : NS_ENSURE_STATE(ownerWindow);
415 :
416 0 : nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aOwner);
417 0 : NS_ENSURE_STATE(sgo);
418 0 : nsCOMPtr<nsIScriptContext> scriptContext = sgo->GetContext();
419 0 : NS_ENSURE_STATE(scriptContext);
420 :
421 : nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal =
422 0 : do_QueryInterface(aOwner);
423 0 : NS_ENSURE_STATE(scriptPrincipal);
424 0 : nsCOMPtr<nsIPrincipal> principal = scriptPrincipal->GetPrincipal();
425 0 : NS_ENSURE_STATE(principal);
426 :
427 0 : bool withCredentialsParam = false;
428 0 : if (aArgc >= 2) {
429 0 : NS_ENSURE_TRUE(!JSVAL_IS_PRIMITIVE(aArgv[1]), NS_ERROR_INVALID_ARG);
430 :
431 0 : JSObject *obj = JSVAL_TO_OBJECT(aArgv[1]);
432 0 : NS_ASSERTION(obj, "obj shouldn't be null!!");
433 :
434 0 : JSBool hasProperty = JS_FALSE;
435 0 : NS_ENSURE_TRUE(JS_HasProperty(aContext, obj, "withCredentials",
436 : &hasProperty), NS_ERROR_FAILURE);
437 :
438 0 : if (hasProperty) {
439 : jsval withCredentialsVal;
440 0 : NS_ENSURE_TRUE(JS_GetProperty(aContext, obj, "withCredentials",
441 : &withCredentialsVal), NS_ERROR_FAILURE);
442 :
443 0 : JSBool withCredentials = JS_FALSE;
444 0 : NS_ENSURE_TRUE(JS_ValueToBoolean(aContext, withCredentialsVal,
445 : &withCredentials), NS_ERROR_FAILURE);
446 0 : withCredentialsParam = !!withCredentials;
447 : }
448 : }
449 :
450 : return Init(principal, scriptContext, ownerWindow,
451 0 : urlParam, withCredentialsParam);
452 : }
453 :
454 : //-----------------------------------------------------------------------------
455 : // nsEventSource::nsIObserver
456 : //-----------------------------------------------------------------------------
457 :
458 : NS_IMETHODIMP
459 0 : nsEventSource::Observe(nsISupports* aSubject,
460 : const char* aTopic,
461 : const PRUnichar* aData)
462 : {
463 0 : if (mReadyState == nsIEventSource::CLOSED) {
464 0 : return NS_OK;
465 : }
466 :
467 0 : nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aSubject);
468 0 : if (!GetOwner() || window != GetOwner()) {
469 0 : return NS_OK;
470 : }
471 :
472 : nsresult rv;
473 0 : if (strcmp(aTopic, DOM_WINDOW_FROZEN_TOPIC) == 0) {
474 0 : rv = Freeze();
475 0 : NS_ASSERTION(rv, "Freeze() failed");
476 0 : } else if (strcmp(aTopic, DOM_WINDOW_THAWED_TOPIC) == 0) {
477 0 : rv = Thaw();
478 0 : NS_ASSERTION(rv, "Thaw() failed");
479 0 : } else if (strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC) == 0) {
480 0 : Close();
481 : }
482 :
483 0 : return NS_OK;
484 : }
485 :
486 : //-----------------------------------------------------------------------------
487 : // nsEventSource::nsIStreamListener
488 : //-----------------------------------------------------------------------------
489 :
490 : NS_IMETHODIMP
491 0 : nsEventSource::OnStartRequest(nsIRequest *aRequest,
492 : nsISupports *ctxt)
493 : {
494 0 : nsresult rv = CheckHealthOfRequestCallback(aRequest);
495 0 : NS_ENSURE_SUCCESS(rv, rv);
496 :
497 0 : nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest, &rv);
498 0 : NS_ENSURE_SUCCESS(rv, rv);
499 :
500 : bool requestSucceeded;
501 0 : rv = httpChannel->GetRequestSucceeded(&requestSucceeded);
502 0 : NS_ENSURE_SUCCESS(rv, rv);
503 :
504 0 : nsCAutoString contentType;
505 0 : rv = httpChannel->GetContentType(contentType);
506 0 : NS_ENSURE_SUCCESS(rv, rv);
507 :
508 0 : if (!requestSucceeded || !contentType.EqualsLiteral(TEXT_EVENT_STREAM)) {
509 0 : DispatchFailConnection();
510 0 : return NS_ERROR_NOT_AVAILABLE;
511 : }
512 :
513 0 : nsCOMPtr<nsIPrincipal> principal = mPrincipal;
514 0 : if (nsContentUtils::IsSystemPrincipal(principal)) {
515 : // Don't give this channel the system principal.
516 0 : principal = do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
517 0 : NS_ENSURE_SUCCESS(rv, rv);
518 : }
519 0 : rv = httpChannel->SetOwner(principal);
520 0 : NS_ENSURE_SUCCESS(rv, rv);
521 :
522 : nsCOMPtr<nsIRunnable> event =
523 0 : NS_NewRunnableMethod(this, &nsEventSource::AnnounceConnection);
524 0 : NS_ENSURE_STATE(event);
525 :
526 0 : rv = NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
527 0 : NS_ENSURE_SUCCESS(rv, rv);
528 :
529 0 : mStatus = PARSE_STATE_BEGIN_OF_STREAM;
530 :
531 0 : return NS_OK;
532 : }
533 :
534 : // this method parses the characters as they become available instead of
535 : // buffering them.
536 : NS_METHOD
537 0 : nsEventSource::StreamReaderFunc(nsIInputStream *aInputStream,
538 : void *aClosure,
539 : const char *aFromRawSegment,
540 : PRUint32 aToOffset,
541 : PRUint32 aCount,
542 : PRUint32 *aWriteCount)
543 : {
544 0 : nsEventSource* thisObject = static_cast<nsEventSource*>(aClosure);
545 0 : if (!thisObject || !aWriteCount) {
546 0 : NS_WARNING("nsEventSource cannot read from stream: no aClosure or aWriteCount");
547 0 : return NS_ERROR_FAILURE;
548 : }
549 :
550 0 : *aWriteCount = 0;
551 :
552 : PRInt32 srcCount, outCount;
553 : PRUnichar out[2];
554 : nsresult rv;
555 :
556 0 : const char *p = aFromRawSegment,
557 0 : *end = aFromRawSegment + aCount;
558 :
559 0 : do {
560 0 : srcCount = aCount - (p - aFromRawSegment);
561 0 : outCount = 2;
562 :
563 : thisObject->mLastConvertionResult =
564 0 : thisObject->mUnicodeDecoder->Convert(p, &srcCount, out, &outCount);
565 :
566 0 : if (thisObject->mLastConvertionResult == NS_ERROR_ILLEGAL_INPUT) {
567 : // There's an illegal byte in the input. It's now the responsibility
568 : // of this calling code to output a U+FFFD REPLACEMENT CHARACTER, advance
569 : // over the bad byte and reset the decoder.
570 0 : rv = thisObject->ParseCharacter(REPLACEMENT_CHAR);
571 0 : NS_ENSURE_SUCCESS(rv, rv);
572 0 : p = p + srcCount + 1;
573 0 : thisObject->mUnicodeDecoder->Reset();
574 : } else {
575 0 : for (PRInt32 i = 0; i < outCount; ++i) {
576 0 : rv = thisObject->ParseCharacter(out[i]);
577 0 : NS_ENSURE_SUCCESS(rv, rv);
578 : }
579 0 : p = p + srcCount;
580 : }
581 : } while (p < end &&
582 : thisObject->mLastConvertionResult != NS_PARTIAL_MORE_INPUT &&
583 : thisObject->mLastConvertionResult != NS_OK);
584 :
585 : // check if the last byte was a bad one and
586 : // clear the state since it was handled above.
587 0 : if (thisObject->mLastConvertionResult == NS_ERROR_ILLEGAL_INPUT) {
588 0 : thisObject->mLastConvertionResult = NS_OK;
589 : }
590 :
591 0 : *aWriteCount = aCount;
592 0 : return NS_OK;
593 : }
594 :
595 : NS_IMETHODIMP
596 0 : nsEventSource::OnDataAvailable(nsIRequest *aRequest,
597 : nsISupports *aContext,
598 : nsIInputStream *aInputStream,
599 : PRUint32 aOffset,
600 : PRUint32 aCount)
601 : {
602 0 : NS_ENSURE_ARG_POINTER(aInputStream);
603 :
604 0 : nsresult rv = CheckHealthOfRequestCallback(aRequest);
605 0 : NS_ENSURE_SUCCESS(rv, rv);
606 :
607 : PRUint32 totalRead;
608 : return aInputStream->ReadSegments(nsEventSource::StreamReaderFunc, this,
609 0 : aCount, &totalRead);
610 : }
611 :
612 : NS_IMETHODIMP
613 0 : nsEventSource::OnStopRequest(nsIRequest *aRequest,
614 : nsISupports *aContext,
615 : nsresult aStatusCode)
616 : {
617 0 : mWaitingForOnStopRequest = false;
618 :
619 0 : if (mReadyState == nsIEventSource::CLOSED) {
620 0 : return NS_ERROR_ABORT;
621 : }
622 :
623 0 : if (NS_FAILED(aStatusCode)) {
624 0 : DispatchFailConnection();
625 0 : return aStatusCode;
626 : }
627 :
628 : nsresult rv;
629 0 : nsresult healthOfRequestResult = CheckHealthOfRequestCallback(aRequest);
630 0 : if (NS_SUCCEEDED(healthOfRequestResult)) {
631 : // check if we had an incomplete UTF8 char at the end of the stream
632 0 : if (mLastConvertionResult == NS_PARTIAL_MORE_INPUT) {
633 0 : rv = ParseCharacter(REPLACEMENT_CHAR);
634 0 : NS_ENSURE_SUCCESS(rv, rv);
635 : }
636 :
637 : // once we reach the end of the stream we must
638 : // dispatch the current event
639 0 : switch (mStatus)
640 : {
641 : case PARSE_STATE_CR_CHAR:
642 : case PARSE_STATE_COMMENT:
643 : case PARSE_STATE_FIELD_NAME:
644 : case PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE:
645 : case PARSE_STATE_FIELD_VALUE:
646 : case PARSE_STATE_BEGIN_OF_LINE:
647 0 : rv = SetFieldAndClear();
648 0 : NS_ENSURE_SUCCESS(rv, rv);
649 :
650 0 : rv = DispatchCurrentMessageEvent(); // there is an empty line (CRCR)
651 0 : NS_ENSURE_SUCCESS(rv, rv);
652 :
653 0 : break;
654 :
655 : // Just for not getting warnings when compiling
656 : case PARSE_STATE_OFF:
657 : case PARSE_STATE_BEGIN_OF_STREAM:
658 : case PARSE_STATE_BOM_WAS_READ:
659 0 : break;
660 : }
661 : }
662 :
663 : nsCOMPtr<nsIRunnable> event =
664 0 : NS_NewRunnableMethod(this, &nsEventSource::ReestablishConnection);
665 0 : NS_ENSURE_STATE(event);
666 :
667 0 : rv = NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
668 0 : NS_ENSURE_SUCCESS(rv, rv);
669 :
670 0 : return healthOfRequestResult;
671 : }
672 :
673 : /**
674 : * Simple helper class that just forwards the redirect callback back
675 : * to the nsEventSource.
676 : */
677 : class AsyncVerifyRedirectCallbackFwr : public nsIAsyncVerifyRedirectCallback
678 0 : {
679 : public:
680 0 : AsyncVerifyRedirectCallbackFwr(nsEventSource* aEventsource)
681 0 : : mEventSource(aEventsource)
682 : {
683 0 : }
684 :
685 0 : NS_DECL_CYCLE_COLLECTING_ISUPPORTS
686 1464 : NS_DECL_CYCLE_COLLECTION_CLASS(AsyncVerifyRedirectCallbackFwr)
687 :
688 : // nsIAsyncVerifyRedirectCallback implementation
689 0 : NS_IMETHOD OnRedirectVerifyCallback(nsresult aResult)
690 : {
691 0 : nsresult rv = mEventSource->OnRedirectVerifyCallback(aResult);
692 0 : if (NS_FAILED(rv)) {
693 0 : mEventSource->mErrorLoadOnRedirect = true;
694 0 : mEventSource->DispatchFailConnection();
695 : }
696 :
697 0 : return NS_OK;
698 : }
699 :
700 : private:
701 : nsRefPtr<nsEventSource> mEventSource;
702 : };
703 :
704 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(AsyncVerifyRedirectCallbackFwr)
705 :
706 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AsyncVerifyRedirectCallbackFwr)
707 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mEventSource, nsIEventSource)
708 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
709 :
710 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AsyncVerifyRedirectCallbackFwr)
711 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mEventSource)
712 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
713 :
714 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AsyncVerifyRedirectCallbackFwr)
715 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
716 0 : NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
717 0 : NS_INTERFACE_MAP_END
718 :
719 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(AsyncVerifyRedirectCallbackFwr)
720 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(AsyncVerifyRedirectCallbackFwr)
721 :
722 : //-----------------------------------------------------------------------------
723 : // nsEventSource::nsIChannelEventSink
724 : //-----------------------------------------------------------------------------
725 :
726 : NS_IMETHODIMP
727 0 : nsEventSource::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
728 : nsIChannel *aNewChannel,
729 : PRUint32 aFlags,
730 : nsIAsyncVerifyRedirectCallback *aCallback)
731 : {
732 0 : nsCOMPtr<nsIRequest> aOldRequest = do_QueryInterface(aOldChannel);
733 0 : NS_PRECONDITION(aOldRequest, "Redirect from a null request?");
734 :
735 0 : nsresult rv = CheckHealthOfRequestCallback(aOldRequest);
736 0 : NS_ENSURE_SUCCESS(rv, rv);
737 :
738 0 : NS_PRECONDITION(aNewChannel, "Redirect without a channel?");
739 :
740 0 : nsCOMPtr<nsIURI> newURI;
741 0 : rv = NS_GetFinalChannelURI(aNewChannel, getter_AddRefs(newURI));
742 0 : NS_ENSURE_SUCCESS(rv, rv);
743 :
744 0 : if (!CheckCanRequestSrc(newURI)) {
745 0 : DispatchFailConnection();
746 0 : return NS_ERROR_DOM_SECURITY_ERR;
747 : }
748 :
749 : // Prepare to receive callback
750 0 : mRedirectFlags = aFlags;
751 0 : mRedirectCallback = aCallback;
752 0 : mNewRedirectChannel = aNewChannel;
753 :
754 0 : if (mChannelEventSink) {
755 : nsRefPtr<AsyncVerifyRedirectCallbackFwr> fwd =
756 0 : new AsyncVerifyRedirectCallbackFwr(this);
757 :
758 0 : rv = mChannelEventSink->AsyncOnChannelRedirect(aOldChannel,
759 : aNewChannel,
760 0 : aFlags, fwd);
761 0 : if (NS_FAILED(rv)) {
762 0 : mRedirectCallback = nsnull;
763 0 : mNewRedirectChannel = nsnull;
764 0 : mErrorLoadOnRedirect = true;
765 0 : DispatchFailConnection();
766 : }
767 0 : return rv;
768 : }
769 0 : OnRedirectVerifyCallback(NS_OK);
770 0 : return NS_OK;
771 : }
772 :
773 : nsresult
774 0 : nsEventSource::OnRedirectVerifyCallback(nsresult aResult)
775 : {
776 0 : NS_ABORT_IF_FALSE(mRedirectCallback, "mRedirectCallback not set in callback");
777 0 : NS_ABORT_IF_FALSE(mNewRedirectChannel,
778 : "mNewRedirectChannel not set in callback");
779 :
780 0 : NS_ENSURE_SUCCESS(aResult, aResult);
781 :
782 : // update our channel
783 :
784 0 : mHttpChannel = do_QueryInterface(mNewRedirectChannel);
785 0 : NS_ENSURE_STATE(mHttpChannel);
786 :
787 0 : nsresult rv = SetupHttpChannel();
788 0 : NS_ENSURE_SUCCESS(rv, rv);
789 :
790 0 : if ((mRedirectFlags & nsIChannelEventSink::REDIRECT_PERMANENT) != 0) {
791 0 : rv = NS_GetFinalChannelURI(mHttpChannel, getter_AddRefs(mSrc));
792 0 : NS_ENSURE_SUCCESS(rv, rv);
793 : }
794 :
795 0 : mNewRedirectChannel = nsnull;
796 :
797 0 : mRedirectCallback->OnRedirectVerifyCallback(aResult);
798 0 : mRedirectCallback = nsnull;
799 :
800 0 : return NS_OK;
801 : }
802 :
803 : //-----------------------------------------------------------------------------
804 : // nsEventSource::nsIInterfaceRequestor
805 : //-----------------------------------------------------------------------------
806 :
807 : NS_IMETHODIMP
808 0 : nsEventSource::GetInterface(const nsIID & aIID,
809 : void **aResult)
810 : {
811 : // Make sure to return ourselves for the channel event sink interface,
812 : // no matter what. We can forward these to mNotificationCallbacks
813 : // if it wants to get notifications for them. But we
814 : // need to see these notifications for proper functioning.
815 0 : if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
816 0 : mChannelEventSink = do_GetInterface(mNotificationCallbacks);
817 0 : *aResult = static_cast<nsIChannelEventSink*>(this);
818 0 : NS_ADDREF_THIS();
819 0 : return NS_OK;
820 : }
821 :
822 : // Now give mNotificationCallbacks (if non-null) a chance to return the
823 : // desired interface.
824 0 : if (mNotificationCallbacks) {
825 0 : nsresult rv = mNotificationCallbacks->GetInterface(aIID, aResult);
826 0 : if (NS_SUCCEEDED(rv)) {
827 0 : NS_ASSERTION(*aResult, "Lying nsIInterfaceRequestor implementation!");
828 0 : return rv;
829 : }
830 : }
831 :
832 0 : if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
833 0 : aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
834 0 : nsresult rv = CheckInnerWindowCorrectness();
835 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
836 :
837 : nsCOMPtr<nsIPromptFactory> wwatch =
838 0 : do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
839 0 : NS_ENSURE_SUCCESS(rv, rv);
840 :
841 : // Get the an auth prompter for our window so that the parenting
842 : // of the dialogs works as it should when using tabs.
843 :
844 0 : nsCOMPtr<nsIDOMWindow> window;
845 0 : if (GetOwner()) {
846 0 : window = GetOwner()->GetOuterWindow();
847 : }
848 :
849 0 : return wwatch->GetPrompt(window, aIID, aResult);
850 : }
851 :
852 0 : return QueryInterface(aIID, aResult);
853 : }
854 :
855 : // static
856 : bool
857 0 : nsEventSource::PrefEnabled()
858 : {
859 0 : return Preferences::GetBool("dom.server-events.enabled", false);
860 : }
861 :
862 : nsresult
863 0 : nsEventSource::GetBaseURI(nsIURI **aBaseURI)
864 : {
865 0 : NS_ENSURE_ARG_POINTER(aBaseURI);
866 :
867 0 : *aBaseURI = nsnull;
868 :
869 0 : nsCOMPtr<nsIURI> baseURI;
870 :
871 : // first we try from document->GetBaseURI()
872 : nsresult rv;
873 0 : nsIScriptContext* sc = GetContextForEventHandlers(&rv);
874 : nsCOMPtr<nsIDocument> doc =
875 0 : nsContentUtils::GetDocumentFromScriptContext(sc);
876 0 : if (doc) {
877 0 : baseURI = doc->GetBaseURI();
878 : }
879 :
880 : // otherwise we get from the doc's principal
881 0 : if (!baseURI) {
882 0 : rv = mPrincipal->GetURI(getter_AddRefs(baseURI));
883 0 : NS_ENSURE_SUCCESS(rv, rv);
884 : }
885 :
886 0 : NS_ENSURE_STATE(baseURI);
887 :
888 0 : baseURI.forget(aBaseURI);
889 0 : return NS_OK;
890 : }
891 :
892 : nsresult
893 0 : nsEventSource::SetupHttpChannel()
894 : {
895 0 : mHttpChannel->SetRequestMethod(NS_LITERAL_CSTRING("GET"));
896 :
897 : /* set the http request headers */
898 :
899 0 : mHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
900 0 : NS_LITERAL_CSTRING(TEXT_EVENT_STREAM), false);
901 :
902 : // LOAD_BYPASS_CACHE already adds the Cache-Control: no-cache header
903 :
904 0 : if (!mLastEventID.IsEmpty()) {
905 0 : mHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Last-Event-ID"),
906 0 : NS_ConvertUTF16toUTF8(mLastEventID), false);
907 : }
908 :
909 0 : nsCOMPtr<nsIURI> codebase;
910 0 : nsresult rv = GetBaseURI(getter_AddRefs(codebase));
911 0 : if (NS_SUCCEEDED(rv)) {
912 0 : rv = mHttpChannel->SetReferrer(codebase);
913 0 : NS_ENSURE_SUCCESS(rv, rv);
914 : }
915 :
916 0 : return NS_OK;
917 : }
918 :
919 : nsresult
920 0 : nsEventSource::InitChannelAndRequestEventSource()
921 : {
922 0 : if (mReadyState == nsIEventSource::CLOSED) {
923 0 : return NS_ERROR_ABORT;
924 : }
925 :
926 : // eventsource validation
927 :
928 0 : if (!CheckCanRequestSrc()) {
929 0 : DispatchFailConnection();
930 0 : return NS_ERROR_DOM_SECURITY_ERR;
931 : }
932 :
933 : nsLoadFlags loadFlags;
934 0 : loadFlags = nsIRequest::LOAD_BACKGROUND | nsIRequest::LOAD_BYPASS_CACHE;
935 :
936 : // get Content Security Policy from principal to pass into channel
937 0 : nsCOMPtr<nsIChannelPolicy> channelPolicy;
938 0 : nsCOMPtr<nsIContentSecurityPolicy> csp;
939 0 : nsresult rv = mPrincipal->GetCsp(getter_AddRefs(csp));
940 0 : NS_ENSURE_SUCCESS(rv, rv);
941 0 : if (csp) {
942 0 : channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
943 0 : channelPolicy->SetContentSecurityPolicy(csp);
944 0 : channelPolicy->SetLoadType(nsIContentPolicy::TYPE_DATAREQUEST);
945 : }
946 :
947 0 : nsCOMPtr<nsIChannel> channel;
948 0 : rv = NS_NewChannel(getter_AddRefs(channel), mSrc, nsnull, mLoadGroup,
949 0 : nsnull, loadFlags, channelPolicy);
950 0 : NS_ENSURE_SUCCESS(rv, rv);
951 :
952 0 : mHttpChannel = do_QueryInterface(channel);
953 0 : NS_ENSURE_TRUE(mHttpChannel, NS_ERROR_NO_INTERFACE);
954 :
955 0 : rv = SetupHttpChannel();
956 0 : NS_ENSURE_SUCCESS(rv, rv);
957 :
958 0 : nsCOMPtr<nsIInterfaceRequestor> notificationCallbacks;
959 0 : mHttpChannel->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks));
960 0 : if (notificationCallbacks != this) {
961 0 : mNotificationCallbacks = notificationCallbacks;
962 0 : mHttpChannel->SetNotificationCallbacks(this);
963 : }
964 :
965 : nsCOMPtr<nsIStreamListener> listener =
966 : new nsCORSListenerProxy(this, mPrincipal, mHttpChannel,
967 0 : mWithCredentials, &rv);
968 0 : NS_ENSURE_SUCCESS(rv, rv);
969 :
970 : // Start reading from the channel
971 0 : rv = mHttpChannel->AsyncOpen(listener, nsnull);
972 0 : if (NS_SUCCEEDED(rv)) {
973 0 : mWaitingForOnStopRequest = true;
974 : }
975 0 : return rv;
976 : }
977 :
978 : void
979 0 : nsEventSource::AnnounceConnection()
980 : {
981 0 : if (mReadyState == nsIEventSource::CLOSED) {
982 0 : return;
983 : }
984 :
985 0 : if (mReadyState != nsIEventSource::CONNECTING) {
986 0 : NS_WARNING("Unexpected mReadyState!!!");
987 0 : return;
988 : }
989 :
990 : // When a user agent is to announce the connection, the user agent must set
991 : // the readyState attribute to OPEN and queue a task to fire a simple event
992 : // named open at the EventSource object.
993 :
994 0 : mReadyState = nsIEventSource::OPEN;
995 :
996 0 : nsresult rv = CheckInnerWindowCorrectness();
997 0 : if (NS_FAILED(rv)) {
998 0 : return;
999 : }
1000 :
1001 0 : nsCOMPtr<nsIDOMEvent> event;
1002 0 : rv = NS_NewDOMEvent(getter_AddRefs(event), nsnull, nsnull);
1003 0 : if (NS_FAILED(rv)) {
1004 0 : NS_WARNING("Failed to create the open event!!!");
1005 : return;
1006 : }
1007 :
1008 : // it doesn't bubble, and it isn't cancelable
1009 0 : rv = event->InitEvent(NS_LITERAL_STRING("open"), false, false);
1010 0 : if (NS_FAILED(rv)) {
1011 0 : NS_WARNING("Failed to init the open event!!!");
1012 : return;
1013 : }
1014 :
1015 0 : nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
1016 0 : privateEvent->SetTrusted(true);
1017 :
1018 0 : rv = DispatchDOMEvent(nsnull, event, nsnull, nsnull);
1019 0 : if (NS_FAILED(rv)) {
1020 0 : NS_WARNING("Failed to dispatch the open event!!!");
1021 : return;
1022 : }
1023 : }
1024 :
1025 : nsresult
1026 0 : nsEventSource::ResetConnection()
1027 : {
1028 0 : if (mHttpChannel) {
1029 0 : mHttpChannel->Cancel(NS_ERROR_ABORT);
1030 : }
1031 :
1032 0 : if (mUnicodeDecoder) {
1033 0 : mUnicodeDecoder->Reset();
1034 : }
1035 0 : mLastConvertionResult = NS_OK;
1036 :
1037 0 : mHttpChannel = nsnull;
1038 0 : mNotificationCallbacks = nsnull;
1039 0 : mChannelEventSink = nsnull;
1040 0 : mStatus = PARSE_STATE_OFF;
1041 0 : mRedirectCallback = nsnull;
1042 0 : mNewRedirectChannel = nsnull;
1043 :
1044 0 : mReadyState = nsIEventSource::CONNECTING;
1045 :
1046 0 : return NS_OK;
1047 : }
1048 :
1049 : void
1050 0 : nsEventSource::ReestablishConnection()
1051 : {
1052 0 : if (mReadyState == nsIEventSource::CLOSED) {
1053 0 : return;
1054 : }
1055 :
1056 0 : if (mReadyState != nsIEventSource::OPEN) {
1057 0 : NS_WARNING("Unexpected mReadyState!!!");
1058 0 : return;
1059 : }
1060 :
1061 0 : nsresult rv = ResetConnection();
1062 0 : if (NS_FAILED(rv)) {
1063 0 : NS_WARNING("Failed to reset the connection!!!");
1064 0 : return;
1065 : }
1066 :
1067 0 : rv = CheckInnerWindowCorrectness();
1068 0 : if (NS_FAILED(rv)) {
1069 0 : return;
1070 : }
1071 :
1072 0 : nsCOMPtr<nsIDOMEvent> event;
1073 0 : rv = NS_NewDOMEvent(getter_AddRefs(event), nsnull, nsnull);
1074 0 : if (NS_FAILED(rv)) {
1075 0 : NS_WARNING("Failed to create the error event!!!");
1076 : return;
1077 : }
1078 :
1079 : // it doesn't bubble, and it isn't cancelable
1080 0 : rv = event->InitEvent(NS_LITERAL_STRING("error"), false, false);
1081 0 : if (NS_FAILED(rv)) {
1082 0 : NS_WARNING("Failed to init the error event!!!");
1083 : return;
1084 : }
1085 :
1086 0 : nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
1087 0 : privateEvent->SetTrusted(true);
1088 :
1089 0 : rv = DispatchDOMEvent(nsnull, event, nsnull, nsnull);
1090 0 : if (NS_FAILED(rv)) {
1091 0 : NS_WARNING("Failed to dispatch the error event!!!");
1092 : return;
1093 : }
1094 :
1095 0 : rv = SetReconnectionTimeout();
1096 0 : if (NS_FAILED(rv)) {
1097 0 : NS_WARNING("Failed to set the timeout for reestablishing the connection!!!");
1098 : return;
1099 : }
1100 : }
1101 :
1102 : nsresult
1103 0 : nsEventSource::SetReconnectionTimeout()
1104 : {
1105 0 : if (mReadyState == nsIEventSource::CLOSED) {
1106 0 : return NS_ERROR_ABORT;
1107 : }
1108 :
1109 : // the timer will be used whenever the requests are going finished.
1110 0 : if (!mTimer) {
1111 0 : mTimer = do_CreateInstance("@mozilla.org/timer;1");
1112 0 : NS_ENSURE_STATE(mTimer);
1113 : }
1114 :
1115 : NS_ASSERTION(mReconnectionTime >= 0, "mReconnectionTime lies");
1116 0 : nsresult rv = mTimer->InitWithFuncCallback(TimerCallback, this,
1117 : mReconnectionTime,
1118 0 : nsITimer::TYPE_ONE_SHOT);
1119 0 : NS_ENSURE_SUCCESS(rv, rv);
1120 :
1121 0 : return NS_OK;
1122 : }
1123 :
1124 : nsresult
1125 0 : nsEventSource::PrintErrorOnConsole(const char *aBundleURI,
1126 : const PRUnichar *aError,
1127 : const PRUnichar **aFormatStrings,
1128 : PRUint32 aFormatStringsLen)
1129 : {
1130 : nsCOMPtr<nsIStringBundleService> bundleService =
1131 0 : mozilla::services::GetStringBundleService();
1132 0 : NS_ENSURE_STATE(bundleService);
1133 :
1134 0 : nsCOMPtr<nsIStringBundle> strBundle;
1135 : nsresult rv =
1136 0 : bundleService->CreateBundle(aBundleURI, getter_AddRefs(strBundle));
1137 0 : NS_ENSURE_SUCCESS(rv, rv);
1138 :
1139 : nsCOMPtr<nsIConsoleService> console(
1140 0 : do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv));
1141 0 : NS_ENSURE_SUCCESS(rv, rv);
1142 :
1143 : nsCOMPtr<nsIScriptError> errObj(
1144 0 : do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv));
1145 0 : NS_ENSURE_SUCCESS(rv, rv);
1146 :
1147 : // Localize the error message
1148 0 : nsXPIDLString message;
1149 0 : if (aFormatStrings) {
1150 0 : rv = strBundle->FormatStringFromName(aError, aFormatStrings,
1151 : aFormatStringsLen,
1152 0 : getter_Copies(message));
1153 : } else {
1154 0 : rv = strBundle->GetStringFromName(aError, getter_Copies(message));
1155 : }
1156 0 : NS_ENSURE_SUCCESS(rv, rv);
1157 :
1158 0 : rv = errObj->InitWithWindowID(message.get(),
1159 : mScriptFile.get(),
1160 : nsnull,
1161 : mScriptLine, 0,
1162 : nsIScriptError::errorFlag,
1163 0 : "Event Source", mInnerWindowID);
1164 0 : NS_ENSURE_SUCCESS(rv, rv);
1165 :
1166 : // print the error message directly to the JS console
1167 0 : rv = console->LogMessage(errObj);
1168 0 : NS_ENSURE_SUCCESS(rv, rv);
1169 :
1170 0 : return NS_OK;
1171 : }
1172 :
1173 : nsresult
1174 0 : nsEventSource::ConsoleError()
1175 : {
1176 0 : nsCAutoString targetSpec;
1177 0 : nsresult rv = mSrc->GetSpec(targetSpec);
1178 0 : NS_ENSURE_SUCCESS(rv, rv);
1179 :
1180 0 : NS_ConvertUTF8toUTF16 specUTF16(targetSpec);
1181 0 : const PRUnichar *formatStrings[] = { specUTF16.get() };
1182 :
1183 0 : if (mReadyState == nsIEventSource::CONNECTING) {
1184 : rv = PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
1185 0 : NS_LITERAL_STRING("connectionFailure").get(),
1186 0 : formatStrings, ArrayLength(formatStrings));
1187 : } else {
1188 : rv = PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
1189 0 : NS_LITERAL_STRING("netInterrupt").get(),
1190 0 : formatStrings, ArrayLength(formatStrings));
1191 : }
1192 0 : NS_ENSURE_SUCCESS(rv, rv);
1193 :
1194 0 : return NS_OK;
1195 : }
1196 :
1197 : nsresult
1198 0 : nsEventSource::DispatchFailConnection()
1199 : {
1200 : nsCOMPtr<nsIRunnable> event =
1201 0 : NS_NewRunnableMethod(this, &nsEventSource::FailConnection);
1202 0 : NS_ENSURE_STATE(event);
1203 :
1204 0 : return NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
1205 : }
1206 :
1207 : void
1208 0 : nsEventSource::FailConnection()
1209 : {
1210 0 : if (mReadyState == nsIEventSource::CLOSED) {
1211 0 : return;
1212 : }
1213 :
1214 0 : nsresult rv = ConsoleError();
1215 0 : if (NS_FAILED(rv)) {
1216 0 : NS_WARNING("Failed to print to the console error");
1217 : }
1218 :
1219 : // When a user agent is to fail the connection, the user agent must set the
1220 : // readyState attribute to CLOSED and queue a task to fire a simple event
1221 : // named error at the EventSource object.
1222 :
1223 0 : Close(); // it sets mReadyState to CLOSED
1224 :
1225 0 : rv = CheckInnerWindowCorrectness();
1226 0 : if (NS_FAILED(rv)) {
1227 0 : return;
1228 : }
1229 :
1230 0 : nsCOMPtr<nsIDOMEvent> event;
1231 0 : rv = NS_NewDOMEvent(getter_AddRefs(event), nsnull, nsnull);
1232 0 : if (NS_FAILED(rv)) {
1233 0 : NS_WARNING("Failed to create the error event!!!");
1234 : return;
1235 : }
1236 :
1237 : // it doesn't bubble, and it isn't cancelable
1238 0 : rv = event->InitEvent(NS_LITERAL_STRING("error"), false, false);
1239 0 : if (NS_FAILED(rv)) {
1240 0 : NS_WARNING("Failed to init the error event!!!");
1241 : return;
1242 : }
1243 :
1244 0 : nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
1245 0 : privateEvent->SetTrusted(true);
1246 :
1247 0 : rv = DispatchDOMEvent(nsnull, event, nsnull, nsnull);
1248 0 : if (NS_FAILED(rv)) {
1249 0 : NS_WARNING("Failed to dispatch the error event!!!");
1250 : return;
1251 : }
1252 : }
1253 :
1254 : bool
1255 0 : nsEventSource::CheckCanRequestSrc(nsIURI* aSrc)
1256 : {
1257 0 : if (mReadyState == nsIEventSource::CLOSED) {
1258 0 : return false;
1259 : }
1260 :
1261 0 : bool isValidURI = false;
1262 0 : bool isValidContentLoadPolicy = false;
1263 0 : bool isValidProtocol = false;
1264 :
1265 0 : nsCOMPtr<nsIURI> srcToTest = aSrc ? aSrc : mSrc.get();
1266 0 : NS_ENSURE_TRUE(srcToTest, false);
1267 :
1268 : PRUint32 aCheckURIFlags =
1269 : nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL |
1270 0 : nsIScriptSecurityManager::DISALLOW_SCRIPT;
1271 :
1272 0 : nsresult rv = nsContentUtils::GetSecurityManager()->
1273 : CheckLoadURIWithPrincipal(mPrincipal,
1274 : srcToTest,
1275 0 : aCheckURIFlags);
1276 0 : isValidURI = NS_SUCCEEDED(rv);
1277 :
1278 : // After the security manager, the content-policy check
1279 :
1280 0 : nsIScriptContext* sc = GetContextForEventHandlers(&rv);
1281 : nsCOMPtr<nsIDocument> doc =
1282 0 : nsContentUtils::GetDocumentFromScriptContext(sc);
1283 :
1284 : // mScriptContext should be initialized because of GetBaseURI() above.
1285 : // Still need to consider the case that doc is nsnull however.
1286 0 : rv = CheckInnerWindowCorrectness();
1287 0 : NS_ENSURE_SUCCESS(rv, false);
1288 0 : PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
1289 : rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_DATAREQUEST,
1290 : srcToTest,
1291 : mPrincipal,
1292 : doc,
1293 0 : NS_LITERAL_CSTRING(TEXT_EVENT_STREAM),
1294 : nsnull, // extra
1295 : &shouldLoad,
1296 : nsContentUtils::GetContentPolicy(),
1297 0 : nsContentUtils::GetSecurityManager());
1298 0 : isValidContentLoadPolicy = NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(shouldLoad);
1299 :
1300 0 : nsCAutoString targetURIScheme;
1301 0 : rv = srcToTest->GetScheme(targetURIScheme);
1302 0 : if (NS_SUCCEEDED(rv)) {
1303 : // We only have the http support for now
1304 0 : isValidProtocol = targetURIScheme.EqualsLiteral("http") ||
1305 0 : targetURIScheme.EqualsLiteral("https");
1306 : }
1307 :
1308 0 : return isValidURI && isValidContentLoadPolicy && isValidProtocol;
1309 : }
1310 :
1311 : // static
1312 : void
1313 0 : nsEventSource::TimerCallback(nsITimer* aTimer, void* aClosure)
1314 : {
1315 0 : nsRefPtr<nsEventSource> thisObject = static_cast<nsEventSource*>(aClosure);
1316 :
1317 0 : if (thisObject->mReadyState == nsIEventSource::CLOSED) {
1318 : return;
1319 : }
1320 :
1321 0 : NS_PRECONDITION(!thisObject->mHttpChannel,
1322 : "the channel hasn't been cancelled!!");
1323 :
1324 0 : if (!thisObject->mFrozen) {
1325 0 : nsresult rv = thisObject->InitChannelAndRequestEventSource();
1326 0 : if (NS_FAILED(rv)) {
1327 0 : NS_WARNING("thisObject->InitChannelAndRequestEventSource() failed");
1328 : return;
1329 : }
1330 : }
1331 : }
1332 :
1333 : nsresult
1334 0 : nsEventSource::Thaw()
1335 : {
1336 0 : if (mReadyState == nsIEventSource::CLOSED || !mFrozen) {
1337 0 : return NS_OK;
1338 : }
1339 :
1340 0 : NS_ASSERTION(!mHttpChannel, "the connection hasn't been closed!!!");
1341 :
1342 0 : mFrozen = false;
1343 : nsresult rv;
1344 0 : if (!mGoingToDispatchAllMessages && mMessagesToDispatch.GetSize() > 0) {
1345 : nsCOMPtr<nsIRunnable> event =
1346 0 : NS_NewRunnableMethod(this, &nsEventSource::DispatchAllMessageEvents);
1347 0 : NS_ENSURE_STATE(event);
1348 :
1349 0 : mGoingToDispatchAllMessages = true;
1350 :
1351 0 : rv = NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
1352 0 : NS_ENSURE_SUCCESS(rv, rv);
1353 : }
1354 :
1355 0 : rv = InitChannelAndRequestEventSource();
1356 0 : NS_ENSURE_SUCCESS(rv, rv);
1357 :
1358 0 : return NS_OK;
1359 : }
1360 :
1361 : nsresult
1362 0 : nsEventSource::Freeze()
1363 : {
1364 0 : if (mReadyState == nsIEventSource::CLOSED || mFrozen) {
1365 0 : return NS_OK;
1366 : }
1367 :
1368 0 : NS_ASSERTION(!mHttpChannel, "the connection hasn't been closed!!!");
1369 0 : mFrozen = true;
1370 0 : return NS_OK;
1371 : }
1372 :
1373 : nsresult
1374 0 : nsEventSource::DispatchCurrentMessageEvent()
1375 : {
1376 0 : nsAutoPtr<Message> message(new Message());
1377 0 : *message = mCurrentMessage;
1378 :
1379 0 : ClearFields();
1380 :
1381 0 : if (message->mData.IsEmpty()) {
1382 0 : return NS_OK;
1383 : }
1384 :
1385 : // removes the trailing LF from mData
1386 0 : NS_ASSERTION(message->mData.CharAt(message->mData.Length() - 1) == LF_CHAR,
1387 : "Invalid trailing character! LF was expected instead.");
1388 0 : message->mData.SetLength(message->mData.Length() - 1);
1389 :
1390 0 : if (message->mEventName.IsEmpty()) {
1391 0 : message->mEventName.AssignLiteral("message");
1392 : }
1393 :
1394 0 : if (message->mLastEventID.IsEmpty() && !mLastEventID.IsEmpty()) {
1395 0 : message->mLastEventID.Assign(mLastEventID);
1396 : }
1397 :
1398 0 : PRInt32 sizeBefore = mMessagesToDispatch.GetSize();
1399 0 : mMessagesToDispatch.Push(message.forget());
1400 0 : NS_ENSURE_TRUE(mMessagesToDispatch.GetSize() == sizeBefore + 1,
1401 : NS_ERROR_OUT_OF_MEMORY);
1402 :
1403 :
1404 0 : if (!mGoingToDispatchAllMessages) {
1405 : nsCOMPtr<nsIRunnable> event =
1406 0 : NS_NewRunnableMethod(this, &nsEventSource::DispatchAllMessageEvents);
1407 0 : NS_ENSURE_STATE(event);
1408 :
1409 0 : mGoingToDispatchAllMessages = true;
1410 :
1411 0 : return NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
1412 : }
1413 :
1414 0 : return NS_OK;
1415 : }
1416 :
1417 : void
1418 0 : nsEventSource::DispatchAllMessageEvents()
1419 : {
1420 0 : if (mReadyState == nsIEventSource::CLOSED || mFrozen) {
1421 0 : return;
1422 : }
1423 :
1424 0 : mGoingToDispatchAllMessages = false;
1425 :
1426 0 : nsresult rv = CheckInnerWindowCorrectness();
1427 0 : if (NS_FAILED(rv)) {
1428 0 : return;
1429 : }
1430 :
1431 : // Let's play get the JSContext
1432 0 : nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(GetOwner());
1433 0 : NS_ENSURE_TRUE(sgo,);
1434 :
1435 0 : nsIScriptContext* scriptContext = sgo->GetContext();
1436 0 : NS_ENSURE_TRUE(scriptContext,);
1437 :
1438 0 : JSContext* cx = scriptContext->GetNativeContext();
1439 0 : NS_ENSURE_TRUE(cx,);
1440 :
1441 0 : while (mMessagesToDispatch.GetSize() > 0) {
1442 : nsAutoPtr<Message>
1443 0 : message(static_cast<Message*>(mMessagesToDispatch.PopFront()));
1444 :
1445 : // Now we can turn our string into a jsval
1446 : jsval jsData;
1447 : {
1448 : JSString* jsString;
1449 0 : JSAutoRequest ar(cx);
1450 : jsString = JS_NewUCStringCopyN(cx,
1451 0 : message->mData.get(),
1452 0 : message->mData.Length());
1453 0 : NS_ENSURE_TRUE(jsString,);
1454 :
1455 0 : jsData = STRING_TO_JSVAL(jsString);
1456 : }
1457 :
1458 : // create an event that uses the MessageEvent interface,
1459 : // which does not bubble, is not cancelable, and has no default action
1460 :
1461 0 : nsCOMPtr<nsIDOMEvent> event;
1462 0 : rv = NS_NewDOMMessageEvent(getter_AddRefs(event), nsnull, nsnull);
1463 0 : if (NS_FAILED(rv)) {
1464 0 : NS_WARNING("Failed to create the message event!!!");
1465 : return;
1466 : }
1467 :
1468 0 : nsCOMPtr<nsIDOMMessageEvent> messageEvent = do_QueryInterface(event);
1469 0 : rv = messageEvent->InitMessageEvent(message->mEventName,
1470 : false, false,
1471 : jsData,
1472 : mOrigin,
1473 0 : message->mLastEventID, nsnull);
1474 0 : if (NS_FAILED(rv)) {
1475 0 : NS_WARNING("Failed to init the message event!!!");
1476 : return;
1477 : }
1478 :
1479 0 : nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
1480 0 : privateEvent->SetTrusted(true);
1481 :
1482 0 : rv = DispatchDOMEvent(nsnull, event, nsnull, nsnull);
1483 0 : if (NS_FAILED(rv)) {
1484 0 : NS_WARNING("Failed to dispatch the message event!!!");
1485 : return;
1486 : }
1487 : }
1488 : }
1489 :
1490 : nsresult
1491 0 : nsEventSource::ClearFields()
1492 : {
1493 : // mLastEventID and mReconnectionTime must be cached
1494 :
1495 0 : mCurrentMessage.mEventName.Truncate();
1496 0 : mCurrentMessage.mLastEventID.Truncate();
1497 0 : mCurrentMessage.mData.Truncate();
1498 :
1499 0 : mLastFieldName.Truncate();
1500 0 : mLastFieldValue.Truncate();
1501 :
1502 0 : return NS_OK;
1503 : }
1504 :
1505 : nsresult
1506 0 : nsEventSource::SetFieldAndClear()
1507 : {
1508 0 : if (mLastFieldName.IsEmpty()) {
1509 0 : mLastFieldValue.Truncate();
1510 0 : return NS_OK;
1511 : }
1512 :
1513 : PRUnichar first_char;
1514 0 : first_char = mLastFieldName.CharAt(0);
1515 :
1516 0 : switch (first_char) // with no case folding performed
1517 : {
1518 : case PRUnichar('d'):
1519 0 : if (mLastFieldName.EqualsLiteral("data")) {
1520 : // If the field name is "data" append the field value to the data
1521 : // buffer, then append a single U+000A LINE FEED (LF) character
1522 : // to the data buffer.
1523 0 : mCurrentMessage.mData.Append(mLastFieldValue);
1524 0 : mCurrentMessage.mData.Append(LF_CHAR);
1525 : }
1526 0 : break;
1527 :
1528 : case PRUnichar('e'):
1529 0 : if (mLastFieldName.EqualsLiteral("event")) {
1530 0 : mCurrentMessage.mEventName.Assign(mLastFieldValue);
1531 : }
1532 0 : break;
1533 :
1534 : case PRUnichar('i'):
1535 0 : if (mLastFieldName.EqualsLiteral("id")) {
1536 0 : mCurrentMessage.mLastEventID.Assign(mLastFieldValue);
1537 0 : mLastEventID.Assign(mLastFieldValue);
1538 : }
1539 0 : break;
1540 :
1541 : case PRUnichar('r'):
1542 0 : if (mLastFieldName.EqualsLiteral("retry")) {
1543 0 : PRUint32 newValue=0;
1544 0 : PRUint32 i = 0; // we must ensure that there are only digits
1545 0 : bool assign = true;
1546 0 : for (i = 0; i < mLastFieldValue.Length(); ++i) {
1547 0 : if (mLastFieldValue.CharAt(i) < (PRUnichar)'0' ||
1548 0 : mLastFieldValue.CharAt(i) > (PRUnichar)'9') {
1549 0 : assign = false;
1550 0 : break;
1551 : }
1552 : newValue = newValue*10 +
1553 0 : (((PRUint32)mLastFieldValue.CharAt(i))-
1554 0 : ((PRUint32)((PRUnichar)'0')));
1555 : }
1556 :
1557 0 : if (assign) {
1558 0 : if (newValue < MIN_RECONNECTION_TIME_VALUE) {
1559 0 : mReconnectionTime = MIN_RECONNECTION_TIME_VALUE;
1560 0 : } else if (newValue > MAX_RECONNECTION_TIME_VALUE) {
1561 0 : mReconnectionTime = MAX_RECONNECTION_TIME_VALUE;
1562 : } else {
1563 0 : mReconnectionTime = newValue;
1564 : }
1565 : }
1566 0 : break;
1567 : }
1568 0 : break;
1569 : }
1570 :
1571 0 : mLastFieldName.Truncate();
1572 0 : mLastFieldValue.Truncate();
1573 :
1574 0 : return NS_OK;
1575 : }
1576 :
1577 : nsresult
1578 0 : nsEventSource::CheckHealthOfRequestCallback(nsIRequest *aRequestCallback)
1579 : {
1580 : // check if we have been closed or if the request has been canceled
1581 : // or if we have been frozen
1582 0 : if (mReadyState == nsIEventSource::CLOSED || !mHttpChannel ||
1583 : mFrozen || mErrorLoadOnRedirect) {
1584 0 : return NS_ERROR_ABORT;
1585 : }
1586 :
1587 0 : nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequestCallback);
1588 0 : NS_ENSURE_STATE(httpChannel);
1589 :
1590 0 : if (httpChannel != mHttpChannel) {
1591 0 : NS_WARNING("wrong channel from request callback");
1592 0 : return NS_ERROR_ABORT;
1593 : }
1594 :
1595 0 : return NS_OK;
1596 : }
1597 :
1598 : nsresult
1599 0 : nsEventSource::ParseCharacter(PRUnichar aChr)
1600 : {
1601 : nsresult rv;
1602 :
1603 0 : if (mReadyState == nsIEventSource::CLOSED) {
1604 0 : return NS_ERROR_ABORT;
1605 : }
1606 :
1607 0 : switch (mStatus)
1608 : {
1609 : case PARSE_STATE_OFF:
1610 0 : NS_ERROR("Invalid state");
1611 0 : return NS_ERROR_FAILURE;
1612 : break;
1613 :
1614 : case PARSE_STATE_BEGIN_OF_STREAM:
1615 0 : if (aChr == BOM_CHAR) {
1616 0 : mStatus = PARSE_STATE_BOM_WAS_READ; // ignore it
1617 0 : } else if (aChr == CR_CHAR) {
1618 0 : mStatus = PARSE_STATE_CR_CHAR;
1619 0 : } else if (aChr == LF_CHAR) {
1620 0 : mStatus = PARSE_STATE_BEGIN_OF_LINE;
1621 0 : } else if (aChr == COLON_CHAR) {
1622 0 : mStatus = PARSE_STATE_COMMENT;
1623 : } else {
1624 0 : mLastFieldName += aChr;
1625 0 : mStatus = PARSE_STATE_FIELD_NAME;
1626 : }
1627 :
1628 0 : break;
1629 :
1630 : case PARSE_STATE_BOM_WAS_READ:
1631 0 : if (aChr == CR_CHAR) {
1632 0 : mStatus = PARSE_STATE_CR_CHAR;
1633 0 : } else if (aChr == LF_CHAR) {
1634 0 : mStatus = PARSE_STATE_BEGIN_OF_LINE;
1635 0 : } else if (aChr == COLON_CHAR) {
1636 0 : mStatus = PARSE_STATE_COMMENT;
1637 : } else {
1638 0 : mLastFieldName += aChr;
1639 0 : mStatus = PARSE_STATE_FIELD_NAME;
1640 : }
1641 0 : break;
1642 :
1643 : case PARSE_STATE_CR_CHAR:
1644 0 : if (aChr == CR_CHAR) {
1645 0 : rv = DispatchCurrentMessageEvent(); // there is an empty line (CRCR)
1646 0 : NS_ENSURE_SUCCESS(rv, rv);
1647 0 : } else if (aChr == LF_CHAR) {
1648 0 : mStatus = PARSE_STATE_BEGIN_OF_LINE;
1649 0 : } else if (aChr == COLON_CHAR) {
1650 0 : mStatus = PARSE_STATE_COMMENT;
1651 : } else {
1652 0 : mLastFieldName += aChr;
1653 0 : mStatus = PARSE_STATE_FIELD_NAME;
1654 : }
1655 :
1656 0 : break;
1657 :
1658 : case PARSE_STATE_COMMENT:
1659 0 : if (aChr == CR_CHAR) {
1660 0 : mStatus = PARSE_STATE_CR_CHAR;
1661 0 : } else if (aChr == LF_CHAR) {
1662 0 : mStatus = PARSE_STATE_BEGIN_OF_LINE;
1663 : }
1664 :
1665 0 : break;
1666 :
1667 : case PARSE_STATE_FIELD_NAME:
1668 0 : if (aChr == CR_CHAR) {
1669 0 : rv = SetFieldAndClear();
1670 0 : NS_ENSURE_SUCCESS(rv, rv);
1671 :
1672 0 : mStatus = PARSE_STATE_CR_CHAR;
1673 0 : } else if (aChr == LF_CHAR) {
1674 0 : rv = SetFieldAndClear();
1675 0 : NS_ENSURE_SUCCESS(rv, rv);
1676 :
1677 0 : mStatus = PARSE_STATE_BEGIN_OF_LINE;
1678 0 : } else if (aChr == COLON_CHAR) {
1679 0 : mStatus = PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE;
1680 : } else {
1681 0 : mLastFieldName += aChr;
1682 : }
1683 :
1684 0 : break;
1685 :
1686 : case PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE:
1687 0 : if (aChr == CR_CHAR) {
1688 0 : rv = SetFieldAndClear();
1689 0 : NS_ENSURE_SUCCESS(rv, rv);
1690 :
1691 0 : mStatus = PARSE_STATE_CR_CHAR;
1692 0 : } else if (aChr == LF_CHAR) {
1693 0 : rv = SetFieldAndClear();
1694 0 : NS_ENSURE_SUCCESS(rv, rv);
1695 :
1696 0 : mStatus = PARSE_STATE_BEGIN_OF_LINE;
1697 0 : } else if (aChr == SPACE_CHAR) {
1698 0 : mStatus = PARSE_STATE_FIELD_VALUE;
1699 : } else {
1700 0 : mLastFieldValue += aChr;
1701 0 : mStatus = PARSE_STATE_FIELD_VALUE;
1702 : }
1703 :
1704 0 : break;
1705 :
1706 : case PARSE_STATE_FIELD_VALUE:
1707 0 : if (aChr == CR_CHAR) {
1708 0 : rv = SetFieldAndClear();
1709 0 : NS_ENSURE_SUCCESS(rv, rv);
1710 :
1711 0 : mStatus = PARSE_STATE_CR_CHAR;
1712 0 : } else if (aChr == LF_CHAR) {
1713 0 : rv = SetFieldAndClear();
1714 0 : NS_ENSURE_SUCCESS(rv, rv);
1715 :
1716 0 : mStatus = PARSE_STATE_BEGIN_OF_LINE;
1717 : } else {
1718 0 : mLastFieldValue += aChr;
1719 : }
1720 :
1721 0 : break;
1722 :
1723 : case PARSE_STATE_BEGIN_OF_LINE:
1724 0 : if (aChr == CR_CHAR) {
1725 0 : rv = DispatchCurrentMessageEvent(); // there is an empty line
1726 0 : NS_ENSURE_SUCCESS(rv, rv);
1727 :
1728 0 : mStatus = PARSE_STATE_CR_CHAR;
1729 0 : } else if (aChr == LF_CHAR) {
1730 0 : rv = DispatchCurrentMessageEvent(); // there is an empty line
1731 0 : NS_ENSURE_SUCCESS(rv, rv);
1732 :
1733 0 : mStatus = PARSE_STATE_BEGIN_OF_LINE;
1734 0 : } else if (aChr == COLON_CHAR) {
1735 0 : mStatus = PARSE_STATE_COMMENT;
1736 : } else {
1737 0 : mLastFieldName += aChr;
1738 0 : mStatus = PARSE_STATE_FIELD_NAME;
1739 : }
1740 :
1741 0 : break;
1742 : }
1743 :
1744 0 : return NS_OK;
1745 4392 : }
|