summaryrefslogtreecommitdiffstats
path: root/dom/base/nsContentSink.h
blob: 39b14217135ac4ffa9e49f1a054fb722f4afdc93 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/*
 * Base class for the XML and HTML content sinks, which construct a
 * DOM based on information from the parser.
 */

#ifndef _nsContentSink_h_
#define _nsContentSink_h_

// Base class for contentsink implementations.

#include "mozilla/Attributes.h"
#include "nsICSSLoaderObserver.h"
#include "nsWeakReference.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "nsGkAtoms.h"
#include "nsITimer.h"
#include "nsStubDocumentObserver.h"
#include "nsIContentSink.h"
#include "mozilla/Logging.h"
#include "nsCycleCollectionParticipant.h"
#include "nsThreadUtils.h"
#include "mozilla/StaticPrefs_content.h"

class nsIURI;
class nsIChannel;
class nsIDocShell;
class nsAtom;
class nsIChannel;
class nsIContent;
class nsNodeInfoManager;

namespace mozilla {
namespace css {
class Loader;
}  // namespace css

namespace dom {
class Document;
class ScriptLoader;
}  // namespace dom

namespace net {
struct LinkHeader;
};
}  // namespace mozilla

#ifdef DEBUG

extern mozilla::LazyLogModule gContentSinkLogModuleInfo;

#  define SINK_TRACE_CALLS 0x1
#  define SINK_TRACE_REFLOW 0x2
#  define SINK_ALWAYS_REFLOW 0x4

#  define SINK_LOG_TEST(_lm, _bit) (int((_lm)->Level()) & (_bit))

#  define SINK_TRACE(_lm, _bit, _args) \
    do {                               \
      if (SINK_LOG_TEST(_lm, _bit)) {  \
        printf_stderr _args;           \
      }                                \
    } while (0)

#else
#  define SINK_TRACE(_lm, _bit, _args)
#endif

#undef SINK_NO_INCREMENTAL

//----------------------------------------------------------------------

class nsContentSink : public nsICSSLoaderObserver,
                      public nsSupportsWeakReference,
                      public nsStubDocumentObserver,
                      public nsITimerCallback,
                      public nsINamed {
 protected:
  using Document = mozilla::dom::Document;

 private:
  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsContentSink, nsICSSLoaderObserver)
  // nsITimerCallback
  NS_DECL_NSITIMERCALLBACK

  NS_DECL_NSINAMED

  // nsICSSLoaderObserver
  NS_IMETHOD StyleSheetLoaded(mozilla::StyleSheet* aSheet, bool aWasDeferred,
                              nsresult aStatus) override;

  // nsIContentSink implementation helpers
  nsresult WillParseImpl(void);
  nsresult WillInterruptImpl(void);
  void WillResumeImpl();
  nsresult DidProcessATokenImpl(void);
  void WillBuildModelImpl(void);
  void DidBuildModelImpl(bool aTerminated);
  void DropParserAndPerfHint(void);
  bool IsScriptExecutingImpl();

  void NotifyAppend(nsIContent* aContent, uint32_t aStartIndex);

  // nsIDocumentObserver
  NS_DECL_NSIDOCUMENTOBSERVER_BEGINUPDATE
  NS_DECL_NSIDOCUMENTOBSERVER_ENDUPDATE

  virtual void UpdateChildCounts() = 0;

  bool IsTimeToNotify();

 protected:
  nsContentSink();
  virtual ~nsContentSink();

  nsresult Init(Document* aDoc, nsIURI* aURI, nsISupports* aContainer,
                nsIChannel* aChannel);

  nsresult ProcessHTTPHeaders(nsIChannel* aChannel);
  // aEarlyHintPreloaderId zero means no early hint channel to connect back
  nsresult ProcessLinkFromHeader(const mozilla::net::LinkHeader& aHeader,
                                 uint64_t aEarlyHintPreloaderId);

  virtual nsresult ProcessStyleLinkFromHeader(
      const nsAString& aHref, bool aAlternate, const nsAString& aTitle,
      const nsAString& aIntegrity, const nsAString& aType,
      const nsAString& aMedia, const nsAString& aReferrerPolicy);

  void PrefetchHref(const nsAString& aHref, const nsAString& aAs,
                    const nsAString& aType, const nsAString& aMedia);
  void PreloadHref(const nsAString& aHref, const nsAString& aAs,
                   const nsAString& aType, const nsAString& aMedia,
                   const nsAString& aIntegrity, const nsAString& aSrcset,
                   const nsAString& aSizes, const nsAString& aCORS,
                   const nsAString& aReferrerPolicy,
                   uint64_t aEarlyHintPreloaderId);

  // For PrefetchDNS() aHref can either be the usual
  // URI format or of the form "//www.hostname.com" without a scheme.
  void PrefetchDNS(const nsAString& aHref);

  // Gets the cache key (used to identify items in a cache) of the channel.
  nsresult GetChannelCacheKey(nsIChannel* aChannel, nsACString& aCacheKey);

 public:
  // For Preconnect() aHref can either be the usual
  // URI format or of the form "//www.hostname.com" without a scheme.
  void Preconnect(const nsAString& aHref, const nsAString& aCrossOrigin);

 protected:
  // Tries to scroll to the URI's named anchor. Once we've successfully
  // done that, further calls to this method will be ignored.
  MOZ_CAN_RUN_SCRIPT_BOUNDARY void ScrollToRef();

  // Start layout.  If aIgnorePendingSheets is true, this will happen even if
  // we still have stylesheet loads pending.  Otherwise, we'll wait until the
  // stylesheets are all done loading.
 public:
  void StartLayout(bool aIgnorePendingSheets);

  static void NotifyDocElementCreated(Document* aDoc);

  Document* GetDocument() { return mDocument; }

  // Later on we might want to make this more involved somehow
  // (e.g. stop waiting after some timeout or whatnot).
  bool WaitForPendingSheets() { return mPendingSheetCount > 0; }

 protected:
  inline int32_t GetNotificationInterval() {
    if (mDynamicLowerValue) {
      return 1000;
    }

    return mozilla::StaticPrefs::content_notify_interval();
  }

  virtual nsresult FlushTags() = 0;

  void DoProcessLinkHeader();

  void StopDeflecting() {
    mDeflectedCount = mozilla::StaticPrefs::content_sink_perf_deflect_count();
  }

 protected:
  RefPtr<Document> mDocument;
  RefPtr<nsParserBase> mParser;
  nsCOMPtr<nsIURI> mDocumentURI;
  nsCOMPtr<nsIDocShell> mDocShell;
  RefPtr<mozilla::css::Loader> mCSSLoader;
  RefPtr<nsNodeInfoManager> mNodeInfoManager;
  RefPtr<mozilla::dom::ScriptLoader> mScriptLoader;

  // back off timer notification after count
  int32_t mBackoffCount;

  // Time of last notification
  // Note: mLastNotificationTime is only valid once mLayoutStarted is true.
  PRTime mLastNotificationTime;

  // Timer used for notification
  nsCOMPtr<nsITimer> mNotificationTimer;

  uint8_t mLayoutStarted : 1;
  uint8_t mDynamicLowerValue : 1;
  uint8_t mParsing : 1;
  uint8_t mDroppedTimer : 1;
  // If true, we deferred starting layout until sheets load
  uint8_t mDeferredLayoutStart : 1;
  // If true, we deferred notifications until sheets load
  uint8_t mDeferredFlushTags : 1;
  // If false, we're not ourselves a document observer; that means we
  // shouldn't be performing any more content model notifications,
  // since we're not longer updating our child counts.
  uint8_t mIsDocumentObserver : 1;
  // True if this is parser is a fragment parser or an HTML DOMParser.
  // XML DOMParser leaves this to false for now!
  uint8_t mRunsToCompletion : 1;
  // True if we are blocking load event.
  bool mIsBlockingOnload : 1;

  //
  // -- Can interrupt parsing members --
  //

  // The number of tokens that have been processed since we measured
  // if it's time to return to the main event loop.
  uint32_t mDeflectedCount;

  // Is there currently a pending event?
  bool mHasPendingEvent;

  // When to return to the main event loop
  uint32_t mCurrentParseEndTime;

  int32_t mBeginLoadTime;

  // Last mouse event or keyboard event time sampled by the content
  // sink
  uint32_t mLastSampledUserEventTime;

  int32_t mInMonolithicContainer;

  int32_t mInNotification;
  uint32_t mUpdatesInNotification;

  uint32_t mPendingSheetCount;

  nsRevocableEventPtr<nsRunnableMethod<nsContentSink, void, false> >
      mProcessLinkHeaderEvent;
};

#endif  // _nsContentSink_h_