Bug 1665252 - remove allowpaymentrequest attribute from HTMLIFrameElement r=dom-worke...
[gecko.git] / dom / base / nsContentSink.cpp
blob112f3b98feb668c2c2297fe9a2931f97d96753e0
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/dom/Document.h"
18 #include "mozilla/dom/LinkStyle.h"
19 #include "mozilla/css/Loader.h"
20 #include "mozilla/dom/MutationObservers.h"
21 #include "mozilla/dom/SRILogHelper.h"
22 #include "mozilla/StoragePrincipalHelper.h"
23 #include "nsIDocShell.h"
24 #include "nsILoadContext.h"
25 #include "nsIPrefetchService.h"
26 #include "nsIURI.h"
27 #include "nsNetUtil.h"
28 #include "nsIMIMEHeaderParam.h"
29 #include "nsIProtocolHandler.h"
30 #include "nsIHttpChannel.h"
31 #include "nsIContent.h"
32 #include "nsPresContext.h"
33 #include "nsViewManager.h"
34 #include "nsAtom.h"
35 #include "nsGkAtoms.h"
36 #include "nsGlobalWindowInner.h"
37 #include "nsNetCID.h"
38 #include "nsIOfflineCacheUpdate.h"
39 #include "nsIApplicationCache.h"
40 #include "nsIApplicationCacheChannel.h"
41 #include "nsICookieService.h"
42 #include "nsContentUtils.h"
43 #include "nsNodeInfoManager.h"
44 #include "nsIAppShell.h"
45 #include "nsIWidget.h"
46 #include "nsWidgetsCID.h"
47 #include "mozAutoDocUpdate.h"
48 #include "nsIWebNavigation.h"
49 #include "nsGenericHTMLElement.h"
50 #include "nsHTMLDNSPrefetch.h"
51 #include "nsIObserverService.h"
52 #include "mozilla/Preferences.h"
53 #include "mozilla/dom/ServiceWorkerDescriptor.h"
54 #include "mozilla/dom/ScriptLoader.h"
55 #include "nsParserConstants.h"
56 #include "nsSandboxFlags.h"
57 #include "Link.h"
58 #include "HTMLLinkElement.h"
59 using namespace mozilla;
60 using namespace mozilla::css;
61 using namespace mozilla::dom;
63 LazyLogModule gContentSinkLogModuleInfo("nscontentsink");
65 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsContentSink)
66 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsContentSink)
68 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsContentSink)
69 NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver)
70 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
71 NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
72 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
73 NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
74 NS_INTERFACE_MAP_ENTRY(nsINamed)
75 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDocumentObserver)
76 NS_INTERFACE_MAP_END
78 NS_IMPL_CYCLE_COLLECTION_CLASS(nsContentSink)
80 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsContentSink)
81 if (tmp->mDocument) {
82 tmp->mDocument->RemoveObserver(tmp);
84 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
85 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParser)
86 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell)
87 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCSSLoader)
88 NS_IMPL_CYCLE_COLLECTION_UNLINK(mNodeInfoManager)
89 NS_IMPL_CYCLE_COLLECTION_UNLINK(mScriptLoader)
90 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
91 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
92 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsContentSink)
93 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
94 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser)
95 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell)
96 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCSSLoader)
97 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfoManager)
98 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader)
99 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
101 nsContentSink::nsContentSink()
102 : mBackoffCount(0),
103 mLastNotificationTime(0),
104 mLayoutStarted(0),
105 mDynamicLowerValue(0),
106 mParsing(0),
107 mDroppedTimer(0),
108 mDeferredLayoutStart(0),
109 mDeferredFlushTags(0),
110 mIsDocumentObserver(0),
111 mRunsToCompletion(0),
112 mIsBlockingOnload(false),
113 mDeflectedCount(0),
114 mHasPendingEvent(false),
115 mCurrentParseEndTime(0),
116 mBeginLoadTime(0),
117 mLastSampledUserEventTime(0),
118 mInMonolithicContainer(0),
119 mInNotification(0),
120 mUpdatesInNotification(0),
121 mPendingSheetCount(0) {
122 NS_ASSERTION(!mLayoutStarted, "What?");
123 NS_ASSERTION(!mDynamicLowerValue, "What?");
124 NS_ASSERTION(!mParsing, "What?");
125 NS_ASSERTION(mLastSampledUserEventTime == 0, "What?");
126 NS_ASSERTION(mDeflectedCount == 0, "What?");
127 NS_ASSERTION(!mDroppedTimer, "What?");
128 NS_ASSERTION(mInMonolithicContainer == 0, "What?");
129 NS_ASSERTION(mInNotification == 0, "What?");
130 NS_ASSERTION(!mDeferredLayoutStart, "What?");
133 nsContentSink::~nsContentSink() {
134 if (mDocument) {
135 // Remove ourselves just to be safe, though we really should have
136 // been removed in DidBuildModel if everything worked right.
137 mDocument->RemoveObserver(this);
141 nsresult nsContentSink::Init(Document* aDoc, nsIURI* aURI,
142 nsISupports* aContainer, nsIChannel* aChannel) {
143 MOZ_ASSERT(aDoc, "null ptr");
144 MOZ_ASSERT(aURI, "null ptr");
146 if (!aDoc || !aURI) {
147 return NS_ERROR_NULL_POINTER;
150 mDocument = aDoc;
152 mDocumentURI = aURI;
153 mDocShell = do_QueryInterface(aContainer);
154 mScriptLoader = mDocument->ScriptLoader();
156 if (!mRunsToCompletion) {
157 if (mDocShell) {
158 uint32_t loadType = 0;
159 mDocShell->GetLoadType(&loadType);
160 mDocument->SetChangeScrollPosWhenScrollingToRef(
161 (loadType & nsIDocShell::LOAD_CMD_HISTORY) == 0);
164 ProcessHTTPHeaders(aChannel);
167 mCSSLoader = aDoc->CSSLoader();
169 mNodeInfoManager = aDoc->NodeInfoManager();
171 mBackoffCount = StaticPrefs::content_notify_backoffcount();
173 if (StaticPrefs::content_sink_enable_perf_mode() != 0) {
174 mDynamicLowerValue = StaticPrefs::content_sink_enable_perf_mode() == 1;
175 FavorPerformanceHint(!mDynamicLowerValue, 0);
178 return NS_OK;
181 NS_IMETHODIMP
182 nsContentSink::StyleSheetLoaded(StyleSheet* aSheet, bool aWasDeferred,
183 nsresult aStatus) {
184 MOZ_ASSERT(!mRunsToCompletion, "How come a fragment parser observed sheets?");
185 if (aWasDeferred) {
186 return NS_OK;
188 MOZ_ASSERT(mPendingSheetCount > 0, "How'd that happen?");
189 --mPendingSheetCount;
191 const bool loadedAllSheets = !mPendingSheetCount;
192 if (loadedAllSheets && (mDeferredLayoutStart || mDeferredFlushTags)) {
193 if (mDeferredFlushTags) {
194 FlushTags();
196 if (mDeferredLayoutStart) {
197 // We might not have really started layout, since this sheet was still
198 // loading. Do it now. Probably doesn't matter whether we do this
199 // before or after we unblock scripts, but before feels saner. Note
200 // that if mDeferredLayoutStart is true, that means any subclass
201 // StartLayout() stuff that needs to happen has already happened, so
202 // we don't need to worry about it.
203 StartLayout(false);
206 // Go ahead and try to scroll to our ref if we have one
207 ScrollToRef();
210 mScriptLoader->RemoveParserBlockingScriptExecutionBlocker();
212 if (loadedAllSheets &&
213 mDocument->GetReadyStateEnum() >= Document::READYSTATE_INTERACTIVE) {
214 mScriptLoader->DeferCheckpointReached();
217 return NS_OK;
220 nsresult nsContentSink::ProcessHTTPHeaders(nsIChannel* aChannel) {
221 nsCOMPtr<nsIHttpChannel> httpchannel(do_QueryInterface(aChannel));
223 if (!httpchannel) {
224 return NS_OK;
227 // Note that the only header we care about is the "link" header, since we
228 // have all the infrastructure for kicking off stylesheet loads.
230 nsAutoCString linkHeader;
232 nsresult rv = httpchannel->GetResponseHeader("link"_ns, linkHeader);
233 if (NS_SUCCEEDED(rv) && !linkHeader.IsEmpty()) {
234 mDocument->SetHeaderData(nsGkAtoms::link,
235 NS_ConvertASCIItoUTF16(linkHeader));
237 NS_ASSERTION(!mProcessLinkHeaderEvent.get(),
238 "Already dispatched an event?");
240 mProcessLinkHeaderEvent =
241 NewNonOwningRunnableMethod("nsContentSink::DoProcessLinkHeader", this,
242 &nsContentSink::DoProcessLinkHeader);
243 rv = NS_DispatchToCurrentThread(mProcessLinkHeaderEvent.get());
244 if (NS_FAILED(rv)) {
245 mProcessLinkHeaderEvent.Forget();
249 return NS_OK;
252 void nsContentSink::DoProcessLinkHeader() {
253 nsAutoString value;
254 mDocument->GetHeaderData(nsGkAtoms::link, value);
255 ProcessLinkHeader(value);
258 // check whether the Link header field applies to the context resource
259 // see <http://tools.ietf.org/html/rfc5988#section-5.2>
261 bool nsContentSink::LinkContextIsOurDocument(const nsAString& aAnchor) {
262 if (aAnchor.IsEmpty()) {
263 // anchor parameter not present or empty -> same document reference
264 return true;
267 nsIURI* docUri = mDocument->GetDocumentURI();
269 // the document URI might contain a fragment identifier ("#...')
270 // we want to ignore that because it's invisible to the server
271 // and just affects the local interpretation in the recipient
272 nsCOMPtr<nsIURI> contextUri;
273 nsresult rv = NS_GetURIWithoutRef(docUri, getter_AddRefs(contextUri));
275 if (NS_FAILED(rv)) {
276 // copying failed
277 return false;
280 // resolve anchor against context
281 nsCOMPtr<nsIURI> resolvedUri;
282 rv = NS_NewURI(getter_AddRefs(resolvedUri), aAnchor, nullptr, contextUri);
284 if (NS_FAILED(rv)) {
285 // resolving failed
286 return false;
289 bool same;
290 rv = contextUri->Equals(resolvedUri, &same);
291 if (NS_FAILED(rv)) {
292 // comparison failed
293 return false;
296 return same;
299 // Decode a parameter value using the encoding defined in RFC 5987 (in place)
301 // charset "'" [ language ] "'" value-chars
303 // returns true when decoding happened successfully (otherwise leaves
304 // passed value alone)
305 bool nsContentSink::Decode5987Format(nsAString& aEncoded) {
306 nsresult rv;
307 nsCOMPtr<nsIMIMEHeaderParam> mimehdrpar =
308 do_GetService(NS_MIMEHEADERPARAM_CONTRACTID, &rv);
309 if (NS_FAILED(rv)) return false;
311 nsAutoCString asciiValue;
313 const char16_t* encstart = aEncoded.BeginReading();
314 const char16_t* encend = aEncoded.EndReading();
316 // create a plain ASCII string, aborting if we can't do that
317 // converted form is always shorter than input
318 while (encstart != encend) {
319 if (*encstart > 0 && *encstart < 128) {
320 asciiValue.Append((char)*encstart);
321 } else {
322 return false;
324 encstart++;
327 nsAutoString decoded;
328 nsAutoCString language;
330 rv = mimehdrpar->DecodeRFC5987Param(asciiValue, language, decoded);
331 if (NS_FAILED(rv)) return false;
333 aEncoded = decoded;
334 return true;
337 nsresult nsContentSink::ProcessLinkHeader(const nsAString& aLinkData) {
338 nsresult rv = NS_OK;
340 // keep track where we are within the header field
341 bool seenParameters = false;
343 // parse link content and call process style link
344 nsAutoString href;
345 nsAutoString rel;
346 nsAutoString title;
347 nsAutoString titleStar;
348 nsAutoString integrity;
349 nsAutoString srcset;
350 nsAutoString sizes;
351 nsAutoString type;
352 nsAutoString media;
353 nsAutoString anchor;
354 nsAutoString crossOrigin;
355 nsAutoString referrerPolicy;
356 nsAutoString as;
358 crossOrigin.SetIsVoid(true);
360 // copy to work buffer
361 nsAutoString stringList(aLinkData);
363 // put an extra null at the end
364 stringList.Append(kNullCh);
366 char16_t* start = stringList.BeginWriting();
367 char16_t* end = start;
368 char16_t* last = start;
369 char16_t endCh;
371 while (*start != kNullCh) {
372 // skip leading space
373 while ((*start != kNullCh) && nsCRT::IsAsciiSpace(*start)) {
374 ++start;
377 end = start;
378 last = end - 1;
380 bool wasQuotedString = false;
382 // look for semicolon or comma
383 while (*end != kNullCh && *end != kSemicolon && *end != kComma) {
384 char16_t ch = *end;
386 if (ch == kQuote || ch == kLessThan) {
387 // quoted string
389 char16_t quote = ch;
390 if (quote == kLessThan) {
391 quote = kGreaterThan;
394 wasQuotedString = (ch == kQuote);
396 char16_t* closeQuote = (end + 1);
398 // seek closing quote
399 while (*closeQuote != kNullCh && quote != *closeQuote) {
400 // in quoted-string, "\" is an escape character
401 if (wasQuotedString && *closeQuote == kBackSlash &&
402 *(closeQuote + 1) != kNullCh) {
403 ++closeQuote;
406 ++closeQuote;
409 if (quote == *closeQuote) {
410 // found closer
412 // skip to close quote
413 end = closeQuote;
415 last = end - 1;
417 ch = *(end + 1);
419 if (ch != kNullCh && ch != kSemicolon && ch != kComma) {
420 // end string here
421 *(++end) = kNullCh;
423 ch = *(end + 1);
425 // keep going until semi or comma
426 while (ch != kNullCh && ch != kSemicolon && ch != kComma) {
427 ++end;
429 ch = *(end + 1);
435 ++end;
436 ++last;
439 endCh = *end;
441 // end string here
442 *end = kNullCh;
444 if (start < end) {
445 if ((*start == kLessThan) && (*last == kGreaterThan)) {
446 *last = kNullCh;
448 // first instance of <...> wins
449 // also, do not allow hrefs after the first param was seen
450 if (href.IsEmpty() && !seenParameters) {
451 href = (start + 1);
452 href.StripWhitespace();
454 } else {
455 char16_t* equals = start;
456 seenParameters = true;
458 while ((*equals != kNullCh) && (*equals != kEqual)) {
459 equals++;
462 if (*equals != kNullCh) {
463 *equals = kNullCh;
464 nsAutoString attr(start);
465 attr.StripWhitespace();
467 char16_t* value = ++equals;
468 while (nsCRT::IsAsciiSpace(*value)) {
469 value++;
472 if ((*value == kQuote) && (*value == *last)) {
473 *last = kNullCh;
474 value++;
477 if (wasQuotedString) {
478 // unescape in-place
479 char16_t* unescaped = value;
480 char16_t* src = value;
482 while (*src != kNullCh) {
483 if (*src == kBackSlash && *(src + 1) != kNullCh) {
484 src++;
486 *unescaped++ = *src++;
489 *unescaped = kNullCh;
492 if (attr.LowerCaseEqualsLiteral("rel")) {
493 if (rel.IsEmpty()) {
494 rel = value;
495 rel.CompressWhitespace();
497 } else if (attr.LowerCaseEqualsLiteral("title")) {
498 if (title.IsEmpty()) {
499 title = value;
500 title.CompressWhitespace();
502 } else if (attr.LowerCaseEqualsLiteral("title*")) {
503 if (titleStar.IsEmpty() && !wasQuotedString) {
504 // RFC 5987 encoding; uses token format only, so skip if we get
505 // here with a quoted-string
506 nsAutoString tmp;
507 tmp = value;
508 if (Decode5987Format(tmp)) {
509 titleStar = tmp;
510 titleStar.CompressWhitespace();
511 } else {
512 // header value did not parse, throw it away
513 titleStar.Truncate();
516 } else if (attr.LowerCaseEqualsLiteral("type")) {
517 if (type.IsEmpty()) {
518 type = value;
519 type.StripWhitespace();
521 } else if (attr.LowerCaseEqualsLiteral("media")) {
522 if (media.IsEmpty()) {
523 media = value;
525 // The HTML5 spec is formulated in terms of the CSS3 spec,
526 // which specifies that media queries are case insensitive.
527 nsContentUtils::ASCIIToLower(media);
529 } else if (attr.LowerCaseEqualsLiteral("anchor")) {
530 if (anchor.IsEmpty()) {
531 anchor = value;
532 anchor.StripWhitespace();
534 } else if (attr.LowerCaseEqualsLiteral("crossorigin")) {
535 if (crossOrigin.IsVoid()) {
536 crossOrigin.SetIsVoid(false);
537 crossOrigin = value;
538 crossOrigin.StripWhitespace();
540 } else if (attr.LowerCaseEqualsLiteral("as")) {
541 if (as.IsEmpty()) {
542 as = value;
543 as.CompressWhitespace();
545 } else if (attr.LowerCaseEqualsLiteral("referrerpolicy")) {
546 // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#referrer-policy-attribute
547 // Specs says referrer policy attribute is an enumerated attribute,
548 // case insensitive and includes the empty string
549 // We will parse the value with AttributeReferrerPolicyFromString
550 // later, which will handle parsing it as an enumerated attribute.
551 if (referrerPolicy.IsEmpty()) {
552 referrerPolicy = value;
554 } else if (attr.LowerCaseEqualsLiteral("integrity")) {
555 if (integrity.IsEmpty()) {
556 integrity = value;
558 } else if (attr.LowerCaseEqualsLiteral("imagesrcset")) {
559 if (srcset.IsEmpty()) {
560 srcset = value;
562 } else if (attr.LowerCaseEqualsLiteral("imagesizes")) {
563 if (sizes.IsEmpty()) {
564 sizes = value;
571 if (endCh == kComma) {
572 // hit a comma, process what we've got so far
574 href.Trim(" \t\n\r\f"); // trim HTML5 whitespace
575 if (!href.IsEmpty() && !rel.IsEmpty()) {
576 rv = ProcessLinkFromHeader(
577 anchor, href, rel,
578 // prefer RFC 5987 variant over non-I18zed version
579 titleStar.IsEmpty() ? title : titleStar, integrity, srcset, sizes,
580 type, media, crossOrigin, referrerPolicy, as);
583 href.Truncate();
584 rel.Truncate();
585 title.Truncate();
586 type.Truncate();
587 integrity.Truncate();
588 srcset.Truncate();
589 sizes.Truncate();
590 media.Truncate();
591 anchor.Truncate();
592 referrerPolicy.Truncate();
593 crossOrigin.SetIsVoid(true);
594 as.Truncate();
596 seenParameters = false;
599 start = ++end;
602 href.Trim(" \t\n\r\f"); // trim HTML5 whitespace
603 if (!href.IsEmpty() && !rel.IsEmpty()) {
604 rv = ProcessLinkFromHeader(
605 anchor, href, rel,
606 // prefer RFC 5987 variant over non-I18zed version
607 titleStar.IsEmpty() ? title : titleStar, integrity, srcset, sizes, type,
608 media, crossOrigin, referrerPolicy, as);
611 return rv;
614 nsresult nsContentSink::ProcessLinkFromHeader(
615 const nsAString& aAnchor, const nsAString& aHref, const nsAString& aRel,
616 const nsAString& aTitle, const nsAString& aIntegrity,
617 const nsAString& aSrcset, const nsAString& aSizes, const nsAString& aType,
618 const nsAString& aMedia, const nsAString& aCrossOrigin,
619 const nsAString& aReferrerPolicy, const nsAString& aAs) {
620 uint32_t linkTypes = LinkStyle::ParseLinkTypes(aRel);
622 // The link relation may apply to a different resource, specified
623 // in the anchor parameter. For the link relations supported so far,
624 // we simply abort if the link applies to a resource different to the
625 // one we've loaded
626 if (!LinkContextIsOurDocument(aAnchor)) {
627 return NS_OK;
630 if (nsContentUtils::PrefetchPreloadEnabled(mDocShell)) {
631 // prefetch href if relation is "next" or "prefetch"
632 if ((linkTypes & LinkStyle::eNEXT) || (linkTypes & LinkStyle::ePREFETCH)) {
633 PrefetchHref(aHref, aAs, aType, aMedia);
636 if (!aHref.IsEmpty() && (linkTypes & LinkStyle::eDNS_PREFETCH)) {
637 PrefetchDNS(aHref);
640 if (!aHref.IsEmpty() && (linkTypes & LinkStyle::ePRECONNECT)) {
641 Preconnect(aHref, aCrossOrigin);
644 if (linkTypes & LinkStyle::ePRELOAD) {
645 PreloadHref(aHref, aAs, aType, aMedia, aIntegrity, aSrcset, aSizes,
646 aCrossOrigin, aReferrerPolicy);
650 // is it a stylesheet link?
651 if (!(linkTypes & LinkStyle::eSTYLESHEET)) {
652 return NS_OK;
655 bool isAlternate = linkTypes & LinkStyle::eALTERNATE;
656 return ProcessStyleLinkFromHeader(aHref, isAlternate, aTitle, aIntegrity,
657 aType, aMedia, aReferrerPolicy);
660 nsresult nsContentSink::ProcessStyleLinkFromHeader(
661 const nsAString& aHref, bool aAlternate, const nsAString& aTitle,
662 const nsAString& aIntegrity, const nsAString& aType,
663 const nsAString& aMedia, const nsAString& aReferrerPolicy) {
664 if (aAlternate && aTitle.IsEmpty()) {
665 // alternates must have title return without error, for now
666 return NS_OK;
669 nsAutoString mimeType;
670 nsAutoString params;
671 nsContentUtils::SplitMimeType(aType, mimeType, params);
673 // see bug 18817
674 if (!mimeType.IsEmpty() && !mimeType.LowerCaseEqualsLiteral("text/css")) {
675 // Unknown stylesheet language
676 return NS_OK;
679 nsCOMPtr<nsIURI> url;
680 nsresult rv = NS_NewURI(getter_AddRefs(url), aHref, nullptr,
681 mDocument->GetDocBaseURI());
683 if (NS_FAILED(rv)) {
684 // The URI is bad, move along, don't propagate the error (for now)
685 return NS_OK;
688 // Link header is working like a <link> node, so referrerPolicy attr should
689 // have higher priority than referrer policy from document.
690 ReferrerPolicy policy =
691 ReferrerInfo::ReferrerPolicyAttributeFromString(aReferrerPolicy);
692 nsCOMPtr<nsIReferrerInfo> referrerInfo =
693 ReferrerInfo::CreateFromDocumentAndPolicyOverride(mDocument, policy);
695 Loader::SheetInfo info{
696 *mDocument,
697 nullptr,
698 url.forget(),
699 nullptr,
700 referrerInfo.forget(),
701 CORS_NONE,
702 aTitle,
703 aMedia,
704 aIntegrity,
705 /* nonce = */ u""_ns,
706 aAlternate ? Loader::HasAlternateRel::Yes : Loader::HasAlternateRel::No,
707 Loader::IsInline::No,
708 Loader::IsExplicitlyEnabled::No,
711 auto loadResultOrErr =
712 mCSSLoader->LoadStyleLink(info, mRunsToCompletion ? nullptr : this);
713 if (loadResultOrErr.isErr()) {
714 return loadResultOrErr.unwrapErr();
717 if (loadResultOrErr.inspect().ShouldBlock() && !mRunsToCompletion) {
718 ++mPendingSheetCount;
719 mScriptLoader->AddParserBlockingScriptExecutionBlocker();
722 return NS_OK;
725 void nsContentSink::PrefetchHref(const nsAString& aHref, const nsAString& aAs,
726 const nsAString& aType,
727 const nsAString& aMedia) {
728 nsCOMPtr<nsIPrefetchService> prefetchService(components::Prefetch::Service());
729 if (prefetchService) {
730 // construct URI using document charset
731 auto encoding = mDocument->GetDocumentCharacterSet();
732 nsCOMPtr<nsIURI> uri;
733 NS_NewURI(getter_AddRefs(uri), aHref, encoding, mDocument->GetDocBaseURI());
734 if (uri) {
735 auto referrerInfo = MakeRefPtr<ReferrerInfo>(*mDocument);
736 referrerInfo = referrerInfo->CloneWithNewOriginalReferrer(mDocumentURI);
738 prefetchService->PrefetchURI(uri, referrerInfo, mDocument, true);
743 void nsContentSink::PreloadHref(const nsAString& aHref, const nsAString& aAs,
744 const nsAString& aType, const nsAString& aMedia,
745 const nsAString& aIntegrity,
746 const nsAString& aSrcset,
747 const nsAString& aSizes, const nsAString& aCORS,
748 const nsAString& aReferrerPolicy) {
749 nsAttrValue asAttr;
750 HTMLLinkElement::ParseAsValue(aAs, asAttr);
751 auto policyType = HTMLLinkElement::AsValueToContentPolicy(asAttr);
753 if (policyType == nsIContentPolicy::TYPE_INVALID) {
754 // Ignore preload with a wrong or empty as attribute.
755 return;
758 nsAutoString mimeType;
759 nsAutoString notUsed;
760 nsContentUtils::SplitMimeType(aType, mimeType, notUsed);
761 if (!HTMLLinkElement::CheckPreloadAttrs(asAttr, mimeType, aMedia,
762 mDocument)) {
763 policyType = nsIContentPolicy::TYPE_INVALID;
766 auto encoding = mDocument->GetDocumentCharacterSet();
767 nsCOMPtr<nsIURI> uri;
768 NS_NewURI(getter_AddRefs(uri), aHref, encoding, mDocument->GetDocBaseURI());
770 if (!uri) {
771 // URL parsing failed.
772 return;
775 auto referrerInfo = MakeRefPtr<ReferrerInfo>(*mDocument);
776 referrerInfo = referrerInfo->CloneWithNewOriginalReferrer(mDocumentURI);
778 mDocument->Preloads().PreloadLinkHeader(uri, aHref, policyType, aAs, aType,
779 aIntegrity, aSrcset, aSizes, aCORS,
780 aReferrerPolicy, referrerInfo);
783 void nsContentSink::PrefetchDNS(const nsAString& aHref) {
784 nsAutoString hostname;
785 bool isHttps = false;
787 if (StringBeginsWith(aHref, u"//"_ns)) {
788 hostname = Substring(aHref, 2);
789 } else {
790 nsCOMPtr<nsIURI> uri;
791 NS_NewURI(getter_AddRefs(uri), aHref);
792 if (!uri) {
793 return;
795 nsresult rv;
796 bool isLocalResource = false;
797 rv = NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
798 &isLocalResource);
799 if (NS_SUCCEEDED(rv) && !isLocalResource) {
800 nsAutoCString host;
801 uri->GetHost(host);
802 CopyUTF8toUTF16(host, hostname);
804 isHttps = uri->SchemeIs("https");
807 if (!hostname.IsEmpty() && nsHTMLDNSPrefetch::IsAllowed(mDocument)) {
808 OriginAttributes oa;
809 StoragePrincipalHelper::GetOriginAttributesForNetworkState(mDocument, oa);
811 nsHTMLDNSPrefetch::PrefetchLow(hostname, isHttps, oa,
812 mDocument->GetChannel()->GetTRRMode());
816 void nsContentSink::Preconnect(const nsAString& aHref,
817 const nsAString& aCrossOrigin) {
818 // construct URI using document charset
819 auto encoding = mDocument->GetDocumentCharacterSet();
820 nsCOMPtr<nsIURI> uri;
821 NS_NewURI(getter_AddRefs(uri), aHref, encoding, mDocument->GetDocBaseURI());
823 if (uri && mDocument) {
824 mDocument->MaybePreconnect(uri,
825 dom::Element::StringToCORSMode(aCrossOrigin));
829 nsresult nsContentSink::SelectDocAppCache(
830 nsIApplicationCache* aLoadApplicationCache, nsIURI* aManifestURI,
831 bool aFetchedWithHTTPGetOrEquiv, CacheSelectionAction* aAction) {
832 nsresult rv;
834 *aAction = CACHE_SELECTION_NONE;
836 if (aLoadApplicationCache) {
837 nsCOMPtr<nsIURI> groupURI;
838 rv = aLoadApplicationCache->GetManifestURI(getter_AddRefs(groupURI));
839 NS_ENSURE_SUCCESS(rv, rv);
841 bool equal = false;
842 rv = groupURI->Equals(aManifestURI, &equal);
843 NS_ENSURE_SUCCESS(rv, rv);
845 if (!equal) {
846 // This is a foreign entry, force a reload to avoid loading the foreign
847 // entry. The entry will be marked as foreign to avoid loading it again.
849 *aAction = CACHE_SELECTION_RELOAD;
850 } else {
851 // The http manifest attribute URI is equal to the manifest URI of
852 // the cache the document was loaded from - associate the document with
853 // that cache and invoke the cache update process.
854 #ifdef DEBUG
855 nsAutoCString docURISpec, clientID;
856 mDocumentURI->GetAsciiSpec(docURISpec);
857 aLoadApplicationCache->GetClientID(clientID);
858 SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo),
859 SINK_TRACE_CALLS,
860 ("Selection: assigning app cache %s to document %s",
861 clientID.get(), docURISpec.get()));
862 #endif
864 rv = mDocument->SetApplicationCache(aLoadApplicationCache);
865 NS_ENSURE_SUCCESS(rv, rv);
867 // Document will be added as implicit entry to the cache as part of
868 // the update process.
869 *aAction = CACHE_SELECTION_UPDATE;
871 } else {
872 // The document was not loaded from an application cache
873 // Here we know the manifest has the same origin as the
874 // document. There is call to CheckMayLoadWithReporting() on it above.
876 if (!aFetchedWithHTTPGetOrEquiv) {
877 // The document was not loaded using HTTP GET or equivalent
878 // method. The spec says to run the cache selection algorithm w/o
879 // the manifest specified.
880 *aAction = CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST;
881 } else {
882 // Always do an update in this case
883 *aAction = CACHE_SELECTION_UPDATE;
887 return NS_OK;
890 nsresult nsContentSink::SelectDocAppCacheNoManifest(
891 nsIApplicationCache* aLoadApplicationCache, nsIURI** aManifestURI,
892 CacheSelectionAction* aAction) {
893 *aManifestURI = nullptr;
894 *aAction = CACHE_SELECTION_NONE;
896 nsresult rv;
898 if (aLoadApplicationCache) {
899 // The document was loaded from an application cache, use that
900 // application cache as the document's application cache.
901 #ifdef DEBUG
902 nsAutoCString docURISpec, clientID;
903 mDocumentURI->GetAsciiSpec(docURISpec);
904 aLoadApplicationCache->GetClientID(clientID);
905 SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo),
906 SINK_TRACE_CALLS,
907 ("Selection, no manifest: assigning app cache %s to document %s",
908 clientID.get(), docURISpec.get()));
909 #endif
911 rv = mDocument->SetApplicationCache(aLoadApplicationCache);
912 NS_ENSURE_SUCCESS(rv, rv);
914 // Return the uri and invoke the update process for the selected
915 // application cache.
916 rv = aLoadApplicationCache->GetManifestURI(aManifestURI);
917 NS_ENSURE_SUCCESS(rv, rv);
919 *aAction = CACHE_SELECTION_UPDATE;
922 return NS_OK;
925 void nsContentSink::ProcessOfflineManifest(nsIContent* aElement) {
926 // Only check the manifest for root document nodes.
927 if (aElement != mDocument->GetRootElement()) {
928 return;
931 // Don't bother processing offline manifest for documents
932 // without a docshell
933 if (!mDocShell) {
934 return;
937 // Check for a manifest= attribute.
938 nsAutoString manifestSpec;
939 aElement->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::manifest,
940 manifestSpec);
941 ProcessOfflineManifest(manifestSpec);
944 void nsContentSink::ProcessOfflineManifest(const nsAString& aManifestSpec) {
945 // Don't bother processing offline manifest for documents
946 // without a docshell
947 if (!mDocShell) {
948 return;
951 // If offline storage is disabled skip processing
952 if (!StaticPrefs::browser_cache_offline_storage_enable()) {
953 return;
956 // If this document has been interecepted, let's skip the processing of the
957 // manifest.
958 if (mDocument->GetController().isSome()) {
959 return;
962 // If the docshell's in private browsing mode, we don't want to do any
963 // manifest processing.
964 nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(mDocShell);
965 if (loadContext->UsePrivateBrowsing()) {
966 return;
969 nsresult rv;
971 // Grab the application cache the document was loaded from, if any.
972 nsCOMPtr<nsIApplicationCache> applicationCache;
974 nsCOMPtr<nsIApplicationCacheChannel> applicationCacheChannel =
975 do_QueryInterface(mDocument->GetChannel());
976 if (applicationCacheChannel) {
977 bool loadedFromApplicationCache;
978 rv = applicationCacheChannel->GetLoadedFromApplicationCache(
979 &loadedFromApplicationCache);
980 if (NS_FAILED(rv)) {
981 return;
984 if (loadedFromApplicationCache) {
985 rv = applicationCacheChannel->GetApplicationCache(
986 getter_AddRefs(applicationCache));
987 if (NS_FAILED(rv)) {
988 return;
993 if (aManifestSpec.IsEmpty() && !applicationCache) {
994 // Not loaded from an application cache, and no manifest
995 // attribute. Nothing to do here.
996 return;
999 CacheSelectionAction action = CACHE_SELECTION_NONE;
1000 nsCOMPtr<nsIURI> manifestURI;
1002 if (aManifestSpec.IsEmpty()) {
1003 action = CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST;
1004 } else {
1005 nsContentUtils::NewURIWithDocumentCharset(
1006 getter_AddRefs(manifestURI), aManifestSpec, mDocument, mDocumentURI);
1007 if (!manifestURI) {
1008 return;
1011 // Documents must list a manifest from the same origin
1012 rv = mDocument->NodePrincipal()->CheckMayLoadWithReporting(
1013 manifestURI, false, mDocument->InnerWindowID());
1014 if (NS_FAILED(rv)) {
1015 action = CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST;
1016 } else {
1017 if (!nsContentUtils::OfflineAppAllowed(mDocument->NodePrincipal())) {
1018 nsCOMPtr<nsIOfflineCacheUpdateService> updateService =
1019 components::OfflineCacheUpdate::Service();
1020 if (!updateService) {
1021 return;
1023 rv = updateService->AllowOfflineApp(mDocument->NodePrincipal());
1024 if (NS_FAILED(rv)) {
1025 return;
1029 bool fetchedWithHTTPGetOrEquiv = false;
1030 nsCOMPtr<nsIHttpChannel> httpChannel(
1031 do_QueryInterface(mDocument->GetChannel()));
1032 if (httpChannel) {
1033 nsAutoCString method;
1034 rv = httpChannel->GetRequestMethod(method);
1035 if (NS_SUCCEEDED(rv))
1036 fetchedWithHTTPGetOrEquiv = method.EqualsLiteral("GET");
1039 rv = SelectDocAppCache(applicationCache, manifestURI,
1040 fetchedWithHTTPGetOrEquiv, &action);
1041 if (NS_FAILED(rv)) {
1042 return;
1047 if (action == CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST) {
1048 rv = SelectDocAppCacheNoManifest(applicationCache,
1049 getter_AddRefs(manifestURI), &action);
1050 if (NS_FAILED(rv)) {
1051 return;
1055 switch (action) {
1056 case CACHE_SELECTION_NONE:
1057 break;
1058 case CACHE_SELECTION_UPDATE: {
1059 nsCOMPtr<nsIOfflineCacheUpdateService> updateService =
1060 components::OfflineCacheUpdate::Service();
1062 if (updateService) {
1063 updateService->ScheduleOnDocumentStop(
1064 manifestURI, mDocumentURI, mDocument->NodePrincipal(), mDocument);
1066 break;
1068 case CACHE_SELECTION_RELOAD: {
1069 // This situation occurs only for toplevel documents, see bottom
1070 // of SelectDocAppCache method.
1071 // The document has been loaded from a different offline cache group than
1072 // the manifest it refers to, i.e. this is a foreign entry, mark it as
1073 // such and force a reload to avoid loading it. The next attempt will not
1074 // choose it.
1076 applicationCacheChannel->MarkOfflineCacheEntryAsForeign();
1078 nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(mDocShell);
1080 webNav->Stop(nsIWebNavigation::STOP_ALL);
1081 webNav->Reload(nsIWebNavigation::LOAD_FLAGS_NONE);
1082 break;
1084 default:
1085 NS_ASSERTION(false,
1086 "Cache selection algorithm didn't decide on proper action");
1087 break;
1091 void nsContentSink::ScrollToRef() {
1092 RefPtr<Document> document = mDocument;
1093 document->ScrollToRef();
1096 void nsContentSink::StartLayout(bool aIgnorePendingSheets) {
1097 AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("nsContentSink::StartLayout", LAYOUT,
1098 mDocumentURI->GetSpecOrDefault());
1100 if (mLayoutStarted) {
1101 // Nothing to do here
1102 return;
1105 mDeferredLayoutStart = true;
1107 if (!aIgnorePendingSheets &&
1108 (WaitForPendingSheets() || mDocument->HasPendingInitialTranslation())) {
1109 // Bail out; we'll start layout when the sheets and l10n load
1110 return;
1113 mDeferredLayoutStart = false;
1115 if (aIgnorePendingSheets) {
1116 nsContentUtils::ReportToConsole(
1117 nsIScriptError::warningFlag, "Layout"_ns, mDocument,
1118 nsContentUtils::eLAYOUT_PROPERTIES, "ForcedLayoutStart");
1121 // Notify on all our content. If none of our presshells have started layout
1122 // yet it'll be a no-op except for updating our data structures, a la
1123 // UpdateChildCounts() (because we don't want to double-notify on whatever we
1124 // have right now). If some of them _have_ started layout, we want to make
1125 // sure to flush tags instead of just calling UpdateChildCounts() after we
1126 // loop over the shells.
1127 FlushTags();
1129 mLayoutStarted = true;
1130 mLastNotificationTime = PR_Now();
1132 mDocument->SetMayStartLayout(true);
1133 RefPtr<PresShell> presShell = mDocument->GetPresShell();
1134 // Make sure we don't call Initialize() for a shell that has
1135 // already called it. This can happen when the layout frame for
1136 // an iframe is constructed *between* the Embed() call for the
1137 // docshell in the iframe, and the content sink's call to OpenBody().
1138 // (Bug 153815)
1139 if (presShell && !presShell->DidInitialize()) {
1140 nsresult rv = presShell->Initialize();
1141 if (NS_FAILED(rv)) {
1142 return;
1146 // If the document we are loading has a reference or it is a
1147 // frameset document, disable the scroll bars on the views.
1149 mDocument->SetScrollToRef(mDocument->GetDocumentURI());
1152 void nsContentSink::NotifyAppend(nsIContent* aContainer, uint32_t aStartIndex) {
1153 mInNotification++;
1156 // Scope so we call EndUpdate before we decrease mInNotification
1158 // Note that aContainer->OwnerDoc() may not be mDocument.
1159 MOZ_AUTO_DOC_UPDATE(aContainer->OwnerDoc(), true);
1160 MutationObservers::NotifyContentAppended(
1161 aContainer, aContainer->GetChildAt_Deprecated(aStartIndex));
1162 mLastNotificationTime = PR_Now();
1165 mInNotification--;
1168 NS_IMETHODIMP
1169 nsContentSink::Notify(nsITimer* timer) {
1170 if (mParsing) {
1171 // We shouldn't interfere with our normal DidProcessAToken logic
1172 mDroppedTimer = true;
1173 return NS_OK;
1176 if (WaitForPendingSheets()) {
1177 mDeferredFlushTags = true;
1178 } else {
1179 FlushTags();
1181 // Now try and scroll to the reference
1182 // XXX Should we scroll unconditionally for history loads??
1183 ScrollToRef();
1186 mNotificationTimer = nullptr;
1187 return NS_OK;
1190 bool nsContentSink::IsTimeToNotify() {
1191 if (!StaticPrefs::content_notify_ontimer() || !mLayoutStarted ||
1192 !mBackoffCount || mInMonolithicContainer) {
1193 return false;
1196 if (WaitForPendingSheets()) {
1197 mDeferredFlushTags = true;
1198 return false;
1201 PRTime now = PR_Now();
1203 int64_t interval = GetNotificationInterval();
1204 int64_t diff = now - mLastNotificationTime;
1206 if (diff > interval) {
1207 mBackoffCount--;
1208 return true;
1211 return false;
1214 nsresult nsContentSink::WillInterruptImpl() {
1215 nsresult result = NS_OK;
1217 SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo),
1218 SINK_TRACE_CALLS, ("nsContentSink::WillInterrupt: this=%p", this));
1219 #ifndef SINK_NO_INCREMENTAL
1220 if (WaitForPendingSheets()) {
1221 mDeferredFlushTags = true;
1222 } else if (StaticPrefs::content_notify_ontimer() && mLayoutStarted) {
1223 if (mBackoffCount && !mInMonolithicContainer) {
1224 int64_t now = PR_Now();
1225 int64_t interval = GetNotificationInterval();
1226 int64_t diff = now - mLastNotificationTime;
1228 // If it's already time for us to have a notification
1229 if (diff > interval || mDroppedTimer) {
1230 mBackoffCount--;
1231 SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo),
1232 SINK_TRACE_REFLOW,
1233 ("nsContentSink::WillInterrupt: flushing tags since we've "
1234 "run out time; backoff count: %d",
1235 mBackoffCount));
1236 result = FlushTags();
1237 if (mDroppedTimer) {
1238 ScrollToRef();
1239 mDroppedTimer = false;
1241 } else if (!mNotificationTimer) {
1242 interval -= diff;
1243 int32_t delay = interval;
1245 // Convert to milliseconds
1246 delay /= PR_USEC_PER_MSEC;
1248 NS_NewTimerWithCallback(getter_AddRefs(mNotificationTimer), this, delay,
1249 nsITimer::TYPE_ONE_SHOT);
1250 if (mNotificationTimer) {
1251 SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo),
1252 SINK_TRACE_REFLOW,
1253 ("nsContentSink::WillInterrupt: setting up timer with "
1254 "delay %d",
1255 delay));
1259 } else {
1260 SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo),
1261 SINK_TRACE_REFLOW,
1262 ("nsContentSink::WillInterrupt: flushing tags "
1263 "unconditionally"));
1264 result = FlushTags();
1266 #endif
1268 mParsing = false;
1270 return result;
1273 nsresult nsContentSink::WillResumeImpl() {
1274 SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo),
1275 SINK_TRACE_CALLS, ("nsContentSink::WillResume: this=%p", this));
1277 mParsing = true;
1279 return NS_OK;
1282 nsresult nsContentSink::DidProcessATokenImpl() {
1283 if (mRunsToCompletion || !mParser) {
1284 return NS_OK;
1287 // Get the current user event time
1288 PresShell* presShell = mDocument->GetPresShell();
1289 if (!presShell) {
1290 // If there's no pres shell in the document, return early since
1291 // we're not laying anything out here.
1292 return NS_OK;
1295 // Increase before comparing to gEventProbeRate
1296 ++mDeflectedCount;
1298 // Check if there's a pending event
1299 if (StaticPrefs::content_sink_pending_event_mode() != 0 &&
1300 !mHasPendingEvent &&
1301 (mDeflectedCount % StaticPrefs::content_sink_event_probe_rate()) == 0) {
1302 nsViewManager* vm = presShell->GetViewManager();
1303 NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
1304 nsCOMPtr<nsIWidget> widget;
1305 vm->GetRootWidget(getter_AddRefs(widget));
1306 mHasPendingEvent = widget && widget->HasPendingInputEvent();
1309 if (mHasPendingEvent && StaticPrefs::content_sink_pending_event_mode() == 2) {
1310 return NS_ERROR_HTMLPARSER_INTERRUPTED;
1313 // Have we processed enough tokens to check time?
1314 if (!mHasPendingEvent &&
1315 mDeflectedCount <
1316 uint32_t(mDynamicLowerValue
1317 ? StaticPrefs::content_sink_interactive_deflect_count()
1318 : StaticPrefs::content_sink_perf_deflect_count())) {
1319 return NS_OK;
1322 mDeflectedCount = 0;
1324 // Check if it's time to return to the main event loop
1325 if (PR_IntervalToMicroseconds(PR_IntervalNow()) > mCurrentParseEndTime) {
1326 return NS_ERROR_HTMLPARSER_INTERRUPTED;
1329 return NS_OK;
1332 //----------------------------------------------------------------------
1334 void nsContentSink::FavorPerformanceHint(bool perfOverStarvation,
1335 uint32_t starvationDelay) {
1336 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
1337 nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
1338 if (appShell)
1339 appShell->FavorPerformanceHint(perfOverStarvation, starvationDelay);
1342 void nsContentSink::BeginUpdate(Document* aDocument) {
1343 // Remember nested updates from updates that we started.
1344 if (mInNotification > 0 && mUpdatesInNotification < 2) {
1345 ++mUpdatesInNotification;
1348 // If we're in a script and we didn't do the notification,
1349 // something else in the script processing caused the
1350 // notification to occur. Since this could result in frame
1351 // creation, make sure we've flushed everything before we
1352 // continue.
1354 if (!mInNotification++) {
1355 FlushTags();
1359 void nsContentSink::EndUpdate(Document* aDocument) {
1360 // If we're in a script and we didn't do the notification,
1361 // something else in the script processing caused the
1362 // notification to occur. Update our notion of how much
1363 // has been flushed to include any new content if ending
1364 // this update leaves us not inside a notification.
1365 if (!--mInNotification) {
1366 UpdateChildCounts();
1370 void nsContentSink::DidBuildModelImpl(bool aTerminated) {
1371 MOZ_ASSERT(aTerminated ||
1372 mDocument->GetReadyStateEnum() == Document::READYSTATE_LOADING,
1373 "Bad readyState");
1374 mDocument->SetReadyStateInternal(Document::READYSTATE_INTERACTIVE);
1376 if (mScriptLoader) {
1377 mScriptLoader->ParsingComplete(aTerminated);
1378 if (!mPendingSheetCount) {
1379 mScriptLoader->DeferCheckpointReached();
1383 if (!mDocument->HaveFiredDOMTitleChange()) {
1384 mDocument->NotifyPossibleTitleChange(false);
1387 // Cancel a timer if we had one out there
1388 if (mNotificationTimer) {
1389 SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo),
1390 SINK_TRACE_REFLOW,
1391 ("nsContentSink::DidBuildModel: canceling notification "
1392 "timeout"));
1393 mNotificationTimer->Cancel();
1394 mNotificationTimer = nullptr;
1398 void nsContentSink::DropParserAndPerfHint(void) {
1399 if (!mParser) {
1400 // Make sure we don't unblock unload too many times
1401 return;
1404 // Ref. Bug 49115
1405 // Do this hack to make sure that the parser
1406 // doesn't get destroyed, accidently, before
1407 // the circularity, between sink & parser, is
1408 // actually broken.
1409 // Drop our reference to the parser to get rid of a circular
1410 // reference.
1411 RefPtr<nsParserBase> kungFuDeathGrip = std::move(mParser);
1412 mozilla::Unused << kungFuDeathGrip;
1414 if (mDynamicLowerValue) {
1415 // Reset the performance hint which was set to FALSE
1416 // when mDynamicLowerValue was set.
1417 FavorPerformanceHint(true, 0);
1420 // Call UnblockOnload only if mRunsToComletion is false and if
1421 // we have already started loading because it's possible that this function
1422 // is called (i.e. the parser is terminated) before we start loading due to
1423 // destroying the window inside unload event callbacks for the previous
1424 // document.
1425 if (!mRunsToCompletion && mIsBlockingOnload) {
1426 mDocument->UnblockOnload(true);
1427 mIsBlockingOnload = false;
1431 bool nsContentSink::IsScriptExecutingImpl() {
1432 return !!mScriptLoader->GetCurrentScript();
1435 nsresult nsContentSink::WillParseImpl(void) {
1436 if (mRunsToCompletion || !mDocument) {
1437 return NS_OK;
1440 PresShell* presShell = mDocument->GetPresShell();
1441 if (!presShell) {
1442 return NS_OK;
1445 uint32_t currentTime = PR_IntervalToMicroseconds(PR_IntervalNow());
1447 if (StaticPrefs::content_sink_enable_perf_mode() == 0) {
1448 nsViewManager* vm = presShell->GetViewManager();
1449 NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
1450 uint32_t lastEventTime;
1451 vm->GetLastUserEventTime(lastEventTime);
1453 bool newDynLower = mDocument->IsInBackgroundWindow() ||
1454 ((currentTime - mBeginLoadTime) >
1455 StaticPrefs::content_sink_initial_perf_time() &&
1456 (currentTime - lastEventTime) <
1457 StaticPrefs::content_sink_interactive_time());
1459 if (mDynamicLowerValue != newDynLower) {
1460 FavorPerformanceHint(!newDynLower, 0);
1461 mDynamicLowerValue = newDynLower;
1465 mDeflectedCount = 0;
1466 mHasPendingEvent = false;
1468 mCurrentParseEndTime =
1469 currentTime + (mDynamicLowerValue
1470 ? StaticPrefs::content_sink_interactive_parse_time()
1471 : StaticPrefs::content_sink_perf_parse_time());
1473 return NS_OK;
1476 void nsContentSink::WillBuildModelImpl() {
1477 if (!mRunsToCompletion) {
1478 mDocument->BlockOnload();
1479 mIsBlockingOnload = true;
1481 mBeginLoadTime = PR_IntervalToMicroseconds(PR_IntervalNow());
1484 mDocument->ResetScrolledToRefAlready();
1486 if (mProcessLinkHeaderEvent.get()) {
1487 mProcessLinkHeaderEvent.Revoke();
1489 DoProcessLinkHeader();
1493 /* static */
1494 void nsContentSink::NotifyDocElementCreated(Document* aDoc) {
1495 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
1497 nsCOMPtr<nsIObserverService> observerService =
1498 mozilla::services::GetObserverService();
1499 MOZ_ASSERT(observerService);
1501 auto* win = nsGlobalWindowInner::Cast(aDoc->GetInnerWindow());
1502 bool fireInitialInsertion = !win || !win->DidFireDocElemInserted();
1503 if (win) {
1504 win->SetDidFireDocElemInserted();
1506 if (fireInitialInsertion) {
1507 observerService->NotifyObservers(ToSupports(aDoc),
1508 "initial-document-element-inserted", u"");
1510 observerService->NotifyObservers(ToSupports(aDoc),
1511 "document-element-inserted", u"");
1513 nsContentUtils::DispatchChromeEvent(aDoc, ToSupports(aDoc),
1514 u"DOMDocElementInserted"_ns,
1515 CanBubble::eYes, Cancelable::eNo);
1518 NS_IMETHODIMP
1519 nsContentSink::GetName(nsACString& aName) {
1520 aName.AssignLiteral("nsContentSink_timer");
1521 return NS_OK;