michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * This implementation has support only for http requests. It is because the michael@0: * spec has defined event streams only for http. HTTP is required because michael@0: * this implementation uses some http headers: "Last-Event-ID", "Cache-Control" michael@0: * and "Accept". michael@0: */ michael@0: michael@0: #ifndef mozilla_dom_EventSource_h michael@0: #define mozilla_dom_EventSource_h michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/DOMEventTargetHelper.h" michael@0: #include "nsIObserver.h" michael@0: #include "nsIStreamListener.h" michael@0: #include "nsIChannelEventSink.h" michael@0: #include "nsIInterfaceRequestor.h" michael@0: #include "nsITimer.h" michael@0: #include "nsIHttpChannel.h" michael@0: #include "nsWeakReference.h" michael@0: #include "nsDeque.h" michael@0: #include "nsIUnicodeDecoder.h" michael@0: michael@0: class nsPIDOMWindow; michael@0: michael@0: namespace mozilla { michael@0: michael@0: class ErrorResult; michael@0: michael@0: namespace dom { michael@0: michael@0: class AsyncVerifyRedirectCallbackFwr; michael@0: struct EventSourceInit; michael@0: michael@0: class EventSource : public DOMEventTargetHelper michael@0: , public nsIObserver michael@0: , public nsIStreamListener michael@0: , public nsIChannelEventSink michael@0: , public nsIInterfaceRequestor michael@0: , public nsSupportsWeakReference michael@0: { michael@0: friend class AsyncVerifyRedirectCallbackFwr; michael@0: michael@0: public: michael@0: EventSource(nsPIDOMWindow* aOwnerWindow); michael@0: virtual ~EventSource(); michael@0: NS_DECL_ISUPPORTS_INHERITED michael@0: NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_INHERITED( michael@0: EventSource, DOMEventTargetHelper) michael@0: michael@0: NS_DECL_NSIOBSERVER michael@0: NS_DECL_NSISTREAMLISTENER michael@0: NS_DECL_NSIREQUESTOBSERVER michael@0: NS_DECL_NSICHANNELEVENTSINK michael@0: NS_DECL_NSIINTERFACEREQUESTOR michael@0: michael@0: // nsWrapperCache michael@0: virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; michael@0: michael@0: // WebIDL michael@0: nsPIDOMWindow* michael@0: GetParentObject() const michael@0: { michael@0: return GetOwner(); michael@0: } michael@0: static already_AddRefed michael@0: Constructor(const GlobalObject& aGlobal, const nsAString& aURL, michael@0: const EventSourceInit& aEventSourceInitDict, michael@0: ErrorResult& aRv); michael@0: michael@0: void GetUrl(nsAString& aURL) const michael@0: { michael@0: aURL = mOriginalURL; michael@0: } michael@0: bool WithCredentials() const michael@0: { michael@0: return mWithCredentials; michael@0: } michael@0: michael@0: enum { michael@0: CONNECTING = 0U, michael@0: OPEN = 1U, michael@0: CLOSED = 2U michael@0: }; michael@0: uint16_t ReadyState() const michael@0: { michael@0: return mReadyState; michael@0: } michael@0: michael@0: IMPL_EVENT_HANDLER(open) michael@0: IMPL_EVENT_HANDLER(message) michael@0: IMPL_EVENT_HANDLER(error) michael@0: void Close(); michael@0: michael@0: // Determine if preferences allow EventSource michael@0: static bool PrefEnabled(JSContext* aCx = nullptr, JSObject* aGlobal = nullptr); michael@0: michael@0: virtual void DisconnectFromOwner() MOZ_OVERRIDE; michael@0: michael@0: protected: michael@0: nsresult Init(nsISupports* aOwner, michael@0: const nsAString& aURL, michael@0: bool aWithCredentials); michael@0: michael@0: nsresult GetBaseURI(nsIURI **aBaseURI); michael@0: michael@0: nsresult SetupHttpChannel(); michael@0: nsresult InitChannelAndRequestEventSource(); michael@0: nsresult ResetConnection(); michael@0: nsresult DispatchFailConnection(); michael@0: nsresult SetReconnectionTimeout(); michael@0: michael@0: void AnnounceConnection(); michael@0: void DispatchAllMessageEvents(); michael@0: void ReestablishConnection(); michael@0: void FailConnection(); michael@0: michael@0: nsresult Thaw(); michael@0: nsresult Freeze(); michael@0: michael@0: static void TimerCallback(nsITimer *aTimer, void *aClosure); michael@0: michael@0: nsresult PrintErrorOnConsole(const char *aBundleURI, michael@0: const char16_t *aError, michael@0: const char16_t **aFormatStrings, michael@0: uint32_t aFormatStringsLen); michael@0: nsresult ConsoleError(); michael@0: michael@0: static NS_METHOD StreamReaderFunc(nsIInputStream *aInputStream, michael@0: void *aClosure, michael@0: const char *aFromRawSegment, michael@0: uint32_t aToOffset, michael@0: uint32_t aCount, michael@0: uint32_t *aWriteCount); michael@0: nsresult SetFieldAndClear(); michael@0: nsresult ClearFields(); michael@0: nsresult ResetEvent(); michael@0: nsresult DispatchCurrentMessageEvent(); michael@0: nsresult ParseCharacter(char16_t aChr); michael@0: bool CheckCanRequestSrc(nsIURI* aSrc = nullptr); // if null, it tests mSrc michael@0: nsresult CheckHealthOfRequestCallback(nsIRequest *aRequestCallback); michael@0: nsresult OnRedirectVerifyCallback(nsresult result); michael@0: michael@0: nsCOMPtr mSrc; michael@0: michael@0: nsString mLastEventID; michael@0: uint32_t mReconnectionTime; // in ms michael@0: michael@0: struct Message { michael@0: nsString mEventName; michael@0: nsString mLastEventID; michael@0: nsString mData; michael@0: }; michael@0: nsDeque mMessagesToDispatch; michael@0: Message mCurrentMessage; michael@0: michael@0: /** michael@0: * A simple state machine used to manage the event-source's line buffer michael@0: * michael@0: * PARSE_STATE_OFF -> PARSE_STATE_BEGIN_OF_STREAM michael@0: * michael@0: * PARSE_STATE_BEGIN_OF_STREAM -> PARSE_STATE_BOM_WAS_READ | michael@0: * PARSE_STATE_CR_CHAR | michael@0: * PARSE_STATE_BEGIN_OF_LINE | michael@0: * PARSE_STATE_COMMENT | michael@0: * PARSE_STATE_FIELD_NAME michael@0: * michael@0: * PARSE_STATE_BOM_WAS_READ -> PARSE_STATE_CR_CHAR | michael@0: * PARSE_STATE_BEGIN_OF_LINE | michael@0: * PARSE_STATE_COMMENT | michael@0: * PARSE_STATE_FIELD_NAME michael@0: * michael@0: * PARSE_STATE_CR_CHAR -> PARSE_STATE_CR_CHAR | michael@0: * PARSE_STATE_COMMENT | michael@0: * PARSE_STATE_FIELD_NAME | michael@0: * PARSE_STATE_BEGIN_OF_LINE michael@0: * michael@0: * PARSE_STATE_COMMENT -> PARSE_STATE_CR_CHAR | michael@0: * PARSE_STATE_BEGIN_OF_LINE michael@0: * michael@0: * PARSE_STATE_FIELD_NAME -> PARSE_STATE_CR_CHAR | michael@0: * PARSE_STATE_BEGIN_OF_LINE | michael@0: * PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE michael@0: * michael@0: * PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE -> PARSE_STATE_FIELD_VALUE | michael@0: * PARSE_STATE_CR_CHAR | michael@0: * PARSE_STATE_BEGIN_OF_LINE michael@0: * michael@0: * PARSE_STATE_FIELD_VALUE -> PARSE_STATE_CR_CHAR | michael@0: * PARSE_STATE_BEGIN_OF_LINE michael@0: * michael@0: * PARSE_STATE_BEGIN_OF_LINE -> PARSE_STATE_CR_CHAR | michael@0: * PARSE_STATE_COMMENT | michael@0: * PARSE_STATE_FIELD_NAME | michael@0: * PARSE_STATE_BEGIN_OF_LINE michael@0: * michael@0: * Whenever the parser find an empty line or the end-of-file michael@0: * it dispatches the stacked event. michael@0: * michael@0: */ michael@0: enum ParserStatus { michael@0: PARSE_STATE_OFF, michael@0: PARSE_STATE_BEGIN_OF_STREAM, michael@0: PARSE_STATE_BOM_WAS_READ, michael@0: PARSE_STATE_CR_CHAR, michael@0: PARSE_STATE_COMMENT, michael@0: PARSE_STATE_FIELD_NAME, michael@0: PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE, michael@0: PARSE_STATE_FIELD_VALUE, michael@0: PARSE_STATE_BEGIN_OF_LINE michael@0: }; michael@0: ParserStatus mStatus; michael@0: michael@0: bool mFrozen; michael@0: bool mErrorLoadOnRedirect; michael@0: bool mGoingToDispatchAllMessages; michael@0: bool mWithCredentials; michael@0: bool mWaitingForOnStopRequest; michael@0: bool mInterrupted; michael@0: michael@0: // used while reading the input streams michael@0: nsCOMPtr mUnicodeDecoder; michael@0: nsresult mLastConvertionResult; michael@0: nsString mLastFieldName; michael@0: nsString mLastFieldValue; michael@0: michael@0: nsCOMPtr mLoadGroup; michael@0: michael@0: /** michael@0: * The notification callbacks the channel had initially. michael@0: * We want to forward things here as needed. michael@0: */ michael@0: nsCOMPtr mNotificationCallbacks; michael@0: nsCOMPtr mChannelEventSink; michael@0: michael@0: nsCOMPtr mHttpChannel; michael@0: michael@0: nsCOMPtr mTimer; michael@0: michael@0: uint16_t mReadyState; michael@0: nsString mOriginalURL; michael@0: michael@0: nsCOMPtr mPrincipal; michael@0: nsString mOrigin; michael@0: michael@0: uint32_t mRedirectFlags; michael@0: nsCOMPtr mRedirectCallback; michael@0: nsCOMPtr mNewRedirectChannel; michael@0: michael@0: // Event Source owner information: michael@0: // - the script file name michael@0: // - source code line number where the Event Source object was constructed. michael@0: // - the ID of the inner window where the script lives. Note that this may not michael@0: // be the same as the Event Source owner window. michael@0: // These attributes are used for error reporting. michael@0: nsString mScriptFile; michael@0: uint32_t mScriptLine; michael@0: uint64_t mInnerWindowID; michael@0: michael@0: private: michael@0: EventSource(const EventSource& x); // prevent bad usage michael@0: EventSource& operator=(const EventSource& x); michael@0: }; michael@0: michael@0: } // namespace dom michael@0: } // namespace mozilla michael@0: michael@0: #endif // mozilla_dom_EventSource_h