Bug 338583, implement EventSource, r=smaug,Ms2ger
authorwfernandom2004@gmail.com
Sun, 22 May 2011 22:30:07 +0100
changeset 69966 a3903f835a81f43b0c58fe8fbf1eb94f1ae3351e
parent 69965 0f9347d4012161d63903d1f187c7d35828e90ade
child 69967 362e3ce08eadf39b68f5f5d9f3d51b463fa54b62
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewerssmaug, Ms2ger
bugs338583
milestone6.0a1
Bug 338583, implement EventSource, r=smaug,Ms2ger
content/base/public/Makefile.in
content/base/public/nsIEventSource.idl
content/base/src/Makefile.in
content/base/src/nsContentUtils.cpp
content/base/src/nsEventSource.cpp
content/base/src/nsEventSource.h
content/base/src/nsGkAtomList.h
content/base/test/Makefile.in
content/base/test/accesscontrol.resource
content/base/test/accesscontrol.resource^headers^
content/base/test/badContentType.eventsource
content/base/test/badContentType.eventsource^headers^
content/base/test/badEventFieldName.eventsource
content/base/test/badEventFieldName.eventsource^headers^
content/base/test/badHTTPResponseCode.eventsource
content/base/test/badHTTPResponseCode.eventsource^headers^
content/base/test/badMessageEvent.eventsource
content/base/test/badMessageEvent.eventsource^headers^
content/base/test/delayedServerEvents.sjs
content/base/test/eventsource.resource
content/base/test/eventsource.resource^headers^
content/base/test/forRemoval.resource
content/base/test/forRemoval.resource^headers^
content/base/test/invalid_accesscontrol.resource
content/base/test/invalid_accesscontrol.resource^headers^
content/base/test/somedatas.resource
content/base/test/somedatas.resource^headers^
content/base/test/test_bug338583.html
content/events/src/nsDOMEvent.cpp
content/events/src/nsDOMEvent.h
dom/base/nsDOMClassInfo.cpp
dom/base/nsDOMClassInfoClasses.h
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
dom/base/nsPIDOMWindow.h
layout/build/nsLayoutModule.cpp
modules/libpref/src/init/all.js
netwerk/mime/nsMimeTypes.h
widget/public/nsGUIEvent.h
xpcom/threads/nsITimer.idl
--- a/content/base/public/Makefile.in
+++ b/content/base/public/Makefile.in
@@ -119,12 +119,13 @@ XPIDLSRCS	= \
 		nsIScriptEventManager.idl \
 		nsIImageLoadingContent.idl \
 		nsIObjectLoadingContent.idl \
 		nsIFrameLoader.idl \
 		nsIXMLHttpRequest.idl \
 		nsIContentSecurityPolicy.idl \
 		nsIFrameMessageManager.idl \
 		nsIWebSocket.idl \
+		nsIEventSource.idl \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
new file mode 100644
--- /dev/null
+++ b/content/base/public/nsIEventSource.idl
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * France Telecom Research and Development.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *     Joshua Randall <jcrandall@alum.mit.edu> (original author)
+ *     Wellington Fernando de Macedo <wfernandom2004@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * The nsIEventSource interface is the interface for server-sent
+ * DOM events as described in
+ * http://dev.w3.org/html5/eventsource/#eventsource
+ *
+ */
+
+#include "nsISupports.idl"
+
+interface nsIDOMEventListener;
+interface nsIPrincipal;
+interface nsIScriptContext;
+interface nsPIDOMWindow;
+
+[scriptable, uuid(755e2d2d-a836-4539-83f4-16b51156341f)]
+interface nsIEventSource : nsISupports
+{
+  readonly attribute DOMString url;
+
+  // ready state
+  const unsigned short CONNECTING = 0;
+  const unsigned short OPEN = 1;
+  const unsigned short CLOSED = 2;
+  readonly attribute long readyState;
+
+  // event handler attributes
+  attribute nsIDOMEventListener onopen;
+  attribute nsIDOMEventListener onmessage;
+  attribute nsIDOMEventListener onerror;
+
+  /**
+   * Close the connection, if any, and set the readyState attribute to CLOSED.
+   * If the connection is already closed, the method does nothing.
+   */
+  void close();
+
+  /**
+   * Initialize the object for use from C++ code with the principal, script
+   * context, and owner window that should be used.
+   *
+   * @param principal The principal to use for the request. This must not be
+   *                  null.
+   * @param scriptContext The script context to use for the request. May be
+   *                      null.
+   * @param ownerWindow The associated window for the request. May be null.
+   * @param url The EventSource's url. This must not be empty.
+   */
+  [noscript] void init(in nsIPrincipal principal,
+                       in nsIScriptContext scriptContext,
+                       in nsPIDOMWindow ownerWindow,
+                       in DOMString url);
+};
--- a/content/base/src/Makefile.in
+++ b/content/base/src/Makefile.in
@@ -147,16 +147,17 @@ CPPSRCS		= \
 		nsXMLContentSerializer.cpp \
 		nsXMLHttpRequest.cpp \
 		nsXMLNameSpaceMap.cpp \
 		Link.cpp \
 		nsFileDataProtocolHandler.cpp \
 		nsFrameMessageManager.cpp \
 		nsInProcessTabChildGlobal.cpp \
 		ThirdPartyUtil.cpp \
+		nsEventSource.cpp \
 		$(NULL)
 
 # Are we targeting x86-32 or x86-64?  If so, we want to include SSE2 code for
 # nsTextFragment.cpp
 ifneq (,$(INTEL_ARCHITECTURE))
 
 CPPSRCS += nsTextFragmentSSE2.cpp
 
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -592,16 +592,18 @@ nsContentUtils::InitializeEventTable() {
     { nsGkAtoms::onMozBeforeResize,             NS_BEFORERESIZE_EVENT, EventNameType_None, NS_EVENT },
     { nsGkAtoms::onresize,                      NS_RESIZE_EVENT,
                                                 (EventNameType_HTMLXUL | EventNameType_SVGSVG), NS_EVENT },
     { nsGkAtoms::onscroll,                      NS_SCROLL_EVENT,
                                                 (EventNameType_HTMLXUL | EventNameType_SVGSVG), NS_EVENT_NULL },
     { nsGkAtoms::oncopy,                        NS_COPY, EventNameType_HTMLXUL, NS_EVENT },
     { nsGkAtoms::oncut,                         NS_CUT, EventNameType_HTMLXUL, NS_EVENT },
     { nsGkAtoms::onpaste,                       NS_PASTE, EventNameType_HTMLXUL, NS_EVENT },
+    { nsGkAtoms::onopen,                        NS_OPEN, EventNameType_None, NS_EVENT },
+    { nsGkAtoms::onmessage,                     NS_MESSAGE, EventNameType_None, NS_EVENT },
     // XUL specific events
     { nsGkAtoms::ontext,                        NS_TEXT_TEXT, EventNameType_XUL, NS_EVENT_NULL },
 
     { nsGkAtoms::oncompositionstart,            NS_COMPOSITION_START, EventNameType_XUL, NS_COMPOSITION_EVENT },
     { nsGkAtoms::oncompositionend,              NS_COMPOSITION_END, EventNameType_XUL, NS_COMPOSITION_EVENT },
 
     { nsGkAtoms::oncommand,                     NS_XUL_COMMAND, EventNameType_XUL, NS_INPUT_EVENT },
 
new file mode 100755
--- /dev/null
+++ b/content/base/src/nsEventSource.cpp
@@ -0,0 +1,1633 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Wellington Fernando de Macedo and Clayton Williams.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *    Wellington Fernando de Macedo <wfernandom2004@gmail.com>
+ *    Clayton Williams <claytonw@mit.edu>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsEventSource.h"
+#include "nsNetUtil.h"
+#include "nsMimeTypes.h"
+#include "nsDOMMessageEvent.h"
+#include "nsIJSContextStack.h"
+#include "nsIPromptFactory.h"
+#include "nsIWindowWatcher.h"
+#include "nsPresContext.h"
+#include "nsContentPolicyUtils.h"
+#include "nsIStringBundle.h"
+#include "nsIConsoleService.h"
+#include "nsIObserverService.h"
+#include "nsIScriptObjectPrincipal.h"
+#include "jsdbgapi.h"
+#include "nsJSUtils.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
+#include "nsIScriptError.h"
+#include "nsICharsetConverterManager.h"
+#include "nsIChannelPolicy.h"
+#include "nsIContentSecurityPolicy.h"
+
+#define REPLACEMENT_CHAR     (PRUnichar)0xFFFD
+#define BOM_CHAR             (PRUnichar)0xFEFF
+#define SPACE_CHAR           (PRUnichar)0x0020
+#define CR_CHAR              (PRUnichar)0x000D
+#define LF_CHAR              (PRUnichar)0x000A
+#define COLON_CHAR           (PRUnichar)0x003A
+
+#define DEFAULT_BUFFER_SIZE 4096
+
+// Reconnection time related values in milliseconds. The default one is equal
+// to the default value of the pref dom.server-events.default-reconnection-time
+#define MIN_RECONNECTION_TIME_VALUE       500
+#define DEFAULT_RECONNECTION_TIME_VALUE   5000
+#define MAX_RECONNECTION_TIME_VALUE       PR_IntervalToMilliseconds(DELAY_INTERVAL_LIMIT)
+
+nsEventSource::nsEventSource() :
+  mStatus(PARSE_STATE_OFF),
+  mFrozen(PR_FALSE),
+  mErrorLoadOnRedirect(PR_FALSE),
+  mGoingToDispatchAllMessages(PR_FALSE),
+  mLastConvertionResult(NS_OK),
+  mReadyState(nsIEventSource::CONNECTING),
+  mScriptLine(0),
+  mWindowID(0)
+{
+}
+
+nsEventSource::~nsEventSource()
+{
+  Close();
+
+  if (mListenerManager) {
+    mListenerManager->Disconnect();
+    mListenerManager = nsnull;
+  }
+}
+
+//-----------------------------------------------------------------------------
+// nsEventSource::nsISupports
+//-----------------------------------------------------------------------------
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(nsEventSource)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsEventSource,
+                                                  nsDOMEventTargetWrapperCache)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mSrc)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mNotificationCallbacks)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mLoadGroup)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mChannelEventSink)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mHttpChannel)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mTimer)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnOpenListener)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnMessageListener)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnErrorListener)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mUnicodeDecoder)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsEventSource, nsDOMEventTargetWrapperCache)
+  tmp->Close();
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnOpenListener)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnMessageListener)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnErrorListener)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+DOMCI_DATA(EventSource, nsEventSource)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsEventSource)
+  NS_INTERFACE_MAP_ENTRY(nsIEventSource)
+  NS_INTERFACE_MAP_ENTRY(nsIJSNativeInitializer)
+  NS_INTERFACE_MAP_ENTRY(nsIObserver)
+  NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
+  NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
+  NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
+  NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
+  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(EventSource)
+NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetWrapperCache)
+
+NS_IMPL_ADDREF_INHERITED(nsEventSource, nsDOMEventTargetWrapperCache)
+NS_IMPL_RELEASE_INHERITED(nsEventSource, nsDOMEventTargetWrapperCache)
+
+//-----------------------------------------------------------------------------
+// nsEventSource::nsIEventSource
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsEventSource::GetUrl(nsAString& aURL)
+{
+  aURL = mOriginalURL;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsEventSource::GetReadyState(PRInt32 *aReadyState)
+{
+  NS_ENSURE_ARG_POINTER(aReadyState);
+  *aReadyState = mReadyState;
+  return NS_OK;
+}
+
+#define NS_EVENTSRC_IMPL_DOMEVENTLISTENER(_eventlistenername, _eventlistener)  \
+  NS_IMETHODIMP                                                                \
+  nsEventSource::GetOn##_eventlistenername(nsIDOMEventListener * *aListener)   \
+  {                                                                            \
+    return GetInnerEventListener(_eventlistener, aListener);                   \
+  }                                                                            \
+                                                                               \
+  NS_IMETHODIMP                                                                \
+  nsEventSource::SetOn##_eventlistenername(nsIDOMEventListener * aListener)    \
+  {                                                                            \
+    return RemoveAddEventListener(NS_LITERAL_STRING(#_eventlistenername),      \
+                                  _eventlistener, aListener);                  \
+  }
+
+NS_EVENTSRC_IMPL_DOMEVENTLISTENER(open, mOnOpenListener)
+NS_EVENTSRC_IMPL_DOMEVENTLISTENER(error, mOnErrorListener)
+NS_EVENTSRC_IMPL_DOMEVENTLISTENER(message, mOnMessageListener)
+
+NS_IMETHODIMP
+nsEventSource::Close()
+{
+  if (mReadyState == nsIEventSource::CLOSED) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+  if (os) {
+    os->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC);
+    os->RemoveObserver(this, DOM_WINDOW_FROZEN_TOPIC);
+    os->RemoveObserver(this, DOM_WINDOW_THAWED_TOPIC);
+  }
+
+  if (mTimer) {
+    mTimer->Cancel();
+    mTimer = nsnull;
+  }
+
+  ResetConnection();
+
+  ClearFields();
+
+  while (mMessagesToDispatch.GetSize() != 0) {
+    delete static_cast<Message*>(mMessagesToDispatch.PopFront());
+  }
+
+  mSrc = nsnull;
+  mFrozen = PR_FALSE;
+
+  mScriptContext = nsnull;
+  mOwner = nsnull;
+
+  mUnicodeDecoder = nsnull;
+
+  mReadyState = nsIEventSource::CLOSED;
+
+  return NS_OK;
+}
+
+/**
+ * This Init method should only be called by C++ consumers.
+ */
+NS_IMETHODIMP
+nsEventSource::Init(nsIPrincipal* aPrincipal,
+                    nsIScriptContext* aScriptContext,
+                    nsPIDOMWindow* aOwnerWindow,
+                    const nsAString& aURL)
+{
+  NS_ENSURE_ARG(aPrincipal);
+
+  if (mReadyState != nsIEventSource::CONNECTING || !PrefEnabled()) {
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
+  mPrincipal = aPrincipal;
+  mScriptContext = aScriptContext;
+  if (aOwnerWindow) {
+    mOwner = aOwnerWindow->IsOuterWindow() ?
+      aOwnerWindow->GetCurrentInnerWindow() : aOwnerWindow;
+  } else {
+    mOwner = nsnull;
+  }
+
+  nsCOMPtr<nsIJSContextStack> stack =
+    do_GetService("@mozilla.org/js/xpc/ContextStack;1");
+  JSContext* cx = nsnull;
+  if (stack && NS_SUCCEEDED(stack->Peek(&cx)) && cx) {
+    const char *filename;
+    if (nsJSUtils::GetCallingLocation(cx, &filename, &mScriptLine)) {
+      mScriptFile.AssignASCII(filename);
+    }
+
+    mWindowID = nsJSUtils::GetCurrentlyRunningCodeWindowID(cx);
+  }
+
+  // Get the load group for the page. When requesting we'll add ourselves to it.
+  // This way any pending requests will be automatically aborted if the user
+  // leaves the page.
+  if (mScriptContext) {
+    nsCOMPtr<nsIDocument> doc =
+      nsContentUtils::GetDocumentFromScriptContext(mScriptContext);
+    if (doc) {
+      mLoadGroup = doc->GetDocumentLoadGroup();
+    }
+  }
+
+  // get the src
+  nsCOMPtr<nsIURI> baseURI;
+  nsresult rv = GetBaseURI(getter_AddRefs(baseURI));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIURI> srcURI;
+  rv = NS_NewURI(getter_AddRefs(srcURI), aURL, nsnull, baseURI);
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
+
+  // we observe when the window freezes and thaws
+  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+  NS_ENSURE_STATE(os);
+
+  rv = os->AddObserver(this, DOM_WINDOW_DESTROYED_TOPIC, PR_TRUE);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = os->AddObserver(this, DOM_WINDOW_FROZEN_TOPIC, PR_TRUE);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = os->AddObserver(this, DOM_WINDOW_THAWED_TOPIC, PR_TRUE);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsXPIDLCString origin;
+  rv = mPrincipal->GetOrigin(getter_Copies(origin));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCAutoString spec;
+  rv = srcURI->GetSpec(spec);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mOriginalURL = NS_ConvertUTF8toUTF16(spec);
+  mSrc = srcURI;
+  mOrigin = origin;
+
+  mReconnectionTime =
+    nsContentUtils::GetIntPref("dom.server-events.default-reconnection-time",
+                               DEFAULT_RECONNECTION_TIME_VALUE);
+
+  nsCOMPtr<nsICharsetConverterManager> convManager =
+    do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = convManager->GetUnicodeDecoder("UTF-8", getter_AddRefs(mUnicodeDecoder));
+  NS_ENSURE_SUCCESS(rv, rv);
+  mUnicodeDecoder->SetInputErrorBehavior(nsIUnicodeDecoder::kOnError_Recover);
+
+  // the constructor should throw a SYNTAX_ERROR only if it fails resolving the
+  // url parameter, so we don't care about the InitChannelAndRequestEventSource
+  // result.
+  InitChannelAndRequestEventSource();
+
+  return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsEventSource::nsIJSNativeInitializer methods:
+//-----------------------------------------------------------------------------
+
+/**
+ * This Initialize method is called from XPConnect via nsIJSNativeInitializer.
+ * It is used for constructing our nsEventSource from javascript. It expects a
+ * URL string parameter. Also, initializes the principal, the script context
+ * and the window owner.
+ */
+NS_IMETHODIMP
+nsEventSource::Initialize(nsISupports* aOwner,
+                          JSContext* aContext,
+                          JSObject* aObject,
+                          PRUint32 aArgc,
+                          jsval* aArgv)
+{
+  if (mReadyState != nsIEventSource::CONNECTING || !PrefEnabled() ||
+      aArgc < 1) {
+    return NS_ERROR_FAILURE;
+  }
+
+  JSAutoRequest ar(aContext);
+
+  JSString* jsstr = JS_ValueToString(aContext, aArgv[0]);
+  if (!jsstr) {
+    return NS_ERROR_DOM_SYNTAX_ERR;
+  }
+
+  JS::Anchor<JSString *> deleteProtector(jsstr);
+  size_t length;
+  const jschar *chars = JS_GetStringCharsAndLength(aContext, jsstr, &length);
+  if (!chars) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  nsAutoString urlParam;
+
+  urlParam.Assign(chars, length);
+
+  nsCOMPtr<nsPIDOMWindow> ownerWindow = do_QueryInterface(aOwner);
+  NS_ENSURE_STATE(ownerWindow);
+
+  nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aOwner);
+  NS_ENSURE_STATE(sgo);
+  nsCOMPtr<nsIScriptContext> scriptContext = sgo->GetContext();
+  NS_ENSURE_STATE(scriptContext);
+
+  nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal =
+    do_QueryInterface(aOwner);
+  NS_ENSURE_STATE(scriptPrincipal);
+  nsCOMPtr<nsIPrincipal> principal = scriptPrincipal->GetPrincipal();
+  NS_ENSURE_STATE(principal);
+
+  return Init(principal, scriptContext, ownerWindow, urlParam);
+}
+
+//-----------------------------------------------------------------------------
+// nsEventSource::nsIObserver
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsEventSource::Observe(nsISupports* aSubject,
+                       const char* aTopic,
+                       const PRUnichar* aData)
+{
+  if (mReadyState == nsIEventSource::CLOSED) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aSubject);
+  if (!mOwner || window != mOwner) {
+    return NS_OK;
+  }
+
+  mozilla::DebugOnly<nsresult> rv;
+  if (strcmp(aTopic, DOM_WINDOW_FROZEN_TOPIC) == 0) {
+    rv = Freeze();
+    NS_ASSERTION(rv, "Freeze() failed");
+  } else if (strcmp(aTopic, DOM_WINDOW_THAWED_TOPIC) == 0) {
+    rv = Thaw();
+    NS_ASSERTION(rv, "Thaw() failed");
+  } else if (strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC) == 0) {
+    Close();
+  }
+
+  return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsEventSource::nsIStreamListener
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsEventSource::OnStartRequest(nsIRequest *aRequest,
+                              nsISupports *ctxt)
+{
+  nsresult rv = CheckHealthOfRequestCallback(aRequest);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRBool requestSucceeded;
+  rv = httpChannel->GetRequestSucceeded(&requestSucceeded);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCAutoString contentType;
+  rv = httpChannel->GetContentType(contentType);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!requestSucceeded || !contentType.EqualsLiteral(TEXT_EVENT_STREAM)) {
+    DispatchFailConnection();
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  nsCOMPtr<nsIPrincipal> principal = mPrincipal;
+  if (nsContentUtils::IsSystemPrincipal(principal)) {
+    // Don't give this channel the system principal.
+    principal = do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+  rv = httpChannel->SetOwner(principal);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIRunnable> event =
+    NS_NewRunnableMethod(this, &nsEventSource::AnnounceConnection);
+  NS_ENSURE_STATE(event);
+
+  rv = NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mStatus = PARSE_STATE_BEGIN_OF_STREAM;
+
+  return NS_OK;
+}
+
+// this method parses the characters as they become available instead of
+// buffering them.
+NS_METHOD
+nsEventSource::StreamReaderFunc(nsIInputStream *aInputStream,
+                                void *aClosure,
+                                const char *aFromRawSegment,
+                                PRUint32 aToOffset,
+                                PRUint32 aCount,
+                                PRUint32 *aWriteCount)
+{
+  nsEventSource* thisObject = static_cast<nsEventSource*>(aClosure);
+  if (!thisObject || !aWriteCount) {
+    NS_WARNING("nsEventSource cannot read from stream: no aClosure or aWriteCount");
+    return NS_ERROR_FAILURE;
+  }
+
+  *aWriteCount = 0;
+
+  PRInt32 srcCount, outCount;
+  PRUnichar out[2];
+  nsresult rv;
+
+  const char *p = aFromRawSegment,
+             *end = aFromRawSegment + aCount;
+
+  do {
+    srcCount = aCount - (p - aFromRawSegment);
+    outCount = 2;
+
+    thisObject->mLastConvertionResult =
+      thisObject->mUnicodeDecoder->Convert(p, &srcCount, out, &outCount);
+
+    if (thisObject->mLastConvertionResult == NS_ERROR_ILLEGAL_INPUT) {
+      // There's an illegal byte in the input. It's now the responsibility
+      // of this calling code to output a U+FFFD REPLACEMENT CHARACTER, advance
+      // over the bad byte and reset the decoder.
+      rv = thisObject->ParseCharacter(REPLACEMENT_CHAR);
+      NS_ENSURE_SUCCESS(rv, rv);
+      p = p + srcCount + 1;
+      thisObject->mUnicodeDecoder->Reset();
+    } else {
+      for (PRInt32 i = 0; i < outCount; ++i) {
+        rv = thisObject->ParseCharacter(out[i]);
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+      p = p + srcCount;
+    }
+  } while (p < end &&
+           thisObject->mLastConvertionResult != NS_PARTIAL_MORE_INPUT &&
+           thisObject->mLastConvertionResult != NS_OK);
+
+  // check if the last byte was a bad one and
+  // clear the state since it was handled above.
+  if (thisObject->mLastConvertionResult == NS_ERROR_ILLEGAL_INPUT) {
+    thisObject->mLastConvertionResult = NS_OK;
+  }
+
+  *aWriteCount = aCount;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsEventSource::OnDataAvailable(nsIRequest *aRequest,
+                               nsISupports *aContext,
+                               nsIInputStream *aInputStream,
+                               PRUint32 aOffset,
+                               PRUint32 aCount)
+{
+  NS_ENSURE_ARG_POINTER(aInputStream);
+
+  nsresult rv = CheckHealthOfRequestCallback(aRequest);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRUint32 totalRead;
+  return aInputStream->ReadSegments(nsEventSource::StreamReaderFunc, this,
+                                    aCount, &totalRead);
+}
+
+NS_IMETHODIMP
+nsEventSource::OnStopRequest(nsIRequest *aRequest,
+                             nsISupports *aContext,
+                             nsresult aStatusCode)
+{
+  if (mReadyState == nsIEventSource::CLOSED) {
+    return NS_ERROR_ABORT;
+  }
+
+  if (NS_FAILED(aStatusCode)) {
+    DispatchFailConnection();
+    return aStatusCode;
+  }
+
+  nsresult rv;
+  nsresult healthOfRequestResult = CheckHealthOfRequestCallback(aRequest);
+  if (NS_SUCCEEDED(healthOfRequestResult)) {
+    // check if we had an incomplete UTF8 char at the end of the stream
+    if (mLastConvertionResult == NS_PARTIAL_MORE_INPUT) {
+      rv = ParseCharacter(REPLACEMENT_CHAR);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+
+    // once we reach the end of the stream we must
+    // dispatch the current event
+    switch (mStatus)
+    {
+      case PARSE_STATE_CR_CHAR:
+      case PARSE_STATE_COMMENT:
+      case PARSE_STATE_FIELD_NAME:
+      case PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE:
+      case PARSE_STATE_FIELD_VALUE:
+      case PARSE_STATE_BEGIN_OF_LINE:
+        rv = SetFieldAndClear();
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        rv = DispatchCurrentMessageEvent();  // there is an empty line (CRCR)
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        break;
+
+      // Just for not getting warnings when compiling
+      case PARSE_STATE_OFF:
+      case PARSE_STATE_BEGIN_OF_STREAM:
+      case PARSE_STATE_BOM_WAS_READ:
+        break;
+    }
+  }
+
+  nsCOMPtr<nsIRunnable> event =
+    NS_NewRunnableMethod(this, &nsEventSource::ReestablishConnection);
+  NS_ENSURE_STATE(event);
+
+  rv = NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return healthOfRequestResult;
+}
+
+/**
+ * Simple helper class that just forwards the redirect callback back
+ * to the nsEventSource.
+ */
+class AsyncVerifyRedirectCallbackFwr : public nsIAsyncVerifyRedirectCallback
+{
+public:
+  AsyncVerifyRedirectCallbackFwr(nsEventSource* aEventsource)
+    : mEventSource(aEventsource)
+  {
+  }
+
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(AsyncVerifyRedirectCallbackFwr)
+
+  // nsIAsyncVerifyRedirectCallback implementation
+  NS_IMETHOD OnRedirectVerifyCallback(nsresult aResult)
+  {
+    nsresult rv = mEventSource->OnRedirectVerifyCallback(aResult);
+    if (NS_FAILED(rv)) {
+      mEventSource->mErrorLoadOnRedirect = PR_TRUE;
+      mEventSource->DispatchFailConnection();
+    }
+
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<nsEventSource> mEventSource;
+};
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(AsyncVerifyRedirectCallbackFwr)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AsyncVerifyRedirectCallbackFwr)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mEventSource, nsIEventSource)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AsyncVerifyRedirectCallbackFwr)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mEventSource)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AsyncVerifyRedirectCallbackFwr)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(AsyncVerifyRedirectCallbackFwr)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(AsyncVerifyRedirectCallbackFwr)
+
+//-----------------------------------------------------------------------------
+// nsEventSource::nsIChannelEventSink
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsEventSource::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
+                                      nsIChannel *aNewChannel,
+                                      PRUint32    aFlags,
+                                      nsIAsyncVerifyRedirectCallback *aCallback)
+{
+  nsCOMPtr<nsIRequest> aOldRequest = do_QueryInterface(aOldChannel);
+  NS_PRECONDITION(aOldRequest, "Redirect from a null request?");
+
+  nsresult rv = CheckHealthOfRequestCallback(aOldRequest);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  NS_PRECONDITION(aNewChannel, "Redirect without a channel?");
+
+  nsCOMPtr<nsIURI> newURI;
+  rv = NS_GetFinalChannelURI(aNewChannel, getter_AddRefs(newURI));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!CheckCanRequestSrc(newURI)) {
+    DispatchFailConnection();
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
+  // Prepare to receive callback
+  mRedirectFlags = aFlags;
+  mRedirectCallback = aCallback;
+  mNewRedirectChannel = aNewChannel;
+
+  if (mChannelEventSink) {
+    nsRefPtr<AsyncVerifyRedirectCallbackFwr> fwd =
+      new AsyncVerifyRedirectCallbackFwr(this);
+
+    rv = mChannelEventSink->AsyncOnChannelRedirect(aOldChannel,
+                                                   aNewChannel,
+                                                   aFlags, fwd);
+    if (NS_FAILED(rv)) {
+      mRedirectCallback = nsnull;
+      mNewRedirectChannel = nsnull;
+      mErrorLoadOnRedirect = PR_TRUE;
+      DispatchFailConnection();
+    }
+    return rv;
+  }
+  OnRedirectVerifyCallback(NS_OK);
+  return NS_OK;
+}
+
+nsresult
+nsEventSource::OnRedirectVerifyCallback(nsresult aResult)
+{
+  NS_ABORT_IF_FALSE(mRedirectCallback, "mRedirectCallback not set in callback");
+  NS_ABORT_IF_FALSE(mNewRedirectChannel,
+                    "mNewRedirectChannel not set in callback");
+
+  NS_ENSURE_SUCCESS(aResult, aResult);
+
+  // update our channel
+
+  mHttpChannel = do_QueryInterface(mNewRedirectChannel);
+  NS_ENSURE_STATE(mHttpChannel);
+
+  nsresult rv = SetupHttpChannel();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if ((mRedirectFlags & nsIChannelEventSink::REDIRECT_PERMANENT) != 0) {
+    rv = NS_GetFinalChannelURI(mHttpChannel, getter_AddRefs(mSrc));
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  mNewRedirectChannel = nsnull;
+
+  mRedirectCallback->OnRedirectVerifyCallback(aResult);
+  mRedirectCallback = nsnull;
+
+  return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsEventSource::nsIInterfaceRequestor
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsEventSource::GetInterface(const nsIID & aIID,
+                            void **aResult)
+{
+  // Make sure to return ourselves for the channel event sink interface,
+  // no matter what.  We can forward these to mNotificationCallbacks
+  // if it wants to get notifications for them.  But we
+  // need to see these notifications for proper functioning.
+  if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
+    mChannelEventSink = do_GetInterface(mNotificationCallbacks);
+    *aResult = static_cast<nsIChannelEventSink*>(this);
+    NS_ADDREF_THIS();
+    return NS_OK;
+  }
+
+  // Now give mNotificationCallbacks (if non-null) a chance to return the
+  // desired interface.
+  if (mNotificationCallbacks) {
+    nsresult rv = mNotificationCallbacks->GetInterface(aIID, aResult);
+    if (NS_SUCCEEDED(rv)) {
+      NS_ASSERTION(*aResult, "Lying nsIInterfaceRequestor implementation!");
+      return rv;
+    }
+  }
+
+  if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
+      aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
+    nsresult rv = CheckInnerWindowCorrectness();
+    NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
+
+    nsCOMPtr<nsIPromptFactory> wwatch =
+      do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // Get the an auth prompter for our window so that the parenting
+    // of the dialogs works as it should when using tabs.
+
+    nsCOMPtr<nsIDOMWindow> window;
+    if (mOwner) {
+      window = mOwner->GetOuterWindow();
+    }
+
+    return wwatch->GetPrompt(window, aIID, aResult);
+  }
+
+  return QueryInterface(aIID, aResult);
+}
+
+// static
+PRBool
+nsEventSource::PrefEnabled()
+{
+  return nsContentUtils::GetBoolPref("dom.server-events.enabled", PR_FALSE);
+}
+
+nsresult
+nsEventSource::GetBaseURI(nsIURI **aBaseURI)
+{
+  NS_ENSURE_ARG_POINTER(aBaseURI);
+
+  *aBaseURI = nsnull;
+
+  nsCOMPtr<nsIURI> baseURI;
+
+  // first we try from document->GetBaseURI()
+  nsCOMPtr<nsIDocument> doc =
+    nsContentUtils::GetDocumentFromScriptContext(mScriptContext);
+  if (doc) {
+    baseURI = doc->GetBaseURI();
+  }
+
+  // otherwise we get from the doc's principal
+  if (!baseURI) {
+    nsresult rv = mPrincipal->GetURI(getter_AddRefs(baseURI));
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  NS_ENSURE_STATE(baseURI);
+
+  baseURI.forget(aBaseURI);
+  return NS_OK;
+}
+
+nsresult
+nsEventSource::SetupHttpChannel()
+{
+  mHttpChannel->SetRequestMethod(NS_LITERAL_CSTRING("GET"));
+
+  /* set the http request headers */
+
+  mHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
+    NS_LITERAL_CSTRING(TEXT_EVENT_STREAM), PR_FALSE);
+
+  // LOAD_BYPASS_CACHE already adds the Cache-Control: no-cache header
+
+  if (!mLastEventID.IsEmpty()) {
+    mHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Last-Event-ID"),
+      NS_ConvertUTF16toUTF8(mLastEventID), PR_FALSE);
+  }
+
+  nsCOMPtr<nsIURI> codebase;
+  nsresult rv = GetBaseURI(getter_AddRefs(codebase));
+  if (NS_SUCCEEDED(rv)) {
+    rv = mHttpChannel->SetReferrer(codebase);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  nsCOMPtr<nsIInterfaceRequestor> notificationCallbacks;
+  mHttpChannel->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks));
+  if (notificationCallbacks != this) {
+    mNotificationCallbacks = notificationCallbacks;
+    mHttpChannel->SetNotificationCallbacks(this);
+  }
+
+  return NS_OK;
+}
+
+nsresult
+nsEventSource::InitChannelAndRequestEventSource()
+{
+  if (mReadyState == nsIEventSource::CLOSED) {
+    return NS_ERROR_ABORT;
+  }
+
+  // eventsource validation
+
+  if (!CheckCanRequestSrc()) {
+    DispatchFailConnection();
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
+  nsLoadFlags loadFlags;
+  loadFlags = nsIRequest::LOAD_BACKGROUND | nsIRequest::LOAD_BYPASS_CACHE;
+
+  // get Content Security Policy from principal to pass into channel
+  nsCOMPtr<nsIChannelPolicy> channelPolicy;
+  nsCOMPtr<nsIContentSecurityPolicy> csp;
+  nsresult rv = mPrincipal->GetCsp(getter_AddRefs(csp));
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (csp) {
+    channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
+    channelPolicy->SetContentSecurityPolicy(csp);
+    channelPolicy->SetLoadType(nsIContentPolicy::TYPE_SCRIPT);
+  }
+
+  nsCOMPtr<nsIChannel> channel;
+  rv = NS_NewChannel(getter_AddRefs(channel), mSrc, nsnull, mLoadGroup,
+                     nsnull, loadFlags, channelPolicy);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mHttpChannel = do_QueryInterface(channel);
+  NS_ENSURE_TRUE(mHttpChannel, NS_ERROR_NO_INTERFACE);
+
+  rv = SetupHttpChannel();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Start reading from the channel
+  return mHttpChannel->AsyncOpen(this, nsnull);
+}
+
+void
+nsEventSource::AnnounceConnection()
+{
+  if (mReadyState == nsIEventSource::CLOSED) {
+    return;
+  }
+
+  if (mReadyState != nsIEventSource::CONNECTING) {
+    NS_WARNING("Unexpected mReadyState!!!");
+    return;
+  }
+
+  // When a user agent is to announce the connection, the user agent must set
+  // the readyState attribute to OPEN and queue a task to fire a simple event
+  // named open at the EventSource object.
+
+  mReadyState = nsIEventSource::OPEN;
+
+  nsresult rv = CheckInnerWindowCorrectness();
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  nsCOMPtr<nsIDOMEvent> event;
+  rv = NS_NewDOMEvent(getter_AddRefs(event), nsnull, nsnull);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to create the open event!!!");
+    return;
+  }
+
+  // it doesn't bubble, and it isn't cancelable
+  rv = event->InitEvent(NS_LITERAL_STRING("open"), PR_FALSE, PR_FALSE);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to init the open event!!!");
+    return;
+  }
+
+  nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
+  privateEvent->SetTrusted(PR_TRUE);
+
+  rv = DispatchDOMEvent(nsnull, event, nsnull, nsnull);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to dispatch the open event!!!");
+    return;
+  }
+}
+
+nsresult
+nsEventSource::ResetConnection()
+{
+  if (mHttpChannel) {
+    mHttpChannel->Cancel(NS_ERROR_ABORT);
+  }
+
+  if (mUnicodeDecoder) {
+    mUnicodeDecoder->Reset();
+  }
+  mLastConvertionResult = NS_OK;
+
+  mHttpChannel = nsnull;
+  mNotificationCallbacks = nsnull;
+  mChannelEventSink = nsnull;
+  mStatus = PARSE_STATE_OFF;
+  mRedirectCallback = nsnull;
+  mNewRedirectChannel = nsnull;
+
+  mReadyState = nsIEventSource::CONNECTING;
+
+  return NS_OK;
+}
+
+void
+nsEventSource::ReestablishConnection()
+{
+  if (mReadyState == nsIEventSource::CLOSED) {
+    return;
+  }
+
+  if (mReadyState != nsIEventSource::OPEN) {
+    NS_WARNING("Unexpected mReadyState!!!");
+    return;
+  }
+
+  nsresult rv = ResetConnection();
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to reset the connection!!!");
+    return;
+  }
+
+  rv = CheckInnerWindowCorrectness();
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  nsCOMPtr<nsIDOMEvent> event;
+  rv = NS_NewDOMEvent(getter_AddRefs(event), nsnull, nsnull);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to create the error event!!!");
+    return;
+  }
+
+  // it doesn't bubble, and it isn't cancelable
+  rv = event->InitEvent(NS_LITERAL_STRING("error"), PR_FALSE, PR_FALSE);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to init the error event!!!");
+    return;
+  }
+
+  nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
+  privateEvent->SetTrusted(PR_TRUE);
+
+  rv = DispatchDOMEvent(nsnull, event, nsnull, nsnull);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to dispatch the error event!!!");
+    return;
+  }
+
+  rv = SetReconnectionTimeout();
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to set the timeout for reestablishing the connection!!!");
+    return;
+  }
+}
+
+nsresult
+nsEventSource::SetReconnectionTimeout()
+{
+  if (mReadyState == nsIEventSource::CLOSED) {
+    return NS_ERROR_ABORT;
+  }
+
+  // the timer will be used whenever the requests are going finished.
+  if (!mTimer) {
+    mTimer = do_CreateInstance("@mozilla.org/timer;1");
+    NS_ENSURE_STATE(mTimer);
+  }
+
+  NS_ASSERTION(mReconnectionTime >= 0, "mReconnectionTime lies");
+  nsresult rv = mTimer->InitWithFuncCallback(TimerCallback, this,
+                                             mReconnectionTime,
+                                             nsITimer::TYPE_ONE_SHOT);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+nsresult
+nsEventSource::PrintErrorOnConsole(const char *aBundleURI,
+                                   const PRUnichar *aError,
+                                   const PRUnichar **aFormatStrings,
+                                   PRUint32 aFormatStringsLen)
+{
+  nsCOMPtr<nsIStringBundleService> bundleService =
+    mozilla::services::GetStringBundleService();
+  NS_ENSURE_STATE(bundleService);
+
+  nsCOMPtr<nsIStringBundle> strBundle;
+  nsresult rv =
+    bundleService->CreateBundle(aBundleURI, getter_AddRefs(strBundle));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIConsoleService> console(
+    do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIScriptError2> errObj(
+    do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Localize the error message
+  nsXPIDLString message;
+  if (aFormatStrings) {
+    rv = strBundle->FormatStringFromName(aError, aFormatStrings,
+                                         aFormatStringsLen,
+                                         getter_Copies(message));
+  } else {
+    rv = strBundle->GetStringFromName(aError, getter_Copies(message));
+  }
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  errObj->InitWithWindowID(message.get(),
+                           mScriptFile.get(),
+                           nsnull,
+                           mScriptLine, 0,
+                           nsIScriptError::errorFlag,
+                           "Event Source", mWindowID);
+
+  // print the error message directly to the JS console
+  nsCOMPtr<nsIScriptError> logError = do_QueryInterface(errObj);
+  rv = console->LogMessage(logError);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+nsresult
+nsEventSource::ConsoleError()
+{
+  nsCAutoString targetSpec;
+  nsresult rv = mSrc->GetSpec(targetSpec);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  NS_ConvertUTF8toUTF16 specUTF16(targetSpec);
+  const PRUnichar *formatStrings[] = { specUTF16.get() };
+
+  if (mReadyState == nsIEventSource::CONNECTING) {
+    rv = PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
+                             NS_LITERAL_STRING("connectionFailure").get(),
+                             formatStrings, NS_ARRAY_LENGTH(formatStrings));
+  } else {
+    rv = PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
+                             NS_LITERAL_STRING("netInterrupt").get(),
+                             formatStrings, NS_ARRAY_LENGTH(formatStrings));
+  }
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+nsresult
+nsEventSource::DispatchFailConnection()
+{
+  nsCOMPtr<nsIRunnable> event =
+    NS_NewRunnableMethod(this, &nsEventSource::FailConnection);
+  NS_ENSURE_STATE(event);
+
+  return NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+}
+
+void
+nsEventSource::FailConnection()
+{
+  if (mReadyState == nsIEventSource::CLOSED) {
+    return;
+  }
+
+  nsresult rv = ConsoleError();
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to print to the console error");
+  }
+
+  // When a user agent is to fail the connection, the user agent must set the
+  // readyState attribute to CLOSED and queue a task to fire a simple event
+  // named error at the EventSource  object.
+
+  Close(); // it sets mReadyState to CLOSED
+
+  rv = CheckInnerWindowCorrectness();
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  nsCOMPtr<nsIDOMEvent> event;
+  rv = NS_NewDOMEvent(getter_AddRefs(event), nsnull, nsnull);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to create the error event!!!");
+    return;
+  }
+
+  // it doesn't bubble, and it isn't cancelable
+  rv = event->InitEvent(NS_LITERAL_STRING("error"), PR_FALSE, PR_FALSE);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to init the error event!!!");
+    return;
+  }
+
+  nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
+  privateEvent->SetTrusted(PR_TRUE);
+
+  rv = DispatchDOMEvent(nsnull, event, nsnull, nsnull);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to dispatch the error event!!!");
+    return;
+  }
+}
+
+PRBool
+nsEventSource::CheckCanRequestSrc(nsIURI* aSrc)
+{
+  if (mReadyState == nsIEventSource::CLOSED) {
+    return PR_FALSE;
+  }
+
+  PRBool isSameOrigin = PR_FALSE;
+  PRBool isValidURI = PR_FALSE;
+  PRBool isValidContentLoadPolicy = PR_FALSE;
+  PRBool isValidProtocol = PR_FALSE;
+
+  nsCOMPtr<nsIURI> srcToTest = aSrc ? aSrc : mSrc.get();
+  NS_ENSURE_TRUE(srcToTest, PR_FALSE);
+
+  isSameOrigin = NS_SUCCEEDED(mPrincipal->CheckMayLoad(srcToTest, PR_FALSE));
+
+  PRUint32 aCheckURIFlags =
+    nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL |
+    nsIScriptSecurityManager::DISALLOW_SCRIPT;
+
+  nsresult rv = nsContentUtils::GetSecurityManager()->
+    CheckLoadURIWithPrincipal(mPrincipal,
+                              srcToTest,
+                              aCheckURIFlags);
+  isValidURI = NS_SUCCEEDED(rv);
+
+  // After the security manager, the content-policy check
+
+  nsCOMPtr<nsIDocument> doc =
+    nsContentUtils::GetDocumentFromScriptContext(mScriptContext);
+
+  // mScriptContext should be initialized because of GetBaseURI() above.
+  // Still need to consider the case that doc is nsnull however.
+  rv = CheckInnerWindowCorrectness();
+  NS_ENSURE_SUCCESS(rv, PR_FALSE);
+  PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
+  rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_SCRIPT,
+                                 srcToTest,
+                                 mPrincipal,
+                                 doc,
+                                 NS_LITERAL_CSTRING(TEXT_EVENT_STREAM),
+                                 nsnull,    // extra
+                                 &shouldLoad,
+                                 nsContentUtils::GetContentPolicy(),
+                                 nsContentUtils::GetSecurityManager());
+  isValidContentLoadPolicy = NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(shouldLoad);
+
+  nsCAutoString targetURIScheme;
+  rv = srcToTest->GetScheme(targetURIScheme);
+  if (NS_SUCCEEDED(rv)) {
+    // We only have the http support for now
+    isValidProtocol = targetURIScheme.EqualsLiteral("http") ||
+                      targetURIScheme.EqualsLiteral("https");
+  }
+
+  return isSameOrigin && isValidURI && isValidContentLoadPolicy &&
+         isValidProtocol;
+}
+
+// static
+void
+nsEventSource::TimerCallback(nsITimer* aTimer, void* aClosure)
+{
+  nsRefPtr<nsEventSource> thisObject = static_cast<nsEventSource*>(aClosure);
+
+  if (thisObject->mReadyState == nsIEventSource::CLOSED) {
+    return;
+  }
+
+  NS_PRECONDITION(!thisObject->mHttpChannel,
+                  "the channel hasn't been cancelled!!");
+
+  if (!thisObject->mFrozen) {
+    nsresult rv = thisObject->InitChannelAndRequestEventSource();
+    if (NS_FAILED(rv)) {
+      NS_WARNING("thisObject->InitChannelAndRequestEventSource() failed");
+      return;
+    }
+  }
+}
+
+nsresult
+nsEventSource::Thaw()
+{
+  if (mReadyState == nsIEventSource::CLOSED || !mFrozen) {
+    return NS_OK;
+  }
+
+  NS_ASSERTION(!mHttpChannel, "the connection hasn't been closed!!!");
+
+  mFrozen = PR_FALSE;
+  nsresult rv;
+  if (!mGoingToDispatchAllMessages && mMessagesToDispatch.GetSize() > 0) {
+    nsCOMPtr<nsIRunnable> event =
+      NS_NewRunnableMethod(this, &nsEventSource::DispatchAllMessageEvents);
+    NS_ENSURE_STATE(event);
+
+    mGoingToDispatchAllMessages = PR_TRUE;
+
+    rv = NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  rv = InitChannelAndRequestEventSource();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+nsresult
+nsEventSource::Freeze()
+{
+  if (mReadyState == nsIEventSource::CLOSED || mFrozen) {
+    return NS_OK;
+  }
+
+  NS_ASSERTION(!mHttpChannel, "the connection hasn't been closed!!!");
+  mFrozen = PR_TRUE;
+  return NS_OK;
+}
+
+nsresult
+nsEventSource::DispatchCurrentMessageEvent()
+{
+  nsAutoPtr<Message> message(new Message());
+  *message = mCurrentMessage;
+
+  ClearFields();
+
+  if (message->mData.IsEmpty()) {
+    return NS_OK;
+  }
+
+  // removes the trailing LF from mData
+  NS_ASSERTION(message->mData.CharAt(message->mData.Length() - 1) == LF_CHAR,
+               "Invalid trailing character! LF was expected instead.");
+  message->mData.SetLength(message->mData.Length() - 1);
+
+  if (message->mEventName.IsEmpty()) {
+    message->mEventName.AssignLiteral("message");
+  }
+
+  if (message->mLastEventID.IsEmpty() && !mLastEventID.IsEmpty()) {
+    message->mLastEventID.Assign(mLastEventID);
+  }
+
+  PRInt32 sizeBefore = mMessagesToDispatch.GetSize();
+  mMessagesToDispatch.Push(message.forget());
+  NS_ENSURE_TRUE(mMessagesToDispatch.GetSize() == sizeBefore + 1,
+                 NS_ERROR_OUT_OF_MEMORY);
+
+
+  if (!mGoingToDispatchAllMessages) {
+    nsCOMPtr<nsIRunnable> event =
+      NS_NewRunnableMethod(this, &nsEventSource::DispatchAllMessageEvents);
+    NS_ENSURE_STATE(event);
+
+    mGoingToDispatchAllMessages = PR_TRUE;
+
+    return NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+  }
+
+  return NS_OK;
+}
+
+void
+nsEventSource::DispatchAllMessageEvents()
+{
+  if (mReadyState == nsIEventSource::CLOSED || mFrozen) {
+    return;
+  }
+
+  mGoingToDispatchAllMessages = PR_FALSE;
+
+  nsresult rv = CheckInnerWindowCorrectness();
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  while (mMessagesToDispatch.GetSize() > 0) {
+    nsAutoPtr<Message>
+      message(static_cast<Message*>(mMessagesToDispatch.PopFront()));
+
+    // create an event that uses the MessageEvent interface,
+    // which does not bubble, is not cancelable, and has no default action
+
+    nsCOMPtr<nsIDOMEvent> event;
+    rv = NS_NewDOMMessageEvent(getter_AddRefs(event), nsnull, nsnull);
+    if (NS_FAILED(rv)) {
+      NS_WARNING("Failed to create the message event!!!");
+      return;
+    }
+
+    nsCOMPtr<nsIDOMMessageEvent> messageEvent = do_QueryInterface(event);
+    rv = messageEvent->InitMessageEvent(message->mEventName,
+                                        PR_FALSE, PR_FALSE,
+                                        message->mData,
+                                        NS_ConvertUTF8toUTF16(mOrigin),
+                                        message->mLastEventID, nsnull);
+    if (NS_FAILED(rv)) {
+      NS_WARNING("Failed to init the message event!!!");
+      return;
+    }
+
+    nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(event);
+    privateEvent->SetTrusted(PR_TRUE);
+
+    rv = DispatchDOMEvent(nsnull, event, nsnull, nsnull);
+    if (NS_FAILED(rv)) {
+      NS_WARNING("Failed to dispatch the message event!!!");
+      return;
+    }
+  }
+}
+
+nsresult
+nsEventSource::ClearFields()
+{
+  // mLastEventID and mReconnectionTime must be cached
+
+  mCurrentMessage.mEventName.Truncate();
+  mCurrentMessage.mLastEventID.Truncate();
+  mCurrentMessage.mData.Truncate();
+
+  mLastFieldName.Truncate();
+  mLastFieldValue.Truncate();
+
+  return NS_OK;
+}
+
+nsresult
+nsEventSource::SetFieldAndClear()
+{
+  if (mLastFieldName.IsEmpty()) {
+    mLastFieldValue.Truncate();
+    return NS_OK;
+  }
+
+  PRUnichar first_char;
+  first_char = mLastFieldName.CharAt(0);
+
+  switch (first_char)  // with no case folding performed
+  {
+    case PRUnichar('d'):
+      if (mLastFieldName.EqualsLiteral("data")) {
+        // If the field name is "data" append the field value to the data
+        // buffer, then append a single U+000A LINE FEED (LF) character
+        // to the data buffer.
+        mCurrentMessage.mData.Append(mLastFieldValue);
+        mCurrentMessage.mData.Append(LF_CHAR);
+      }
+      break;
+
+    case PRUnichar('e'):
+      if (mLastFieldName.EqualsLiteral("event")) {
+        mCurrentMessage.mEventName.Assign(mLastFieldValue);
+      }
+      break;
+
+    case PRUnichar('i'):
+      if (mLastFieldName.EqualsLiteral("id")) {
+        mCurrentMessage.mLastEventID.Assign(mLastFieldValue);
+        mLastEventID.Assign(mLastFieldValue);
+      }
+      break;
+
+    case PRUnichar('r'):
+      if (mLastFieldName.EqualsLiteral("retry")) {
+        PRUint32 newValue=0;
+        PRUint32 i = 0;  // we must ensure that there are only digits
+        PRBool assign = PR_TRUE;
+        for (i = 0; i < mLastFieldValue.Length(); ++i) {
+          if (mLastFieldValue.CharAt(i) < (PRUnichar)'0' ||
+              mLastFieldValue.CharAt(i) > (PRUnichar)'9') {
+            assign = PR_FALSE;
+            break;
+          }
+          newValue = newValue*10 +
+                     (((PRUint32)mLastFieldValue.CharAt(i))-
+                       ((PRUint32)((PRUnichar)'0')));
+        }
+
+        if (assign) {
+          if (newValue < MIN_RECONNECTION_TIME_VALUE) {
+            mReconnectionTime = MIN_RECONNECTION_TIME_VALUE;
+          } else if (newValue > MAX_RECONNECTION_TIME_VALUE) {
+            mReconnectionTime = MAX_RECONNECTION_TIME_VALUE;
+          } else {
+            mReconnectionTime = newValue;
+          }
+        }
+        break;
+      }
+      break;
+  }
+
+  mLastFieldName.Truncate();
+  mLastFieldValue.Truncate();
+
+  return NS_OK;
+}
+
+nsresult
+nsEventSource::CheckHealthOfRequestCallback(nsIRequest *aRequestCallback)
+{
+  // check if we have been closed or if the request has been canceled
+  // or if we have been frozen
+  if (mReadyState == nsIEventSource::CLOSED || !mHttpChannel ||
+      mFrozen || mErrorLoadOnRedirect) {
+    return NS_ERROR_ABORT;
+  }
+
+  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequestCallback);
+  NS_ENSURE_STATE(httpChannel);
+
+  if (httpChannel != mHttpChannel) {
+    NS_WARNING("wrong channel from request callback");
+    return NS_ERROR_ABORT;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+nsEventSource::ParseCharacter(PRUnichar aChr)
+{
+  nsresult rv;
+
+  if (mReadyState == nsIEventSource::CLOSED) {
+    return NS_ERROR_ABORT;
+  }
+
+  switch (mStatus)
+  {
+    case PARSE_STATE_OFF:
+      NS_ERROR("Invalid state");
+      return NS_ERROR_FAILURE;
+      break;
+
+    case PARSE_STATE_BEGIN_OF_STREAM:
+      if (aChr == BOM_CHAR) {
+        mStatus = PARSE_STATE_BOM_WAS_READ;  // ignore it
+      } else if (aChr == CR_CHAR) {
+        mStatus = PARSE_STATE_CR_CHAR;
+      } else if (aChr == LF_CHAR) {
+        mStatus = PARSE_STATE_BEGIN_OF_LINE;
+      } else if (aChr == COLON_CHAR) {
+        mStatus = PARSE_STATE_COMMENT;
+      } else {
+        mLastFieldName += aChr;
+        mStatus = PARSE_STATE_FIELD_NAME;
+      }
+
+      break;
+
+    case PARSE_STATE_BOM_WAS_READ:
+      if (aChr == CR_CHAR) {
+        mStatus = PARSE_STATE_CR_CHAR;
+      } else if (aChr == LF_CHAR) {
+        mStatus = PARSE_STATE_BEGIN_OF_LINE;
+      } else if (aChr == COLON_CHAR) {
+        mStatus = PARSE_STATE_COMMENT;
+      } else {
+        mLastFieldName += aChr;
+        mStatus = PARSE_STATE_FIELD_NAME;
+      }
+      break;
+
+    case PARSE_STATE_CR_CHAR:
+      if (aChr == CR_CHAR) {
+        rv = DispatchCurrentMessageEvent();  // there is an empty line (CRCR)
+        NS_ENSURE_SUCCESS(rv, rv);
+      } else if (aChr == LF_CHAR) {
+        mStatus = PARSE_STATE_BEGIN_OF_LINE;
+      } else if (aChr == COLON_CHAR) {
+        mStatus = PARSE_STATE_COMMENT;
+      } else {
+        mLastFieldName += aChr;
+        mStatus = PARSE_STATE_FIELD_NAME;
+      }
+
+      break;
+
+    case PARSE_STATE_COMMENT:
+      if (aChr == CR_CHAR) {
+        mStatus = PARSE_STATE_CR_CHAR;
+      } else if (aChr == LF_CHAR) {
+        mStatus = PARSE_STATE_BEGIN_OF_LINE;
+      }
+
+      break;
+
+    case PARSE_STATE_FIELD_NAME:
+      if (aChr == CR_CHAR) {
+        rv = SetFieldAndClear();
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        mStatus = PARSE_STATE_CR_CHAR;
+      } else if (aChr == LF_CHAR) {
+        rv = SetFieldAndClear();
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        mStatus = PARSE_STATE_BEGIN_OF_LINE;
+      } else if (aChr == COLON_CHAR) {
+        mStatus = PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE;
+      } else {
+        mLastFieldName += aChr;
+      }
+
+      break;
+
+    case PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE:
+      if (aChr == CR_CHAR) {
+        rv = SetFieldAndClear();
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        mStatus = PARSE_STATE_CR_CHAR;
+      } else if (aChr == LF_CHAR) {
+        rv = SetFieldAndClear();
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        mStatus = PARSE_STATE_BEGIN_OF_LINE;
+      } else if (aChr == SPACE_CHAR) {
+        mStatus = PARSE_STATE_FIELD_VALUE;
+      } else {
+        mLastFieldValue += aChr;
+        mStatus = PARSE_STATE_FIELD_VALUE;
+      }
+
+      break;
+
+    case PARSE_STATE_FIELD_VALUE:
+      if (aChr == CR_CHAR) {
+        rv = SetFieldAndClear();
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        mStatus = PARSE_STATE_CR_CHAR;
+      } else if (aChr == LF_CHAR) {
+        rv = SetFieldAndClear();
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        mStatus = PARSE_STATE_BEGIN_OF_LINE;
+      } else {
+        mLastFieldValue += aChr;
+      }
+
+      break;
+
+    case PARSE_STATE_BEGIN_OF_LINE:
+      if (aChr == CR_CHAR) {
+        rv = DispatchCurrentMessageEvent();  // there is an empty line
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        mStatus = PARSE_STATE_CR_CHAR;
+      } else if (aChr == LF_CHAR) {
+        rv = DispatchCurrentMessageEvent();  // there is an empty line
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        mStatus = PARSE_STATE_BEGIN_OF_LINE;
+      } else if (aChr == COLON_CHAR) {
+        mStatus = PARSE_STATE_COMMENT;
+      } else {
+        mLastFieldName += aChr;
+        mStatus = PARSE_STATE_FIELD_NAME;
+      }
+
+      break;
+  }
+
+  return NS_OK;
+}
new file mode 100755
--- /dev/null
+++ b/content/base/src/nsEventSource.h
@@ -0,0 +1,267 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Wellington Fernando de Macedo.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *    Wellington Fernando de Macedo <wfernandom2004@gmail.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+ * This implementation has support only for http requests. It is because the
+ * spec has defined event streams only for http. HTTP is required because
+ * this implementation uses some http headers: "Last-Event-ID", "Cache-Control"
+ * and "Accept".
+ */
+
+#ifndef nsEventSource_h__
+#define nsEventSource_h__
+
+#include "nsIEventSource.h"
+#include "nsIJSNativeInitializer.h"
+#include "nsDOMEventTargetWrapperCache.h"
+#include "nsIObserver.h"
+#include "nsIStreamListener.h"
+#include "nsIChannelEventSink.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsITimer.h"
+#include "nsIHttpChannel.h"
+#include "nsWeakReference.h"
+#include "nsDeque.h"
+#include "nsIUnicodeDecoder.h"
+
+#define NS_EVENTSOURCE_CID                          \
+ { /* 755e2d2d-a836-4539-83f4-16b51156341f */       \
+  0x755e2d2d, 0xa836, 0x4539,                       \
+ {0x83, 0xf4, 0x16, 0xb5, 0x11, 0x56, 0x34, 0x1f} }
+
+#define NS_EVENTSOURCE_CONTRACTID "@mozilla.org/eventsource;1"
+
+class AsyncVerifyRedirectCallbackFwr;
+class nsAutoClearFields;
+
+class nsEventSource: public nsDOMEventTargetWrapperCache,
+                     public nsIEventSource,
+                     public nsIJSNativeInitializer,
+                     public nsIObserver,
+                     public nsIStreamListener,
+                     public nsIChannelEventSink,
+                     public nsIInterfaceRequestor,
+                     public nsSupportsWeakReference
+{
+friend class AsyncVerifyRedirectCallbackFwr;
+
+public:
+  nsEventSource();
+  virtual ~nsEventSource();
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsEventSource,
+                                           nsDOMEventTargetWrapperCache)
+
+  NS_DECL_NSIEVENTSOURCE
+
+  // nsIJSNativeInitializer
+  NS_IMETHOD Initialize(nsISupports* aOwner, JSContext* cx, JSObject* obj,
+                        PRUint32 argc, jsval* argv);
+
+  NS_DECL_NSIOBSERVER
+  NS_DECL_NSISTREAMLISTENER
+  NS_DECL_NSIREQUESTOBSERVER
+  NS_DECL_NSICHANNELEVENTSINK
+  NS_DECL_NSIINTERFACEREQUESTOR
+
+  // Determine if preferences allow EventSource
+  static PRBool PrefEnabled();
+
+protected:
+  nsresult GetBaseURI(nsIURI **aBaseURI);
+
+  nsresult SetupHttpChannel();
+  nsresult InitChannelAndRequestEventSource();
+  nsresult ResetConnection();
+  nsresult DispatchFailConnection();
+  nsresult SetReconnectionTimeout();
+
+  void AnnounceConnection();
+  void DispatchAllMessageEvents();
+  void ReestablishConnection();
+  void FailConnection();
+
+  nsresult Thaw();
+  nsresult Freeze();
+
+  static void TimerCallback(nsITimer *aTimer, void *aClosure);
+
+  nsresult PrintErrorOnConsole(const char       *aBundleURI,
+                               const PRUnichar  *aError,
+                               const PRUnichar **aFormatStrings,
+                               PRUint32          aFormatStringsLen);
+  nsresult ConsoleError();
+
+  static NS_METHOD StreamReaderFunc(nsIInputStream *aInputStream,
+                                    void           *aClosure,
+                                    const char     *aFromRawSegment,
+                                    PRUint32        aToOffset,
+                                    PRUint32        aCount,
+                                    PRUint32       *aWriteCount);
+  nsresult SetFieldAndClear();
+  nsresult ClearFields();
+  nsresult ResetEvent();
+  nsresult DispatchCurrentMessageEvent();
+  nsresult ParseCharacter(PRUnichar aChr);
+  PRBool CheckCanRequestSrc(nsIURI* aSrc = nsnull);  // if null, it tests mSrc
+  nsresult CheckHealthOfRequestCallback(nsIRequest *aRequestCallback);
+  nsresult OnRedirectVerifyCallback(nsresult result);
+
+  nsCOMPtr<nsIURI> mSrc;
+
+  nsString mLastEventID;
+  PRUint32 mReconnectionTime;  // in ms
+
+  struct Message {
+    nsString mEventName;
+    nsString mLastEventID;
+    nsString mData;
+  };
+  nsDeque mMessagesToDispatch;
+  Message mCurrentMessage;
+
+  /**
+   * A simple state machine used to manage the event-source's line buffer
+   *
+   * PARSE_STATE_OFF              -> PARSE_STATE_BEGIN_OF_STREAM
+   *
+   * PARSE_STATE_BEGIN_OF_STREAM  -> PARSE_STATE_BOM_WAS_READ |
+   *                                 PARSE_STATE_CR_CHAR |
+   *                                 PARSE_STATE_BEGIN_OF_LINE |
+   *                                 PARSE_STATE_COMMENT |
+   *                                 PARSE_STATE_FIELD_NAME
+   *
+   * PARSE_STATE_BOM_WAS_READ     -> PARSE_STATE_CR_CHAR |
+   *                                 PARSE_STATE_BEGIN_OF_LINE |
+   *                                 PARSE_STATE_COMMENT |
+   *                                 PARSE_STATE_FIELD_NAME
+   *
+   * PARSE_STATE_CR_CHAR -> PARSE_STATE_CR_CHAR |
+   *                        PARSE_STATE_COMMENT |
+   *                        PARSE_STATE_FIELD_NAME |
+   *                        PARSE_STATE_BEGIN_OF_LINE
+   *
+   * PARSE_STATE_COMMENT -> PARSE_STATE_CR_CHAR |
+   *                        PARSE_STATE_BEGIN_OF_LINE
+   *
+   * PARSE_STATE_FIELD_NAME   -> PARSE_STATE_CR_CHAR |
+   *                             PARSE_STATE_BEGIN_OF_LINE |
+   *                             PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE
+   *
+   * PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE  -> PARSE_STATE_FIELD_VALUE |
+   *                                           PARSE_STATE_CR_CHAR |
+   *                                           PARSE_STATE_BEGIN_OF_LINE
+   *
+   * PARSE_STATE_FIELD_VALUE      -> PARSE_STATE_CR_CHAR |
+   *                                 PARSE_STATE_BEGIN_OF_LINE
+   *
+   * PARSE_STATE_BEGIN_OF_LINE   -> PARSE_STATE_CR_CHAR |
+   *                                PARSE_STATE_COMMENT |
+   *                                PARSE_STATE_FIELD_NAME |
+   *                                PARSE_STATE_BEGIN_OF_LINE
+   *
+   * Whenever the parser find an empty line or the end-of-file
+   * it dispatches the stacked event.
+   *
+   */
+  enum ParserStatus {
+    PARSE_STATE_OFF,
+    PARSE_STATE_BEGIN_OF_STREAM,
+    PARSE_STATE_BOM_WAS_READ,
+    PARSE_STATE_CR_CHAR,
+    PARSE_STATE_COMMENT,
+    PARSE_STATE_FIELD_NAME,
+    PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE,
+    PARSE_STATE_FIELD_VALUE,
+    PARSE_STATE_BEGIN_OF_LINE
+  };
+  ParserStatus mStatus;
+
+  PRPackedBool mFrozen;
+  PRPackedBool mErrorLoadOnRedirect;
+  PRPackedBool mGoingToDispatchAllMessages;
+
+  // used while reading the input streams
+  nsCOMPtr<nsIUnicodeDecoder> mUnicodeDecoder;
+  nsresult mLastConvertionResult;
+  nsString mLastFieldName;
+  nsString mLastFieldValue;
+
+  nsRefPtr<nsDOMEventListenerWrapper> mOnOpenListener;
+  nsRefPtr<nsDOMEventListenerWrapper> mOnErrorListener;
+  nsRefPtr<nsDOMEventListenerWrapper> mOnMessageListener;
+
+  nsCOMPtr<nsILoadGroup> mLoadGroup;
+
+  /**
+   * The notification callbacks the channel had initially.
+   * We want to forward things here as needed.
+   */
+  nsCOMPtr<nsIInterfaceRequestor> mNotificationCallbacks;
+  nsCOMPtr<nsIChannelEventSink>   mChannelEventSink;
+
+  nsCOMPtr<nsIHttpChannel> mHttpChannel;
+
+  nsCOMPtr<nsITimer> mTimer;
+
+  PRInt32 mReadyState;
+  nsString mOriginalURL;
+
+  nsCOMPtr<nsIPrincipal> mPrincipal;
+  nsCString mOrigin;
+
+  PRUint32 mRedirectFlags;
+  nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
+  nsCOMPtr<nsIChannel> mNewRedirectChannel;
+
+  // Event Source owner information:
+  // - the script file name
+  // - source code line number where the Event Source object was constructed.
+  // - the window ID of the outer window where the script lives. Note that this
+  // may not be the same as the Event Source owner window.
+  // These attributes are used for error reporting.
+  nsString mScriptFile;
+  PRUint32 mScriptLine;
+  PRUint64 mWindowID;
+
+private:
+  nsEventSource(const nsEventSource& x);   // prevent bad usage
+  nsEventSource& operator=(const nsEventSource& x);
+};
+
+#endif // nsEventSource_h__
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -687,28 +687,30 @@ GK_ATOM(oninput, "oninput")
 GK_ATOM(oninvalid, "oninvalid")
 GK_ATOM(onkeydown, "onkeydown")
 GK_ATOM(onkeypress, "onkeypress")
 GK_ATOM(onkeyup, "onkeyup")
 GK_ATOM(onLoad, "onLoad")
 GK_ATOM(onload, "onload")
 GK_ATOM(onpopstate, "onpopstate")
 GK_ATOM(only, "only")               // this one is not an event
+GK_ATOM(onmessage, "onmessage")
 GK_ATOM(onmousedown, "onmousedown")
 GK_ATOM(onmousemove, "onmousemove")
 GK_ATOM(onmouseout, "onmouseout")
 GK_ATOM(onmouseover, "onmouseover")
 GK_ATOM(onMozMouseHittest, "onMozMouseHittest")
 GK_ATOM(onmouseup, "onmouseup")
 GK_ATOM(onMozAfterPaint, "onMozAfterPaint")
 GK_ATOM(onMozBeforePaint, "onMozBeforePaint")
 GK_ATOM(onMozMousePixelScroll, "onMozMousePixelScroll")
 GK_ATOM(onMozScrolledAreaChanged, "onMozScrolledAreaChanged")
 GK_ATOM(ononline, "ononline")
 GK_ATOM(onoffline, "onoffline")
+GK_ATOM(onopen, "onopen")
 GK_ATOM(onoverflow, "onoverflow")
 GK_ATOM(onoverflowchanged, "onoverflowchanged")
 GK_ATOM(onpagehide, "onpagehide")
 GK_ATOM(onpageshow, "onpageshow")
 GK_ATOM(onpaint, "onpaint")
 GK_ATOM(onpaste, "onpaste")
 GK_ATOM(onpopuphidden, "onpopuphidden")
 GK_ATOM(onpopuphiding, "onpopuphiding")
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -478,16 +478,36 @@ include $(topsrcdir)/config/rules.mk
 		test_bug626262.html \
 		test_plugin_freezing.html \
 		test_bug638112.html \
 		bug638112-response.txt \
 		bug638112.sjs \
 		test_bug656283.html \
 		test_blobbuilder.html \
 		fileutils.js \
+		test_bug338583.html \
+		eventsource.resource \
+		eventsource.resource^headers^ \
+		badContentType.eventsource \
+		badContentType.eventsource^headers^ \
+		badEventFieldName.eventsource \
+		badEventFieldName.eventsource^headers^ \
+		badHTTPResponseCode.eventsource \
+		badHTTPResponseCode.eventsource^headers^ \
+		badMessageEvent.eventsource \
+		badMessageEvent.eventsource^headers^ \
+		forRemoval.resource \
+		forRemoval.resource^headers^ \
+		accesscontrol.resource \
+		accesscontrol.resource^headers^ \
+		invalid_accesscontrol.resource \
+		invalid_accesscontrol.resource^headers^ \
+		somedatas.resource \
+		somedatas.resource^headers^ \
+		delayedServerEvents.sjs \
 		$(NULL)
 
 # This test fails on the Mac for some reason
 ifneq (,$(filter gtk2 windows,$(MOZ_WIDGET_TOOLKIT)))
 _TEST_FILES2 += 	test_copyimage.html \
 		$(NULL)
 endif
 
new file mode 100644
--- /dev/null
+++ b/content/base/test/accesscontrol.resource
@@ -0,0 +1,5 @@
+:this file must be enconded in utf8
+:and its Content-Type must be equal to text/event-stream
+
+event: message
+data: 1
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/content/base/test/accesscontrol.resource^headers^
@@ -0,0 +1,4 @@
+Access-Control-Allow-Origin: http://localhost:8888
+Content-Type: text/event-stream
+Cache-Control: no-cache, must-revalidate
+
new file mode 100644
--- /dev/null
+++ b/content/base/test/badContentType.eventsource
@@ -0,0 +1,3 @@
+retry:500
+event: message
+data: 1
new file mode 100644
--- /dev/null
+++ b/content/base/test/badContentType.eventsource^headers^
@@ -0,0 +1,1 @@
+Content-Type: text/plain
new file mode 100644
--- /dev/null
+++ b/content/base/test/badEventFieldName.eventsource
@@ -0,0 +1,4 @@
+retry:500
+:space isn't a valid NCName char, am I right?
+event: message event
+data: 1
new file mode 100644
--- /dev/null
+++ b/content/base/test/badEventFieldName.eventsource^headers^
@@ -0,0 +1,1 @@
+Content-Type: text/event-stream
new file mode 100644
--- /dev/null
+++ b/content/base/test/badHTTPResponseCode.eventsource
@@ -0,0 +1,3 @@
+retry:500
+event: message
+data: 1
new file mode 100644
--- /dev/null
+++ b/content/base/test/badHTTPResponseCode.eventsource^headers^
@@ -0,0 +1,2 @@
+HTTP 404 Not Found
+Content-Type: text/event-stream
new file mode 100644
--- /dev/null
+++ b/content/base/test/badMessageEvent.eventsource
@@ -0,0 +1,2 @@
+retry:500
+event: message
new file mode 100644
--- /dev/null
+++ b/content/base/test/badMessageEvent.eventsource^headers^
@@ -0,0 +1,1 @@
+Content-Type: text/event-stream
new file mode 100644
--- /dev/null
+++ b/content/base/test/delayedServerEvents.sjs
@@ -0,0 +1,42 @@
+// this will take strings_to_send.length*5000 ms = 50 sec
+
+var timer = null;
+var strings_to_send = ["data\r\n\nda", "ta", ":", "de", "layed1\n\n",
+                       "",
+                       "",
+                       "data:delayed2", "", ""];
+var resp = null;
+
+function sendNextString()
+{
+  if (strings_to_send.length == 0) {
+    timer.cancel();
+    resp.finish();
+    timer = null;
+    resp = null;
+    return;
+  }
+
+  resp.write(strings_to_send[0]);
+  strings_to_send = strings_to_send.splice(1, strings_to_send.length - 1);
+}
+
+function handleRequest(request, response)
+{
+  var b = 0;
+  for (var i=0; i < strings_to_send.length; ++i) {
+    b += strings_to_send[i].length;
+  }
+  
+  response.seizePower();
+  response.write("HTTP/1.1 200 OK\r\n")
+  response.write("Content-Length: " + b + "\r\n");
+  response.write("Content-Type: text/event-stream; charset=utf-8\r\n");
+  response.write("Cache-Control: no-cache, must-revalidate\r\n");
+  response.write("\r\n");
+  
+  resp = response;
+
+  timer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
+  timer.initWithCallback(sendNextString, 5000, Components.interfaces.nsITimer.TYPE_REPEATING_SLACK);
+}
new file mode 100644
--- /dev/null
+++ b/content/base/test/eventsource.resource
@@ -0,0 +1,20 @@
+:this file must be enconded in utf8
+:and its Content-Type must be equal to text/event-stream
+
+retry:500
+data: 1
+unknow: unknow
+
+event: other_event_name
+retry:500
+data: 1
+unknow: unknow
+
+event: click
+retry:500
+
+event: blur
+retry:500
+
+event:keypress
+retry:500
new file mode 100644
--- /dev/null
+++ b/content/base/test/eventsource.resource^headers^
@@ -0,0 +1,3 @@
+Content-Type: text/event-stream
+Cache-Control: no-cache, must-revalidate
+
new file mode 100644
--- /dev/null
+++ b/content/base/test/forRemoval.resource
@@ -0,0 +1,23 @@
+:this file must be enconded in utf8
+:and its Content-Type must be equal to text/event-stream
+
+retry:500
+event: message
+data: 1
+
+retry:500
+event: message
+data: 2
+
+retry:500
+event: message
+data: 3
+
+retry:500
+event: message
+data: 4
+
+retry:500
+event: message
+data: 5
+
new file mode 100644
--- /dev/null
+++ b/content/base/test/forRemoval.resource^headers^
@@ -0,0 +1,3 @@
+Content-Type: text/event-stream
+Cache-Control: no-cache, must-revalidate
+
new file mode 100644
--- /dev/null
+++ b/content/base/test/invalid_accesscontrol.resource
@@ -0,0 +1,5 @@
+:this file must be enconded in utf8
+:and its Content-Type must be equal to text/event-stream
+
+event: message
+data: 1
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/content/base/test/invalid_accesscontrol.resource^headers^
@@ -0,0 +1,4 @@
+Access-Control-Allow-Origin: http://an.accesscrontrol.domain:80
+Content-Type: text/event-stream
+Cache-Control: no-cache, must-revalidate
+
new file mode 100644
--- /dev/null
+++ b/content/base/test/somedatas.resource
@@ -0,0 +1,17 @@
+
+
+retry: 500
+
+data:123456789
+data: 123456789123456789
+data:123456789123456789123456789123456789
+data:  123456789123456789123456789123456789123456789123456789123456789123456789
+:some utf-8 characteres
+data:çãá"'@`~Ý Ḿyyyy
+
+:test if the character ":"(which is used for comments) isn't misunderstood
+data:  :xxabcdefghij
+data:çãá"'@`~Ý Ḿyyyy : zz
+
+:a message dispatched because of eof
+data:1
new file mode 100644
--- /dev/null
+++ b/content/base/test/somedatas.resource^headers^
@@ -0,0 +1,3 @@
+Content-Type: text/event-stream
+Cache-Control: no-cache, must-revalidate
+
new file mode 100755
--- /dev/null
+++ b/content/base/test/test_bug338583.html
@@ -0,0 +1,454 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=338583
+-->
+<head>
+  <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
+  <title>Test for Bug 338583</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+</head>
+<body bgColor=white>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=338583">Mozilla Bug 338583</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 338583 **/
+
+// we test:
+//   1) the EventSource behaviour
+//   2) if the events are trusted
+//   3) possible invalid eventsources
+//   4) the close method when the object is just been used
+//   5) access-control
+//   6) the data parameter
+//   7) delayed server responses
+
+// --
+
+  function fn_onmessage(e) {
+    if (e.currentTarget == e.target && e.target.hits != null)
+      e.target.hits['fn_onmessage']++;
+  }
+
+  function fn_event_listener_message(e) {
+    if (e.currentTarget == e.target && e.target.hits != null)
+      e.target.hits['fn_event_listener_message']++;
+  }
+
+  function fn_other_event_name(e) {
+    if (e.currentTarget == e.target && e.target.hits != null)
+      e.target.hits['fn_other_event_name']++;
+  }
+
+  var domBranch;
+  var oldPrefVal;
+
+  var gEventSourceObj1 = null;
+  var gEventSourceObj2 = null;
+  var gEventSourceObj3 = null;
+  var gEventSourceObj4 = null;
+  var gEventSourceObj5 = null;
+  var gEventSourceObj6 = null;
+  var gEventSourceObj7 = null;
+  var stress_factor;  // used in the setTimeouts in order to help
+                      // the test when running in slow machines
+
+  function hasBeenHitFor1And2(obj, min) {
+    if (obj.hits['fn_onmessage'] < min ||
+        obj.hits['fn_event_listener_message'] < min ||
+        obj.hits['fn_other_event_name'] < min)
+      return false;
+    return true;
+  }
+
+// in order to test (1):
+//   a) if the EventSource constructor parameter is equal to its url attribute
+//   b) let its fn_onmessage, fn_event_listener_message, and fn_other_event_name functions listeners be hit four times each
+//   c) the close method (we expect readyState == CLOSED)
+//   d) the close method (we expect no message events anymore)
+
+  function doTest1() {
+    gEventSourceObj1 = new EventSource("eventsource.resource");
+    ok(gEventSourceObj1.url == "http://mochi.test:8888/tests/content/base/test/eventsource.resource", "Test 1.a failed.");
+    ok(gEventSourceObj1.readyState == 0 || gEventSourceObj1.readyState == 1, "Test 1.a failed.");
+
+    doTest1_b();
+  }
+
+  function doTest1_b() {
+    gEventSourceObj1.hits = [];
+    gEventSourceObj1.hits['fn_onmessage'] = 0;
+    gEventSourceObj1.onmessage = fn_onmessage;
+    gEventSourceObj1.hits['fn_event_listener_message'] = 0;
+    gEventSourceObj1.addEventListener('message', fn_event_listener_message, true);
+    gEventSourceObj1.hits['fn_other_event_name'] = 0;
+    gEventSourceObj1.addEventListener('other_event_name', fn_other_event_name, true);
+
+    // the eventsources.res always use a retry of 0.5 second, so for four hits a timeout of 3 seconds is enough
+    setTimeout(function(){
+      bhits = hasBeenHitFor1And2(gEventSourceObj1, 4);
+      ok(bhits, "Test 1.b failed.");
+
+      doTest1_c();
+    }, parseInt(3000*stress_factor));
+  }
+
+  function doTest1_c() {
+    gEventSourceObj1.close();
+    ok(gEventSourceObj1.readyState == 2, "Test 1.c failed.");
+
+    doTest1_d();
+  }
+
+  function doTest1_d() {
+    gEventSourceObj1.hits['fn_onmessage'] = 0;
+    gEventSourceObj1.hits['fn_event_listener_message'] = 0;
+    gEventSourceObj1.hits['fn_other_event_name'] = 0;
+
+    setTimeout(function(){
+      bhits = hasBeenHitFor1And2(gEventSourceObj1, 1);
+      ok(!bhits, "Test 1.d failed.");
+      gEventSourceObj1.close();
+
+      doTest2();
+    }, parseInt(2000*stress_factor));
+  }
+
+// in order to test (2)
+//   a) set a eventsource that give the dom events messages
+//   b) expect trusted events
+
+  function doTest2() {
+    var func = function(e) {
+      ok(e.isTrusted, "Test 2 failed");
+      gEventSourceObj2.close();
+    };
+
+    gEventSourceObj2 = new EventSource("eventsource.resource");
+    gEventSourceObj2.onmessage = func;
+
+    setTimeout(function(){  // just to clean...
+      gEventSourceObj2.close();
+    }, parseInt(5000*stress_factor));
+
+    doTest3();
+  }
+
+// in order to test (3)
+//   a) XSite domain error test
+//   b) protocol file:/// test
+//   c) protocol javascript: test
+//   d) wrong Content-Type test
+//   e) bad http response code test
+//   f) message eventsource without a data test
+//   g) eventsource with invalid NCName char in the event field test
+//   h) DNS error
+
+  function doTest3() {
+    gEventSourceObj3 = new EventSource("http://example.org/tests/content/base/test/eventsource.resource");
+
+    gEventSourceObj3.onmessage = fn_onmessage;
+    gEventSourceObj3.hits = [];
+    gEventSourceObj3.hits['fn_onmessage'] = 0;
+
+    setTimeout(function() {
+      ok(gEventSourceObj3.hits['fn_onmessage'] == 0, "Test 3.a failed");
+      gEventSourceObj3.close();
+
+      doTest3_b();
+    }, parseInt(1500*stress_factor));
+  }
+
+  function doTest3_b() {
+    gEventSourceObj3 = new EventSource("file:///home/wellington/src/content/base/test/eventsource.resource");
+
+    gEventSourceObj3.onmessage = fn_onmessage;
+    gEventSourceObj3.hits = [];
+    gEventSourceObj3.hits['fn_onmessage'] = 0;
+
+    setTimeout(function() {
+      ok(gEventSourceObj3.hits['fn_onmessage'] == 0, "Test 3.b failed");
+      gEventSourceObj3.close();
+
+      doTest3_c();
+    }, parseInt(1500*stress_factor));
+  }
+
+  function jsEvtSource()
+  {
+    return "event: message\n" +
+           "data: 1\n\n";
+  }
+
+  function doTest3_c() {
+    gEventSourceObj3 = new EventSource("javascript: return jsEvtSource()");
+
+    gEventSourceObj3.onmessage = fn_onmessage;
+    gEventSourceObj3.hits = [];
+    gEventSourceObj3.hits['fn_onmessage'] = 0;
+
+    setTimeout(function() {
+      ok(gEventSourceObj3.hits['fn_onmessage'] == 0, "Test 3.c failed");
+      gEventSourceObj3.close();
+
+      doTest3_d();
+    }, parseInt(1500*stress_factor));
+  }
+
+  function doTest3_d() {
+    gEventSourceObj3 = new EventSource("badContentType.eventsource");
+
+    gEventSourceObj3.onmessage = fn_onmessage;
+    gEventSourceObj3.hits = [];
+    gEventSourceObj3.hits['fn_onmessage'] = 0;
+
+    setTimeout(function() {
+      ok(gEventSourceObj3.hits['fn_onmessage'] == 0, "Test 3.d failed");
+      gEventSourceObj3.close();
+
+      doTest3_e();
+    }, parseInt(1500*stress_factor));
+  }
+
+  function doTest3_e() {
+    gEventSourceObj3 = new EventSource("badHTTPResponseCode.eventsource");
+
+    gEventSourceObj3.onmessage = fn_onmessage;
+    gEventSourceObj3.hits = [];
+    gEventSourceObj3.hits['fn_onmessage'] = 0;
+
+    setTimeout(function() {
+      ok(gEventSourceObj3.hits['fn_onmessage'] == 0, "Test 3.e failed");
+      gEventSourceObj3.close();
+
+      doTest3_f();
+    }, parseInt(1500*stress_factor));
+  }
+
+  function doTest3_f() {
+    gEventSourceObj3 = new EventSource("badMessageEvent.eventsource");
+
+    gEventSourceObj3.onmessage = fn_onmessage;
+    gEventSourceObj3.hits = [];
+    gEventSourceObj3.hits['fn_onmessage'] = 0;
+
+    setTimeout(function() {
+      ok(gEventSourceObj3.hits['fn_onmessage'] == 0, "Test 3.f failed");
+      gEventSourceObj3.close();
+
+      doTest3_g();
+    }, parseInt(1500*stress_factor));
+  }
+
+  function fnInvalidNCName() {
+    fnInvalidNCName.hits++;
+  }
+
+  function doTest3_g() {
+    gEventSourceObj3 = new EventSource("badEventFieldName.eventsource");
+
+    fnInvalidNCName.hits = 0;
+    gEventSourceObj3.addEventListener('message event', fnInvalidNCName, true);
+
+    setTimeout(function() {
+      ok(fnInvalidNCName.hits != 0, "Test 3.g failed");
+      gEventSourceObj3.close();
+
+      doTest3_h();
+    }, parseInt(1500*stress_factor));
+  }
+
+  function doTest3_h() {
+    gEventSourceObj3 = new EventSource("http://hdfskjghsbg.jtiyoejowe.dafsgbhjab.com");
+
+    gEventSourceObj3.onmessage = fn_onmessage;
+    gEventSourceObj3.hits = [];
+    gEventSourceObj3.hits['fn_onmessage'] = 0;
+
+    setTimeout(function() {
+      ok(gEventSourceObj3.hits['fn_onmessage'] == 0, "Test 3.h failed");
+      gEventSourceObj3.close();
+
+      doTest4();
+    }, parseInt(1500*stress_factor));
+  }
+
+// in order to test (4)
+//   a) close the object when it is in use, which is being processed and that is expected
+//      to dispatch more eventlisteners
+//   b) remove an eventlistener in use
+
+  function fn_onmessage4_a(e)
+  {
+    if (e.data > gEventSourceObj4.lastData)
+      gEventSourceObj4.lastData = e.data;
+    if (e.data == 2)
+      gEventSourceObj4.close();
+  }
+
+  function fn_onmessage4_b(e)
+  {
+    if (e.data > gEventSourceObj4.lastData)
+      gEventSourceObj4.lastData = e.data;
+    if (e.data == 2)
+      gEventSourceObj4.removeEventListener('message', fn_onmessage4_b, true);
+  }
+
+  function doTest4() {
+    gEventSourceObj4 = new EventSource("forRemoval.resource");
+    gEventSourceObj4.lastData = 0;
+    gEventSourceObj4.onmessage = fn_onmessage4_a;
+
+    setTimeout(function() {
+      ok(gEventSourceObj4.lastData == 2, "Test 4.a failed");
+      gEventSourceObj4.close();
+
+      doTest4_b();
+    }, parseInt(3000*stress_factor));
+  }
+
+  function doTest4_b()
+  {
+    gEventSourceObj4 = new EventSource("forRemoval.resource");
+    gEventSourceObj4.lastData = 0;
+    gEventSourceObj4.addEventListener('message', fn_onmessage4_b, true);
+
+    setTimeout(function() {
+      ok(gEventSourceObj4.lastData == 2, "Test 4.b failed");
+      gEventSourceObj4.close();
+
+      doTest5();
+    }, parseInt(3000*stress_factor));
+  }
+
+// in order to test (5)
+//   a) valid access-control xsite request (but must fail)
+//   b) invalid access-control xsite request
+
+  function doTest5()
+  {
+    gEventSourceObj5 = new EventSource("http://example.org/tests/content/base/test/accesscontrol.resource");
+
+    gEventSourceObj5.onmessage = fn_onmessage;
+    gEventSourceObj5.hits = [];
+    gEventSourceObj5.hits['fn_onmessage'] = 0;
+
+    setTimeout(function() {
+      ok(gEventSourceObj5.hits['fn_onmessage'] == 0, "Test 5.a failed");
+      gEventSourceObj5.close();
+
+      doTest5_b();
+    }, parseInt(3000*stress_factor));
+  }
+
+  function doTest5_b()
+  {
+    gEventSourceObj5 = new EventSource("http://example.org/tests/content/base/test/invalid_accesscontrol.resource");
+
+    gEventSourceObj5.onmessage = fn_onmessage;
+    gEventSourceObj5.hits = [];
+    gEventSourceObj5.hits['fn_onmessage'] = 0;
+
+    setTimeout(function() {
+      ok(gEventSourceObj5.hits['fn_onmessage'] == 0, "Test 5.b failed");
+      gEventSourceObj5.close();
+
+      doTest6();
+    }, parseInt(3000*stress_factor));
+  }
+
+  function doTest6()
+  {
+    gEventSourceObj6 = new EventSource("somedatas.resource");
+    var fn_somedata = function(e) {
+      if (fn_somedata.expected == 0) {
+        ok(e.data == "123456789\n123456789123456789\n123456789123456789123456789123456789\n 123456789123456789123456789123456789123456789123456789123456789123456789\nçãá\"\'@`~Ý Ḿyyyy",
+          "Test 6.a failed");
+      } else if (fn_somedata.expected == 1) {
+        ok(e.data == " :xxabcdefghij\nçãá\"\'@`~Ý Ḿyyyy : zz",
+          "Test 6.b failed");
+      } else if (fn_somedata.expected == 2) {
+        ok(e.data == "1", "Test 6.c failed");
+        gEventSourceObj6.close();
+      } else {
+        ok(false, "Test 6 failed (unexpected message event)");
+      }
+      fn_somedata.expected++;
+    }
+    fn_somedata.expected = 0;
+    gEventSourceObj6.onmessage = fn_somedata;
+
+    setTimeout(function() {
+      gEventSourceObj6.close();
+
+      doTest7();
+    }, parseInt(2500*stress_factor));
+  }
+
+  function doTest7()
+  {
+    gEventSourceObj7 = new EventSource("delayedServerEvents.sjs");
+    gEventSourceObj7.msg_received = [];
+    gEventSourceObj7.onmessage = function(e)
+    {
+      e.target.msg_received.push(e.data);
+    }
+    
+    setTimeout(function() {
+      gEventSourceObj7.close();
+      
+      ok(gEventSourceObj7.msg_received[0] == "" &&
+         gEventSourceObj7.msg_received[1] == "delayed1" &&
+         gEventSourceObj7.msg_received[2] == "delayed2", "Test 7 failed");
+
+      SpecialPowers.setBoolPref("dom.server-events.enabled", oldPrefVal);
+      document.getElementById('waitSpan').innerHTML = '';
+      SimpleTest.finish();
+    }, parseInt(50000*stress_factor));
+  }
+
+  function doTest()
+  {
+    oldPrefVal = SpecialPowers.getBoolPref("dom.server-events.enabled");
+    SpecialPowers.setBoolPref("dom.server-events.enabled", true);
+
+    // we get a good stress_factor by testing 10 setTimeouts and some float
+    // arithmetic taking my machine as stress_factor==1 (time=589)
+
+    var begin_time = (new Date()).getTime();
+
+    var f = function() {
+      for (var j=0; j<f.i; ++j)
+        eval("Math.log(Math.atan(Math.sqrt(Math.pow(3.1415, 13.1415))/0.0007))");
+      if (f.i < 10) {
+        f.i++;
+        setTimeout(f, 10 + 10*f.i);
+      } else {
+        stress_factor = ((new Date()).getTime()-begin_time)*1/589;
+        stress_factor *= 1.10; // also, a margin of 10%
+
+        doTest1();
+      }
+    }
+    f.i = 0;
+
+    setTimeout(f, 10);
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  addLoadEvent(doTest);
+
+</script>
+</pre>
+  <span id=waitSpan>Wait please...</span>
+</body>
+</html>
+
--- a/content/events/src/nsDOMEvent.cpp
+++ b/content/events/src/nsDOMEvent.cpp
@@ -70,17 +70,17 @@ static const char* const sEventNames[] =
   "dragenter", "dragover", "dragexit", "dragdrop", "draggesture",
   "drag", "dragend", "dragstart", "dragleave", "drop", "resize",
   "scroll", "overflow", "underflow", "overflowchanged",
   "DOMSubtreeModified", "DOMNodeInserted", "DOMNodeRemoved", 
   "DOMNodeRemovedFromDocument", "DOMNodeInsertedIntoDocument",
   "DOMAttrModified", "DOMCharacterDataModified",
   "DOMActivate", "DOMFocusIn", "DOMFocusOut",
   "pageshow", "pagehide", "DOMMouseScroll", "MozMousePixelScroll",
-  "offline", "online", "copy", "cut", "paste",
+  "offline", "online", "copy", "cut", "paste", "open", "message",
 #ifdef MOZ_SVG
   "SVGLoad", "SVGUnload", "SVGAbort", "SVGError", "SVGResize", "SVGScroll",
   "SVGZoom",
 #endif // MOZ_SVG
 #ifdef MOZ_SMIL
   "beginEvent", "endEvent", "repeatEvent",
 #endif // MOZ_SMIL
 #ifdef MOZ_MEDIA
@@ -1256,16 +1256,20 @@ const char* nsDOMEvent::GetEventName(PRU
   case NS_ONLINE:
     return sEventNames[eDOMEvents_online];
   case NS_COPY:
     return sEventNames[eDOMEvents_copy];
   case NS_CUT:
     return sEventNames[eDOMEvents_cut];
   case NS_PASTE:
     return sEventNames[eDOMEvents_paste];
+  case NS_OPEN:
+    return sEventNames[eDOMEvents_open];
+  case NS_MESSAGE:
+    return sEventNames[eDOMEvents_message];
 #ifdef MOZ_SVG
   case NS_SVG_LOAD:
     return sEventNames[eDOMEvents_SVGLoad];
   case NS_SVG_UNLOAD:
     return sEventNames[eDOMEvents_SVGUnload];
   case NS_SVG_ABORT:
     return sEventNames[eDOMEvents_SVGAbort];
   case NS_SVG_ERROR:
--- a/content/events/src/nsDOMEvent.h
+++ b/content/events/src/nsDOMEvent.h
@@ -131,16 +131,18 @@ public:
     eDOMEvents_pagehide,
     eDOMEvents_DOMMouseScroll,
     eDOMEvents_MozMousePixelScroll,
     eDOMEvents_offline,
     eDOMEvents_online,
     eDOMEvents_copy,
     eDOMEvents_cut,
     eDOMEvents_paste,
+    eDOMEvents_open,
+    eDOMEvents_message,
 #ifdef MOZ_SVG
     eDOMEvents_SVGLoad,
     eDOMEvents_SVGUnload,
     eDOMEvents_SVGAbort,
     eDOMEvents_SVGError,
     eDOMEvents_SVGResize,
     eDOMEvents_SVGScroll,
     eDOMEvents_SVGZoom,
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -208,16 +208,17 @@
 #include "nsIXSLTProcessorPrivate.h"
 
 #include "nsIDOMLSProgressEvent.h"
 #include "nsIDOMParser.h"
 #include "nsIDOMSerializer.h"
 #include "nsXMLHttpRequest.h"
 #include "nsWebSocket.h"
 #include "nsIDOMCloseEvent.h"
+#include "nsEventSource.h"
 
 // includes needed for the prototype chain interfaces
 #include "nsIDOMNavigator.h"
 #include "nsIDOMBarProp.h"
 #include "nsIDOMScreen.h"
 #include "nsIDOMDocumentType.h"
 #include "nsIDOMDOMImplementation.h"
 #include "nsIDOMDocumentFragment.h"
@@ -1316,16 +1317,19 @@ static nsDOMClassInfoData sClassInfoData
   NS_DEFINE_CLASSINFO_DATA(XMLSerializer, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(XMLHttpProgressEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(XMLHttpRequest, nsEventTargetSH,
                            EVENTTARGET_SCRIPTABLE_FLAGS)
 
+  NS_DEFINE_CLASSINFO_DATA(EventSource, nsEventTargetSH,
+                           EVENTTARGET_SCRIPTABLE_FLAGS)
+
   NS_DEFINE_CLASSINFO_DATA(ClientRect, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(ClientRectList, nsClientRectListSH,
                            ARRAY_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(SVGForeignObjectElement, nsElementSH,
                            ELEMENT_SCRIPTABLE_FLAGS)
 
@@ -1556,16 +1560,17 @@ static const nsContractIDMapData kConstr
   NS_DEFINE_CONSTRUCTOR_DATA(FileReader, NS_FILEREADER_CONTRACTID)
   NS_DEFINE_CONSTRUCTOR_DATA(FormData, NS_FORMDATA_CONTRACTID)
   NS_DEFINE_CONSTRUCTOR_DATA(XMLSerializer, NS_XMLSERIALIZER_CONTRACTID)
   NS_DEFINE_CONSTRUCTOR_DATA(XMLHttpRequest, NS_XMLHTTPREQUEST_CONTRACTID)
   NS_DEFINE_CONSTRUCTOR_DATA(WebSocket, NS_WEBSOCKET_CONTRACTID)
   NS_DEFINE_CONSTRUCTOR_DATA(XPathEvaluator, NS_XPATH_EVALUATOR_CONTRACTID)
   NS_DEFINE_CONSTRUCTOR_DATA(XSLTProcessor,
                              "@mozilla.org/document-transformer;1?type=xslt")
+  NS_DEFINE_CONSTRUCTOR_DATA(EventSource, NS_EVENTSOURCE_CONTRACTID)
 };
 
 struct nsConstructorFuncMapData
 {
   PRInt32 mDOMClassInfoID;
   nsDOMConstructorFunc mConstructorFunc;
 };
 
@@ -3960,16 +3965,22 @@ nsDOMClassInfo::Init()
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(XMLHttpProgressEvent, nsIDOMEvent)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMLSProgressEvent)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMProgressEvent)
     DOM_CLASSINFO_EVENT_MAP_ENTRIES
   DOM_CLASSINFO_MAP_END
 
+  DOM_CLASSINFO_MAP_BEGIN(EventSource, nsIEventSource)
+    DOM_CLASSINFO_MAP_ENTRY(nsIEventSource)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMNSEventTarget)
+  DOM_CLASSINFO_MAP_END
+
   DOM_CLASSINFO_MAP_BEGIN(SVGForeignObjectElement, nsIDOMSVGForeignObjectElement)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMSVGForeignObjectElement)
     DOM_CLASSINFO_SVG_GRAPHIC_ELEMENT_MAP_ENTRIES
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(XULCommandEvent, nsIDOMXULCommandEvent)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMXULCommandEvent)
     DOM_CLASSINFO_UI_EVENT_MAP_ENTRIES
@@ -6551,16 +6562,23 @@ nsWindowSH::GlobalResolve(nsGlobalWindow
 
     // For now don't expose web sockets unless user has explicitly enabled them
     if (name_struct->mDOMClassInfoID == eDOMClassInfo_WebSocket_id) {
       if (!nsWebSocket::PrefEnabled()) {
         return NS_OK;
       }
     }
 
+    // For now don't expose server events unless user has explicitly enabled them
+    if (name_struct->mDOMClassInfoID == eDOMClassInfo_EventSource_id) {
+      if (!nsEventSource::PrefEnabled()) {
+        return NS_OK;
+      }
+    }
+
     // Create the XPConnect prototype for our classinfo, PostCreateProto will
     // set up the prototype chain.
     nsCOMPtr<nsIXPConnectJSObjectHolder> proto_holder;
     rv = GetXPCProto(sXPConnect, cx, aWin, name_struct,
                      getter_AddRefs(proto_holder));
 
     if (NS_SUCCEEDED(rv) && obj != aWin->GetGlobalJSObject()) {
       JSObject* dot_prototype;
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -383,16 +383,19 @@ DOMCI_CLASS(StorageEventObsolete)
 // DOMParser, XMLSerializer
 DOMCI_CLASS(DOMParser)
 DOMCI_CLASS(XMLSerializer)
 
 // XMLHttpRequest
 DOMCI_CLASS(XMLHttpProgressEvent)
 DOMCI_CLASS(XMLHttpRequest)
 
+// Server-sent events
+DOMCI_CLASS(EventSource)
+
 DOMCI_CLASS(ClientRect)
 DOMCI_CLASS(ClientRectList)
 
 #ifdef MOZ_SVG
 DOMCI_CLASS(SVGForeignObjectElement)
 #endif
 
 DOMCI_CLASS(XULCommandEvent)
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -289,17 +289,17 @@ nsGlobalWindow::DOMMinTimeoutValue() con
 
 // The number of nested timeouts before we start clamping. HTML5 says 1, WebKit
 // uses 5.
 #define DOM_CLAMP_TIMEOUT_NESTING_LEVEL 5
 
 // The longest interval (as PRIntervalTime) we permit, or that our
 // timer code can handle, really. See DELAY_INTERVAL_LIMIT in
 // nsTimerImpl.h for details.
-#define DOM_MAX_TIMEOUT_VALUE    PR_BIT(8 * sizeof(PRIntervalTime) - 1)
+#define DOM_MAX_TIMEOUT_VALUE    DELAY_INTERVAL_LIMIT
 
 #define FORWARD_TO_OUTER(method, args, err_rval)                              \
   PR_BEGIN_MACRO                                                              \
   if (IsInnerWindow()) {                                                      \
     nsGlobalWindow *outer = GetOuterWindowInternal();                         \
     if (!outer) {                                                             \
       NS_WARNING("No outer window available!");                               \
       return err_rval;                                                        \
@@ -6532,16 +6532,44 @@ nsGlobalWindow::NotifyWindowIDDestroyed(
 {
   nsRefPtr<nsIRunnable> runnable = new WindowDestroyedEvent(mWindowID, aTopic);
   nsresult rv = NS_DispatchToCurrentThread(runnable);
   if (NS_SUCCEEDED(rv)) {
     mNotifiedIDDestroyed = PR_TRUE;
   }
 }
 
+// static
+void
+nsGlobalWindow::NotifyDOMWindowFrozen(nsGlobalWindow* aWindow) {
+  if (aWindow && aWindow->IsInnerWindow()) {
+    nsCOMPtr<nsIObserverService> observerService =
+      do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
+    if (observerService) {
+      observerService->
+        NotifyObservers(static_cast<nsIScriptGlobalObject*>(aWindow),
+                        DOM_WINDOW_FROZEN_TOPIC, nsnull);
+    }
+  }
+}
+
+// static
+void
+nsGlobalWindow::NotifyDOMWindowThawed(nsGlobalWindow* aWindow) {
+  if (aWindow && aWindow->IsInnerWindow()) {
+    nsCOMPtr<nsIObserverService> observerService =
+      do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
+    if (observerService) {
+      observerService->
+        NotifyObservers(static_cast<nsIScriptGlobalObject*>(aWindow),
+                        DOM_WINDOW_THAWED_TOPIC, nsnull);
+    }
+  }
+}
+
 void
 nsGlobalWindow::InitJavaProperties()
 {
   nsIScriptContext *scx = GetContextInternal();
 
   if (mDidInitJavaProperties || IsOuterWindow() || !scx || !mJSObject) {
     return;
   }
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -755,21 +755,23 @@ protected:
 
   already_AddRefed<nsIWidget> GetMainWidget();
   nsIWidget* GetNearestWidget();
 
   void Freeze()
   {
     NS_ASSERTION(!IsFrozen(), "Double-freezing?");
     mIsFrozen = PR_TRUE;
+    NotifyDOMWindowFrozen(this);
   }
 
   void Thaw()
   {
     mIsFrozen = PR_FALSE;
+    NotifyDOMWindowThawed(this);
   }
 
   PRBool IsInModalState();
 
   nsTimeout* FirstTimeout() {
     // Note: might not actually return an nsTimeout.  Use IsTimeout to check.
     return static_cast<nsTimeout*>(PR_LIST_HEAD(&mTimeouts));
   }
@@ -805,16 +807,19 @@ protected:
                                      PRBool* aShowFocusRings);
 
   void UpdateCanvasFocus(PRBool aFocusChanged, nsIContent* aNewContent);
 
   already_AddRefed<nsPIWindowRoot> GetTopWindowRoot();
 
   static void NotifyDOMWindowDestroyed(nsGlobalWindow* aWindow);
   void NotifyWindowIDDestroyed(const char* aTopic);
+
+  static void NotifyDOMWindowFrozen(nsGlobalWindow* aWindow);
+  static void NotifyDOMWindowThawed(nsGlobalWindow* aWindow);
   
   void ClearStatus();
 
   virtual void UpdateParentTarget();
 
   PRBool GetIsTabModalPromptAllowed();
 
   inline PRInt32 DOMMinTimeoutValue() const;
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -47,16 +47,18 @@
 #include "nsIDOMWindowInternal.h"
 #include "nsPIDOMEventTarget.h"
 #include "nsIDOMDocument.h"
 #include "nsCOMPtr.h"
 #include "nsEvent.h"
 #include "nsIURI.h"
 
 #define DOM_WINDOW_DESTROYED_TOPIC "dom-window-destroyed"
+#define DOM_WINDOW_FROZEN_TOPIC "dom-window-frozen"
+#define DOM_WINDOW_THAWED_TOPIC "dom-window-thawed"
 
 class nsIPrincipal;
 
 // Popup control state enum. The values in this enum must go from most
 // permissive to least permissive so that it's safe to push state in
 // all situations. Pushing popup state onto the stack never makes the
 // current popup state less permissive (see
 // nsGlobalWindow::PushPopupControlState()).
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -106,16 +106,17 @@
 #include "txNodeSetAdaptor.h"
 
 #include "nsDOMParser.h"
 #include "nsDOMSerializer.h"
 #include "nsXMLHttpRequest.h"
 #include "nsChannelPolicy.h"
 #include "nsWebSocket.h"
 #include "nsDOMWorker.h"
+#include "nsEventSource.h"
 
 // view stuff
 #include "nsViewsCID.h"
 #include "nsViewManager.h"
 #include "nsContentCreatorFunctions.h"
 
 // DOM includes
 #include "nsDOMException.h"
@@ -303,16 +304,17 @@ static void Shutdown();
 "@mozilla.org/transformiix-nodeset;1"
 
 // Factory Constructor
 NS_GENERIC_FACTORY_CONSTRUCTOR(txMozillaXSLTProcessor)
 NS_GENERIC_AGGREGATED_CONSTRUCTOR_INIT(nsXPathEvaluator, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(txNodeSetAdaptor, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDOMSerializer)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsXMLHttpRequest, Init)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsEventSource)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsWebSocket)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsDOMFileReader, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsFormData)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsFileDataProtocolHandler)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDOMParser)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsDOMStorageManager,
                                          nsDOMStorageManager::GetInstance)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsChannelPolicy)
@@ -834,16 +836,17 @@ NS_DEFINE_NAMED_CID(NS_STYLESHEETSERVICE
 NS_DEFINE_NAMED_CID(TRANSFORMIIX_XSLT_PROCESSOR_CID);
 NS_DEFINE_NAMED_CID(TRANSFORMIIX_XPATH_EVALUATOR_CID);
 NS_DEFINE_NAMED_CID(TRANSFORMIIX_NODESET_CID);
 NS_DEFINE_NAMED_CID(NS_XMLSERIALIZER_CID);
 NS_DEFINE_NAMED_CID(NS_FILEREADER_CID);
 NS_DEFINE_NAMED_CID(NS_FORMDATA_CID);
 NS_DEFINE_NAMED_CID(NS_FILEDATAPROTOCOLHANDLER_CID);
 NS_DEFINE_NAMED_CID(NS_XMLHTTPREQUEST_CID);
+NS_DEFINE_NAMED_CID(NS_EVENTSOURCE_CID);
 NS_DEFINE_NAMED_CID(NS_WEBSOCKET_CID);
 NS_DEFINE_NAMED_CID(NS_DOMPARSER_CID);
 NS_DEFINE_NAMED_CID(NS_DOMSTORAGE_CID);
 NS_DEFINE_NAMED_CID(NS_DOMSTORAGE2_CID);
 NS_DEFINE_NAMED_CID(NS_DOMSTORAGEMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_DOMJSON_CID);
 NS_DEFINE_NAMED_CID(NS_TEXTEDITOR_CID);
 NS_DEFINE_NAMED_CID(INDEXEDDB_MANAGER_CID );
@@ -981,16 +984,17 @@ static const mozilla::Module::CIDEntry k
   { &kTRANSFORMIIX_XSLT_PROCESSOR_CID, false, NULL, txMozillaXSLTProcessorConstructor },
   { &kTRANSFORMIIX_XPATH_EVALUATOR_CID, false, NULL, nsXPathEvaluatorConstructor },
   { &kTRANSFORMIIX_NODESET_CID, false, NULL, txNodeSetAdaptorConstructor },
   { &kNS_XMLSERIALIZER_CID, false, NULL, nsDOMSerializerConstructor },
   { &kNS_FILEREADER_CID, false, NULL, nsDOMFileReaderConstructor },
   { &kNS_FORMDATA_CID, false, NULL, nsFormDataConstructor },
   { &kNS_FILEDATAPROTOCOLHANDLER_CID, false, NULL, nsFileDataProtocolHandlerConstructor },
   { &kNS_XMLHTTPREQUEST_CID, false, NULL, nsXMLHttpRequestConstructor },
+  { &kNS_EVENTSOURCE_CID, false, NULL, nsEventSourceConstructor },
   { &kNS_WEBSOCKET_CID, false, NULL, nsWebSocketConstructor },
   { &kNS_DOMPARSER_CID, false, NULL, nsDOMParserConstructor },
   { &kNS_DOMSTORAGE_CID, false, NULL, NS_NewDOMStorage },
   { &kNS_DOMSTORAGE2_CID, false, NULL, NS_NewDOMStorage2 },
   { &kNS_DOMSTORAGEMANAGER_CID, false, NULL, nsDOMStorageManagerConstructor },
   { &kNS_DOMJSON_CID, false, NULL, NS_NewJSON },
   { &kNS_TEXTEDITOR_CID, false, NULL, nsPlaintextEditorConstructor },
   { &kINDEXEDDB_MANAGER_CID, false, NULL, IndexedDatabaseManagerConstructor },
@@ -1123,16 +1127,17 @@ static const mozilla::Module::ContractID
   { TRANSFORMIIX_XSLT_PROCESSOR_CONTRACTID, &kTRANSFORMIIX_XSLT_PROCESSOR_CID },
   { NS_XPATH_EVALUATOR_CONTRACTID, &kTRANSFORMIIX_XPATH_EVALUATOR_CID },
   { TRANSFORMIIX_NODESET_CONTRACTID, &kTRANSFORMIIX_NODESET_CID },
   { NS_XMLSERIALIZER_CONTRACTID, &kNS_XMLSERIALIZER_CID },
   { NS_FILEREADER_CONTRACTID, &kNS_FILEREADER_CID },
   { NS_FORMDATA_CONTRACTID, &kNS_FORMDATA_CID },
   { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX FILEDATA_SCHEME, &kNS_FILEDATAPROTOCOLHANDLER_CID },
   { NS_XMLHTTPREQUEST_CONTRACTID, &kNS_XMLHTTPREQUEST_CID },
+  { NS_EVENTSOURCE_CONTRACTID, &kNS_EVENTSOURCE_CID },
   { NS_WEBSOCKET_CONTRACTID, &kNS_WEBSOCKET_CID },
   { NS_DOMPARSER_CONTRACTID, &kNS_DOMPARSER_CID },
   { "@mozilla.org/dom/storage;1", &kNS_DOMSTORAGE_CID },
   { "@mozilla.org/dom/storage;2", &kNS_DOMSTORAGE2_CID },
   { "@mozilla.org/dom/storagemanager;1", &kNS_DOMSTORAGEMANAGER_CID },
   { "@mozilla.org/dom/json;1", &kNS_DOMJSON_CID },
   { "@mozilla.org/editor/texteditor;1", &kNS_TEXTEDITOR_CID },
   { INDEXEDDB_MANAGER_CONTRACTID, &kINDEXEDDB_MANAGER_CID },
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -806,16 +806,22 @@ pref("network.websocket.timeout.ping.req
 pref("network.websocket.timeout.ping.response", 10);
 
 // Defines whether or not to try and negotiate the stream-deflate compression
 // extension with the websocket server
 pref("network.websocket.extensions.stream-deflate", false);
 
 // </ws>
 
+// Server-Sent Events
+
+pref("dom.server-events.enabled", true);
+// Equal to the DEFAULT_RECONNECTION_TIME_VALUE value in nsEventSource.cpp
+pref("dom.server-events.default-reconnection-time", 5000); // in milliseconds
+
 // If false, remote JAR files that are served with a content type other than
 // application/java-archive or application/x-jar will not be opened
 // by the jar channel.
 pref("network.jar.open-unsafe-types", false);
 
 // This preference controls whether or not internationalized domain names (IDN)
 // are handled.  IDN requires a nsIIDNService implementation.
 pref("network.enableIDN", true);
--- a/netwerk/mime/nsMimeTypes.h
+++ b/netwerk/mime/nsMimeTypes.h
@@ -149,16 +149,17 @@
 #define TEXT_CSS                            "text/css"
 #define TEXT_JSSS                           "text/jsss"
 #define TEXT_XML                            "text/xml"
 #define TEXT_RDF                            "text/rdf"
 #define TEXT_XUL                            "application/vnd.mozilla.xul+xml"
 #define TEXT_ECMASCRIPT                     "text/ecmascript"
 #define TEXT_JAVASCRIPT                     "text/javascript"
 #define TEXT_XSL                            "text/xsl"
+#define TEXT_EVENT_STREAM                   "text/event-stream"
 
 #define VIDEO_MPEG                          "video/mpeg"
 #define VIDEO_RAW                           "video/x-raw-yuv"
 #define VIDEO_OGG                           "video/ogg"
 #define VIDEO_WEBM                          "video/webm"
 #define APPLICATION_OGG                     "application/ogg"
 
 /* x-uuencode-apple-single. QuickMail made me do this. */
--- a/widget/public/nsGUIEvent.h
+++ b/widget/public/nsGUIEvent.h
@@ -526,16 +526,24 @@ class nsHashKey;
 #define NS_NOTIFYSCRIPT_START        4500
 #define NS_BEFORE_SCRIPT_EXECUTE     (NS_NOTIFYSCRIPT_START)
 #define NS_AFTER_SCRIPT_EXECUTE      (NS_NOTIFYSCRIPT_START+1)
 
 #define NS_PRINT_EVENT_START         4600
 #define NS_BEFOREPRINT               (NS_PRINT_EVENT_START)
 #define NS_AFTERPRINT                (NS_PRINT_EVENT_START + 1)
 
+#define NS_MESSAGE_EVENT_START       4700
+#define NS_MESSAGE                   (NS_MESSAGE_EVENT_START)
+
+// Open and close events
+#define NS_OPENCLOSE_EVENT_START     4800
+#define NS_OPEN                      (NS_OPENCLOSE_EVENT_START)
+#define NS_CLOSE                     (NS_OPENCLOSE_EVENT_START+1)
+
 /**
  * Return status for event processors, nsEventStatus, is defined in
  * nsEvent.h.
  */
 
 /**
  * different types of (top-level) window z-level positioning
  */
--- a/xpcom/threads/nsITimer.idl
+++ b/xpcom/threads/nsITimer.idl
@@ -71,16 +71,20 @@ interface nsITimer;
 interface nsITimerCallback : nsISupports
 {
   /**
    * @param aTimer the timer which has expired
    */
   void notify(in nsITimer timer);
 };
 
+%{C++
+// Two timer deadlines must differ by less than half the PRIntervalTime domain.
+#define DELAY_INTERVAL_LIMIT    PR_BIT(8 * sizeof(PRIntervalTime) - 1)
+%}
 
 /**
  * nsITimer instances must be initialized by calling one of the "init" methods
  * documented below.  You may also re-initialize (using one of the init()
  * methods) an existing instance to avoid the overhead of destroying and
  * creating a timer.  It is not necessary to cancel the timer in that case.
  */
 [scriptable, uuid(193fc37a-8aa4-4d29-aa57-1acd87c26b66)]