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: * Base class for the XML and HTML content sinks, which construct a michael@0: * DOM based on information from the parser. michael@0: */ michael@0: michael@0: #ifndef _nsContentSink_h_ michael@0: #define _nsContentSink_h_ michael@0: michael@0: // Base class for contentsink implementations. michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: #include "nsICSSLoaderObserver.h" michael@0: #include "nsWeakReference.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsString.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsITimer.h" michael@0: #include "nsStubDocumentObserver.h" michael@0: #include "nsIContentSink.h" michael@0: #include "prlog.h" michael@0: #include "nsCycleCollectionParticipant.h" michael@0: #include "nsThreadUtils.h" michael@0: michael@0: class nsIDocument; michael@0: class nsIURI; michael@0: class nsIChannel; michael@0: class nsIDocShell; michael@0: class nsIParser; michael@0: class nsIAtom; michael@0: class nsIChannel; michael@0: class nsIContent; michael@0: class nsViewManager; michael@0: class nsNodeInfoManager; michael@0: class nsScriptLoader; michael@0: class nsIApplicationCache; michael@0: michael@0: namespace mozilla { michael@0: namespace css { michael@0: class Loader; michael@0: } michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: michael@0: extern PRLogModuleInfo* gContentSinkLogModuleInfo; michael@0: michael@0: #define SINK_TRACE_CALLS 0x1 michael@0: #define SINK_TRACE_REFLOW 0x2 michael@0: #define SINK_ALWAYS_REFLOW 0x4 michael@0: michael@0: #define SINK_LOG_TEST(_lm, _bit) (int((_lm)->level) & (_bit)) michael@0: michael@0: #define SINK_TRACE(_lm, _bit, _args) \ michael@0: PR_BEGIN_MACRO \ michael@0: if (SINK_LOG_TEST(_lm, _bit)) { \ michael@0: PR_LogPrint _args; \ michael@0: } \ michael@0: PR_END_MACRO michael@0: michael@0: #else michael@0: #define SINK_TRACE(_lm, _bit, _args) michael@0: #endif michael@0: michael@0: #undef SINK_NO_INCREMENTAL michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: // 1/2 second fudge factor for window creation michael@0: #define NS_DELAY_FOR_WINDOW_CREATION 500000 michael@0: michael@0: class nsContentSink : public nsICSSLoaderObserver, michael@0: public nsSupportsWeakReference, michael@0: public nsStubDocumentObserver, michael@0: public nsITimerCallback michael@0: { michael@0: NS_DECL_CYCLE_COLLECTING_ISUPPORTS michael@0: NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsContentSink, michael@0: nsICSSLoaderObserver) michael@0: // nsITimerCallback michael@0: NS_DECL_NSITIMERCALLBACK michael@0: michael@0: // nsICSSLoaderObserver michael@0: NS_IMETHOD StyleSheetLoaded(nsCSSStyleSheet* aSheet, bool aWasAlternate, michael@0: nsresult aStatus) MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult ProcessMETATag(nsIContent* aContent); michael@0: michael@0: // nsIContentSink implementation helpers michael@0: NS_HIDDEN_(nsresult) WillParseImpl(void); michael@0: NS_HIDDEN_(nsresult) WillInterruptImpl(void); michael@0: NS_HIDDEN_(nsresult) WillResumeImpl(void); michael@0: NS_HIDDEN_(nsresult) DidProcessATokenImpl(void); michael@0: NS_HIDDEN_(void) WillBuildModelImpl(void); michael@0: NS_HIDDEN_(void) DidBuildModelImpl(bool aTerminated); michael@0: NS_HIDDEN_(void) DropParserAndPerfHint(void); michael@0: bool IsScriptExecutingImpl(); michael@0: michael@0: void NotifyAppend(nsIContent* aContent, uint32_t aStartIndex); michael@0: michael@0: // nsIDocumentObserver michael@0: NS_DECL_NSIDOCUMENTOBSERVER_BEGINUPDATE michael@0: NS_DECL_NSIDOCUMENTOBSERVER_ENDUPDATE michael@0: michael@0: virtual void UpdateChildCounts() = 0; michael@0: michael@0: bool IsTimeToNotify(); michael@0: bool LinkContextIsOurDocument(const nsSubstring& aAnchor); michael@0: bool Decode5987Format(nsAString& aEncoded); michael@0: michael@0: static void InitializeStatics(); michael@0: michael@0: protected: michael@0: nsContentSink(); michael@0: virtual ~nsContentSink(); michael@0: michael@0: enum CacheSelectionAction { michael@0: // There is no offline cache manifest specified by the document, michael@0: // or the document was loaded from a cache other than the one it michael@0: // specifies via its manifest attribute and IS NOT a top-level michael@0: // document, or an error occurred during the cache selection michael@0: // algorithm. michael@0: CACHE_SELECTION_NONE = 0, michael@0: michael@0: // The offline cache manifest must be updated. michael@0: CACHE_SELECTION_UPDATE = 1, michael@0: michael@0: // The document was loaded from a cache other than the one it michael@0: // specifies via its manifest attribute and IS a top-level michael@0: // document. In this case, the document is marked as foreign in michael@0: // the cache it was loaded from and must be reloaded from the michael@0: // correct cache (the one it specifies). michael@0: CACHE_SELECTION_RELOAD = 2, michael@0: michael@0: // Some conditions require we must reselect the cache without the manifest michael@0: CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST = 3 michael@0: }; michael@0: michael@0: nsresult Init(nsIDocument* aDoc, nsIURI* aURI, michael@0: nsISupports* aContainer, nsIChannel* aChannel); michael@0: michael@0: nsresult ProcessHTTPHeaders(nsIChannel* aChannel); michael@0: nsresult ProcessHeaderData(nsIAtom* aHeader, const nsAString& aValue, michael@0: nsIContent* aContent = nullptr); michael@0: nsresult ProcessLinkHeader(const nsAString& aLinkData); michael@0: nsresult ProcessLink(const nsSubstring& aAnchor, michael@0: const nsSubstring& aHref, const nsSubstring& aRel, michael@0: const nsSubstring& aTitle, const nsSubstring& aType, michael@0: const nsSubstring& aMedia); michael@0: michael@0: virtual nsresult ProcessStyleLink(nsIContent* aElement, michael@0: const nsSubstring& aHref, michael@0: bool aAlternate, michael@0: const nsSubstring& aTitle, michael@0: const nsSubstring& aType, michael@0: const nsSubstring& aMedia); michael@0: michael@0: void PrefetchHref(const nsAString &aHref, nsINode *aSource, michael@0: bool aExplicit); michael@0: michael@0: // aHref can either be the usual URI format or of the form "//www.hostname.com" michael@0: // without a scheme. michael@0: void PrefetchDNS(const nsAString &aHref); michael@0: michael@0: // Gets the cache key (used to identify items in a cache) of the channel. michael@0: nsresult GetChannelCacheKey(nsIChannel* aChannel, nsACString& aCacheKey); michael@0: michael@0: // There is an offline cache manifest attribute specified and the michael@0: // document is allowed to use the offline cache. Process the cache michael@0: // selection algorithm for this document and the manifest. Result is michael@0: // an action that must be taken on the manifest, see michael@0: // CacheSelectionAction enum above. michael@0: // michael@0: // @param aLoadApplicationCache michael@0: // The application cache from which the load originated, if michael@0: // any. michael@0: // @param aManifestURI michael@0: // The manifest URI listed in the document. michael@0: // @param aFetchedWithHTTPGetOrEquiv michael@0: // TRUE if this was fetched using the HTTP GET method. michael@0: // @param aAction michael@0: // Out parameter, returns the action that should be performed michael@0: // by the calling function. michael@0: nsresult SelectDocAppCache(nsIApplicationCache *aLoadApplicationCache, michael@0: nsIURI *aManifestURI, michael@0: bool aFetchedWithHTTPGetOrEquiv, michael@0: CacheSelectionAction *aAction); michael@0: michael@0: // There is no offline cache manifest attribute specified. Process michael@0: // the cache selection algorithm w/o the manifest. Result is an michael@0: // action that must be taken, see CacheSelectionAction enum michael@0: // above. In case the offline cache manifest has to be updated the michael@0: // manifest URI is returned in aManifestURI. michael@0: // michael@0: // @param aLoadApplicationCache michael@0: // The application cache from which the load originated, if michael@0: // any. michael@0: // @param aManifestURI michael@0: // Out parameter, returns the manifest URI of the cache that michael@0: // was selected. michael@0: // @param aAction michael@0: // Out parameter, returns the action that should be performed michael@0: // by the calling function. michael@0: nsresult SelectDocAppCacheNoManifest(nsIApplicationCache *aLoadApplicationCache, michael@0: nsIURI **aManifestURI, michael@0: CacheSelectionAction *aAction); michael@0: michael@0: public: michael@0: // Searches for the offline cache manifest attribute and calls one michael@0: // of the above defined methods to select the document's application michael@0: // cache, let it be associated with the document and eventually michael@0: // schedule the cache update process. michael@0: // This method MUST be called with the empty string as the argument michael@0: // when there is no manifest attribute! michael@0: void ProcessOfflineManifest(const nsAString& aManifestSpec); michael@0: michael@0: // Extracts the manifest attribute from the element if it is the root michael@0: // element and calls the above method. michael@0: void ProcessOfflineManifest(nsIContent *aElement); michael@0: michael@0: protected: michael@0: // Tries to scroll to the URI's named anchor. Once we've successfully michael@0: // done that, further calls to this method will be ignored. michael@0: void ScrollToRef(); michael@0: michael@0: // Start layout. If aIgnorePendingSheets is true, this will happen even if michael@0: // we still have stylesheet loads pending. Otherwise, we'll wait until the michael@0: // stylesheets are all done loading. michael@0: public: michael@0: void StartLayout(bool aIgnorePendingSheets); michael@0: michael@0: static void NotifyDocElementCreated(nsIDocument* aDoc); michael@0: michael@0: protected: michael@0: void michael@0: FavorPerformanceHint(bool perfOverStarvation, uint32_t starvationDelay); michael@0: michael@0: inline int32_t GetNotificationInterval() michael@0: { michael@0: if (mDynamicLowerValue) { michael@0: return 1000; michael@0: } michael@0: michael@0: return sNotificationInterval; michael@0: } michael@0: michael@0: virtual nsresult FlushTags() = 0; michael@0: michael@0: // Later on we might want to make this more involved somehow michael@0: // (e.g. stop waiting after some timeout or whatnot). michael@0: bool WaitForPendingSheets() { return mPendingSheetCount > 0; } michael@0: michael@0: void DoProcessLinkHeader(); michael@0: michael@0: void StopDeflecting() { michael@0: mDeflectedCount = sPerfDeflectCount; michael@0: } michael@0: michael@0: private: michael@0: // People shouldn't be allocating this class directly. All subclasses should michael@0: // be allocated using a zeroing operator new. michael@0: void* operator new(size_t sz) CPP_THROW_NEW; // Not to be implemented michael@0: michael@0: protected: michael@0: michael@0: nsCOMPtr mDocument; michael@0: nsRefPtr mParser; michael@0: nsCOMPtr mDocumentURI; michael@0: nsCOMPtr mDocShell; michael@0: nsRefPtr mCSSLoader; michael@0: nsRefPtr mNodeInfoManager; michael@0: nsRefPtr mScriptLoader; michael@0: michael@0: // back off timer notification after count michael@0: int32_t mBackoffCount; michael@0: michael@0: // Time of last notification michael@0: // Note: mLastNotificationTime is only valid once mLayoutStarted is true. michael@0: PRTime mLastNotificationTime; michael@0: michael@0: // Timer used for notification michael@0: nsCOMPtr mNotificationTimer; michael@0: michael@0: // Have we already called BeginUpdate for this set of content changes? michael@0: uint8_t mBeganUpdate : 1; michael@0: uint8_t mLayoutStarted : 1; michael@0: uint8_t mDynamicLowerValue : 1; michael@0: uint8_t mParsing : 1; michael@0: uint8_t mDroppedTimer : 1; michael@0: // If true, we deferred starting layout until sheets load michael@0: uint8_t mDeferredLayoutStart : 1; michael@0: // If true, we deferred notifications until sheets load michael@0: uint8_t mDeferredFlushTags : 1; michael@0: // If false, we're not ourselves a document observer; that means we michael@0: // shouldn't be performing any more content model notifications, michael@0: // since we're not longer updating our child counts. michael@0: uint8_t mIsDocumentObserver : 1; michael@0: // True if this is parser is a fragment parser or an HTML DOMParser. michael@0: // XML DOMParser leaves this to false for now! michael@0: uint8_t mRunsToCompletion : 1; michael@0: michael@0: // michael@0: // -- Can interrupt parsing members -- michael@0: // michael@0: michael@0: // The number of tokens that have been processed since we measured michael@0: // if it's time to return to the main event loop. michael@0: uint32_t mDeflectedCount; michael@0: michael@0: // Is there currently a pending event? michael@0: bool mHasPendingEvent; michael@0: michael@0: // When to return to the main event loop michael@0: uint32_t mCurrentParseEndTime; michael@0: michael@0: int32_t mBeginLoadTime; michael@0: michael@0: // Last mouse event or keyboard event time sampled by the content michael@0: // sink michael@0: uint32_t mLastSampledUserEventTime; michael@0: michael@0: int32_t mInMonolithicContainer; michael@0: michael@0: int32_t mInNotification; michael@0: uint32_t mUpdatesInNotification; michael@0: michael@0: uint32_t mPendingSheetCount; michael@0: michael@0: nsRevocableEventPtr > michael@0: mProcessLinkHeaderEvent; michael@0: michael@0: // Do we notify based on time? michael@0: static bool sNotifyOnTimer; michael@0: // Back off timer notification after count. michael@0: static int32_t sBackoffCount; michael@0: // Notification interval in microseconds michael@0: static int32_t sNotificationInterval; michael@0: // How many times to deflect in interactive/perf modes michael@0: static int32_t sInteractiveDeflectCount; michael@0: static int32_t sPerfDeflectCount; michael@0: // 0 = don't check for pending events michael@0: // 1 = don't deflect if there are pending events michael@0: // 2 = bail if there are pending events michael@0: static int32_t sPendingEventMode; michael@0: // How often to probe for pending events. 1=every token michael@0: static int32_t sEventProbeRate; michael@0: // How long to stay off the event loop in interactive/perf modes michael@0: static int32_t sInteractiveParseTime; michael@0: static int32_t sPerfParseTime; michael@0: // How long to be in interactive mode after an event michael@0: static int32_t sInteractiveTime; michael@0: // How long to stay in perf mode after initial loading michael@0: static int32_t sInitialPerfTime; michael@0: // Should we switch between perf-mode and interactive-mode michael@0: static int32_t sEnablePerfMode; michael@0: }; michael@0: michael@0: #endif // _nsContentSink_h_