Bug 1842773 - Part 5: Add ArrayBuffer.prototype.{maxByteLength,resizable} getters...
[gecko.git] / dom / base / nsContentSink.cpp
blobe967e6701560cdfcd55122668355dac7fbb817da
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(
339 aHeader.mHref, isAlternate, aHeader.mTitle, aHeader.mIntegrity,
340 aHeader.mType, aHeader.mMedia, aHeader.mReferrerPolicy,
341 StaticPrefs::network_fetchpriority_enabled() ? aHeader.mFetchPriority
342 : EmptyString());
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
352 return NS_OK;
355 nsAutoString mimeType;
356 nsAutoString params;
357 nsContentUtils::SplitMimeType(aType, mimeType, params);
359 // see bug 18817
360 if (!mimeType.IsEmpty() && !mimeType.LowerCaseEqualsLiteral("text/css")) {
361 // Unknown stylesheet language
362 return NS_OK;
365 nsCOMPtr<nsIURI> url;
366 nsresult rv = NS_NewURI(getter_AddRefs(url), aHref, nullptr,
367 mDocument->GetDocBaseURI());
369 if (NS_FAILED(rv)) {
370 // The URI is bad, move along, don't propagate the error (for now)
371 return NS_OK;
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{
385 *mDocument,
386 nullptr,
387 url.forget(),
388 nullptr,
389 referrerInfo.forget(),
390 CORS_NONE,
391 aTitle,
392 aMedia,
393 aIntegrity,
394 /* nonce = */ u""_ns,
395 aAlternate ? Loader::HasAlternateRel::Yes : Loader::HasAlternateRel::No,
396 Loader::IsInline::No,
397 Loader::IsExplicitlyEnabled::No,
398 fetchPriority,
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();
412 return NS_OK;
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());
424 if (uri) {
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());
445 if (!uri) {
446 // URL parsing failed.
447 return;
450 nsAttrValue asAttr;
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);
462 return;
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();
481 return;
484 RefPtr<mozilla::dom::MediaList> mediaList =
485 mozilla::dom::MediaList::Create(NS_ConvertUTF16toUTF8(aMedia));
486 if (!mediaList->Matches(*mDocument)) {
487 return;
490 if (aHref.IsEmpty()) {
491 return;
494 if (!net::IsScriptLikeOrInvalid(aAs)) {
495 return;
498 auto encoding = mDocument->GetDocumentCharacterSet();
499 nsCOMPtr<nsIURI> uri;
500 NS_NewURI(getter_AddRefs(uri), aHref, encoding, mDocument->GetDocBaseURI());
501 if (!uri) {
502 return;
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);
519 } else {
520 nsCOMPtr<nsIURI> uri;
521 NS_NewURI(getter_AddRefs(uri), aHref);
522 if (!uri) {
523 return;
525 nsresult rv;
526 bool isLocalResource = false;
527 rv = NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
528 &isLocalResource);
529 if (NS_SUCCEEDED(rv) && !isLocalResource) {
530 nsAutoCString host;
531 uri->GetHost(host);
532 CopyUTF8toUTF16(host, hostname);
534 isHttps = uri->SchemeIs("https");
537 if (!hostname.IsEmpty() && HTMLDNSPrefetch::IsAllowed(mDocument)) {
538 OriginAttributes oa;
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
568 return;
571 mDeferredLayoutStart = true;
573 if (!aIgnorePendingSheets &&
574 (WaitForPendingSheets() || mDocument->HasPendingInitialTranslation())) {
575 // Bail out; we'll start layout when the sheets and l10n load
576 return;
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.
596 FlushTags();
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().
607 // (Bug 153815)
608 if (presShell && !presShell->DidInitialize()) {
609 nsresult rv = presShell->Initialize();
610 if (NS_FAILED(rv)) {
611 return;
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) {
622 mInNotification++;
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();
634 mInNotification--;
637 NS_IMETHODIMP
638 nsContentSink::Notify(nsITimer* timer) {
639 if (mParsing) {
640 // We shouldn't interfere with our normal DidProcessAToken logic
641 mDroppedTimer = true;
642 return NS_OK;
645 if (WaitForPendingSheets()) {
646 mDeferredFlushTags = true;
647 } else {
648 FlushTags();
650 // Now try and scroll to the reference
651 // XXX Should we scroll unconditionally for history loads??
652 ScrollToRef();
655 mNotificationTimer = nullptr;
656 return NS_OK;
659 bool nsContentSink::IsTimeToNotify() {
660 if (!StaticPrefs::content_notify_ontimer() || !mLayoutStarted ||
661 !mBackoffCount || mInMonolithicContainer) {
662 return false;
665 if (WaitForPendingSheets()) {
666 mDeferredFlushTags = true;
667 return false;
670 PRTime now = PR_Now();
672 int64_t interval = GetNotificationInterval();
673 int64_t diff = now - mLastNotificationTime;
675 if (diff > interval) {
676 mBackoffCount--;
677 return true;
680 return false;
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) {
699 mBackoffCount--;
700 SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo),
701 SINK_TRACE_REFLOW,
702 ("nsContentSink::WillInterrupt: flushing tags since we've "
703 "run out time; backoff count: %d",
704 mBackoffCount));
705 result = FlushTags();
706 if (mDroppedTimer) {
707 ScrollToRef();
708 mDroppedTimer = false;
710 } else if (!mNotificationTimer) {
711 interval -= diff;
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),
721 SINK_TRACE_REFLOW,
722 ("nsContentSink::WillInterrupt: setting up timer with "
723 "delay %d",
724 delay));
728 } else {
729 SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo),
730 SINK_TRACE_REFLOW,
731 ("nsContentSink::WillInterrupt: flushing tags "
732 "unconditionally"));
733 result = FlushTags();
735 #endif
737 mParsing = false;
739 return result;
742 void nsContentSink::WillResumeImpl() {
743 SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo),
744 SINK_TRACE_CALLS, ("nsContentSink::WillResume: this=%p", this));
746 mParsing = true;
749 nsresult nsContentSink::DidProcessATokenImpl() {
750 if (mRunsToCompletion || !mParser) {
751 return NS_OK;
754 // Get the current user event time
755 PresShell* presShell = mDocument->GetPresShell();
756 if (!presShell) {
757 // If there's no pres shell in the document, return early since
758 // we're not laying anything out here.
759 return NS_OK;
762 // Increase before comparing to gEventProbeRate
763 ++mDeflectedCount;
765 // Check if there's a pending event
766 if (StaticPrefs::content_sink_pending_event_mode() != 0 &&
767 !mHasPendingEvent &&
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 &&
781 mDeflectedCount <
782 uint32_t(mDynamicLowerValue
783 ? StaticPrefs::content_sink_interactive_deflect_count()
784 : StaticPrefs::content_sink_perf_deflect_count())) {
785 return NS_OK;
788 mDeflectedCount = 0;
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;
795 return NS_OK;
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
810 // continue.
812 if (!mInNotification++) {
813 FlushTags();
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) {
824 UpdateChildCounts();
828 void nsContentSink::DidBuildModelImpl(bool aTerminated) {
829 MOZ_ASSERT(aTerminated || (mParser && mParser->IsParserClosed()) ||
830 mDocument->GetReadyStateEnum() == Document::READYSTATE_LOADING,
831 "Bad readyState");
832 mDocument->SetReadyStateInternal(Document::READYSTATE_INTERACTIVE);
834 if (mScriptLoader) {
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),
848 SINK_TRACE_REFLOW,
849 ("nsContentSink::DidBuildModel: canceling notification "
850 "timeout"));
851 mNotificationTimer->Cancel();
852 mNotificationTimer = nullptr;
856 void nsContentSink::DropParserAndPerfHint(void) {
857 if (!mParser) {
858 // Make sure we don't unblock unload too many times
859 return;
862 // Ref. Bug 49115
863 // Do this hack to make sure that the parser
864 // doesn't get destroyed, accidently, before
865 // the circularity, between sink & parser, is
866 // actually broken.
867 // Drop our reference to the parser to get rid of a circular
868 // reference.
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
876 // document.
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) {
889 return NS_OK;
892 PresShell* presShell = mDocument->GetPresShell();
893 if (!presShell) {
894 return NS_OK;
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;
916 mDeflectedCount = 0;
917 mHasPendingEvent = false;
919 mCurrentParseEndTime =
920 currentTime + (mDynamicLowerValue
921 ? StaticPrefs::content_sink_interactive_parse_time()
922 : StaticPrefs::content_sink_perf_parse_time());
924 return NS_OK;
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();
944 /* static */
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();
954 if (win) {
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);
968 NS_IMETHODIMP
969 nsContentSink::GetName(nsACString& aName) {
970 aName.AssignLiteral("nsContentSink_timer");
971 return NS_OK;