Bug 1892041 - Part 1: Update test262 features. r=spidermonkey-reviewers,dminor
[gecko.git] / dom / base / nsContentSink.cpp
blobb7f13db239548476009d98efa0d453c1269570c5
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/. */
7 /*
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"
30 #include "nsIURI.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"
38 #include "nsAtom.h"
39 #include "nsGkAtoms.h"
40 #include "nsGlobalWindowInner.h"
41 #include "nsNetCID.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"
59 #include "Link.h"
60 #include "HTMLLinkElement.h"
61 #include "MediaList.h"
62 #include "nsString.h"
63 #include "nsStringFwd.h"
64 #include <stdint.h>
65 #include "mozilla/RefPtr.h"
66 #include "nsCOMPtr.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)
86 NS_INTERFACE_MAP_END
88 NS_IMPL_CYCLE_COLLECTION_CLASS(nsContentSink)
90 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsContentSink)
91 if (tmp->mDocument) {
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()
112 : mBackoffCount(0),
113 mLastNotificationTime(0),
114 mLayoutStarted(0),
115 mDynamicLowerValue(0),
116 mParsing(0),
117 mDroppedTimer(0),
118 mDeferredLayoutStart(0),
119 mDeferredFlushTags(0),
120 mIsDocumentObserver(0),
121 mRunsToCompletion(0),
122 mIsBlockingOnload(false),
123 mDeflectedCount(0),
124 mHasPendingEvent(false),
125 mCurrentParseEndTime(0),
126 mBeginLoadTime(0),
127 mLastSampledUserEventTime(0),
128 mInMonolithicContainer(0),
129 mInNotification(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() {
144 if (mDocument) {
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;
160 mDocument = aDoc;
162 mDocumentURI = aURI;
163 mDocShell = do_QueryInterface(aContainer);
164 mScriptLoader = mDocument->ScriptLoader();
166 if (!mRunsToCompletion) {
167 if (mDocShell) {
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;
187 return NS_OK;
190 NS_IMETHODIMP
191 nsContentSink::StyleSheetLoaded(StyleSheet* aSheet, bool aWasDeferred,
192 nsresult aStatus) {
193 MOZ_ASSERT(!mRunsToCompletion, "How come a fragment parser observed sheets?");
194 if (aWasDeferred) {
195 return NS_OK;
197 MOZ_ASSERT(mPendingSheetCount > 0, "How'd that happen?");
198 --mPendingSheetCount;
200 const bool loadedAllSheets = !mPendingSheetCount;
201 if (loadedAllSheets && (mDeferredLayoutStart || mDeferredFlushTags)) {
202 if (mDeferredFlushTags) {
203 FlushTags();
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.
212 StartLayout(false);
215 // Go ahead and try to scroll to our ref if we have one
216 ScrollToRef();
219 mScriptLoader->RemoveParserBlockingScriptExecutionBlocker();
221 if (loadedAllSheets &&
222 mDocument->GetReadyStateEnum() >= Document::READYSTATE_INTERACTIVE) {
223 mScriptLoader->DeferCheckpointReached();
226 return NS_OK;
229 nsresult nsContentSink::ProcessHTTPHeaders(nsIChannel* aChannel) {
230 nsCOMPtr<nsIHttpChannel> httpchannel(do_QueryInterface(aChannel));
232 if (!httpchannel) {
233 return NS_OK;
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();
252 if (gotLinkHeader) {
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());
264 if (NS_FAILED(rv)) {
265 mProcessLinkHeaderEvent.Forget();
269 return NS_OK;
272 void nsContentSink::DoProcessLinkHeader() {
273 for (const auto& earlyHint : mDocument->GetEarlyHints()) {
274 ProcessLinkFromHeader(earlyHint.link(), earlyHint.earlyHintPreloaderId());
277 nsAutoString value;
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
296 // one we've loaded
297 if (!nsContentUtils::LinkContextIsURI(aHeader.mAnchor,
298 mDocument->GetDocumentURI())) {
299 return NS_OK;
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)) {
334 return NS_OK;
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
351 return NS_OK;
354 nsAutoString mimeType;
355 nsAutoString params;
356 nsContentUtils::SplitMimeType(aType, mimeType, params);
358 // see bug 18817
359 if (!mimeType.IsEmpty() && !mimeType.LowerCaseEqualsLiteral("text/css")) {
360 // Unknown stylesheet language
361 return NS_OK;
364 nsCOMPtr<nsIURI> url;
365 nsresult rv = NS_NewURI(getter_AddRefs(url), aHref, nullptr,
366 mDocument->GetDocBaseURI());
368 if (NS_FAILED(rv)) {
369 // The URI is bad, move along, don't propagate the error (for now)
370 return NS_OK;
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{
384 *mDocument,
385 nullptr,
386 url.forget(),
387 nullptr,
388 referrerInfo.forget(),
389 CORS_NONE,
390 aTitle,
391 aMedia,
392 aIntegrity,
393 /* nonce = */ u""_ns,
394 aAlternate ? Loader::HasAlternateRel::Yes : Loader::HasAlternateRel::No,
395 Loader::IsInline::No,
396 Loader::IsExplicitlyEnabled::No,
397 fetchPriority,
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();
411 return NS_OK;
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());
423 if (uri) {
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());
444 if (!uri) {
445 // URL parsing failed.
446 return;
449 nsAttrValue asAttr;
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);
461 return;
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();
480 return;
483 RefPtr<mozilla::dom::MediaList> mediaList =
484 mozilla::dom::MediaList::Create(NS_ConvertUTF16toUTF8(aMedia));
485 if (!mediaList->Matches(*mDocument)) {
486 return;
489 if (aHref.IsEmpty()) {
490 return;
493 if (!net::IsScriptLikeOrInvalid(aAs)) {
494 return;
497 auto encoding = mDocument->GetDocumentCharacterSet();
498 nsCOMPtr<nsIURI> uri;
499 NS_NewURI(getter_AddRefs(uri), aHref, encoding, mDocument->GetDocBaseURI());
500 if (!uri) {
501 return;
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);
518 } else {
519 nsCOMPtr<nsIURI> uri;
520 NS_NewURI(getter_AddRefs(uri), aHref);
521 if (!uri) {
522 return;
524 nsresult rv;
525 bool isLocalResource = false;
526 rv = NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
527 &isLocalResource);
528 if (NS_SUCCEEDED(rv) && !isLocalResource) {
529 nsAutoCString host;
530 uri->GetHost(host);
531 CopyUTF8toUTF16(host, hostname);
533 isHttps = uri->SchemeIs("https");
536 if (!hostname.IsEmpty() && HTMLDNSPrefetch::IsAllowed(mDocument)) {
537 OriginAttributes oa;
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
567 return;
570 mDeferredLayoutStart = true;
572 if (!aIgnorePendingSheets &&
573 (WaitForPendingSheets() || mDocument->HasPendingInitialTranslation())) {
574 // Bail out; we'll start layout when the sheets and l10n load
575 return;
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.
595 FlushTags();
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().
606 // (Bug 153815)
607 if (presShell && !presShell->DidInitialize()) {
608 nsresult rv = presShell->Initialize();
609 if (NS_FAILED(rv)) {
610 return;
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) {
621 mInNotification++;
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();
633 mInNotification--;
636 NS_IMETHODIMP
637 nsContentSink::Notify(nsITimer* timer) {
638 if (mParsing) {
639 // We shouldn't interfere with our normal DidProcessAToken logic
640 mDroppedTimer = true;
641 return NS_OK;
644 if (WaitForPendingSheets()) {
645 mDeferredFlushTags = true;
646 } else {
647 FlushTags();
649 // Now try and scroll to the reference
650 // XXX Should we scroll unconditionally for history loads??
651 ScrollToRef();
654 mNotificationTimer = nullptr;
655 return NS_OK;
658 bool nsContentSink::IsTimeToNotify() {
659 if (!StaticPrefs::content_notify_ontimer() || !mLayoutStarted ||
660 !mBackoffCount || mInMonolithicContainer) {
661 return false;
664 if (WaitForPendingSheets()) {
665 mDeferredFlushTags = true;
666 return false;
669 PRTime now = PR_Now();
671 int64_t interval = GetNotificationInterval();
672 int64_t diff = now - mLastNotificationTime;
674 if (diff > interval) {
675 mBackoffCount--;
676 return true;
679 return false;
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) {
698 mBackoffCount--;
699 SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo),
700 SINK_TRACE_REFLOW,
701 ("nsContentSink::WillInterrupt: flushing tags since we've "
702 "run out time; backoff count: %d",
703 mBackoffCount));
704 result = FlushTags();
705 if (mDroppedTimer) {
706 ScrollToRef();
707 mDroppedTimer = false;
709 } else if (!mNotificationTimer) {
710 interval -= diff;
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),
720 SINK_TRACE_REFLOW,
721 ("nsContentSink::WillInterrupt: setting up timer with "
722 "delay %d",
723 delay));
727 } else {
728 SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo),
729 SINK_TRACE_REFLOW,
730 ("nsContentSink::WillInterrupt: flushing tags "
731 "unconditionally"));
732 result = FlushTags();
734 #endif
736 mParsing = false;
738 return result;
741 void nsContentSink::WillResumeImpl() {
742 SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo),
743 SINK_TRACE_CALLS, ("nsContentSink::WillResume: this=%p", this));
745 mParsing = true;
748 nsresult nsContentSink::DidProcessATokenImpl() {
749 if (mRunsToCompletion || !mParser) {
750 return NS_OK;
753 // Get the current user event time
754 PresShell* presShell = mDocument->GetPresShell();
755 if (!presShell) {
756 // If there's no pres shell in the document, return early since
757 // we're not laying anything out here.
758 return NS_OK;
761 // Increase before comparing to gEventProbeRate
762 ++mDeflectedCount;
764 // Check if there's a pending event
765 if (StaticPrefs::content_sink_pending_event_mode() != 0 &&
766 !mHasPendingEvent &&
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 &&
780 mDeflectedCount <
781 uint32_t(mDynamicLowerValue
782 ? StaticPrefs::content_sink_interactive_deflect_count()
783 : StaticPrefs::content_sink_perf_deflect_count())) {
784 return NS_OK;
787 mDeflectedCount = 0;
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;
794 return NS_OK;
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
809 // continue.
811 if (!mInNotification++) {
812 FlushTags();
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) {
823 UpdateChildCounts();
827 void nsContentSink::DidBuildModelImpl(bool aTerminated) {
828 MOZ_ASSERT(aTerminated || (mParser && mParser->IsParserClosed()) ||
829 mDocument->GetReadyStateEnum() == Document::READYSTATE_LOADING,
830 "Bad readyState");
831 mDocument->SetReadyStateInternal(Document::READYSTATE_INTERACTIVE);
833 if (mScriptLoader) {
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),
847 SINK_TRACE_REFLOW,
848 ("nsContentSink::DidBuildModel: canceling notification "
849 "timeout"));
850 mNotificationTimer->Cancel();
851 mNotificationTimer = nullptr;
855 void nsContentSink::DropParserAndPerfHint(void) {
856 if (!mParser) {
857 // Make sure we don't unblock unload too many times
858 return;
861 // Ref. Bug 49115
862 // Do this hack to make sure that the parser
863 // doesn't get destroyed, accidently, before
864 // the circularity, between sink & parser, is
865 // actually broken.
866 // Drop our reference to the parser to get rid of a circular
867 // reference.
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
875 // document.
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) {
888 return NS_OK;
891 PresShell* presShell = mDocument->GetPresShell();
892 if (!presShell) {
893 return NS_OK;
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;
915 mDeflectedCount = 0;
916 mHasPendingEvent = false;
918 mCurrentParseEndTime =
919 currentTime + (mDynamicLowerValue
920 ? StaticPrefs::content_sink_interactive_parse_time()
921 : StaticPrefs::content_sink_perf_parse_time());
923 return NS_OK;
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();
943 /* static */
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();
953 if (win) {
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);
967 NS_IMETHODIMP
968 nsContentSink::GetName(nsACString& aName) {
969 aName.AssignLiteral("nsContentSink_timer");
970 return NS_OK;