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 
39 namespace mozilla {
40 namespace css {
41 class Loader;
42 }  // namespace css
43 
44 namespace dom {
45 class Document;
46 class ScriptLoader;
47 }  // namespace dom
48 }  // namespace mozilla
49 
50 #ifdef DEBUG
51 
52 extern mozilla::LazyLogModule gContentSinkLogModuleInfo;
53 
54 #  define SINK_TRACE_CALLS 0x1
55 #  define SINK_TRACE_REFLOW 0x2
56 #  define SINK_ALWAYS_REFLOW 0x4
57 
58 #  define SINK_LOG_TEST(_lm, _bit) (int((_lm)->Level()) & (_bit))
59 
60 #  define SINK_TRACE(_lm, _bit, _args) \
61     do {                               \
62       if (SINK_LOG_TEST(_lm, _bit)) {  \
63         printf_stderr _args;           \
64       }                                \
65     } while (0)
66 
67 #else
68 #  define SINK_TRACE(_lm, _bit, _args)
69 #endif
70 
71 #undef SINK_NO_INCREMENTAL
72 
73 //----------------------------------------------------------------------
74 
75 class nsContentSink : public nsICSSLoaderObserver,
76                       public nsSupportsWeakReference,
77                       public nsStubDocumentObserver,
78                       public nsITimerCallback,
79                       public nsINamed {
80  protected:
81   typedef mozilla::dom::Document Document;
82 
83  private:
84   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
85   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsContentSink, nsICSSLoaderObserver)
86   // nsITimerCallback
87   NS_DECL_NSITIMERCALLBACK
88 
89   NS_DECL_NSINAMED
90 
91   // nsICSSLoaderObserver
92   NS_IMETHOD StyleSheetLoaded(mozilla::StyleSheet* aSheet, bool aWasDeferred,
93                               nsresult aStatus) override;
94 
95   // nsIContentSink implementation helpers
96   nsresult WillParseImpl(void);
97   nsresult WillInterruptImpl(void);
98   nsresult WillResumeImpl(void);
99   nsresult DidProcessATokenImpl(void);
100   void WillBuildModelImpl(void);
101   void DidBuildModelImpl(bool aTerminated);
102   void DropParserAndPerfHint(void);
103   bool IsScriptExecutingImpl();
104 
105   void NotifyAppend(nsIContent* aContent, uint32_t aStartIndex);
106 
107   // nsIDocumentObserver
108   NS_DECL_NSIDOCUMENTOBSERVER_BEGINUPDATE
109   NS_DECL_NSIDOCUMENTOBSERVER_ENDUPDATE
110 
111   virtual void UpdateChildCounts() = 0;
112 
113   bool IsTimeToNotify();
114   bool LinkContextIsOurDocument(const nsAString& aAnchor);
115   bool Decode5987Format(nsAString& aEncoded);
116 
117  protected:
118   nsContentSink();
119   virtual ~nsContentSink();
120 
121   nsresult Init(Document* aDoc, nsIURI* aURI, nsISupports* aContainer,
122                 nsIChannel* aChannel);
123 
124   nsresult ProcessHTTPHeaders(nsIChannel* aChannel);
125   nsresult ProcessLinkHeader(const nsAString& aLinkData);
126   nsresult ProcessLinkFromHeader(
127       const nsAString& aAnchor, const nsAString& aHref, const nsAString& aRel,
128       const nsAString& aTitle, const nsAString& aIntegrity,
129       const nsAString& aSrcset, const nsAString& aSizes, const nsAString& aType,
130       const nsAString& aMedia, const nsAString& aCrossOrigin,
131       const nsAString& aReferrerPolicy, const nsAString& aAs);
132 
133   virtual nsresult ProcessStyleLinkFromHeader(
134       const nsAString& aHref, bool aAlternate, const nsAString& aTitle,
135       const nsAString& aIntegrity, const nsAString& aType,
136       const nsAString& aMedia, const nsAString& aReferrerPolicy);
137 
138   void PrefetchHref(const nsAString& aHref, const nsAString& aAs,
139                     const nsAString& aType, const nsAString& aMedia);
140   void PreloadHref(const nsAString& aHref, const nsAString& aAs,
141                    const nsAString& aType, const nsAString& aMedia,
142                    const nsAString& aIntegrity, const nsAString& aSrcset,
143                    const nsAString& aSizes, const nsAString& aCORS,
144                    const nsAString& aReferrerPolicy);
145 
146   // For PrefetchDNS() aHref can either be the usual
147   // URI format or of the form "//www.hostname.com" without a scheme.
148   void PrefetchDNS(const nsAString& aHref);
149 
150   // Gets the cache key (used to identify items in a cache) of the channel.
151   nsresult GetChannelCacheKey(nsIChannel* aChannel, nsACString& aCacheKey);
152 
153  public:
154   // For Preconnect() aHref can either be the usual
155   // URI format or of the form "//www.hostname.com" without a scheme.
156   void Preconnect(const nsAString& aHref, const nsAString& aCrossOrigin);
157 
158  protected:
159   // Tries to scroll to the URI's named anchor. Once we've successfully
160   // done that, further calls to this method will be ignored.
161   MOZ_CAN_RUN_SCRIPT_BOUNDARY void ScrollToRef();
162 
163   // Start layout.  If aIgnorePendingSheets is true, this will happen even if
164   // we still have stylesheet loads pending.  Otherwise, we'll wait until the
165   // stylesheets are all done loading.
166  public:
167   void StartLayout(bool aIgnorePendingSheets);
168 
169   static void NotifyDocElementCreated(Document* aDoc);
170 
GetDocument()171   Document* GetDocument() { return mDocument; }
172 
173  protected:
174   void FavorPerformanceHint(bool perfOverStarvation, uint32_t starvationDelay);
175 
GetNotificationInterval()176   inline int32_t GetNotificationInterval() {
177     if (mDynamicLowerValue) {
178       return 1000;
179     }
180 
181     return mozilla::StaticPrefs::content_notify_interval();
182   }
183 
184   virtual nsresult FlushTags() = 0;
185 
186   // Later on we might want to make this more involved somehow
187   // (e.g. stop waiting after some timeout or whatnot).
WaitForPendingSheets()188   bool WaitForPendingSheets() { return mPendingSheetCount > 0; }
189 
190   void DoProcessLinkHeader();
191 
StopDeflecting()192   void StopDeflecting() {
193     mDeflectedCount = mozilla::StaticPrefs::content_sink_perf_deflect_count();
194   }
195 
196  protected:
197   RefPtr<Document> mDocument;
198   RefPtr<nsParserBase> mParser;
199   nsCOMPtr<nsIURI> mDocumentURI;
200   nsCOMPtr<nsIDocShell> mDocShell;
201   RefPtr<mozilla::css::Loader> mCSSLoader;
202   RefPtr<nsNodeInfoManager> mNodeInfoManager;
203   RefPtr<mozilla::dom::ScriptLoader> mScriptLoader;
204 
205   // back off timer notification after count
206   int32_t mBackoffCount;
207 
208   // Time of last notification
209   // Note: mLastNotificationTime is only valid once mLayoutStarted is true.
210   PRTime mLastNotificationTime;
211 
212   // Timer used for notification
213   nsCOMPtr<nsITimer> mNotificationTimer;
214 
215   uint8_t mLayoutStarted : 1;
216   uint8_t mDynamicLowerValue : 1;
217   uint8_t mParsing : 1;
218   uint8_t mDroppedTimer : 1;
219   // If true, we deferred starting layout until sheets load
220   uint8_t mDeferredLayoutStart : 1;
221   // If true, we deferred notifications until sheets load
222   uint8_t mDeferredFlushTags : 1;
223   // If false, we're not ourselves a document observer; that means we
224   // shouldn't be performing any more content model notifications,
225   // since we're not longer updating our child counts.
226   uint8_t mIsDocumentObserver : 1;
227   // True if this is parser is a fragment parser or an HTML DOMParser.
228   // XML DOMParser leaves this to false for now!
229   uint8_t mRunsToCompletion : 1;
230   // True if we are blocking load event.
231   bool mIsBlockingOnload : 1;
232 
233   //
234   // -- Can interrupt parsing members --
235   //
236 
237   // The number of tokens that have been processed since we measured
238   // if it's time to return to the main event loop.
239   uint32_t mDeflectedCount;
240 
241   // Is there currently a pending event?
242   bool mHasPendingEvent;
243 
244   // When to return to the main event loop
245   uint32_t mCurrentParseEndTime;
246 
247   int32_t mBeginLoadTime;
248 
249   // Last mouse event or keyboard event time sampled by the content
250   // sink
251   uint32_t mLastSampledUserEventTime;
252 
253   int32_t mInMonolithicContainer;
254 
255   int32_t mInNotification;
256   uint32_t mUpdatesInNotification;
257 
258   uint32_t mPendingSheetCount;
259 
260   nsRevocableEventPtr<nsRunnableMethod<nsContentSink, void, false> >
261       mProcessLinkHeaderEvent;
262 };
263 
264 #endif  // _nsContentSink_h_
265