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 "nsNetUtil.h"
20 #include "nsWeakReference.h"
21 #include "nsCOMPtr.h"
22 #include "nsString.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 #include "mozilla/StaticPrefs_content.h"
31 
32 class nsIURI;
33 class nsIChannel;
34 class nsIDocShell;
35 class nsAtom;
36 class nsIChannel;
37 class nsIContent;
38 class nsNodeInfoManager;
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   using Document = mozilla::dom::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   // nsIContentSink implementation helpers
97   nsresult WillParseImpl(void);
98   nsresult WillInterruptImpl(void);
99   void WillResumeImpl();
100   nsresult DidProcessATokenImpl(void);
101   void WillBuildModelImpl(void);
102   void DidBuildModelImpl(bool aTerminated);
103   void DropParserAndPerfHint(void);
104   bool IsScriptExecutingImpl();
105 
106   void NotifyAppend(nsIContent* aContent, uint32_t aStartIndex);
107 
108   // nsIDocumentObserver
109   NS_DECL_NSIDOCUMENTOBSERVER_BEGINUPDATE
110   NS_DECL_NSIDOCUMENTOBSERVER_ENDUPDATE
111 
112   virtual void UpdateChildCounts() = 0;
113 
114   bool IsTimeToNotify();
115   bool LinkContextIsOurDocument(const nsAString& aAnchor);
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 ProcessLinkFromHeader(const LinkHeader& aHeader);
126 
127   virtual nsresult ProcessStyleLinkFromHeader(
128       const nsAString& aHref, bool aAlternate, const nsAString& aTitle,
129       const nsAString& aIntegrity, const nsAString& aType,
130       const nsAString& aMedia, const nsAString& aReferrerPolicy);
131 
132   void PrefetchHref(const nsAString& aHref, const nsAString& aAs,
133                     const nsAString& aType, const nsAString& aMedia);
134   void PreloadHref(const nsAString& aHref, const nsAString& aAs,
135                    const nsAString& aType, const nsAString& aMedia,
136                    const nsAString& aIntegrity, const nsAString& aSrcset,
137                    const nsAString& aSizes, const nsAString& aCORS,
138                    const nsAString& aReferrerPolicy);
139 
140   // For PrefetchDNS() aHref can either be the usual
141   // URI format or of the form "//www.hostname.com" without a scheme.
142   void PrefetchDNS(const nsAString& aHref);
143 
144   // Gets the cache key (used to identify items in a cache) of the channel.
145   nsresult GetChannelCacheKey(nsIChannel* aChannel, nsACString& aCacheKey);
146 
147  public:
148   // For Preconnect() aHref can either be the usual
149   // URI format or of the form "//www.hostname.com" without a scheme.
150   void Preconnect(const nsAString& aHref, const nsAString& aCrossOrigin);
151 
152  protected:
153   // Tries to scroll to the URI's named anchor. Once we've successfully
154   // done that, further calls to this method will be ignored.
155   MOZ_CAN_RUN_SCRIPT_BOUNDARY void ScrollToRef();
156 
157   // Start layout.  If aIgnorePendingSheets is true, this will happen even if
158   // we still have stylesheet loads pending.  Otherwise, we'll wait until the
159   // stylesheets are all done loading.
160  public:
161   void StartLayout(bool aIgnorePendingSheets);
162 
163   static void NotifyDocElementCreated(Document* aDoc);
164 
GetDocument()165   Document* GetDocument() { return mDocument; }
166 
167  protected:
168   void FavorPerformanceHint(bool perfOverStarvation, uint32_t starvationDelay);
169 
GetNotificationInterval()170   inline int32_t GetNotificationInterval() {
171     if (mDynamicLowerValue) {
172       return 1000;
173     }
174 
175     return mozilla::StaticPrefs::content_notify_interval();
176   }
177 
178   virtual nsresult FlushTags() = 0;
179 
180   // Later on we might want to make this more involved somehow
181   // (e.g. stop waiting after some timeout or whatnot).
WaitForPendingSheets()182   bool WaitForPendingSheets() { return mPendingSheetCount > 0; }
183 
184   void DoProcessLinkHeader();
185 
StopDeflecting()186   void StopDeflecting() {
187     mDeflectedCount = mozilla::StaticPrefs::content_sink_perf_deflect_count();
188   }
189 
190  protected:
191   RefPtr<Document> mDocument;
192   RefPtr<nsParserBase> mParser;
193   nsCOMPtr<nsIURI> mDocumentURI;
194   nsCOMPtr<nsIDocShell> mDocShell;
195   RefPtr<mozilla::css::Loader> mCSSLoader;
196   RefPtr<nsNodeInfoManager> mNodeInfoManager;
197   RefPtr<mozilla::dom::ScriptLoader> mScriptLoader;
198 
199   // back off timer notification after count
200   int32_t mBackoffCount;
201 
202   // Time of last notification
203   // Note: mLastNotificationTime is only valid once mLayoutStarted is true.
204   PRTime mLastNotificationTime;
205 
206   // Timer used for notification
207   nsCOMPtr<nsITimer> mNotificationTimer;
208 
209   uint8_t mLayoutStarted : 1;
210   uint8_t mDynamicLowerValue : 1;
211   uint8_t mParsing : 1;
212   uint8_t mDroppedTimer : 1;
213   // If true, we deferred starting layout until sheets load
214   uint8_t mDeferredLayoutStart : 1;
215   // If true, we deferred notifications until sheets load
216   uint8_t mDeferredFlushTags : 1;
217   // If false, we're not ourselves a document observer; that means we
218   // shouldn't be performing any more content model notifications,
219   // since we're not longer updating our child counts.
220   uint8_t mIsDocumentObserver : 1;
221   // True if this is parser is a fragment parser or an HTML DOMParser.
222   // XML DOMParser leaves this to false for now!
223   uint8_t mRunsToCompletion : 1;
224   // True if we are blocking load event.
225   bool mIsBlockingOnload : 1;
226 
227   //
228   // -- Can interrupt parsing members --
229   //
230 
231   // The number of tokens that have been processed since we measured
232   // if it's time to return to the main event loop.
233   uint32_t mDeflectedCount;
234 
235   // Is there currently a pending event?
236   bool mHasPendingEvent;
237 
238   // When to return to the main event loop
239   uint32_t mCurrentParseEndTime;
240 
241   int32_t mBeginLoadTime;
242 
243   // Last mouse event or keyboard event time sampled by the content
244   // sink
245   uint32_t mLastSampledUserEventTime;
246 
247   int32_t mInMonolithicContainer;
248 
249   int32_t mInNotification;
250   uint32_t mUpdatesInNotification;
251 
252   uint32_t mPendingSheetCount;
253 
254   nsRevocableEventPtr<nsRunnableMethod<nsContentSink, void, false> >
255       mProcessLinkHeaderEvent;
256 };
257 
258 #endif  // _nsContentSink_h_
259