1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* 8 * Base class for the XML and HTML content sinks, which construct a 9 * DOM based on information from the parser. 10 */ 11 12 #ifndef _nsContentSink_h_ 13 #define _nsContentSink_h_ 14 15 // Base class for contentsink implementations. 16 17 #include "mozilla/Attributes.h" 18 #include "nsICSSLoaderObserver.h" 19 #include "nsWeakReference.h" 20 #include "nsCOMPtr.h" 21 #include "nsString.h" 22 #include "nsAutoPtr.h" 23 #include "nsGkAtoms.h" 24 #include "nsITimer.h" 25 #include "nsStubDocumentObserver.h" 26 #include "nsIContentSink.h" 27 #include "mozilla/Logging.h" 28 #include "nsCycleCollectionParticipant.h" 29 #include "nsThreadUtils.h" 30 31 class nsIDocument; 32 class nsIURI; 33 class nsIChannel; 34 class nsIDocShell; 35 class nsIAtom; 36 class nsIChannel; 37 class nsIContent; 38 class nsNodeInfoManager; 39 class nsScriptLoader; 40 class nsIApplicationCache; 41 42 namespace mozilla { 43 namespace css { 44 class Loader; 45 } // namespace css 46 } // namespace mozilla 47 48 #ifdef DEBUG 49 50 extern mozilla::LazyLogModule gContentSinkLogModuleInfo; 51 52 #define SINK_TRACE_CALLS 0x1 53 #define SINK_TRACE_REFLOW 0x2 54 #define SINK_ALWAYS_REFLOW 0x4 55 56 #define SINK_LOG_TEST(_lm, _bit) (int((_lm)->Level()) & (_bit)) 57 58 #define SINK_TRACE(_lm, _bit, _args) \ 59 PR_BEGIN_MACRO \ 60 if (SINK_LOG_TEST(_lm, _bit)) { \ 61 PR_LogPrint _args; \ 62 } \ 63 PR_END_MACRO 64 65 #else 66 #define SINK_TRACE(_lm, _bit, _args) 67 #endif 68 69 #undef SINK_NO_INCREMENTAL 70 71 //---------------------------------------------------------------------- 72 73 // 1/2 second fudge factor for window creation 74 #define NS_DELAY_FOR_WINDOW_CREATION 500000 75 76 class nsContentSink : public nsICSSLoaderObserver, 77 public nsSupportsWeakReference, 78 public nsStubDocumentObserver, 79 public nsITimerCallback 80 { 81 NS_DECL_CYCLE_COLLECTING_ISUPPORTS 82 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsContentSink, 83 nsICSSLoaderObserver) 84 // nsITimerCallback 85 NS_DECL_NSITIMERCALLBACK 86 87 // nsICSSLoaderObserver 88 NS_IMETHOD StyleSheetLoaded(mozilla::StyleSheet* aSheet, 89 bool aWasAlternate, 90 nsresult aStatus) override; 91 92 virtual nsresult ProcessMETATag(nsIContent* aContent); 93 94 // nsIContentSink implementation helpers 95 nsresult WillParseImpl(void); 96 nsresult WillInterruptImpl(void); 97 nsresult WillResumeImpl(void); 98 nsresult DidProcessATokenImpl(void); 99 void WillBuildModelImpl(void); 100 void DidBuildModelImpl(bool aTerminated); 101 void DropParserAndPerfHint(void); 102 bool IsScriptExecutingImpl(); 103 104 void NotifyAppend(nsIContent* aContent, uint32_t aStartIndex); 105 106 // nsIDocumentObserver 107 NS_DECL_NSIDOCUMENTOBSERVER_BEGINUPDATE 108 NS_DECL_NSIDOCUMENTOBSERVER_ENDUPDATE 109 110 virtual void UpdateChildCounts() = 0; 111 112 bool IsTimeToNotify(); 113 bool LinkContextIsOurDocument(const nsSubstring& aAnchor); 114 bool Decode5987Format(nsAString& aEncoded); 115 116 static void InitializeStatics(); 117 118 protected: 119 nsContentSink(); 120 virtual ~nsContentSink(); 121 122 enum CacheSelectionAction { 123 // There is no offline cache manifest specified by the document, 124 // or the document was loaded from a cache other than the one it 125 // specifies via its manifest attribute and IS NOT a top-level 126 // document, or an error occurred during the cache selection 127 // algorithm. 128 CACHE_SELECTION_NONE = 0, 129 130 // The offline cache manifest must be updated. 131 CACHE_SELECTION_UPDATE = 1, 132 133 // The document was loaded from a cache other than the one it 134 // specifies via its manifest attribute and IS a top-level 135 // document. In this case, the document is marked as foreign in 136 // the cache it was loaded from and must be reloaded from the 137 // correct cache (the one it specifies). 138 CACHE_SELECTION_RELOAD = 2, 139 140 // Some conditions require we must reselect the cache without the manifest 141 CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST = 3 142 }; 143 144 nsresult Init(nsIDocument* aDoc, nsIURI* aURI, 145 nsISupports* aContainer, nsIChannel* aChannel); 146 147 nsresult ProcessHTTPHeaders(nsIChannel* aChannel); 148 nsresult ProcessHeaderData(nsIAtom* aHeader, const nsAString& aValue, 149 nsIContent* aContent = nullptr); 150 nsresult ProcessLinkHeader(const nsAString& aLinkData); 151 nsresult ProcessLink(const nsSubstring& aAnchor, 152 const nsSubstring& aHref, const nsSubstring& aRel, 153 const nsSubstring& aTitle, const nsSubstring& aType, 154 const nsSubstring& aMedia, const nsSubstring& aCrossOrigin); 155 156 virtual nsresult ProcessStyleLink(nsIContent* aElement, 157 const nsSubstring& aHref, 158 bool aAlternate, 159 const nsSubstring& aTitle, 160 const nsSubstring& aType, 161 const nsSubstring& aMedia); 162 163 void PrefetchHref(const nsAString &aHref, nsINode *aSource, 164 bool aExplicit); 165 166 // For PrefetchDNS() aHref can either be the usual 167 // URI format or of the form "//www.hostname.com" without a scheme. 168 void PrefetchDNS(const nsAString &aHref); 169 170 // Gets the cache key (used to identify items in a cache) of the channel. 171 nsresult GetChannelCacheKey(nsIChannel* aChannel, nsACString& aCacheKey); 172 173 // There is an offline cache manifest attribute specified and the 174 // document is allowed to use the offline cache. Process the cache 175 // selection algorithm for this document and the manifest. Result is 176 // an action that must be taken on the manifest, see 177 // CacheSelectionAction enum above. 178 // 179 // @param aLoadApplicationCache 180 // The application cache from which the load originated, if 181 // any. 182 // @param aManifestURI 183 // The manifest URI listed in the document. 184 // @param aFetchedWithHTTPGetOrEquiv 185 // TRUE if this was fetched using the HTTP GET method. 186 // @param aAction 187 // Out parameter, returns the action that should be performed 188 // by the calling function. 189 nsresult SelectDocAppCache(nsIApplicationCache *aLoadApplicationCache, 190 nsIURI *aManifestURI, 191 bool aFetchedWithHTTPGetOrEquiv, 192 CacheSelectionAction *aAction); 193 194 // There is no offline cache manifest attribute specified. Process 195 // the cache selection algorithm w/o the manifest. Result is an 196 // action that must be taken, see CacheSelectionAction enum 197 // above. In case the offline cache manifest has to be updated the 198 // manifest URI is returned in aManifestURI. 199 // 200 // @param aLoadApplicationCache 201 // The application cache from which the load originated, if 202 // any. 203 // @param aManifestURI 204 // Out parameter, returns the manifest URI of the cache that 205 // was selected. 206 // @param aAction 207 // Out parameter, returns the action that should be performed 208 // by the calling function. 209 nsresult SelectDocAppCacheNoManifest(nsIApplicationCache *aLoadApplicationCache, 210 nsIURI **aManifestURI, 211 CacheSelectionAction *aAction); 212 213 public: 214 // Searches for the offline cache manifest attribute and calls one 215 // of the above defined methods to select the document's application 216 // cache, let it be associated with the document and eventually 217 // schedule the cache update process. 218 // This method MUST be called with the empty string as the argument 219 // when there is no manifest attribute! 220 void ProcessOfflineManifest(const nsAString& aManifestSpec); 221 222 // Extracts the manifest attribute from the element if it is the root 223 // element and calls the above method. 224 void ProcessOfflineManifest(nsIContent *aElement); 225 226 // For Preconnect() aHref can either be the usual 227 // URI format or of the form "//www.hostname.com" without a scheme. 228 void Preconnect(const nsAString& aHref, const nsAString& aCrossOrigin); 229 230 protected: 231 // Tries to scroll to the URI's named anchor. Once we've successfully 232 // done that, further calls to this method will be ignored. 233 void ScrollToRef(); 234 235 // Start layout. If aIgnorePendingSheets is true, this will happen even if 236 // we still have stylesheet loads pending. Otherwise, we'll wait until the 237 // stylesheets are all done loading. 238 public: 239 void StartLayout(bool aIgnorePendingSheets); 240 241 static void NotifyDocElementCreated(nsIDocument* aDoc); 242 243 protected: 244 void 245 FavorPerformanceHint(bool perfOverStarvation, uint32_t starvationDelay); 246 GetNotificationInterval()247 inline int32_t GetNotificationInterval() 248 { 249 if (mDynamicLowerValue) { 250 return 1000; 251 } 252 253 return sNotificationInterval; 254 } 255 256 virtual nsresult FlushTags() = 0; 257 258 // Later on we might want to make this more involved somehow 259 // (e.g. stop waiting after some timeout or whatnot). WaitForPendingSheets()260 bool WaitForPendingSheets() { return mPendingSheetCount > 0; } 261 262 void DoProcessLinkHeader(); 263 StopDeflecting()264 void StopDeflecting() { 265 mDeflectedCount = sPerfDeflectCount; 266 } 267 268 protected: 269 270 nsCOMPtr<nsIDocument> mDocument; 271 RefPtr<nsParserBase> mParser; 272 nsCOMPtr<nsIURI> mDocumentURI; 273 nsCOMPtr<nsIDocShell> mDocShell; 274 RefPtr<mozilla::css::Loader> mCSSLoader; 275 RefPtr<nsNodeInfoManager> mNodeInfoManager; 276 RefPtr<nsScriptLoader> mScriptLoader; 277 278 // back off timer notification after count 279 int32_t mBackoffCount; 280 281 // Time of last notification 282 // Note: mLastNotificationTime is only valid once mLayoutStarted is true. 283 PRTime mLastNotificationTime; 284 285 // Timer used for notification 286 nsCOMPtr<nsITimer> mNotificationTimer; 287 288 // Have we already called BeginUpdate for this set of content changes? 289 uint8_t mBeganUpdate : 1; 290 uint8_t mLayoutStarted : 1; 291 uint8_t mDynamicLowerValue : 1; 292 uint8_t mParsing : 1; 293 uint8_t mDroppedTimer : 1; 294 // If true, we deferred starting layout until sheets load 295 uint8_t mDeferredLayoutStart : 1; 296 // If true, we deferred notifications until sheets load 297 uint8_t mDeferredFlushTags : 1; 298 // If false, we're not ourselves a document observer; that means we 299 // shouldn't be performing any more content model notifications, 300 // since we're not longer updating our child counts. 301 uint8_t mIsDocumentObserver : 1; 302 // True if this is parser is a fragment parser or an HTML DOMParser. 303 // XML DOMParser leaves this to false for now! 304 uint8_t mRunsToCompletion : 1; 305 306 // 307 // -- Can interrupt parsing members -- 308 // 309 310 // The number of tokens that have been processed since we measured 311 // if it's time to return to the main event loop. 312 uint32_t mDeflectedCount; 313 314 // Is there currently a pending event? 315 bool mHasPendingEvent; 316 317 // When to return to the main event loop 318 uint32_t mCurrentParseEndTime; 319 320 int32_t mBeginLoadTime; 321 322 // Last mouse event or keyboard event time sampled by the content 323 // sink 324 uint32_t mLastSampledUserEventTime; 325 326 int32_t mInMonolithicContainer; 327 328 int32_t mInNotification; 329 uint32_t mUpdatesInNotification; 330 331 uint32_t mPendingSheetCount; 332 333 nsRevocableEventPtr<nsRunnableMethod<nsContentSink, void, false> > 334 mProcessLinkHeaderEvent; 335 336 // Do we notify based on time? 337 static bool sNotifyOnTimer; 338 // Back off timer notification after count. 339 static int32_t sBackoffCount; 340 // Notification interval in microseconds 341 static int32_t sNotificationInterval; 342 // How many times to deflect in interactive/perf modes 343 static int32_t sInteractiveDeflectCount; 344 static int32_t sPerfDeflectCount; 345 // 0 = don't check for pending events 346 // 1 = don't deflect if there are pending events 347 // 2 = bail if there are pending events 348 static int32_t sPendingEventMode; 349 // How often to probe for pending events. 1=every token 350 static int32_t sEventProbeRate; 351 // How long to stay off the event loop in interactive/perf modes 352 static int32_t sInteractiveParseTime; 353 static int32_t sPerfParseTime; 354 // How long to be in interactive mode after an event 355 static int32_t sInteractiveTime; 356 // How long to stay in perf mode after initial loading 357 static int32_t sInitialPerfTime; 358 // Should we switch between perf-mode and interactive-mode 359 static int32_t sEnablePerfMode; 360 }; 361 362 #endif // _nsContentSink_h_ 363