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