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