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(aHeader
.mHref
, isAlternate
, aHeader
.mTitle
,
339 aHeader
.mIntegrity
, aHeader
.mType
,
340 aHeader
.mMedia
, aHeader
.mReferrerPolicy
,
341 aHeader
.mFetchPriority
);
344 nsresult
nsContentSink::ProcessStyleLinkFromHeader(
345 const nsAString
& aHref
, bool aAlternate
, const nsAString
& aTitle
,
346 const nsAString
& aIntegrity
, const nsAString
& aType
,
347 const nsAString
& aMedia
, const nsAString
& aReferrerPolicy
,
348 const nsAString
& aFetchPriority
) {
349 if (aAlternate
&& aTitle
.IsEmpty()) {
350 // alternates must have title return without error, for now
354 nsAutoString mimeType
;
356 nsContentUtils::SplitMimeType(aType
, mimeType
, params
);
359 if (!mimeType
.IsEmpty() && !mimeType
.LowerCaseEqualsLiteral("text/css")) {
360 // Unknown stylesheet language
364 nsCOMPtr
<nsIURI
> url
;
365 nsresult rv
= NS_NewURI(getter_AddRefs(url
), aHref
, nullptr,
366 mDocument
->GetDocBaseURI());
369 // The URI is bad, move along, don't propagate the error (for now)
373 // Link header is working like a <link> node, so referrerPolicy attr should
374 // have higher priority than referrer policy from document.
375 ReferrerPolicy policy
=
376 ReferrerInfo::ReferrerPolicyAttributeFromString(aReferrerPolicy
);
377 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
378 ReferrerInfo::CreateFromDocumentAndPolicyOverride(mDocument
, policy
);
380 const FetchPriority fetchPriority
=
381 nsGenericHTMLElement::ToFetchPriority(aFetchPriority
);
383 Loader::SheetInfo info
{
388 referrerInfo
.forget(),
393 /* nonce = */ u
""_ns
,
394 aAlternate
? Loader::HasAlternateRel::Yes
: Loader::HasAlternateRel::No
,
395 Loader::IsInline::No
,
396 Loader::IsExplicitlyEnabled::No
,
400 auto loadResultOrErr
=
401 mCSSLoader
->LoadStyleLink(info
, mRunsToCompletion
? nullptr : this);
402 if (loadResultOrErr
.isErr()) {
403 return loadResultOrErr
.unwrapErr();
406 if (loadResultOrErr
.inspect().ShouldBlock() && !mRunsToCompletion
) {
407 ++mPendingSheetCount
;
408 mScriptLoader
->AddParserBlockingScriptExecutionBlocker();
414 void nsContentSink::PrefetchHref(const nsAString
& aHref
, const nsAString
& aAs
,
415 const nsAString
& aType
,
416 const nsAString
& aMedia
) {
417 nsCOMPtr
<nsIPrefetchService
> prefetchService(components::Prefetch::Service());
418 if (prefetchService
) {
419 // construct URI using document charset
420 auto encoding
= mDocument
->GetDocumentCharacterSet();
421 nsCOMPtr
<nsIURI
> uri
;
422 NS_NewURI(getter_AddRefs(uri
), aHref
, encoding
, mDocument
->GetDocBaseURI());
424 auto referrerInfo
= MakeRefPtr
<ReferrerInfo
>(*mDocument
);
425 referrerInfo
= referrerInfo
->CloneWithNewOriginalReferrer(mDocumentURI
);
427 prefetchService
->PrefetchURI(uri
, referrerInfo
, mDocument
, true);
432 void nsContentSink::PreloadHref(const nsAString
& aHref
, const nsAString
& aAs
,
433 const nsAString
& aType
, const nsAString
& aMedia
,
434 const nsAString
& aNonce
,
435 const nsAString
& aIntegrity
,
436 const nsAString
& aSrcset
,
437 const nsAString
& aSizes
, const nsAString
& aCORS
,
438 const nsAString
& aReferrerPolicy
,
439 uint64_t aEarlyHintPreloaderId
,
440 const nsAString
& aFetchPriority
) {
441 auto encoding
= mDocument
->GetDocumentCharacterSet();
442 nsCOMPtr
<nsIURI
> uri
;
443 NS_NewURI(getter_AddRefs(uri
), aHref
, encoding
, mDocument
->GetDocBaseURI());
445 // URL parsing failed.
450 mozilla::net::ParseAsValue(aAs
, asAttr
);
452 nsAutoString mimeType
;
453 nsAutoString notUsed
;
454 nsContentUtils::SplitMimeType(aType
, mimeType
, notUsed
);
456 auto policyType
= mozilla::net::AsValueToContentPolicy(asAttr
);
457 if (policyType
== nsIContentPolicy::TYPE_INVALID
||
458 !mozilla::net::CheckPreloadAttrs(asAttr
, mimeType
, aMedia
, mDocument
)) {
459 // Ignore preload wrong or empty attributes.
460 mozilla::net::WarnIgnoredPreload(*mDocument
, *uri
);
464 mDocument
->Preloads().PreloadLinkHeader(
465 uri
, aHref
, policyType
, aAs
, aType
, aNonce
, aIntegrity
, aSrcset
, aSizes
,
466 aCORS
, aReferrerPolicy
, aEarlyHintPreloaderId
, aFetchPriority
);
469 void nsContentSink::PreloadModule(
470 const nsAString
& aHref
, const nsAString
& aAs
, const nsAString
& aMedia
,
471 const nsAString
& aNonce
, const nsAString
& aIntegrity
,
472 const nsAString
& aCORS
, const nsAString
& aReferrerPolicy
,
473 uint64_t aEarlyHintPreloaderId
, const nsAString
& aFetchPriority
) {
474 ModuleLoader
* moduleLoader
= mDocument
->ScriptLoader()->GetModuleLoader();
476 if (!StaticPrefs::network_modulepreload()) {
477 // Keep behavior from https://phabricator.services.mozilla.com/D149371,
478 // prior to main implementation of modulepreload
479 moduleLoader
->DisallowImportMaps();
483 RefPtr
<mozilla::dom::MediaList
> mediaList
=
484 mozilla::dom::MediaList::Create(NS_ConvertUTF16toUTF8(aMedia
));
485 if (!mediaList
->Matches(*mDocument
)) {
489 if (aHref
.IsEmpty()) {
493 if (!net::IsScriptLikeOrInvalid(aAs
)) {
497 auto encoding
= mDocument
->GetDocumentCharacterSet();
498 nsCOMPtr
<nsIURI
> uri
;
499 NS_NewURI(getter_AddRefs(uri
), aHref
, encoding
, mDocument
->GetDocBaseURI());
504 moduleLoader
->DisallowImportMaps();
506 mDocument
->Preloads().PreloadLinkHeader(
507 uri
, aHref
, nsIContentPolicy::TYPE_SCRIPT
, u
"script"_ns
, u
"module"_ns
,
508 aNonce
, aIntegrity
, u
""_ns
, u
""_ns
, aCORS
, aReferrerPolicy
,
509 aEarlyHintPreloaderId
, aFetchPriority
);
512 void nsContentSink::PrefetchDNS(const nsAString
& aHref
) {
513 nsAutoString hostname
;
514 bool isHttps
= false;
516 if (StringBeginsWith(aHref
, u
"//"_ns
)) {
517 hostname
= Substring(aHref
, 2);
519 nsCOMPtr
<nsIURI
> uri
;
520 NS_NewURI(getter_AddRefs(uri
), aHref
);
525 bool isLocalResource
= false;
526 rv
= NS_URIChainHasFlags(uri
, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE
,
528 if (NS_SUCCEEDED(rv
) && !isLocalResource
) {
531 CopyUTF8toUTF16(host
, hostname
);
533 isHttps
= uri
->SchemeIs("https");
536 if (!hostname
.IsEmpty() && HTMLDNSPrefetch::IsAllowed(mDocument
)) {
538 StoragePrincipalHelper::GetOriginAttributesForNetworkState(mDocument
, oa
);
540 HTMLDNSPrefetch::Prefetch(hostname
, isHttps
, oa
,
541 mDocument
->GetChannel()->GetTRRMode(),
542 HTMLDNSPrefetch::Priority::Low
);
546 void nsContentSink::Preconnect(const nsAString
& aHref
,
547 const nsAString
& aCrossOrigin
) {
548 // construct URI using document charset
549 auto encoding
= mDocument
->GetDocumentCharacterSet();
550 nsCOMPtr
<nsIURI
> uri
;
551 NS_NewURI(getter_AddRefs(uri
), aHref
, encoding
, mDocument
->GetDocBaseURI());
553 if (uri
&& mDocument
) {
554 mDocument
->MaybePreconnect(uri
,
555 dom::Element::StringToCORSMode(aCrossOrigin
));
559 void nsContentSink::ScrollToRef() {
560 RefPtr
<Document
> document
= mDocument
;
561 document
->ScrollToRef();
564 void nsContentSink::StartLayout(bool aIgnorePendingSheets
) {
565 if (mLayoutStarted
) {
566 // Nothing to do here
570 mDeferredLayoutStart
= true;
572 if (!aIgnorePendingSheets
&&
573 (WaitForPendingSheets() || mDocument
->HasPendingInitialTranslation())) {
574 // Bail out; we'll start layout when the sheets and l10n load
578 AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING_RELEVANT_FOR_JS(
579 "Layout", LAYOUT
, mDocumentURI
->GetSpecOrDefault());
581 mDeferredLayoutStart
= false;
583 if (aIgnorePendingSheets
) {
584 nsContentUtils::ReportToConsole(
585 nsIScriptError::warningFlag
, "Layout"_ns
, mDocument
,
586 nsContentUtils::eLAYOUT_PROPERTIES
, "ForcedLayoutStart");
589 // Notify on all our content. If none of our presshells have started layout
590 // yet it'll be a no-op except for updating our data structures, a la
591 // UpdateChildCounts() (because we don't want to double-notify on whatever we
592 // have right now). If some of them _have_ started layout, we want to make
593 // sure to flush tags instead of just calling UpdateChildCounts() after we
594 // loop over the shells.
597 mLayoutStarted
= true;
598 mLastNotificationTime
= PR_Now();
600 mDocument
->SetMayStartLayout(true);
601 RefPtr
<PresShell
> presShell
= mDocument
->GetPresShell();
602 // Make sure we don't call Initialize() for a shell that has
603 // already called it. This can happen when the layout frame for
604 // an iframe is constructed *between* the Embed() call for the
605 // docshell in the iframe, and the content sink's call to OpenBody().
607 if (presShell
&& !presShell
->DidInitialize()) {
608 nsresult rv
= presShell
->Initialize();
614 // If the document we are loading has a reference or it is a
615 // frameset document, disable the scroll bars on the views.
617 mDocument
->SetScrollToRef(mDocument
->GetDocumentURI());
620 void nsContentSink::NotifyAppend(nsIContent
* aContainer
, uint32_t aStartIndex
) {
624 // Scope so we call EndUpdate before we decrease mInNotification
626 // Note that aContainer->OwnerDoc() may not be mDocument.
627 MOZ_AUTO_DOC_UPDATE(aContainer
->OwnerDoc(), true);
628 MutationObservers::NotifyContentAppended(
629 aContainer
, aContainer
->GetChildAt_Deprecated(aStartIndex
));
630 mLastNotificationTime
= PR_Now();
637 nsContentSink::Notify(nsITimer
* timer
) {
639 // We shouldn't interfere with our normal DidProcessAToken logic
640 mDroppedTimer
= true;
644 if (WaitForPendingSheets()) {
645 mDeferredFlushTags
= true;
649 // Now try and scroll to the reference
650 // XXX Should we scroll unconditionally for history loads??
654 mNotificationTimer
= nullptr;
658 bool nsContentSink::IsTimeToNotify() {
659 if (!StaticPrefs::content_notify_ontimer() || !mLayoutStarted
||
660 !mBackoffCount
|| mInMonolithicContainer
) {
664 if (WaitForPendingSheets()) {
665 mDeferredFlushTags
= true;
669 PRTime now
= PR_Now();
671 int64_t interval
= GetNotificationInterval();
672 int64_t diff
= now
- mLastNotificationTime
;
674 if (diff
> interval
) {
682 nsresult
nsContentSink::WillInterruptImpl() {
683 nsresult result
= NS_OK
;
685 SINK_TRACE(static_cast<LogModule
*>(gContentSinkLogModuleInfo
),
686 SINK_TRACE_CALLS
, ("nsContentSink::WillInterrupt: this=%p", this));
687 #ifndef SINK_NO_INCREMENTAL
688 if (WaitForPendingSheets()) {
689 mDeferredFlushTags
= true;
690 } else if (StaticPrefs::content_notify_ontimer() && mLayoutStarted
) {
691 if (mBackoffCount
&& !mInMonolithicContainer
) {
692 int64_t now
= PR_Now();
693 int64_t interval
= GetNotificationInterval();
694 int64_t diff
= now
- mLastNotificationTime
;
696 // If it's already time for us to have a notification
697 if (diff
> interval
|| mDroppedTimer
) {
699 SINK_TRACE(static_cast<LogModule
*>(gContentSinkLogModuleInfo
),
701 ("nsContentSink::WillInterrupt: flushing tags since we've "
702 "run out time; backoff count: %d",
704 result
= FlushTags();
707 mDroppedTimer
= false;
709 } else if (!mNotificationTimer
) {
711 int32_t delay
= interval
;
713 // Convert to milliseconds
714 delay
/= PR_USEC_PER_MSEC
;
716 NS_NewTimerWithCallback(getter_AddRefs(mNotificationTimer
), this, delay
,
717 nsITimer::TYPE_ONE_SHOT
);
718 if (mNotificationTimer
) {
719 SINK_TRACE(static_cast<LogModule
*>(gContentSinkLogModuleInfo
),
721 ("nsContentSink::WillInterrupt: setting up timer with "
728 SINK_TRACE(static_cast<LogModule
*>(gContentSinkLogModuleInfo
),
730 ("nsContentSink::WillInterrupt: flushing tags "
732 result
= FlushTags();
741 void nsContentSink::WillResumeImpl() {
742 SINK_TRACE(static_cast<LogModule
*>(gContentSinkLogModuleInfo
),
743 SINK_TRACE_CALLS
, ("nsContentSink::WillResume: this=%p", this));
748 nsresult
nsContentSink::DidProcessATokenImpl() {
749 if (mRunsToCompletion
|| !mParser
) {
753 // Get the current user event time
754 PresShell
* presShell
= mDocument
->GetPresShell();
756 // If there's no pres shell in the document, return early since
757 // we're not laying anything out here.
761 // Increase before comparing to gEventProbeRate
764 // Check if there's a pending event
765 if (StaticPrefs::content_sink_pending_event_mode() != 0 &&
767 (mDeflectedCount
% StaticPrefs::content_sink_event_probe_rate()) == 0) {
768 nsViewManager
* vm
= presShell
->GetViewManager();
769 NS_ENSURE_TRUE(vm
, NS_ERROR_FAILURE
);
770 nsCOMPtr
<nsIWidget
> widget
= vm
->GetRootWidget();
771 mHasPendingEvent
= widget
&& widget
->HasPendingInputEvent();
774 if (mHasPendingEvent
&& StaticPrefs::content_sink_pending_event_mode() == 2) {
775 return NS_ERROR_HTMLPARSER_INTERRUPTED
;
778 // Have we processed enough tokens to check time?
779 if (!mHasPendingEvent
&&
781 uint32_t(mDynamicLowerValue
782 ? StaticPrefs::content_sink_interactive_deflect_count()
783 : StaticPrefs::content_sink_perf_deflect_count())) {
789 // Check if it's time to return to the main event loop
790 if (PR_IntervalToMicroseconds(PR_IntervalNow()) > mCurrentParseEndTime
) {
791 return NS_ERROR_HTMLPARSER_INTERRUPTED
;
797 //----------------------------------------------------------------------
799 void nsContentSink::BeginUpdate(Document
* aDocument
) {
800 // Remember nested updates from updates that we started.
801 if (mInNotification
> 0 && mUpdatesInNotification
< 2) {
802 ++mUpdatesInNotification
;
805 // If we're in a script and we didn't do the notification,
806 // something else in the script processing caused the
807 // notification to occur. Since this could result in frame
808 // creation, make sure we've flushed everything before we
811 if (!mInNotification
++) {
816 void nsContentSink::EndUpdate(Document
* aDocument
) {
817 // If we're in a script and we didn't do the notification,
818 // something else in the script processing caused the
819 // notification to occur. Update our notion of how much
820 // has been flushed to include any new content if ending
821 // this update leaves us not inside a notification.
822 if (!--mInNotification
) {
827 void nsContentSink::DidBuildModelImpl(bool aTerminated
) {
828 MOZ_ASSERT(aTerminated
|| (mParser
&& mParser
->IsParserClosed()) ||
829 mDocument
->GetReadyStateEnum() == Document::READYSTATE_LOADING
,
831 mDocument
->SetReadyStateInternal(Document::READYSTATE_INTERACTIVE
);
834 mScriptLoader
->ParsingComplete(aTerminated
);
835 if (!mPendingSheetCount
) {
836 mScriptLoader
->DeferCheckpointReached();
840 if (!mDocument
->HaveFiredDOMTitleChange()) {
841 mDocument
->NotifyPossibleTitleChange(false);
844 // Cancel a timer if we had one out there
845 if (mNotificationTimer
) {
846 SINK_TRACE(static_cast<LogModule
*>(gContentSinkLogModuleInfo
),
848 ("nsContentSink::DidBuildModel: canceling notification "
850 mNotificationTimer
->Cancel();
851 mNotificationTimer
= nullptr;
855 void nsContentSink::DropParserAndPerfHint(void) {
857 // Make sure we don't unblock unload too many times
862 // Do this hack to make sure that the parser
863 // doesn't get destroyed, accidently, before
864 // the circularity, between sink & parser, is
866 // Drop our reference to the parser to get rid of a circular
868 RefPtr
<nsParserBase
> kungFuDeathGrip
= std::move(mParser
);
869 mozilla::Unused
<< kungFuDeathGrip
;
871 // Call UnblockOnload only if mRunsToComletion is false and if
872 // we have already started loading because it's possible that this function
873 // is called (i.e. the parser is terminated) before we start loading due to
874 // destroying the window inside unload event callbacks for the previous
876 if (!mRunsToCompletion
&& mIsBlockingOnload
) {
877 mDocument
->UnblockOnload(true);
878 mIsBlockingOnload
= false;
882 bool nsContentSink::IsScriptExecutingImpl() {
883 return !!mScriptLoader
->GetCurrentScript();
886 nsresult
nsContentSink::WillParseImpl(void) {
887 if (mRunsToCompletion
|| !mDocument
) {
891 PresShell
* presShell
= mDocument
->GetPresShell();
896 uint32_t currentTime
= PR_IntervalToMicroseconds(PR_IntervalNow());
898 if (StaticPrefs::content_sink_enable_perf_mode() == 0) {
899 nsViewManager
* vm
= presShell
->GetViewManager();
900 NS_ENSURE_TRUE(vm
, NS_ERROR_FAILURE
);
901 uint32_t lastEventTime
;
902 vm
->GetLastUserEventTime(lastEventTime
);
904 bool newDynLower
= mDocument
->IsInBackgroundWindow() ||
905 ((currentTime
- mBeginLoadTime
) >
906 StaticPrefs::content_sink_initial_perf_time() &&
907 (currentTime
- lastEventTime
) <
908 StaticPrefs::content_sink_interactive_time());
910 if (mDynamicLowerValue
!= newDynLower
) {
911 mDynamicLowerValue
= newDynLower
;
916 mHasPendingEvent
= false;
918 mCurrentParseEndTime
=
919 currentTime
+ (mDynamicLowerValue
920 ? StaticPrefs::content_sink_interactive_parse_time()
921 : StaticPrefs::content_sink_perf_parse_time());
926 void nsContentSink::WillBuildModelImpl() {
927 if (!mRunsToCompletion
) {
928 mDocument
->BlockOnload();
929 mIsBlockingOnload
= true;
931 mBeginLoadTime
= PR_IntervalToMicroseconds(PR_IntervalNow());
934 mDocument
->ResetScrolledToRefAlready();
936 if (mProcessLinkHeaderEvent
.get()) {
937 mProcessLinkHeaderEvent
.Revoke();
939 DoProcessLinkHeader();
944 void nsContentSink::NotifyDocElementCreated(Document
* aDoc
) {
945 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
947 nsCOMPtr
<nsIObserverService
> observerService
=
948 mozilla::services::GetObserverService();
949 MOZ_ASSERT(observerService
);
951 auto* win
= nsGlobalWindowInner::Cast(aDoc
->GetInnerWindow());
952 bool fireInitialInsertion
= !win
|| !win
->DidFireDocElemInserted();
954 win
->SetDidFireDocElemInserted();
956 if (fireInitialInsertion
) {
957 observerService
->NotifyObservers(ToSupports(aDoc
),
958 "initial-document-element-inserted", u
"");
960 observerService
->NotifyObservers(ToSupports(aDoc
),
961 "document-element-inserted", u
"");
963 nsContentUtils::DispatchChromeEvent(aDoc
, aDoc
, u
"DOMDocElementInserted"_ns
,
964 CanBubble::eYes
, Cancelable::eNo
);
968 nsContentSink::GetName(nsACString
& aName
) {
969 aName
.AssignLiteral("nsContentSink_timer");