Bug 1550519 - Show a translucent parent highlight when a subgrid is highlighted....
[gecko.git] / dom / base / nsContentSink.cpp
blob45fca9682b63cd2ce426c0649304ca61b39f67b7
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/dom/Document.h"
16 #include "mozilla/css/Loader.h"
17 #include "mozilla/dom/SRILogHelper.h"
18 #include "nsStyleLinkElement.h"
19 #include "nsIDocShell.h"
20 #include "nsILoadContext.h"
21 #include "nsIPrefetchService.h"
22 #include "nsIURI.h"
23 #include "nsNetUtil.h"
24 #include "nsIMIMEHeaderParam.h"
25 #include "nsIProtocolHandler.h"
26 #include "nsIHttpChannel.h"
27 #include "nsIContent.h"
28 #include "nsPresContext.h"
29 #include "nsViewManager.h"
30 #include "nsAtom.h"
31 #include "nsGkAtoms.h"
32 #include "nsGlobalWindowInner.h"
33 #include "nsNetCID.h"
34 #include "nsIOfflineCacheUpdate.h"
35 #include "nsIApplicationCache.h"
36 #include "nsIApplicationCacheContainer.h"
37 #include "nsIApplicationCacheChannel.h"
38 #include "nsIScriptSecurityManager.h"
39 #include "nsICookieService.h"
40 #include "nsContentUtils.h"
41 #include "nsNodeInfoManager.h"
42 #include "nsIAppShell.h"
43 #include "nsIWidget.h"
44 #include "nsWidgetsCID.h"
45 #include "mozAutoDocUpdate.h"
46 #include "nsIWebNavigation.h"
47 #include "nsGenericHTMLElement.h"
48 #include "nsHTMLDNSPrefetch.h"
49 #include "nsIObserverService.h"
50 #include "mozilla/Preferences.h"
51 #include "mozilla/dom/ServiceWorkerDescriptor.h"
52 #include "mozilla/dom/ScriptLoader.h"
53 #include "nsParserConstants.h"
54 #include "nsSandboxFlags.h"
55 #include "Link.h"
56 #include "HTMLLinkElement.h"
58 using namespace mozilla;
59 using namespace mozilla::css;
60 using namespace mozilla::dom;
62 LazyLogModule gContentSinkLogModuleInfo("nscontentsink");
64 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsContentSink)
65 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsContentSink)
67 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsContentSink)
68 NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver)
69 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
70 NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
71 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
72 NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
73 NS_INTERFACE_MAP_ENTRY(nsINamed)
74 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDocumentObserver)
75 NS_INTERFACE_MAP_END
77 NS_IMPL_CYCLE_COLLECTION_CLASS(nsContentSink)
79 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsContentSink)
80 if (tmp->mDocument) {
81 tmp->mDocument->RemoveObserver(tmp);
83 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
84 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParser)
85 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell)
86 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCSSLoader)
87 NS_IMPL_CYCLE_COLLECTION_UNLINK(mNodeInfoManager)
88 NS_IMPL_CYCLE_COLLECTION_UNLINK(mScriptLoader)
89 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
90 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsContentSink)
91 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
92 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser)
93 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell)
94 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCSSLoader)
95 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfoManager)
96 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader)
97 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
99 nsContentSink::nsContentSink()
100 : mBackoffCount(0),
101 mLastNotificationTime(0),
102 mLayoutStarted(0),
103 mDynamicLowerValue(0),
104 mParsing(0),
105 mDroppedTimer(0),
106 mDeferredLayoutStart(0),
107 mDeferredFlushTags(0),
108 mIsDocumentObserver(0),
109 mRunsToCompletion(0),
110 mIsBlockingOnload(false),
111 mDeflectedCount(0),
112 mHasPendingEvent(false),
113 mCurrentParseEndTime(0),
114 mBeginLoadTime(0),
115 mLastSampledUserEventTime(0),
116 mInMonolithicContainer(0),
117 mInNotification(0),
118 mUpdatesInNotification(0),
119 mPendingSheetCount(0) {
120 NS_ASSERTION(!mLayoutStarted, "What?");
121 NS_ASSERTION(!mDynamicLowerValue, "What?");
122 NS_ASSERTION(!mParsing, "What?");
123 NS_ASSERTION(mLastSampledUserEventTime == 0, "What?");
124 NS_ASSERTION(mDeflectedCount == 0, "What?");
125 NS_ASSERTION(!mDroppedTimer, "What?");
126 NS_ASSERTION(mInMonolithicContainer == 0, "What?");
127 NS_ASSERTION(mInNotification == 0, "What?");
128 NS_ASSERTION(!mDeferredLayoutStart, "What?");
131 nsContentSink::~nsContentSink() {
132 if (mDocument) {
133 // Remove ourselves just to be safe, though we really should have
134 // been removed in DidBuildModel if everything worked right.
135 mDocument->RemoveObserver(this);
139 bool nsContentSink::sNotifyOnTimer;
140 int32_t nsContentSink::sBackoffCount;
141 int32_t nsContentSink::sNotificationInterval;
142 int32_t nsContentSink::sInteractiveDeflectCount;
143 int32_t nsContentSink::sPerfDeflectCount;
144 int32_t nsContentSink::sPendingEventMode;
145 int32_t nsContentSink::sEventProbeRate;
146 int32_t nsContentSink::sInteractiveParseTime;
147 int32_t nsContentSink::sPerfParseTime;
148 int32_t nsContentSink::sInteractiveTime;
149 int32_t nsContentSink::sInitialPerfTime;
150 int32_t nsContentSink::sEnablePerfMode;
152 void nsContentSink::InitializeStatics() {
153 Preferences::AddBoolVarCache(&sNotifyOnTimer, "content.notify.ontimer", true);
154 // -1 means never.
155 Preferences::AddIntVarCache(&sBackoffCount, "content.notify.backoffcount",
156 -1);
157 // The gNotificationInterval has a dramatic effect on how long it
158 // takes to initially display content for slow connections.
159 // The current value provides good
160 // incremental display of content without causing an increase
161 // in page load time. If this value is set below 1/10 of second
162 // it starts to impact page load performance.
163 // see bugzilla bug 72138 for more info.
164 Preferences::AddIntVarCache(&sNotificationInterval, "content.notify.interval",
165 120000);
166 Preferences::AddIntVarCache(&sInteractiveDeflectCount,
167 "content.sink.interactive_deflect_count", 0);
168 Preferences::AddIntVarCache(&sPerfDeflectCount,
169 "content.sink.perf_deflect_count", 200);
170 Preferences::AddIntVarCache(&sPendingEventMode,
171 "content.sink.pending_event_mode", 1);
172 Preferences::AddIntVarCache(&sEventProbeRate, "content.sink.event_probe_rate",
174 Preferences::AddIntVarCache(&sInteractiveParseTime,
175 "content.sink.interactive_parse_time", 3000);
176 Preferences::AddIntVarCache(&sPerfParseTime, "content.sink.perf_parse_time",
177 360000);
178 Preferences::AddIntVarCache(&sInteractiveTime,
179 "content.sink.interactive_time", 750000);
180 Preferences::AddIntVarCache(&sInitialPerfTime,
181 "content.sink.initial_perf_time", 2000000);
182 Preferences::AddIntVarCache(&sEnablePerfMode, "content.sink.enable_perf_mode",
186 nsresult nsContentSink::Init(Document* aDoc, nsIURI* aURI,
187 nsISupports* aContainer, nsIChannel* aChannel) {
188 MOZ_ASSERT(aDoc, "null ptr");
189 MOZ_ASSERT(aURI, "null ptr");
191 if (!aDoc || !aURI) {
192 return NS_ERROR_NULL_POINTER;
195 mDocument = aDoc;
197 mDocumentURI = aURI;
198 mDocShell = do_QueryInterface(aContainer);
199 mScriptLoader = mDocument->ScriptLoader();
201 if (!mRunsToCompletion) {
202 if (mDocShell) {
203 uint32_t loadType = 0;
204 mDocShell->GetLoadType(&loadType);
205 mDocument->SetChangeScrollPosWhenScrollingToRef(
206 (loadType & nsIDocShell::LOAD_CMD_HISTORY) == 0);
209 ProcessHTTPHeaders(aChannel);
212 mCSSLoader = aDoc->CSSLoader();
214 mNodeInfoManager = aDoc->NodeInfoManager();
216 mBackoffCount = sBackoffCount;
218 if (sEnablePerfMode != 0) {
219 mDynamicLowerValue = sEnablePerfMode == 1;
220 FavorPerformanceHint(!mDynamicLowerValue, 0);
223 return NS_OK;
226 NS_IMETHODIMP
227 nsContentSink::StyleSheetLoaded(StyleSheet* aSheet, bool aWasDeferred,
228 nsresult aStatus) {
229 MOZ_ASSERT(!mRunsToCompletion, "How come a fragment parser observed sheets?");
230 if (!aWasDeferred) {
231 MOZ_ASSERT(mPendingSheetCount > 0, "How'd that happen?");
232 --mPendingSheetCount;
234 if (mPendingSheetCount == 0 &&
235 (mDeferredLayoutStart || mDeferredFlushTags)) {
236 if (mDeferredFlushTags) {
237 FlushTags();
239 if (mDeferredLayoutStart) {
240 // We might not have really started layout, since this sheet was still
241 // loading. Do it now. Probably doesn't matter whether we do this
242 // before or after we unblock scripts, but before feels saner. Note
243 // that if mDeferredLayoutStart is true, that means any subclass
244 // StartLayout() stuff that needs to happen has already happened, so we
245 // don't need to worry about it.
246 StartLayout(false);
249 // Go ahead and try to scroll to our ref if we have one
250 ScrollToRef();
253 mScriptLoader->RemoveParserBlockingScriptExecutionBlocker();
256 return NS_OK;
259 nsresult nsContentSink::ProcessHTTPHeaders(nsIChannel* aChannel) {
260 nsCOMPtr<nsIHttpChannel> httpchannel(do_QueryInterface(aChannel));
262 if (!httpchannel) {
263 return NS_OK;
266 // Note that the only header we care about is the "link" header, since we
267 // have all the infrastructure for kicking off stylesheet loads.
269 nsAutoCString linkHeader;
271 nsresult rv =
272 httpchannel->GetResponseHeader(NS_LITERAL_CSTRING("link"), linkHeader);
273 if (NS_SUCCEEDED(rv) && !linkHeader.IsEmpty()) {
274 mDocument->SetHeaderData(nsGkAtoms::link,
275 NS_ConvertASCIItoUTF16(linkHeader));
277 NS_ASSERTION(!mProcessLinkHeaderEvent.get(),
278 "Already dispatched an event?");
280 mProcessLinkHeaderEvent =
281 NewNonOwningRunnableMethod("nsContentSink::DoProcessLinkHeader", this,
282 &nsContentSink::DoProcessLinkHeader);
283 rv = NS_DispatchToCurrentThread(mProcessLinkHeaderEvent.get());
284 if (NS_FAILED(rv)) {
285 mProcessLinkHeaderEvent.Forget();
289 return NS_OK;
292 nsresult nsContentSink::ProcessHeaderData(nsAtom* aHeader,
293 const nsAString& aValue,
294 nsIContent* aContent) {
295 nsresult rv = NS_OK;
296 // necko doesn't process headers coming in from the parser
298 mDocument->SetHeaderData(aHeader, aValue);
300 if (aHeader == nsGkAtoms::setcookie &&
301 StaticPrefs::dom_metaElement_setCookie_allowed()) {
302 // Note: Necko already handles cookies set via the channel. We can't just
303 // call SetCookie on the channel because we want to do some security checks
304 // here.
305 nsCOMPtr<nsICookieService> cookieServ =
306 do_GetService(NS_COOKIESERVICE_CONTRACTID, &rv);
307 if (NS_FAILED(rv)) {
308 return rv;
311 // Get a URI from the document principal
313 // We use the original codebase in case the codebase was changed
314 // by SetDomain
316 // Note that a non-codebase principal (eg the system principal) will return
317 // a null URI.
318 nsCOMPtr<nsIURI> codebaseURI;
319 rv = mDocument->NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
320 NS_ENSURE_TRUE(codebaseURI, rv);
322 nsCOMPtr<nsIChannel> channel;
323 if (mParser) {
324 mParser->GetChannel(getter_AddRefs(channel));
327 rv = cookieServ->SetCookieString(codebaseURI, nullptr,
328 NS_ConvertUTF16toUTF8(aValue), channel);
329 if (NS_FAILED(rv)) {
330 return rv;
334 return rv;
337 void nsContentSink::DoProcessLinkHeader() {
338 nsAutoString value;
339 mDocument->GetHeaderData(nsGkAtoms::link, value);
340 ProcessLinkHeader(value);
343 // check whether the Link header field applies to the context resource
344 // see <http://tools.ietf.org/html/rfc5988#section-5.2>
346 bool nsContentSink::LinkContextIsOurDocument(const nsAString& aAnchor) {
347 if (aAnchor.IsEmpty()) {
348 // anchor parameter not present or empty -> same document reference
349 return true;
352 nsIURI* docUri = mDocument->GetDocumentURI();
354 // the document URI might contain a fragment identifier ("#...')
355 // we want to ignore that because it's invisible to the server
356 // and just affects the local interpretation in the recipient
357 nsCOMPtr<nsIURI> contextUri;
358 nsresult rv = NS_GetURIWithoutRef(docUri, getter_AddRefs(contextUri));
360 if (NS_FAILED(rv)) {
361 // copying failed
362 return false;
365 // resolve anchor against context
366 nsCOMPtr<nsIURI> resolvedUri;
367 rv = NS_NewURI(getter_AddRefs(resolvedUri), aAnchor, nullptr, contextUri);
369 if (NS_FAILED(rv)) {
370 // resolving failed
371 return false;
374 bool same;
375 rv = contextUri->Equals(resolvedUri, &same);
376 if (NS_FAILED(rv)) {
377 // comparison failed
378 return false;
381 return same;
384 // Decode a parameter value using the encoding defined in RFC 5987 (in place)
386 // charset "'" [ language ] "'" value-chars
388 // returns true when decoding happened successfully (otherwise leaves
389 // passed value alone)
390 bool nsContentSink::Decode5987Format(nsAString& aEncoded) {
391 nsresult rv;
392 nsCOMPtr<nsIMIMEHeaderParam> mimehdrpar =
393 do_GetService(NS_MIMEHEADERPARAM_CONTRACTID, &rv);
394 if (NS_FAILED(rv)) return false;
396 nsAutoCString asciiValue;
398 const char16_t* encstart = aEncoded.BeginReading();
399 const char16_t* encend = aEncoded.EndReading();
401 // create a plain ASCII string, aborting if we can't do that
402 // converted form is always shorter than input
403 while (encstart != encend) {
404 if (*encstart > 0 && *encstart < 128) {
405 asciiValue.Append((char)*encstart);
406 } else {
407 return false;
409 encstart++;
412 nsAutoString decoded;
413 nsAutoCString language;
415 rv = mimehdrpar->DecodeRFC5987Param(asciiValue, language, decoded);
416 if (NS_FAILED(rv)) return false;
418 aEncoded = decoded;
419 return true;
422 nsresult nsContentSink::ProcessLinkHeader(const nsAString& aLinkData) {
423 nsresult rv = NS_OK;
425 // keep track where we are within the header field
426 bool seenParameters = false;
428 // parse link content and call process style link
429 nsAutoString href;
430 nsAutoString rel;
431 nsAutoString title;
432 nsAutoString titleStar;
433 nsAutoString type;
434 nsAutoString media;
435 nsAutoString anchor;
436 nsAutoString crossOrigin;
437 nsAutoString referrerPolicy;
438 nsAutoString as;
440 crossOrigin.SetIsVoid(true);
442 // copy to work buffer
443 nsAutoString stringList(aLinkData);
445 // put an extra null at the end
446 stringList.Append(kNullCh);
448 char16_t* start = stringList.BeginWriting();
449 char16_t* end = start;
450 char16_t* last = start;
451 char16_t endCh;
453 while (*start != kNullCh) {
454 // skip leading space
455 while ((*start != kNullCh) && nsCRT::IsAsciiSpace(*start)) {
456 ++start;
459 end = start;
460 last = end - 1;
462 bool wasQuotedString = false;
464 // look for semicolon or comma
465 while (*end != kNullCh && *end != kSemicolon && *end != kComma) {
466 char16_t ch = *end;
468 if (ch == kQuote || ch == kLessThan) {
469 // quoted string
471 char16_t quote = ch;
472 if (quote == kLessThan) {
473 quote = kGreaterThan;
476 wasQuotedString = (ch == kQuote);
478 char16_t* closeQuote = (end + 1);
480 // seek closing quote
481 while (*closeQuote != kNullCh && quote != *closeQuote) {
482 // in quoted-string, "\" is an escape character
483 if (wasQuotedString && *closeQuote == kBackSlash &&
484 *(closeQuote + 1) != kNullCh) {
485 ++closeQuote;
488 ++closeQuote;
491 if (quote == *closeQuote) {
492 // found closer
494 // skip to close quote
495 end = closeQuote;
497 last = end - 1;
499 ch = *(end + 1);
501 if (ch != kNullCh && ch != kSemicolon && ch != kComma) {
502 // end string here
503 *(++end) = kNullCh;
505 ch = *(end + 1);
507 // keep going until semi or comma
508 while (ch != kNullCh && ch != kSemicolon && ch != kComma) {
509 ++end;
511 ch = *(end + 1);
517 ++end;
518 ++last;
521 endCh = *end;
523 // end string here
524 *end = kNullCh;
526 if (start < end) {
527 if ((*start == kLessThan) && (*last == kGreaterThan)) {
528 *last = kNullCh;
530 // first instance of <...> wins
531 // also, do not allow hrefs after the first param was seen
532 if (href.IsEmpty() && !seenParameters) {
533 href = (start + 1);
534 href.StripWhitespace();
536 } else {
537 char16_t* equals = start;
538 seenParameters = true;
540 while ((*equals != kNullCh) && (*equals != kEqual)) {
541 equals++;
544 if (*equals != kNullCh) {
545 *equals = kNullCh;
546 nsAutoString attr(start);
547 attr.StripWhitespace();
549 char16_t* value = ++equals;
550 while (nsCRT::IsAsciiSpace(*value)) {
551 value++;
554 if ((*value == kQuote) && (*value == *last)) {
555 *last = kNullCh;
556 value++;
559 if (wasQuotedString) {
560 // unescape in-place
561 char16_t* unescaped = value;
562 char16_t* src = value;
564 while (*src != kNullCh) {
565 if (*src == kBackSlash && *(src + 1) != kNullCh) {
566 src++;
568 *unescaped++ = *src++;
571 *unescaped = kNullCh;
574 if (attr.LowerCaseEqualsLiteral("rel")) {
575 if (rel.IsEmpty()) {
576 rel = value;
577 rel.CompressWhitespace();
579 } else if (attr.LowerCaseEqualsLiteral("title")) {
580 if (title.IsEmpty()) {
581 title = value;
582 title.CompressWhitespace();
584 } else if (attr.LowerCaseEqualsLiteral("title*")) {
585 if (titleStar.IsEmpty() && !wasQuotedString) {
586 // RFC 5987 encoding; uses token format only, so skip if we get
587 // here with a quoted-string
588 nsAutoString tmp;
589 tmp = value;
590 if (Decode5987Format(tmp)) {
591 titleStar = tmp;
592 titleStar.CompressWhitespace();
593 } else {
594 // header value did not parse, throw it away
595 titleStar.Truncate();
598 } else if (attr.LowerCaseEqualsLiteral("type")) {
599 if (type.IsEmpty()) {
600 type = value;
601 type.StripWhitespace();
603 } else if (attr.LowerCaseEqualsLiteral("media")) {
604 if (media.IsEmpty()) {
605 media = value;
607 // The HTML5 spec is formulated in terms of the CSS3 spec,
608 // which specifies that media queries are case insensitive.
609 nsContentUtils::ASCIIToLower(media);
611 } else if (attr.LowerCaseEqualsLiteral("anchor")) {
612 if (anchor.IsEmpty()) {
613 anchor = value;
614 anchor.StripWhitespace();
616 } else if (attr.LowerCaseEqualsLiteral("crossorigin")) {
617 if (crossOrigin.IsVoid()) {
618 crossOrigin.SetIsVoid(false);
619 crossOrigin = value;
620 crossOrigin.StripWhitespace();
622 } else if (attr.LowerCaseEqualsLiteral("as")) {
623 if (as.IsEmpty()) {
624 as = value;
625 as.CompressWhitespace();
627 } else if (attr.LowerCaseEqualsLiteral("referrerpolicy")) {
628 // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#referrer-policy-attribute
629 // Specs says referrer policy attribute is an enumerated attribute,
630 // case insensitive and includes the empty string
631 // We will parse the value with AttributeReferrerPolicyFromString
632 // later, which will handle parsing it as an enumerated attribute.
633 if (referrerPolicy.IsEmpty()) {
634 referrerPolicy = value;
641 if (endCh == kComma) {
642 // hit a comma, process what we've got so far
644 href.Trim(" \t\n\r\f"); // trim HTML5 whitespace
645 if (!href.IsEmpty() && !rel.IsEmpty()) {
646 rv = ProcessLinkFromHeader(
647 anchor, href, rel,
648 // prefer RFC 5987 variant over non-I18zed version
649 titleStar.IsEmpty() ? title : titleStar, type, media, crossOrigin,
650 referrerPolicy, as);
653 href.Truncate();
654 rel.Truncate();
655 title.Truncate();
656 type.Truncate();
657 media.Truncate();
658 anchor.Truncate();
659 referrerPolicy.Truncate();
660 crossOrigin.SetIsVoid(true);
661 as.Truncate();
663 seenParameters = false;
666 start = ++end;
669 href.Trim(" \t\n\r\f"); // trim HTML5 whitespace
670 if (!href.IsEmpty() && !rel.IsEmpty()) {
671 rv =
672 ProcessLinkFromHeader(anchor, href, rel,
673 // prefer RFC 5987 variant over non-I18zed version
674 titleStar.IsEmpty() ? title : titleStar, type,
675 media, crossOrigin, referrerPolicy, as);
678 return rv;
681 nsresult nsContentSink::ProcessLinkFromHeader(
682 const nsAString& aAnchor, const nsAString& aHref, const nsAString& aRel,
683 const nsAString& aTitle, const nsAString& aType, const nsAString& aMedia,
684 const nsAString& aCrossOrigin, const nsAString& aReferrerPolicy,
685 const nsAString& aAs) {
686 uint32_t linkTypes = nsStyleLinkElement::ParseLinkTypes(aRel);
688 // The link relation may apply to a different resource, specified
689 // in the anchor parameter. For the link relations supported so far,
690 // we simply abort if the link applies to a resource different to the
691 // one we've loaded
692 if (!LinkContextIsOurDocument(aAnchor)) {
693 return NS_OK;
696 if (nsContentUtils::PrefetchPreloadEnabled(mDocShell)) {
697 // prefetch href if relation is "next" or "prefetch"
698 if ((linkTypes & nsStyleLinkElement::eNEXT) ||
699 (linkTypes & nsStyleLinkElement::ePREFETCH) ||
700 (linkTypes & nsStyleLinkElement::ePRELOAD)) {
701 PrefetchPreloadHref(aHref, mDocument, linkTypes, aAs, aType, aMedia);
704 if (!aHref.IsEmpty() && (linkTypes & nsStyleLinkElement::eDNS_PREFETCH)) {
705 PrefetchDNS(aHref);
708 if (!aHref.IsEmpty() && (linkTypes & nsStyleLinkElement::ePRECONNECT)) {
709 Preconnect(aHref, aCrossOrigin);
713 // is it a stylesheet link?
714 if (!(linkTypes & nsStyleLinkElement::eSTYLESHEET)) {
715 return NS_OK;
718 bool isAlternate = linkTypes & nsStyleLinkElement::eALTERNATE;
719 return ProcessStyleLinkFromHeader(aHref, isAlternate, aTitle, aType, aMedia,
720 aReferrerPolicy);
723 nsresult nsContentSink::ProcessStyleLinkFromHeader(
724 const nsAString& aHref, bool aAlternate, const nsAString& aTitle,
725 const nsAString& aType, const nsAString& aMedia,
726 const nsAString& aReferrerPolicy) {
727 if (aAlternate && aTitle.IsEmpty()) {
728 // alternates must have title return without error, for now
729 return NS_OK;
732 nsAutoString mimeType;
733 nsAutoString params;
734 nsContentUtils::SplitMimeType(aType, mimeType, params);
736 // see bug 18817
737 if (!mimeType.IsEmpty() && !mimeType.LowerCaseEqualsLiteral("text/css")) {
738 // Unknown stylesheet language
739 return NS_OK;
742 nsCOMPtr<nsIURI> url;
743 nsresult rv = NS_NewURI(getter_AddRefs(url), aHref, nullptr,
744 mDocument->GetDocBaseURI());
746 if (NS_FAILED(rv)) {
747 // The URI is bad, move along, don't propagate the error (for now)
748 return NS_OK;
751 Loader::SheetInfo info{
752 *mDocument,
753 nullptr,
754 url.forget(),
755 nullptr,
756 net::AttributeReferrerPolicyFromString(aReferrerPolicy),
757 CORS_NONE,
758 aTitle,
759 aMedia,
760 aAlternate ? Loader::HasAlternateRel::Yes : Loader::HasAlternateRel::No,
761 Loader::IsInline::No,
762 Loader::IsExplicitlyEnabled::No,
765 auto loadResultOrErr =
766 mCSSLoader->LoadStyleLink(info, mRunsToCompletion ? nullptr : this);
767 if (loadResultOrErr.isErr()) {
768 return loadResultOrErr.unwrapErr();
771 if (loadResultOrErr.unwrap().ShouldBlock() && !mRunsToCompletion) {
772 ++mPendingSheetCount;
773 mScriptLoader->AddParserBlockingScriptExecutionBlocker();
776 return NS_OK;
779 nsresult nsContentSink::ProcessMETATag(nsIContent* aContent) {
780 NS_ASSERTION(aContent, "missing meta-element");
781 MOZ_ASSERT(aContent->IsElement());
783 Element* element = aContent->AsElement();
785 nsresult rv = NS_OK;
787 // set any HTTP-EQUIV data into document's header data as well as url
788 nsAutoString header;
789 element->GetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, header);
790 if (!header.IsEmpty()) {
791 // Ignore META REFRESH when document is sandboxed from automatic features.
792 nsContentUtils::ASCIIToLower(header);
793 if (nsGkAtoms::refresh->Equals(header) &&
794 (mDocument->GetSandboxFlags() & SANDBOXED_AUTOMATIC_FEATURES)) {
795 return NS_OK;
798 // Don't allow setting cookies in <meta http-equiv> in cookie averse
799 // documents.
800 if (nsGkAtoms::setcookie->Equals(header) && mDocument->IsCookieAverse() &&
801 StaticPrefs::dom_metaElement_setCookie_allowed()) {
802 return NS_OK;
805 nsAutoString result;
806 element->GetAttr(kNameSpaceID_None, nsGkAtoms::content, result);
807 if (!result.IsEmpty()) {
808 RefPtr<nsAtom> fieldAtom(NS_Atomize(header));
809 rv = ProcessHeaderData(fieldAtom, result, element);
812 NS_ENSURE_SUCCESS(rv, rv);
814 if (element->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
815 nsGkAtoms::handheldFriendly, eIgnoreCase)) {
816 nsAutoString result;
817 element->GetAttr(kNameSpaceID_None, nsGkAtoms::content, result);
818 if (!result.IsEmpty()) {
819 nsContentUtils::ASCIIToLower(result);
820 mDocument->SetHeaderData(nsGkAtoms::handheldFriendly, result);
824 return rv;
827 void nsContentSink::PrefetchPreloadHref(const nsAString& aHref,
828 nsINode* aSource, uint32_t aLinkTypes,
829 const nsAString& aAs,
830 const nsAString& aType,
831 const nsAString& aMedia) {
832 nsCOMPtr<nsIPrefetchService> prefetchService(components::Prefetch::Service());
833 if (prefetchService) {
834 // construct URI using document charset
835 auto encoding = mDocument->GetDocumentCharacterSet();
836 nsCOMPtr<nsIURI> uri;
837 NS_NewURI(getter_AddRefs(uri), aHref, encoding, mDocument->GetDocBaseURI());
838 if (uri) {
839 if (aLinkTypes & nsStyleLinkElement::ePRELOAD) {
840 nsAttrValue asAttr;
841 Link::ParseAsValue(aAs, asAttr);
842 nsContentPolicyType policyType = Link::AsValueToContentPolicy(asAttr);
844 if (policyType == nsIContentPolicy::TYPE_INVALID) {
845 // Ignore preload with a wrong or empty as attribute.
846 return;
849 nsAutoString mimeType;
850 nsAutoString notUsed;
851 nsContentUtils::SplitMimeType(aType, mimeType, notUsed);
852 if (!HTMLLinkElement::CheckPreloadAttrs(asAttr, mimeType, aMedia,
853 mDocument)) {
854 policyType = nsIContentPolicy::TYPE_INVALID;
857 prefetchService->PreloadURI(uri, mDocumentURI, aSource, policyType);
858 } else {
859 prefetchService->PrefetchURI(
860 uri, mDocumentURI, aSource,
861 aLinkTypes & nsStyleLinkElement::ePREFETCH);
867 void nsContentSink::PrefetchDNS(const nsAString& aHref) {
868 nsAutoString hostname;
869 bool isHttps = false;
871 if (StringBeginsWith(aHref, NS_LITERAL_STRING("//"))) {
872 hostname = Substring(aHref, 2);
873 } else {
874 nsCOMPtr<nsIURI> uri;
875 NS_NewURI(getter_AddRefs(uri), aHref);
876 if (!uri) {
877 return;
879 nsresult rv;
880 bool isLocalResource = false;
881 rv = NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
882 &isLocalResource);
883 if (NS_SUCCEEDED(rv) && !isLocalResource) {
884 nsAutoCString host;
885 uri->GetHost(host);
886 CopyUTF8toUTF16(host, hostname);
888 uri->SchemeIs("https", &isHttps);
891 if (!hostname.IsEmpty() && nsHTMLDNSPrefetch::IsAllowed(mDocument)) {
892 nsHTMLDNSPrefetch::PrefetchLow(
893 hostname, isHttps, mDocument->NodePrincipal()->OriginAttributesRef());
897 void nsContentSink::Preconnect(const nsAString& aHref,
898 const nsAString& aCrossOrigin) {
899 // construct URI using document charset
900 auto encoding = mDocument->GetDocumentCharacterSet();
901 nsCOMPtr<nsIURI> uri;
902 NS_NewURI(getter_AddRefs(uri), aHref, encoding, mDocument->GetDocBaseURI());
904 if (uri && mDocument) {
905 mDocument->MaybePreconnect(uri,
906 dom::Element::StringToCORSMode(aCrossOrigin));
910 nsresult nsContentSink::SelectDocAppCache(
911 nsIApplicationCache* aLoadApplicationCache, nsIURI* aManifestURI,
912 bool aFetchedWithHTTPGetOrEquiv, CacheSelectionAction* aAction) {
913 nsresult rv;
915 *aAction = CACHE_SELECTION_NONE;
917 if (aLoadApplicationCache) {
918 nsCOMPtr<nsIURI> groupURI;
919 rv = aLoadApplicationCache->GetManifestURI(getter_AddRefs(groupURI));
920 NS_ENSURE_SUCCESS(rv, rv);
922 bool equal = false;
923 rv = groupURI->Equals(aManifestURI, &equal);
924 NS_ENSURE_SUCCESS(rv, rv);
926 if (!equal) {
927 // This is a foreign entry, force a reload to avoid loading the foreign
928 // entry. The entry will be marked as foreign to avoid loading it again.
930 *aAction = CACHE_SELECTION_RELOAD;
931 } else {
932 // The http manifest attribute URI is equal to the manifest URI of
933 // the cache the document was loaded from - associate the document with
934 // that cache and invoke the cache update process.
935 #ifdef DEBUG
936 nsAutoCString docURISpec, clientID;
937 mDocumentURI->GetAsciiSpec(docURISpec);
938 aLoadApplicationCache->GetClientID(clientID);
939 SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo),
940 SINK_TRACE_CALLS,
941 ("Selection: assigning app cache %s to document %s",
942 clientID.get(), docURISpec.get()));
943 #endif
945 rv = mDocument->SetApplicationCache(aLoadApplicationCache);
946 NS_ENSURE_SUCCESS(rv, rv);
948 // Document will be added as implicit entry to the cache as part of
949 // the update process.
950 *aAction = CACHE_SELECTION_UPDATE;
952 } else {
953 // The document was not loaded from an application cache
954 // Here we know the manifest has the same origin as the
955 // document. There is call to CheckMayLoad() on it above.
957 if (!aFetchedWithHTTPGetOrEquiv) {
958 // The document was not loaded using HTTP GET or equivalent
959 // method. The spec says to run the cache selection algorithm w/o
960 // the manifest specified.
961 *aAction = CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST;
962 } else {
963 // Always do an update in this case
964 *aAction = CACHE_SELECTION_UPDATE;
968 return NS_OK;
971 nsresult nsContentSink::SelectDocAppCacheNoManifest(
972 nsIApplicationCache* aLoadApplicationCache, nsIURI** aManifestURI,
973 CacheSelectionAction* aAction) {
974 *aManifestURI = nullptr;
975 *aAction = CACHE_SELECTION_NONE;
977 nsresult rv;
979 if (aLoadApplicationCache) {
980 // The document was loaded from an application cache, use that
981 // application cache as the document's application cache.
982 #ifdef DEBUG
983 nsAutoCString docURISpec, clientID;
984 mDocumentURI->GetAsciiSpec(docURISpec);
985 aLoadApplicationCache->GetClientID(clientID);
986 SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo),
987 SINK_TRACE_CALLS,
988 ("Selection, no manifest: assigning app cache %s to document %s",
989 clientID.get(), docURISpec.get()));
990 #endif
992 rv = mDocument->SetApplicationCache(aLoadApplicationCache);
993 NS_ENSURE_SUCCESS(rv, rv);
995 // Return the uri and invoke the update process for the selected
996 // application cache.
997 rv = aLoadApplicationCache->GetManifestURI(aManifestURI);
998 NS_ENSURE_SUCCESS(rv, rv);
1000 *aAction = CACHE_SELECTION_UPDATE;
1003 return NS_OK;
1006 void nsContentSink::ProcessOfflineManifest(nsIContent* aElement) {
1007 // Only check the manifest for root document nodes.
1008 if (aElement != mDocument->GetRootElement()) {
1009 return;
1012 // Don't bother processing offline manifest for documents
1013 // without a docshell
1014 if (!mDocShell) {
1015 return;
1018 // Check for a manifest= attribute.
1019 nsAutoString manifestSpec;
1020 aElement->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::manifest,
1021 manifestSpec);
1022 ProcessOfflineManifest(manifestSpec);
1025 void nsContentSink::ProcessOfflineManifest(const nsAString& aManifestSpec) {
1026 // Don't bother processing offline manifest for documents
1027 // without a docshell
1028 if (!mDocShell) {
1029 return;
1032 // If this document has been interecepted, let's skip the processing of the
1033 // manifest.
1034 if (mDocument->GetController().isSome()) {
1035 return;
1038 // If the docshell's in private browsing mode, we don't want to do any
1039 // manifest processing.
1040 nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(mDocShell);
1041 if (loadContext->UsePrivateBrowsing()) {
1042 return;
1045 nsresult rv;
1047 // Grab the application cache the document was loaded from, if any.
1048 nsCOMPtr<nsIApplicationCache> applicationCache;
1050 nsCOMPtr<nsIApplicationCacheChannel> applicationCacheChannel =
1051 do_QueryInterface(mDocument->GetChannel());
1052 if (applicationCacheChannel) {
1053 bool loadedFromApplicationCache;
1054 rv = applicationCacheChannel->GetLoadedFromApplicationCache(
1055 &loadedFromApplicationCache);
1056 if (NS_FAILED(rv)) {
1057 return;
1060 if (loadedFromApplicationCache) {
1061 rv = applicationCacheChannel->GetApplicationCache(
1062 getter_AddRefs(applicationCache));
1063 if (NS_FAILED(rv)) {
1064 return;
1069 if (aManifestSpec.IsEmpty() && !applicationCache) {
1070 // Not loaded from an application cache, and no manifest
1071 // attribute. Nothing to do here.
1072 return;
1075 CacheSelectionAction action = CACHE_SELECTION_NONE;
1076 nsCOMPtr<nsIURI> manifestURI;
1078 if (aManifestSpec.IsEmpty()) {
1079 action = CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST;
1080 } else {
1081 nsContentUtils::NewURIWithDocumentCharset(
1082 getter_AddRefs(manifestURI), aManifestSpec, mDocument, mDocumentURI);
1083 if (!manifestURI) {
1084 return;
1087 // Documents must list a manifest from the same origin
1088 rv = mDocument->NodePrincipal()->CheckMayLoad(manifestURI, true, false);
1089 if (NS_FAILED(rv)) {
1090 action = CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST;
1091 } else {
1092 // Only continue if the document has permission to use offline APIs or
1093 // when preferences indicate to permit it automatically.
1094 if (!nsContentUtils::OfflineAppAllowed(mDocument->NodePrincipal()) &&
1095 !nsContentUtils::MaybeAllowOfflineAppByDefault(
1096 mDocument->NodePrincipal()) &&
1097 !nsContentUtils::OfflineAppAllowed(mDocument->NodePrincipal())) {
1098 return;
1101 bool fetchedWithHTTPGetOrEquiv = false;
1102 nsCOMPtr<nsIHttpChannel> httpChannel(
1103 do_QueryInterface(mDocument->GetChannel()));
1104 if (httpChannel) {
1105 nsAutoCString method;
1106 rv = httpChannel->GetRequestMethod(method);
1107 if (NS_SUCCEEDED(rv))
1108 fetchedWithHTTPGetOrEquiv = method.EqualsLiteral("GET");
1111 rv = SelectDocAppCache(applicationCache, manifestURI,
1112 fetchedWithHTTPGetOrEquiv, &action);
1113 if (NS_FAILED(rv)) {
1114 return;
1119 if (action == CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST) {
1120 rv = SelectDocAppCacheNoManifest(applicationCache,
1121 getter_AddRefs(manifestURI), &action);
1122 if (NS_FAILED(rv)) {
1123 return;
1127 switch (action) {
1128 case CACHE_SELECTION_NONE:
1129 break;
1130 case CACHE_SELECTION_UPDATE: {
1131 nsCOMPtr<nsIOfflineCacheUpdateService> updateService =
1132 components::OfflineCacheUpdate::Service();
1134 if (updateService) {
1135 updateService->ScheduleOnDocumentStop(
1136 manifestURI, mDocumentURI, mDocument->NodePrincipal(), mDocument);
1138 break;
1140 case CACHE_SELECTION_RELOAD: {
1141 // This situation occurs only for toplevel documents, see bottom
1142 // of SelectDocAppCache method.
1143 // The document has been loaded from a different offline cache group than
1144 // the manifest it refers to, i.e. this is a foreign entry, mark it as
1145 // such and force a reload to avoid loading it. The next attempt will not
1146 // choose it.
1148 applicationCacheChannel->MarkOfflineCacheEntryAsForeign();
1150 nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(mDocShell);
1152 webNav->Stop(nsIWebNavigation::STOP_ALL);
1153 webNav->Reload(nsIWebNavigation::LOAD_FLAGS_NONE);
1154 break;
1156 default:
1157 NS_ASSERTION(false,
1158 "Cache selection algorithm didn't decide on proper action");
1159 break;
1163 void nsContentSink::ScrollToRef() {
1164 RefPtr<Document> document = mDocument;
1165 document->ScrollToRef();
1168 void nsContentSink::StartLayout(bool aIgnorePendingSheets) {
1169 AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("nsContentSink::StartLayout", LAYOUT,
1170 mDocumentURI->GetSpecOrDefault());
1172 if (mLayoutStarted) {
1173 // Nothing to do here
1174 return;
1177 mDeferredLayoutStart = true;
1179 if (!aIgnorePendingSheets &&
1180 (WaitForPendingSheets() || mDocument->HasPendingInitialTranslation())) {
1181 // Bail out; we'll start layout when the sheets and l10n load
1182 return;
1185 mDeferredLayoutStart = false;
1187 // Notify on all our content. If none of our presshells have started layout
1188 // yet it'll be a no-op except for updating our data structures, a la
1189 // UpdateChildCounts() (because we don't want to double-notify on whatever we
1190 // have right now). If some of them _have_ started layout, we want to make
1191 // sure to flush tags instead of just calling UpdateChildCounts() after we
1192 // loop over the shells.
1193 FlushTags();
1195 mLayoutStarted = true;
1196 mLastNotificationTime = PR_Now();
1198 mDocument->SetMayStartLayout(true);
1199 RefPtr<PresShell> presShell = mDocument->GetPresShell();
1200 // Make sure we don't call Initialize() for a shell that has
1201 // already called it. This can happen when the layout frame for
1202 // an iframe is constructed *between* the Embed() call for the
1203 // docshell in the iframe, and the content sink's call to OpenBody().
1204 // (Bug 153815)
1205 if (presShell && !presShell->DidInitialize()) {
1206 nsresult rv = presShell->Initialize();
1207 if (NS_FAILED(rv)) {
1208 return;
1212 // If the document we are loading has a reference or it is a
1213 // frameset document, disable the scroll bars on the views.
1215 mDocument->SetScrollToRef(mDocument->GetDocumentURI());
1218 void nsContentSink::NotifyAppend(nsIContent* aContainer, uint32_t aStartIndex) {
1219 mInNotification++;
1222 // Scope so we call EndUpdate before we decrease mInNotification
1224 // Note that aContainer->OwnerDoc() may not be mDocument.
1225 MOZ_AUTO_DOC_UPDATE(aContainer->OwnerDoc(), true);
1226 nsNodeUtils::ContentAppended(
1227 aContainer, aContainer->GetChildAt_Deprecated(aStartIndex));
1228 mLastNotificationTime = PR_Now();
1231 mInNotification--;
1234 NS_IMETHODIMP
1235 nsContentSink::Notify(nsITimer* timer) {
1236 if (mParsing) {
1237 // We shouldn't interfere with our normal DidProcessAToken logic
1238 mDroppedTimer = true;
1239 return NS_OK;
1242 if (WaitForPendingSheets()) {
1243 mDeferredFlushTags = true;
1244 } else {
1245 FlushTags();
1247 // Now try and scroll to the reference
1248 // XXX Should we scroll unconditionally for history loads??
1249 ScrollToRef();
1252 mNotificationTimer = nullptr;
1253 return NS_OK;
1256 bool nsContentSink::IsTimeToNotify() {
1257 if (!sNotifyOnTimer || !mLayoutStarted || !mBackoffCount ||
1258 mInMonolithicContainer) {
1259 return false;
1262 if (WaitForPendingSheets()) {
1263 mDeferredFlushTags = true;
1264 return false;
1267 PRTime now = PR_Now();
1269 int64_t interval = GetNotificationInterval();
1270 int64_t diff = now - mLastNotificationTime;
1272 if (diff > interval) {
1273 mBackoffCount--;
1274 return true;
1277 return false;
1280 nsresult nsContentSink::WillInterruptImpl() {
1281 nsresult result = NS_OK;
1283 SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo),
1284 SINK_TRACE_CALLS, ("nsContentSink::WillInterrupt: this=%p", this));
1285 #ifndef SINK_NO_INCREMENTAL
1286 if (WaitForPendingSheets()) {
1287 mDeferredFlushTags = true;
1288 } else if (sNotifyOnTimer && mLayoutStarted) {
1289 if (mBackoffCount && !mInMonolithicContainer) {
1290 int64_t now = PR_Now();
1291 int64_t interval = GetNotificationInterval();
1292 int64_t diff = now - mLastNotificationTime;
1294 // If it's already time for us to have a notification
1295 if (diff > interval || mDroppedTimer) {
1296 mBackoffCount--;
1297 SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo),
1298 SINK_TRACE_REFLOW,
1299 ("nsContentSink::WillInterrupt: flushing tags since we've "
1300 "run out time; backoff count: %d",
1301 mBackoffCount));
1302 result = FlushTags();
1303 if (mDroppedTimer) {
1304 ScrollToRef();
1305 mDroppedTimer = false;
1307 } else if (!mNotificationTimer) {
1308 interval -= diff;
1309 int32_t delay = interval;
1311 // Convert to milliseconds
1312 delay /= PR_USEC_PER_MSEC;
1314 NS_NewTimerWithCallback(getter_AddRefs(mNotificationTimer), this, delay,
1315 nsITimer::TYPE_ONE_SHOT);
1316 if (mNotificationTimer) {
1317 SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo),
1318 SINK_TRACE_REFLOW,
1319 ("nsContentSink::WillInterrupt: setting up timer with "
1320 "delay %d",
1321 delay));
1325 } else {
1326 SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo),
1327 SINK_TRACE_REFLOW,
1328 ("nsContentSink::WillInterrupt: flushing tags "
1329 "unconditionally"));
1330 result = FlushTags();
1332 #endif
1334 mParsing = false;
1336 return result;
1339 nsresult nsContentSink::WillResumeImpl() {
1340 SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo),
1341 SINK_TRACE_CALLS, ("nsContentSink::WillResume: this=%p", this));
1343 mParsing = true;
1345 return NS_OK;
1348 nsresult nsContentSink::DidProcessATokenImpl() {
1349 if (mRunsToCompletion || !mParser) {
1350 return NS_OK;
1353 // Get the current user event time
1354 PresShell* presShell = mDocument->GetPresShell();
1355 if (!presShell) {
1356 // If there's no pres shell in the document, return early since
1357 // we're not laying anything out here.
1358 return NS_OK;
1361 // Increase before comparing to gEventProbeRate
1362 ++mDeflectedCount;
1364 // Check if there's a pending event
1365 if (sPendingEventMode != 0 && !mHasPendingEvent &&
1366 (mDeflectedCount % sEventProbeRate) == 0) {
1367 nsViewManager* vm = presShell->GetViewManager();
1368 NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
1369 nsCOMPtr<nsIWidget> widget;
1370 vm->GetRootWidget(getter_AddRefs(widget));
1371 mHasPendingEvent = widget && widget->HasPendingInputEvent();
1374 if (mHasPendingEvent && sPendingEventMode == 2) {
1375 return NS_ERROR_HTMLPARSER_INTERRUPTED;
1378 // Have we processed enough tokens to check time?
1379 if (!mHasPendingEvent &&
1380 mDeflectedCount < uint32_t(mDynamicLowerValue ? sInteractiveDeflectCount
1381 : sPerfDeflectCount)) {
1382 return NS_OK;
1385 mDeflectedCount = 0;
1387 // Check if it's time to return to the main event loop
1388 if (PR_IntervalToMicroseconds(PR_IntervalNow()) > mCurrentParseEndTime) {
1389 return NS_ERROR_HTMLPARSER_INTERRUPTED;
1392 return NS_OK;
1395 //----------------------------------------------------------------------
1397 void nsContentSink::FavorPerformanceHint(bool perfOverStarvation,
1398 uint32_t starvationDelay) {
1399 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
1400 nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
1401 if (appShell)
1402 appShell->FavorPerformanceHint(perfOverStarvation, starvationDelay);
1405 void nsContentSink::BeginUpdate(Document* aDocument) {
1406 // Remember nested updates from updates that we started.
1407 if (mInNotification > 0 && mUpdatesInNotification < 2) {
1408 ++mUpdatesInNotification;
1411 // If we're in a script and we didn't do the notification,
1412 // something else in the script processing caused the
1413 // notification to occur. Since this could result in frame
1414 // creation, make sure we've flushed everything before we
1415 // continue.
1417 if (!mInNotification++) {
1418 FlushTags();
1422 void nsContentSink::EndUpdate(Document* aDocument) {
1423 // If we're in a script and we didn't do the notification,
1424 // something else in the script processing caused the
1425 // notification to occur. Update our notion of how much
1426 // has been flushed to include any new content if ending
1427 // this update leaves us not inside a notification.
1428 if (!--mInNotification) {
1429 UpdateChildCounts();
1433 void nsContentSink::DidBuildModelImpl(bool aTerminated) {
1434 if (mDocument) {
1435 MOZ_ASSERT(aTerminated || mDocument->GetReadyStateEnum() ==
1436 Document::READYSTATE_LOADING,
1437 "Bad readyState");
1438 mDocument->SetReadyStateInternal(Document::READYSTATE_INTERACTIVE);
1441 if (mScriptLoader) {
1442 mScriptLoader->ParsingComplete(aTerminated);
1445 if (!mDocument->HaveFiredDOMTitleChange()) {
1446 mDocument->NotifyPossibleTitleChange(false);
1449 // Cancel a timer if we had one out there
1450 if (mNotificationTimer) {
1451 SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo),
1452 SINK_TRACE_REFLOW,
1453 ("nsContentSink::DidBuildModel: canceling notification "
1454 "timeout"));
1455 mNotificationTimer->Cancel();
1456 mNotificationTimer = nullptr;
1460 void nsContentSink::DropParserAndPerfHint(void) {
1461 if (!mParser) {
1462 // Make sure we don't unblock unload too many times
1463 return;
1466 // Ref. Bug 49115
1467 // Do this hack to make sure that the parser
1468 // doesn't get destroyed, accidently, before
1469 // the circularity, between sink & parser, is
1470 // actually broken.
1471 // Drop our reference to the parser to get rid of a circular
1472 // reference.
1473 RefPtr<nsParserBase> kungFuDeathGrip(mParser.forget());
1475 if (mDynamicLowerValue) {
1476 // Reset the performance hint which was set to FALSE
1477 // when mDynamicLowerValue was set.
1478 FavorPerformanceHint(true, 0);
1481 // Call UnblockOnload only if mRunsToComletion is false and if
1482 // we have already started loading because it's possible that this function
1483 // is called (i.e. the parser is terminated) before we start loading due to
1484 // destroying the window inside unload event callbacks for the previous
1485 // document.
1486 if (!mRunsToCompletion && mIsBlockingOnload) {
1487 mDocument->UnblockOnload(true);
1488 mIsBlockingOnload = false;
1492 bool nsContentSink::IsScriptExecutingImpl() {
1493 return !!mScriptLoader->GetCurrentScript();
1496 nsresult nsContentSink::WillParseImpl(void) {
1497 if (mRunsToCompletion || !mDocument) {
1498 return NS_OK;
1501 PresShell* presShell = mDocument->GetPresShell();
1502 if (!presShell) {
1503 return NS_OK;
1506 uint32_t currentTime = PR_IntervalToMicroseconds(PR_IntervalNow());
1508 if (sEnablePerfMode == 0) {
1509 nsViewManager* vm = presShell->GetViewManager();
1510 NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
1511 uint32_t lastEventTime;
1512 vm->GetLastUserEventTime(lastEventTime);
1514 bool newDynLower =
1515 mDocument->IsInBackgroundWindow() ||
1516 ((currentTime - mBeginLoadTime) > uint32_t(sInitialPerfTime) &&
1517 (currentTime - lastEventTime) < uint32_t(sInteractiveTime));
1519 if (mDynamicLowerValue != newDynLower) {
1520 FavorPerformanceHint(!newDynLower, 0);
1521 mDynamicLowerValue = newDynLower;
1525 mDeflectedCount = 0;
1526 mHasPendingEvent = false;
1528 mCurrentParseEndTime =
1529 currentTime +
1530 (mDynamicLowerValue ? sInteractiveParseTime : sPerfParseTime);
1532 return NS_OK;
1535 void nsContentSink::WillBuildModelImpl() {
1536 if (!mRunsToCompletion) {
1537 mDocument->BlockOnload();
1538 mIsBlockingOnload = true;
1540 mBeginLoadTime = PR_IntervalToMicroseconds(PR_IntervalNow());
1543 mDocument->ResetScrolledToRefAlready();
1545 if (mProcessLinkHeaderEvent.get()) {
1546 mProcessLinkHeaderEvent.Revoke();
1548 DoProcessLinkHeader();
1552 /* static */
1553 void nsContentSink::NotifyDocElementCreated(Document* aDoc) {
1554 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
1556 nsCOMPtr<nsIObserverService> observerService =
1557 mozilla::services::GetObserverService();
1558 MOZ_ASSERT(observerService);
1560 auto* win = nsGlobalWindowInner::Cast(aDoc->GetInnerWindow());
1561 bool fireInitialInsertion = !win || !win->DidFireDocElemInserted();
1562 if (win) {
1563 win->SetDidFireDocElemInserted();
1565 if (fireInitialInsertion) {
1566 observerService->NotifyObservers(ToSupports(aDoc),
1567 "initial-document-element-inserted",
1568 EmptyString().get());
1570 observerService->NotifyObservers(
1571 ToSupports(aDoc), "document-element-inserted", EmptyString().get());
1573 nsContentUtils::DispatchChromeEvent(
1574 aDoc, ToSupports(aDoc), NS_LITERAL_STRING("DOMDocElementInserted"),
1575 CanBubble::eYes, Cancelable::eNo);
1578 NS_IMETHODIMP
1579 nsContentSink::GetName(nsACString& aName) {
1580 aName.AssignLiteral("nsContentSink_timer");
1581 return NS_OK;