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/. */
8 * Base class for the XML and HTML content sinks, which construct a
9 * DOM based on information from the parser.
12 #include "nsContentSink.h"
13 #include "mozilla/Components.h"
14 #include "mozilla/PresShell.h"
15 #include "mozilla/StaticPrefs_browser.h"
16 #include "mozilla/StaticPrefs_content.h"
17 #include "mozilla/StaticPrefs_network.h"
18 #include "mozilla/dom/Document.h"
19 #include "mozilla/dom/LinkStyle.h"
20 #include "mozilla/dom/ReferrerInfo.h"
21 #include "mozilla/css/Loader.h"
22 #include "mozilla/dom/MutationObservers.h"
23 #include "mozilla/dom/SRILogHelper.h"
24 #include "mozilla/StoragePrincipalHelper.h"
25 #include "mozilla/net/HttpBaseChannel.h"
26 #include "mozilla/net/NeckoChannelParams.h"
27 #include "nsIDocShell.h"
28 #include "nsILoadContext.h"
29 #include "nsIPrefetchService.h"
31 #include "nsNetUtil.h"
32 #include "nsIMIMEHeaderParam.h"
33 #include "nsIProtocolHandler.h"
34 #include "nsIHttpChannel.h"
35 #include "nsIContent.h"
36 #include "nsPresContext.h"
37 #include "nsViewManager.h"
39 #include "nsGkAtoms.h"
40 #include "nsGlobalWindowInner.h"
42 #include "nsICookieService.h"
43 #include "nsContentUtils.h"
44 #include "nsNodeInfoManager.h"
45 #include "nsIAppShell.h"
46 #include "nsIWidget.h"
47 #include "nsWidgetsCID.h"
48 #include "mozAutoDocUpdate.h"
49 #include "nsIWebNavigation.h"
50 #include "nsGenericHTMLElement.h"
51 #include "nsIObserverService.h"
52 #include "mozilla/Preferences.h"
53 #include "mozilla/ProfilerLabels.h"
54 #include "mozilla/dom/HTMLDNSPrefetch.h"
55 #include "mozilla/dom/ServiceWorkerDescriptor.h"
56 #include "mozilla/dom/ScriptLoader.h"
57 #include "nsParserConstants.h"
58 #include "nsSandboxFlags.h"
60 #include "HTMLLinkElement.h"
61 #include "MediaList.h"
63 #include "nsStringFwd.h"
65 #include "mozilla/RefPtr.h"
67 #include "nsLiteralString.h"
68 #include "nsIContentPolicy.h"
69 using namespace mozilla
;
70 using namespace mozilla::css
;
71 using namespace mozilla::dom
;
73 LazyLogModule
gContentSinkLogModuleInfo("nscontentsink");
75 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsContentSink
)
76 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsContentSink
)
78 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsContentSink
)
79 NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver
)
80 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
81 NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver
)
82 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver
)
83 NS_INTERFACE_MAP_ENTRY(nsITimerCallback
)
84 NS_INTERFACE_MAP_ENTRY(nsINamed
)
85 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIDocumentObserver
)
88 NS_IMPL_CYCLE_COLLECTION_CLASS(nsContentSink
)
90 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsContentSink
)
92 tmp
->mDocument
->RemoveObserver(tmp
);
94 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument
)
95 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParser
)
96 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell
)
97 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCSSLoader
)
98 NS_IMPL_CYCLE_COLLECTION_UNLINK(mNodeInfoManager
)
99 NS_IMPL_CYCLE_COLLECTION_UNLINK(mScriptLoader
)
100 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
101 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
102 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsContentSink
)
103 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument
)
104 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser
)
105 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell
)
106 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCSSLoader
)
107 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfoManager
)
108 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader
)
109 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
111 nsContentSink::nsContentSink()
113 mLastNotificationTime(0),
115 mDynamicLowerValue(0),
118 mDeferredLayoutStart(0),
119 mDeferredFlushTags(0),
120 mIsDocumentObserver(0),
121 mRunsToCompletion(0),
122 mIsBlockingOnload(false),
124 mHasPendingEvent(false),
125 mCurrentParseEndTime(0),
127 mLastSampledUserEventTime(0),
128 mInMonolithicContainer(0),
130 mUpdatesInNotification(0),
131 mPendingSheetCount(0) {
132 NS_ASSERTION(!mLayoutStarted
, "What?");
133 NS_ASSERTION(!mDynamicLowerValue
, "What?");
134 NS_ASSERTION(!mParsing
, "What?");
135 NS_ASSERTION(mLastSampledUserEventTime
== 0, "What?");
136 NS_ASSERTION(mDeflectedCount
== 0, "What?");
137 NS_ASSERTION(!mDroppedTimer
, "What?");
138 NS_ASSERTION(mInMonolithicContainer
== 0, "What?");
139 NS_ASSERTION(mInNotification
== 0, "What?");
140 NS_ASSERTION(!mDeferredLayoutStart
, "What?");
143 nsContentSink::~nsContentSink() {
145 // Remove ourselves just to be safe, though we really should have
146 // been removed in DidBuildModel if everything worked right.
147 mDocument
->RemoveObserver(this);
151 nsresult
nsContentSink::Init(Document
* aDoc
, nsIURI
* aURI
,
152 nsISupports
* aContainer
, nsIChannel
* aChannel
) {
153 MOZ_ASSERT(aDoc
, "null ptr");
154 MOZ_ASSERT(aURI
, "null ptr");
156 if (!aDoc
|| !aURI
) {
157 return NS_ERROR_NULL_POINTER
;
163 mDocShell
= do_QueryInterface(aContainer
);
164 mScriptLoader
= mDocument
->ScriptLoader();
166 if (!mRunsToCompletion
) {
168 uint32_t loadType
= 0;
169 mDocShell
->GetLoadType(&loadType
);
170 mDocument
->SetChangeScrollPosWhenScrollingToRef(
171 (loadType
& nsIDocShell::LOAD_CMD_HISTORY
) == 0);
174 ProcessHTTPHeaders(aChannel
);
177 mCSSLoader
= aDoc
->CSSLoader();
179 mNodeInfoManager
= aDoc
->NodeInfoManager();
181 mBackoffCount
= StaticPrefs::content_notify_backoffcount();
183 if (StaticPrefs::content_sink_enable_perf_mode() != 0) {
184 mDynamicLowerValue
= StaticPrefs::content_sink_enable_perf_mode() == 1;
191 nsContentSink::StyleSheetLoaded(StyleSheet
* aSheet
, bool aWasDeferred
,
193 MOZ_ASSERT(!mRunsToCompletion
, "How come a fragment parser observed sheets?");
197 MOZ_ASSERT(mPendingSheetCount
> 0, "How'd that happen?");
198 --mPendingSheetCount
;
200 const bool loadedAllSheets
= !mPendingSheetCount
;
201 if (loadedAllSheets
&& (mDeferredLayoutStart
|| mDeferredFlushTags
)) {
202 if (mDeferredFlushTags
) {
205 if (mDeferredLayoutStart
) {
206 // We might not have really started layout, since this sheet was still
207 // loading. Do it now. Probably doesn't matter whether we do this
208 // before or after we unblock scripts, but before feels saner. Note
209 // that if mDeferredLayoutStart is true, that means any subclass
210 // StartLayout() stuff that needs to happen has already happened, so
211 // we don't need to worry about it.
215 // Go ahead and try to scroll to our ref if we have one
219 mScriptLoader
->RemoveParserBlockingScriptExecutionBlocker();
221 if (loadedAllSheets
&&
222 mDocument
->GetReadyStateEnum() >= Document::READYSTATE_INTERACTIVE
) {
223 mScriptLoader
->DeferCheckpointReached();
229 nsresult
nsContentSink::ProcessHTTPHeaders(nsIChannel
* aChannel
) {
230 nsCOMPtr
<nsIHttpChannel
> httpchannel(do_QueryInterface(aChannel
));
236 bool gotEarlyHints
= false;
237 if (nsCOMPtr
<mozilla::net::HttpBaseChannel
> baseChannel
=
238 do_QueryInterface(aChannel
)) {
239 nsTArray
<mozilla::net::EarlyHintConnectArgs
> earlyHints
=
240 baseChannel
->TakeEarlyHints();
241 gotEarlyHints
= !earlyHints
.IsEmpty();
242 mDocument
->SetEarlyHints(std::move(earlyHints
));
245 // Note that the only header we care about is the "link" header, since we
246 // have all the infrastructure for kicking off stylesheet loads.
248 nsAutoCString linkHeader
;
250 nsresult rv
= httpchannel
->GetResponseHeader("link"_ns
, linkHeader
);
251 bool gotLinkHeader
= NS_SUCCEEDED(rv
) && !linkHeader
.IsEmpty();
253 mDocument
->SetHeaderData(nsGkAtoms::link
,
254 NS_ConvertASCIItoUTF16(linkHeader
));
256 if (gotLinkHeader
|| gotEarlyHints
) {
257 NS_ASSERTION(!mProcessLinkHeaderEvent
.get(),
258 "Already dispatched an event?");
260 mProcessLinkHeaderEvent
=
261 NewNonOwningRunnableMethod("nsContentSink::DoProcessLinkHeader", this,
262 &nsContentSink::DoProcessLinkHeader
);
263 rv
= NS_DispatchToCurrentThread(mProcessLinkHeaderEvent
.get());
265 mProcessLinkHeaderEvent
.Forget();
272 void nsContentSink::DoProcessLinkHeader() {
273 for (const auto& earlyHint
: mDocument
->GetEarlyHints()) {
274 ProcessLinkFromHeader(earlyHint
.link(), earlyHint
.earlyHintPreloaderId());
279 // Getting the header data and parsing the link header together roughly
280 // implement <https://httpwg.org/specs/rfc8288.html#parse-set>.
281 mDocument
->GetHeaderData(nsGkAtoms::link
, value
);
282 auto linkHeaders
= net::ParseLinkHeader(value
);
284 for (const auto& linkHeader
: linkHeaders
) {
285 ProcessLinkFromHeader(linkHeader
, 0);
289 nsresult
nsContentSink::ProcessLinkFromHeader(const net::LinkHeader
& aHeader
,
290 uint64_t aEarlyHintPreloaderId
) {
291 uint32_t linkTypes
= LinkStyle::ParseLinkTypes(aHeader
.mRel
);
293 // The link relation may apply to a different resource, specified
294 // in the anchor parameter. For the link relations supported so far,
295 // we simply abort if the link applies to a resource different to the
297 if (!nsContentUtils::LinkContextIsURI(aHeader
.mAnchor
,
298 mDocument
->GetDocumentURI())) {
302 if (nsContentUtils::PrefetchPreloadEnabled(mDocShell
)) {
303 // prefetch href if relation is "next" or "prefetch"
304 if ((linkTypes
& LinkStyle::eNEXT
) || (linkTypes
& LinkStyle::ePREFETCH
)) {
305 PrefetchHref(aHeader
.mHref
, aHeader
.mAs
, aHeader
.mType
, aHeader
.mMedia
);
308 if (!aHeader
.mHref
.IsEmpty() && (linkTypes
& LinkStyle::eDNS_PREFETCH
)) {
309 PrefetchDNS(aHeader
.mHref
);
312 if (!aHeader
.mHref
.IsEmpty() && (linkTypes
& LinkStyle::ePRECONNECT
)) {
313 Preconnect(aHeader
.mHref
, aHeader
.mCrossOrigin
);
316 if (linkTypes
& LinkStyle::ePRELOAD
) {
317 PreloadHref(aHeader
.mHref
, aHeader
.mAs
, aHeader
.mType
, aHeader
.mMedia
,
318 aHeader
.mNonce
, aHeader
.mIntegrity
, aHeader
.mSrcset
,
319 aHeader
.mSizes
, aHeader
.mCrossOrigin
, aHeader
.mReferrerPolicy
,
320 aEarlyHintPreloaderId
, aHeader
.mFetchPriority
);
323 if ((linkTypes
& LinkStyle::eMODULE_PRELOAD
) &&
324 mDocument
->ScriptLoader()->GetModuleLoader()) {
325 PreloadModule(aHeader
.mHref
, aHeader
.mAs
, aHeader
.mMedia
, aHeader
.mNonce
,
326 aHeader
.mIntegrity
, aHeader
.mCrossOrigin
,
327 aHeader
.mReferrerPolicy
, aEarlyHintPreloaderId
,
328 aHeader
.mFetchPriority
);
332 // is it a stylesheet link?
333 if (!(linkTypes
& LinkStyle::eSTYLESHEET
)) {
337 bool isAlternate
= linkTypes
& LinkStyle::eALTERNATE
;
338 return ProcessStyleLinkFromHeader(
339 aHeader
.mHref
, isAlternate
, aHeader
.mTitle
, aHeader
.mIntegrity
,
340 aHeader
.mType
, aHeader
.mMedia
, aHeader
.mReferrerPolicy
,
341 StaticPrefs::network_fetchpriority_enabled() ? aHeader
.mFetchPriority
345 nsresult
nsContentSink::ProcessStyleLinkFromHeader(
346 const nsAString
& aHref
, bool aAlternate
, const nsAString
& aTitle
,
347 const nsAString
& aIntegrity
, const nsAString
& aType
,
348 const nsAString
& aMedia
, const nsAString
& aReferrerPolicy
,
349 const nsAString
& aFetchPriority
) {
350 if (aAlternate
&& aTitle
.IsEmpty()) {
351 // alternates must have title return without error, for now
355 nsAutoString mimeType
;
357 nsContentUtils::SplitMimeType(aType
, mimeType
, params
);
360 if (!mimeType
.IsEmpty() && !mimeType
.LowerCaseEqualsLiteral("text/css")) {
361 // Unknown stylesheet language
365 nsCOMPtr
<nsIURI
> url
;
366 nsresult rv
= NS_NewURI(getter_AddRefs(url
), aHref
, nullptr,
367 mDocument
->GetDocBaseURI());
370 // The URI is bad, move along, don't propagate the error (for now)
374 // Link header is working like a <link> node, so referrerPolicy attr should
375 // have higher priority than referrer policy from document.
376 ReferrerPolicy policy
=
377 ReferrerInfo::ReferrerPolicyAttributeFromString(aReferrerPolicy
);
378 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
379 ReferrerInfo::CreateFromDocumentAndPolicyOverride(mDocument
, policy
);
381 const FetchPriority fetchPriority
=
382 nsGenericHTMLElement::ToFetchPriority(aFetchPriority
);
384 Loader::SheetInfo info
{
389 referrerInfo
.forget(),
394 /* nonce = */ u
""_ns
,
395 aAlternate
? Loader::HasAlternateRel::Yes
: Loader::HasAlternateRel::No
,
396 Loader::IsInline::No
,
397 Loader::IsExplicitlyEnabled::No
,
401 auto loadResultOrErr
=
402 mCSSLoader
->LoadStyleLink(info
, mRunsToCompletion
? nullptr : this);
403 if (loadResultOrErr
.isErr()) {
404 return loadResultOrErr
.unwrapErr();
407 if (loadResultOrErr
.inspect().ShouldBlock() && !mRunsToCompletion
) {
408 ++mPendingSheetCount
;
409 mScriptLoader
->AddParserBlockingScriptExecutionBlocker();
415 void nsContentSink::PrefetchHref(const nsAString
& aHref
, const nsAString
& aAs
,
416 const nsAString
& aType
,
417 const nsAString
& aMedia
) {
418 nsCOMPtr
<nsIPrefetchService
> prefetchService(components::Prefetch::Service());
419 if (prefetchService
) {
420 // construct URI using document charset
421 auto encoding
= mDocument
->GetDocumentCharacterSet();
422 nsCOMPtr
<nsIURI
> uri
;
423 NS_NewURI(getter_AddRefs(uri
), aHref
, encoding
, mDocument
->GetDocBaseURI());
425 auto referrerInfo
= MakeRefPtr
<ReferrerInfo
>(*mDocument
);
426 referrerInfo
= referrerInfo
->CloneWithNewOriginalReferrer(mDocumentURI
);
428 prefetchService
->PrefetchURI(uri
, referrerInfo
, mDocument
, true);
433 void nsContentSink::PreloadHref(const nsAString
& aHref
, const nsAString
& aAs
,
434 const nsAString
& aType
, const nsAString
& aMedia
,
435 const nsAString
& aNonce
,
436 const nsAString
& aIntegrity
,
437 const nsAString
& aSrcset
,
438 const nsAString
& aSizes
, const nsAString
& aCORS
,
439 const nsAString
& aReferrerPolicy
,
440 uint64_t aEarlyHintPreloaderId
,
441 const nsAString
& aFetchPriority
) {
442 auto encoding
= mDocument
->GetDocumentCharacterSet();
443 nsCOMPtr
<nsIURI
> uri
;
444 NS_NewURI(getter_AddRefs(uri
), aHref
, encoding
, mDocument
->GetDocBaseURI());
446 // URL parsing failed.
451 mozilla::net::ParseAsValue(aAs
, asAttr
);
453 nsAutoString mimeType
;
454 nsAutoString notUsed
;
455 nsContentUtils::SplitMimeType(aType
, mimeType
, notUsed
);
457 auto policyType
= mozilla::net::AsValueToContentPolicy(asAttr
);
458 if (policyType
== nsIContentPolicy::TYPE_INVALID
||
459 !mozilla::net::CheckPreloadAttrs(asAttr
, mimeType
, aMedia
, mDocument
)) {
460 // Ignore preload wrong or empty attributes.
461 mozilla::net::WarnIgnoredPreload(*mDocument
, *uri
);
465 mDocument
->Preloads().PreloadLinkHeader(
466 uri
, aHref
, policyType
, aAs
, aType
, aNonce
, aIntegrity
, aSrcset
, aSizes
,
467 aCORS
, aReferrerPolicy
, aEarlyHintPreloaderId
, aFetchPriority
);
470 void nsContentSink::PreloadModule(
471 const nsAString
& aHref
, const nsAString
& aAs
, const nsAString
& aMedia
,
472 const nsAString
& aNonce
, const nsAString
& aIntegrity
,
473 const nsAString
& aCORS
, const nsAString
& aReferrerPolicy
,
474 uint64_t aEarlyHintPreloaderId
, const nsAString
& aFetchPriority
) {
475 ModuleLoader
* moduleLoader
= mDocument
->ScriptLoader()->GetModuleLoader();
477 if (!StaticPrefs::network_modulepreload()) {
478 // Keep behavior from https://phabricator.services.mozilla.com/D149371,
479 // prior to main implementation of modulepreload
480 moduleLoader
->DisallowImportMaps();
484 RefPtr
<mozilla::dom::MediaList
> mediaList
=
485 mozilla::dom::MediaList::Create(NS_ConvertUTF16toUTF8(aMedia
));
486 if (!mediaList
->Matches(*mDocument
)) {
490 if (aHref
.IsEmpty()) {
494 if (!net::IsScriptLikeOrInvalid(aAs
)) {
498 auto encoding
= mDocument
->GetDocumentCharacterSet();
499 nsCOMPtr
<nsIURI
> uri
;
500 NS_NewURI(getter_AddRefs(uri
), aHref
, encoding
, mDocument
->GetDocBaseURI());
505 moduleLoader
->DisallowImportMaps();
507 mDocument
->Preloads().PreloadLinkHeader(
508 uri
, aHref
, nsIContentPolicy::TYPE_SCRIPT
, u
"script"_ns
, u
"module"_ns
,
509 aNonce
, aIntegrity
, u
""_ns
, u
""_ns
, aCORS
, aReferrerPolicy
,
510 aEarlyHintPreloaderId
, aFetchPriority
);
513 void nsContentSink::PrefetchDNS(const nsAString
& aHref
) {
514 nsAutoString hostname
;
515 bool isHttps
= false;
517 if (StringBeginsWith(aHref
, u
"//"_ns
)) {
518 hostname
= Substring(aHref
, 2);
520 nsCOMPtr
<nsIURI
> uri
;
521 NS_NewURI(getter_AddRefs(uri
), aHref
);
526 bool isLocalResource
= false;
527 rv
= NS_URIChainHasFlags(uri
, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE
,
529 if (NS_SUCCEEDED(rv
) && !isLocalResource
) {
532 CopyUTF8toUTF16(host
, hostname
);
534 isHttps
= uri
->SchemeIs("https");
537 if (!hostname
.IsEmpty() && HTMLDNSPrefetch::IsAllowed(mDocument
)) {
539 StoragePrincipalHelper::GetOriginAttributesForNetworkState(mDocument
, oa
);
541 HTMLDNSPrefetch::Prefetch(hostname
, isHttps
, oa
,
542 mDocument
->GetChannel()->GetTRRMode(),
543 HTMLDNSPrefetch::Priority::Low
);
547 void nsContentSink::Preconnect(const nsAString
& aHref
,
548 const nsAString
& aCrossOrigin
) {
549 // construct URI using document charset
550 auto encoding
= mDocument
->GetDocumentCharacterSet();
551 nsCOMPtr
<nsIURI
> uri
;
552 NS_NewURI(getter_AddRefs(uri
), aHref
, encoding
, mDocument
->GetDocBaseURI());
554 if (uri
&& mDocument
) {
555 mDocument
->MaybePreconnect(uri
,
556 dom::Element::StringToCORSMode(aCrossOrigin
));
560 void nsContentSink::ScrollToRef() {
561 RefPtr
<Document
> document
= mDocument
;
562 document
->ScrollToRef();
565 void nsContentSink::StartLayout(bool aIgnorePendingSheets
) {
566 if (mLayoutStarted
) {
567 // Nothing to do here
571 mDeferredLayoutStart
= true;
573 if (!aIgnorePendingSheets
&&
574 (WaitForPendingSheets() || mDocument
->HasPendingInitialTranslation())) {
575 // Bail out; we'll start layout when the sheets and l10n load
579 AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING_RELEVANT_FOR_JS(
580 "Layout", LAYOUT
, mDocumentURI
->GetSpecOrDefault());
582 mDeferredLayoutStart
= false;
584 if (aIgnorePendingSheets
) {
585 nsContentUtils::ReportToConsole(
586 nsIScriptError::warningFlag
, "Layout"_ns
, mDocument
,
587 nsContentUtils::eLAYOUT_PROPERTIES
, "ForcedLayoutStart");
590 // Notify on all our content. If none of our presshells have started layout
591 // yet it'll be a no-op except for updating our data structures, a la
592 // UpdateChildCounts() (because we don't want to double-notify on whatever we
593 // have right now). If some of them _have_ started layout, we want to make
594 // sure to flush tags instead of just calling UpdateChildCounts() after we
595 // loop over the shells.
598 mLayoutStarted
= true;
599 mLastNotificationTime
= PR_Now();
601 mDocument
->SetMayStartLayout(true);
602 RefPtr
<PresShell
> presShell
= mDocument
->GetPresShell();
603 // Make sure we don't call Initialize() for a shell that has
604 // already called it. This can happen when the layout frame for
605 // an iframe is constructed *between* the Embed() call for the
606 // docshell in the iframe, and the content sink's call to OpenBody().
608 if (presShell
&& !presShell
->DidInitialize()) {
609 nsresult rv
= presShell
->Initialize();
615 // If the document we are loading has a reference or it is a
616 // frameset document, disable the scroll bars on the views.
618 mDocument
->SetScrollToRef(mDocument
->GetDocumentURI());
621 void nsContentSink::NotifyAppend(nsIContent
* aContainer
, uint32_t aStartIndex
) {
625 // Scope so we call EndUpdate before we decrease mInNotification
627 // Note that aContainer->OwnerDoc() may not be mDocument.
628 MOZ_AUTO_DOC_UPDATE(aContainer
->OwnerDoc(), true);
629 MutationObservers::NotifyContentAppended(
630 aContainer
, aContainer
->GetChildAt_Deprecated(aStartIndex
));
631 mLastNotificationTime
= PR_Now();
638 nsContentSink::Notify(nsITimer
* timer
) {
640 // We shouldn't interfere with our normal DidProcessAToken logic
641 mDroppedTimer
= true;
645 if (WaitForPendingSheets()) {
646 mDeferredFlushTags
= true;
650 // Now try and scroll to the reference
651 // XXX Should we scroll unconditionally for history loads??
655 mNotificationTimer
= nullptr;
659 bool nsContentSink::IsTimeToNotify() {
660 if (!StaticPrefs::content_notify_ontimer() || !mLayoutStarted
||
661 !mBackoffCount
|| mInMonolithicContainer
) {
665 if (WaitForPendingSheets()) {
666 mDeferredFlushTags
= true;
670 PRTime now
= PR_Now();
672 int64_t interval
= GetNotificationInterval();
673 int64_t diff
= now
- mLastNotificationTime
;
675 if (diff
> interval
) {
683 nsresult
nsContentSink::WillInterruptImpl() {
684 nsresult result
= NS_OK
;
686 SINK_TRACE(static_cast<LogModule
*>(gContentSinkLogModuleInfo
),
687 SINK_TRACE_CALLS
, ("nsContentSink::WillInterrupt: this=%p", this));
688 #ifndef SINK_NO_INCREMENTAL
689 if (WaitForPendingSheets()) {
690 mDeferredFlushTags
= true;
691 } else if (StaticPrefs::content_notify_ontimer() && mLayoutStarted
) {
692 if (mBackoffCount
&& !mInMonolithicContainer
) {
693 int64_t now
= PR_Now();
694 int64_t interval
= GetNotificationInterval();
695 int64_t diff
= now
- mLastNotificationTime
;
697 // If it's already time for us to have a notification
698 if (diff
> interval
|| mDroppedTimer
) {
700 SINK_TRACE(static_cast<LogModule
*>(gContentSinkLogModuleInfo
),
702 ("nsContentSink::WillInterrupt: flushing tags since we've "
703 "run out time; backoff count: %d",
705 result
= FlushTags();
708 mDroppedTimer
= false;
710 } else if (!mNotificationTimer
) {
712 int32_t delay
= interval
;
714 // Convert to milliseconds
715 delay
/= PR_USEC_PER_MSEC
;
717 NS_NewTimerWithCallback(getter_AddRefs(mNotificationTimer
), this, delay
,
718 nsITimer::TYPE_ONE_SHOT
);
719 if (mNotificationTimer
) {
720 SINK_TRACE(static_cast<LogModule
*>(gContentSinkLogModuleInfo
),
722 ("nsContentSink::WillInterrupt: setting up timer with "
729 SINK_TRACE(static_cast<LogModule
*>(gContentSinkLogModuleInfo
),
731 ("nsContentSink::WillInterrupt: flushing tags "
733 result
= FlushTags();
742 void nsContentSink::WillResumeImpl() {
743 SINK_TRACE(static_cast<LogModule
*>(gContentSinkLogModuleInfo
),
744 SINK_TRACE_CALLS
, ("nsContentSink::WillResume: this=%p", this));
749 nsresult
nsContentSink::DidProcessATokenImpl() {
750 if (mRunsToCompletion
|| !mParser
) {
754 // Get the current user event time
755 PresShell
* presShell
= mDocument
->GetPresShell();
757 // If there's no pres shell in the document, return early since
758 // we're not laying anything out here.
762 // Increase before comparing to gEventProbeRate
765 // Check if there's a pending event
766 if (StaticPrefs::content_sink_pending_event_mode() != 0 &&
768 (mDeflectedCount
% StaticPrefs::content_sink_event_probe_rate()) == 0) {
769 nsViewManager
* vm
= presShell
->GetViewManager();
770 NS_ENSURE_TRUE(vm
, NS_ERROR_FAILURE
);
771 nsCOMPtr
<nsIWidget
> widget
= vm
->GetRootWidget();
772 mHasPendingEvent
= widget
&& widget
->HasPendingInputEvent();
775 if (mHasPendingEvent
&& StaticPrefs::content_sink_pending_event_mode() == 2) {
776 return NS_ERROR_HTMLPARSER_INTERRUPTED
;
779 // Have we processed enough tokens to check time?
780 if (!mHasPendingEvent
&&
782 uint32_t(mDynamicLowerValue
783 ? StaticPrefs::content_sink_interactive_deflect_count()
784 : StaticPrefs::content_sink_perf_deflect_count())) {
790 // Check if it's time to return to the main event loop
791 if (PR_IntervalToMicroseconds(PR_IntervalNow()) > mCurrentParseEndTime
) {
792 return NS_ERROR_HTMLPARSER_INTERRUPTED
;
798 //----------------------------------------------------------------------
800 void nsContentSink::BeginUpdate(Document
* aDocument
) {
801 // Remember nested updates from updates that we started.
802 if (mInNotification
> 0 && mUpdatesInNotification
< 2) {
803 ++mUpdatesInNotification
;
806 // If we're in a script and we didn't do the notification,
807 // something else in the script processing caused the
808 // notification to occur. Since this could result in frame
809 // creation, make sure we've flushed everything before we
812 if (!mInNotification
++) {
817 void nsContentSink::EndUpdate(Document
* aDocument
) {
818 // If we're in a script and we didn't do the notification,
819 // something else in the script processing caused the
820 // notification to occur. Update our notion of how much
821 // has been flushed to include any new content if ending
822 // this update leaves us not inside a notification.
823 if (!--mInNotification
) {
828 void nsContentSink::DidBuildModelImpl(bool aTerminated
) {
829 MOZ_ASSERT(aTerminated
|| (mParser
&& mParser
->IsParserClosed()) ||
830 mDocument
->GetReadyStateEnum() == Document::READYSTATE_LOADING
,
832 mDocument
->SetReadyStateInternal(Document::READYSTATE_INTERACTIVE
);
835 mScriptLoader
->ParsingComplete(aTerminated
);
836 if (!mPendingSheetCount
) {
837 mScriptLoader
->DeferCheckpointReached();
841 if (!mDocument
->HaveFiredDOMTitleChange()) {
842 mDocument
->NotifyPossibleTitleChange(false);
845 // Cancel a timer if we had one out there
846 if (mNotificationTimer
) {
847 SINK_TRACE(static_cast<LogModule
*>(gContentSinkLogModuleInfo
),
849 ("nsContentSink::DidBuildModel: canceling notification "
851 mNotificationTimer
->Cancel();
852 mNotificationTimer
= nullptr;
856 void nsContentSink::DropParserAndPerfHint(void) {
858 // Make sure we don't unblock unload too many times
863 // Do this hack to make sure that the parser
864 // doesn't get destroyed, accidently, before
865 // the circularity, between sink & parser, is
867 // Drop our reference to the parser to get rid of a circular
869 RefPtr
<nsParserBase
> kungFuDeathGrip
= std::move(mParser
);
870 mozilla::Unused
<< kungFuDeathGrip
;
872 // Call UnblockOnload only if mRunsToComletion is false and if
873 // we have already started loading because it's possible that this function
874 // is called (i.e. the parser is terminated) before we start loading due to
875 // destroying the window inside unload event callbacks for the previous
877 if (!mRunsToCompletion
&& mIsBlockingOnload
) {
878 mDocument
->UnblockOnload(true);
879 mIsBlockingOnload
= false;
883 bool nsContentSink::IsScriptExecutingImpl() {
884 return !!mScriptLoader
->GetCurrentScript();
887 nsresult
nsContentSink::WillParseImpl(void) {
888 if (mRunsToCompletion
|| !mDocument
) {
892 PresShell
* presShell
= mDocument
->GetPresShell();
897 uint32_t currentTime
= PR_IntervalToMicroseconds(PR_IntervalNow());
899 if (StaticPrefs::content_sink_enable_perf_mode() == 0) {
900 nsViewManager
* vm
= presShell
->GetViewManager();
901 NS_ENSURE_TRUE(vm
, NS_ERROR_FAILURE
);
902 uint32_t lastEventTime
;
903 vm
->GetLastUserEventTime(lastEventTime
);
905 bool newDynLower
= mDocument
->IsInBackgroundWindow() ||
906 ((currentTime
- mBeginLoadTime
) >
907 StaticPrefs::content_sink_initial_perf_time() &&
908 (currentTime
- lastEventTime
) <
909 StaticPrefs::content_sink_interactive_time());
911 if (mDynamicLowerValue
!= newDynLower
) {
912 mDynamicLowerValue
= newDynLower
;
917 mHasPendingEvent
= false;
919 mCurrentParseEndTime
=
920 currentTime
+ (mDynamicLowerValue
921 ? StaticPrefs::content_sink_interactive_parse_time()
922 : StaticPrefs::content_sink_perf_parse_time());
927 void nsContentSink::WillBuildModelImpl() {
928 if (!mRunsToCompletion
) {
929 mDocument
->BlockOnload();
930 mIsBlockingOnload
= true;
932 mBeginLoadTime
= PR_IntervalToMicroseconds(PR_IntervalNow());
935 mDocument
->ResetScrolledToRefAlready();
937 if (mProcessLinkHeaderEvent
.get()) {
938 mProcessLinkHeaderEvent
.Revoke();
940 DoProcessLinkHeader();
945 void nsContentSink::NotifyDocElementCreated(Document
* aDoc
) {
946 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
948 nsCOMPtr
<nsIObserverService
> observerService
=
949 mozilla::services::GetObserverService();
950 MOZ_ASSERT(observerService
);
952 auto* win
= nsGlobalWindowInner::Cast(aDoc
->GetInnerWindow());
953 bool fireInitialInsertion
= !win
|| !win
->DidFireDocElemInserted();
955 win
->SetDidFireDocElemInserted();
957 if (fireInitialInsertion
) {
958 observerService
->NotifyObservers(ToSupports(aDoc
),
959 "initial-document-element-inserted", u
"");
961 observerService
->NotifyObservers(ToSupports(aDoc
),
962 "document-element-inserted", u
"");
964 nsContentUtils::DispatchChromeEvent(aDoc
, aDoc
, u
"DOMDocElementInserted"_ns
,
965 CanBubble::eYes
, Cancelable::eNo
);
969 nsContentSink::GetName(nsACString
& aName
) {
970 aName
.AssignLiteral("nsContentSink_timer");