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