Bumping manifests a=b2g-bump
[gecko.git] / dom / base / nsContentSink.cpp
blob222e77e93bd1a71d1f7b194be290626b9c6aa816
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et tw=78: */
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 "nsScriptLoader.h"
14 #include "nsIDocument.h"
15 #include "nsIDOMDocument.h"
16 #include "mozilla/css/Loader.h"
17 #include "nsStyleLinkElement.h"
18 #include "nsIDocShell.h"
19 #include "nsILoadContext.h"
20 #include "nsCPrefetchService.h"
21 #include "nsIURI.h"
22 #include "nsNetUtil.h"
23 #include "nsIHttpChannel.h"
24 #include "nsIContent.h"
25 #include "nsIPresShell.h"
26 #include "nsPresContext.h"
27 #include "nsViewManager.h"
28 #include "nsIAtom.h"
29 #include "nsGkAtoms.h"
30 #include "nsNetCID.h"
31 #include "nsIOfflineCacheUpdate.h"
32 #include "nsIApplicationCache.h"
33 #include "nsIApplicationCacheContainer.h"
34 #include "nsIApplicationCacheChannel.h"
35 #include "nsIScriptSecurityManager.h"
36 #include "nsICookieService.h"
37 #include "nsContentUtils.h"
38 #include "nsNodeInfoManager.h"
39 #include "nsIAppShell.h"
40 #include "nsIWidget.h"
41 #include "nsWidgetsCID.h"
42 #include "nsIDOMNode.h"
43 #include "mozAutoDocUpdate.h"
44 #include "nsIWebNavigation.h"
45 #include "nsGenericHTMLElement.h"
46 #include "nsHTMLDNSPrefetch.h"
47 #include "nsIObserverService.h"
48 #include "mozilla/Preferences.h"
49 #include "nsParserConstants.h"
51 using namespace mozilla;
53 PRLogModuleInfo* gContentSinkLogModuleInfo;
55 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsContentSink)
56 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsContentSink)
58 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsContentSink)
59 NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver)
60 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
61 NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
62 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
63 NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
64 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDocumentObserver)
65 NS_INTERFACE_MAP_END
67 NS_IMPL_CYCLE_COLLECTION_CLASS(nsContentSink)
69 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsContentSink)
70 if (tmp->mDocument) {
71 tmp->mDocument->RemoveObserver(tmp);
73 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
74 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParser)
75 NS_IMPL_CYCLE_COLLECTION_UNLINK(mNodeInfoManager)
76 NS_IMPL_CYCLE_COLLECTION_UNLINK(mScriptLoader)
77 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
78 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsContentSink)
79 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
80 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser)
81 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfoManager)
82 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader)
83 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
86 nsContentSink::nsContentSink()
88 // We have a zeroing operator new
89 NS_ASSERTION(!mLayoutStarted, "What?");
90 NS_ASSERTION(!mDynamicLowerValue, "What?");
91 NS_ASSERTION(!mParsing, "What?");
92 NS_ASSERTION(mLastSampledUserEventTime == 0, "What?");
93 NS_ASSERTION(mDeflectedCount == 0, "What?");
94 NS_ASSERTION(!mDroppedTimer, "What?");
95 NS_ASSERTION(mInMonolithicContainer == 0, "What?");
96 NS_ASSERTION(mInNotification == 0, "What?");
97 NS_ASSERTION(!mDeferredLayoutStart, "What?");
99 #ifdef DEBUG
100 if (!gContentSinkLogModuleInfo) {
101 gContentSinkLogModuleInfo = PR_NewLogModule("nscontentsink");
103 #endif
106 nsContentSink::~nsContentSink()
108 if (mDocument) {
109 // Remove ourselves just to be safe, though we really should have
110 // been removed in DidBuildModel if everything worked right.
111 mDocument->RemoveObserver(this);
115 bool nsContentSink::sNotifyOnTimer;
116 int32_t nsContentSink::sBackoffCount;
117 int32_t nsContentSink::sNotificationInterval;
118 int32_t nsContentSink::sInteractiveDeflectCount;
119 int32_t nsContentSink::sPerfDeflectCount;
120 int32_t nsContentSink::sPendingEventMode;
121 int32_t nsContentSink::sEventProbeRate;
122 int32_t nsContentSink::sInteractiveParseTime;
123 int32_t nsContentSink::sPerfParseTime;
124 int32_t nsContentSink::sInteractiveTime;
125 int32_t nsContentSink::sInitialPerfTime;
126 int32_t nsContentSink::sEnablePerfMode;
128 void
129 nsContentSink::InitializeStatics()
131 Preferences::AddBoolVarCache(&sNotifyOnTimer,
132 "content.notify.ontimer", true);
133 // -1 means never.
134 Preferences::AddIntVarCache(&sBackoffCount,
135 "content.notify.backoffcount", -1);
136 // The gNotificationInterval has a dramatic effect on how long it
137 // takes to initially display content for slow connections.
138 // The current value provides good
139 // incremental display of content without causing an increase
140 // in page load time. If this value is set below 1/10 of second
141 // it starts to impact page load performance.
142 // see bugzilla bug 72138 for more info.
143 Preferences::AddIntVarCache(&sNotificationInterval,
144 "content.notify.interval", 120000);
145 Preferences::AddIntVarCache(&sInteractiveDeflectCount,
146 "content.sink.interactive_deflect_count", 0);
147 Preferences::AddIntVarCache(&sPerfDeflectCount,
148 "content.sink.perf_deflect_count", 200);
149 Preferences::AddIntVarCache(&sPendingEventMode,
150 "content.sink.pending_event_mode", 1);
151 Preferences::AddIntVarCache(&sEventProbeRate,
152 "content.sink.event_probe_rate", 1);
153 Preferences::AddIntVarCache(&sInteractiveParseTime,
154 "content.sink.interactive_parse_time", 3000);
155 Preferences::AddIntVarCache(&sPerfParseTime,
156 "content.sink.perf_parse_time", 360000);
157 Preferences::AddIntVarCache(&sInteractiveTime,
158 "content.sink.interactive_time", 750000);
159 Preferences::AddIntVarCache(&sInitialPerfTime,
160 "content.sink.initial_perf_time", 2000000);
161 Preferences::AddIntVarCache(&sEnablePerfMode,
162 "content.sink.enable_perf_mode", 0);
165 nsresult
166 nsContentSink::Init(nsIDocument* aDoc,
167 nsIURI* aURI,
168 nsISupports* aContainer,
169 nsIChannel* aChannel)
171 NS_PRECONDITION(aDoc, "null ptr");
172 NS_PRECONDITION(aURI, "null ptr");
174 if (!aDoc || !aURI) {
175 return NS_ERROR_NULL_POINTER;
178 mDocument = aDoc;
180 mDocumentURI = aURI;
181 mDocShell = do_QueryInterface(aContainer);
182 mScriptLoader = mDocument->ScriptLoader();
184 if (!mRunsToCompletion) {
185 if (mDocShell) {
186 uint32_t loadType = 0;
187 mDocShell->GetLoadType(&loadType);
188 mDocument->SetChangeScrollPosWhenScrollingToRef(
189 (loadType & nsIDocShell::LOAD_CMD_HISTORY) == 0);
192 ProcessHTTPHeaders(aChannel);
195 mCSSLoader = aDoc->CSSLoader();
197 mNodeInfoManager = aDoc->NodeInfoManager();
199 mBackoffCount = sBackoffCount;
201 if (sEnablePerfMode != 0) {
202 mDynamicLowerValue = sEnablePerfMode == 1;
203 FavorPerformanceHint(!mDynamicLowerValue, 0);
206 return NS_OK;
209 NS_IMETHODIMP
210 nsContentSink::StyleSheetLoaded(CSSStyleSheet* aSheet,
211 bool aWasAlternate,
212 nsresult aStatus)
214 NS_ASSERTION(!mRunsToCompletion, "How come a fragment parser observed sheets?");
215 if (!aWasAlternate) {
216 NS_ASSERTION(mPendingSheetCount > 0, "How'd that happen?");
217 --mPendingSheetCount;
219 if (mPendingSheetCount == 0 &&
220 (mDeferredLayoutStart || mDeferredFlushTags)) {
221 if (mDeferredFlushTags) {
222 FlushTags();
224 if (mDeferredLayoutStart) {
225 // We might not have really started layout, since this sheet was still
226 // loading. Do it now. Probably doesn't matter whether we do this
227 // before or after we unblock scripts, but before feels saner. Note
228 // that if mDeferredLayoutStart is true, that means any subclass
229 // StartLayout() stuff that needs to happen has already happened, so we
230 // don't need to worry about it.
231 StartLayout(false);
234 // Go ahead and try to scroll to our ref if we have one
235 ScrollToRef();
238 mScriptLoader->RemoveExecuteBlocker();
241 return NS_OK;
244 nsresult
245 nsContentSink::ProcessHTTPHeaders(nsIChannel* aChannel)
247 nsCOMPtr<nsIHttpChannel> httpchannel(do_QueryInterface(aChannel));
249 if (!httpchannel) {
250 return NS_OK;
253 // Note that the only header we care about is the "link" header, since we
254 // have all the infrastructure for kicking off stylesheet loads.
256 nsAutoCString linkHeader;
258 nsresult rv = httpchannel->GetResponseHeader(NS_LITERAL_CSTRING("link"),
259 linkHeader);
260 if (NS_SUCCEEDED(rv) && !linkHeader.IsEmpty()) {
261 mDocument->SetHeaderData(nsGkAtoms::link,
262 NS_ConvertASCIItoUTF16(linkHeader));
264 NS_ASSERTION(!mProcessLinkHeaderEvent.get(),
265 "Already dispatched an event?");
267 mProcessLinkHeaderEvent =
268 NS_NewNonOwningRunnableMethod(this,
269 &nsContentSink::DoProcessLinkHeader);
270 rv = NS_DispatchToCurrentThread(mProcessLinkHeaderEvent.get());
271 if (NS_FAILED(rv)) {
272 mProcessLinkHeaderEvent.Forget();
276 return NS_OK;
279 nsresult
280 nsContentSink::ProcessHeaderData(nsIAtom* aHeader, const nsAString& aValue,
281 nsIContent* aContent)
283 nsresult rv = NS_OK;
284 // necko doesn't process headers coming in from the parser
286 mDocument->SetHeaderData(aHeader, aValue);
288 if (aHeader == nsGkAtoms::setcookie) {
289 // Note: Necko already handles cookies set via the channel. We can't just
290 // call SetCookie on the channel because we want to do some security checks
291 // here.
292 nsCOMPtr<nsICookieService> cookieServ =
293 do_GetService(NS_COOKIESERVICE_CONTRACTID, &rv);
294 if (NS_FAILED(rv)) {
295 return rv;
298 // Get a URI from the document principal
300 // We use the original codebase in case the codebase was changed
301 // by SetDomain
303 // Note that a non-codebase principal (eg the system principal) will return
304 // a null URI.
305 nsCOMPtr<nsIURI> codebaseURI;
306 rv = mDocument->NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
307 NS_ENSURE_TRUE(codebaseURI, rv);
309 nsCOMPtr<nsIChannel> channel;
310 if (mParser) {
311 mParser->GetChannel(getter_AddRefs(channel));
314 rv = cookieServ->SetCookieString(codebaseURI,
315 nullptr,
316 NS_ConvertUTF16toUTF8(aValue).get(),
317 channel);
318 if (NS_FAILED(rv)) {
319 return rv;
322 else if (aHeader == nsGkAtoms::msthemecompatible) {
323 // Disable theming for the presshell if the value is no.
324 // XXXbz don't we want to support this as an HTTP header too?
325 nsAutoString value(aValue);
326 if (value.LowerCaseEqualsLiteral("no")) {
327 nsIPresShell* shell = mDocument->GetShell();
328 if (shell) {
329 shell->DisableThemeSupport();
334 return rv;
338 void
339 nsContentSink::DoProcessLinkHeader()
341 nsAutoString value;
342 mDocument->GetHeaderData(nsGkAtoms::link, value);
343 ProcessLinkHeader(value);
346 // check whether the Link header field applies to the context resource
347 // see <http://tools.ietf.org/html/rfc5988#section-5.2>
349 bool
350 nsContentSink::LinkContextIsOurDocument(const nsSubstring& aAnchor)
352 if (aAnchor.IsEmpty()) {
353 // anchor parameter not present or empty -> same document reference
354 return true;
357 nsIURI* docUri = mDocument->GetDocumentURI();
359 // the document URI might contain a fragment identifier ("#...')
360 // we want to ignore that because it's invisible to the server
361 // and just affects the local interpretation in the recipient
362 nsCOMPtr<nsIURI> contextUri;
363 nsresult rv = docUri->CloneIgnoringRef(getter_AddRefs(contextUri));
365 if (NS_FAILED(rv)) {
366 // copying failed
367 return false;
370 // resolve anchor against context
371 nsCOMPtr<nsIURI> resolvedUri;
372 rv = NS_NewURI(getter_AddRefs(resolvedUri), aAnchor,
373 nullptr, contextUri);
375 if (NS_FAILED(rv)) {
376 // resolving failed
377 return false;
380 bool same;
381 rv = contextUri->Equals(resolvedUri, &same);
382 if (NS_FAILED(rv)) {
383 // comparison failed
384 return false;
387 return same;
390 // Decode a parameter value using the encoding defined in RFC 5987 (in place)
392 // charset "'" [ language ] "'" value-chars
394 // returns true when decoding happened successfully (otherwise leaves
395 // passed value alone)
396 bool
397 nsContentSink::Decode5987Format(nsAString& aEncoded) {
399 nsresult rv;
400 nsCOMPtr<nsIMIMEHeaderParam> mimehdrpar =
401 do_GetService(NS_MIMEHEADERPARAM_CONTRACTID, &rv);
402 if (NS_FAILED(rv))
403 return false;
405 nsAutoCString asciiValue;
407 const char16_t* encstart = aEncoded.BeginReading();
408 const char16_t* encend = aEncoded.EndReading();
410 // create a plain ASCII string, aborting if we can't do that
411 // converted form is always shorter than input
412 while (encstart != encend) {
413 if (*encstart > 0 && *encstart < 128) {
414 asciiValue.Append((char)*encstart);
415 } else {
416 return false;
418 encstart++;
421 nsAutoString decoded;
422 nsAutoCString language;
424 rv = mimehdrpar->DecodeRFC5987Param(asciiValue, language, decoded);
425 if (NS_FAILED(rv))
426 return false;
428 aEncoded = decoded;
429 return true;
432 nsresult
433 nsContentSink::ProcessLinkHeader(const nsAString& aLinkData)
435 nsresult rv = NS_OK;
437 // keep track where we are within the header field
438 bool seenParameters = false;
440 // parse link content and call process style link
441 nsAutoString href;
442 nsAutoString rel;
443 nsAutoString title;
444 nsAutoString titleStar;
445 nsAutoString type;
446 nsAutoString media;
447 nsAutoString anchor;
449 // copy to work buffer
450 nsAutoString stringList(aLinkData);
452 // put an extra null at the end
453 stringList.Append(kNullCh);
455 char16_t* start = stringList.BeginWriting();
456 char16_t* end = start;
457 char16_t* last = start;
458 char16_t endCh;
460 while (*start != kNullCh) {
461 // skip leading space
462 while ((*start != kNullCh) && nsCRT::IsAsciiSpace(*start)) {
463 ++start;
466 end = start;
467 last = end - 1;
469 bool wasQuotedString = false;
471 // look for semicolon or comma
472 while (*end != kNullCh && *end != kSemicolon && *end != kComma) {
473 char16_t ch = *end;
475 if (ch == kQuote || ch == kLessThan) {
476 // quoted string
478 char16_t quote = ch;
479 if (quote == kLessThan) {
480 quote = kGreaterThan;
483 wasQuotedString = (ch == kQuote);
485 char16_t* closeQuote = (end + 1);
487 // seek closing quote
488 while (*closeQuote != kNullCh && quote != *closeQuote) {
489 // in quoted-string, "\" is an escape character
490 if (wasQuotedString && *closeQuote == kBackSlash && *(closeQuote + 1) != kNullCh) {
491 ++closeQuote;
494 ++closeQuote;
497 if (quote == *closeQuote) {
498 // found closer
500 // skip to close quote
501 end = closeQuote;
503 last = end - 1;
505 ch = *(end + 1);
507 if (ch != kNullCh && ch != kSemicolon && ch != kComma) {
508 // end string here
509 *(++end) = kNullCh;
511 ch = *(end + 1);
513 // keep going until semi or comma
514 while (ch != kNullCh && ch != kSemicolon && ch != kComma) {
515 ++end;
517 ch = *end;
523 ++end;
524 ++last;
527 endCh = *end;
529 // end string here
530 *end = kNullCh;
532 if (start < end) {
533 if ((*start == kLessThan) && (*last == kGreaterThan)) {
534 *last = kNullCh;
536 // first instance of <...> wins
537 // also, do not allow hrefs after the first param was seen
538 if (href.IsEmpty() && !seenParameters) {
539 href = (start + 1);
540 href.StripWhitespace();
542 } else {
543 char16_t* equals = start;
544 seenParameters = true;
546 while ((*equals != kNullCh) && (*equals != kEqual)) {
547 equals++;
550 if (*equals != kNullCh) {
551 *equals = kNullCh;
552 nsAutoString attr(start);
553 attr.StripWhitespace();
555 char16_t* value = ++equals;
556 while (nsCRT::IsAsciiSpace(*value)) {
557 value++;
560 if ((*value == kQuote) && (*value == *last)) {
561 *last = kNullCh;
562 value++;
565 if (wasQuotedString) {
566 // unescape in-place
567 char16_t* unescaped = value;
568 char16_t *src = value;
570 while (*src != kNullCh) {
571 if (*src == kBackSlash && *(src + 1) != kNullCh) {
572 src++;
574 *unescaped++ = *src++;
577 *unescaped = kNullCh;
580 if (attr.LowerCaseEqualsLiteral("rel")) {
581 if (rel.IsEmpty()) {
582 rel = value;
583 rel.CompressWhitespace();
585 } else if (attr.LowerCaseEqualsLiteral("title")) {
586 if (title.IsEmpty()) {
587 title = value;
588 title.CompressWhitespace();
590 } else if (attr.LowerCaseEqualsLiteral("title*")) {
591 if (titleStar.IsEmpty() && !wasQuotedString) {
592 // RFC 5987 encoding; uses token format only, so skip if we get
593 // here with a quoted-string
594 nsAutoString tmp;
595 tmp = value;
596 if (Decode5987Format(tmp)) {
597 titleStar = tmp;
598 titleStar.CompressWhitespace();
599 } else {
600 // header value did not parse, throw it away
601 titleStar.Truncate();
604 } else if (attr.LowerCaseEqualsLiteral("type")) {
605 if (type.IsEmpty()) {
606 type = value;
607 type.StripWhitespace();
609 } else if (attr.LowerCaseEqualsLiteral("media")) {
610 if (media.IsEmpty()) {
611 media = value;
613 // The HTML5 spec is formulated in terms of the CSS3 spec,
614 // which specifies that media queries are case insensitive.
615 nsContentUtils::ASCIIToLower(media);
617 } else if (attr.LowerCaseEqualsLiteral("anchor")) {
618 if (anchor.IsEmpty()) {
619 anchor = value;
620 anchor.StripWhitespace();
627 if (endCh == kComma) {
628 // hit a comma, process what we've got so far
630 href.Trim(" \t\n\r\f"); // trim HTML5 whitespace
631 if (!href.IsEmpty() && !rel.IsEmpty()) {
632 rv = ProcessLink(anchor, href, rel,
633 // prefer RFC 5987 variant over non-I18zed version
634 titleStar.IsEmpty() ? title : titleStar,
635 type, media);
638 href.Truncate();
639 rel.Truncate();
640 title.Truncate();
641 type.Truncate();
642 media.Truncate();
643 anchor.Truncate();
645 seenParameters = false;
648 start = ++end;
651 href.Trim(" \t\n\r\f"); // trim HTML5 whitespace
652 if (!href.IsEmpty() && !rel.IsEmpty()) {
653 rv = ProcessLink(anchor, href, rel,
654 // prefer RFC 5987 variant over non-I18zed version
655 titleStar.IsEmpty() ? title : titleStar,
656 type, media);
659 return rv;
663 nsresult
664 nsContentSink::ProcessLink(const nsSubstring& aAnchor, const nsSubstring& aHref,
665 const nsSubstring& aRel, const nsSubstring& aTitle,
666 const nsSubstring& aType, const nsSubstring& aMedia)
668 uint32_t linkTypes =
669 nsStyleLinkElement::ParseLinkTypes(aRel, mDocument->NodePrincipal());
671 // The link relation may apply to a different resource, specified
672 // in the anchor parameter. For the link relations supported so far,
673 // we simply abort if the link applies to a resource different to the
674 // one we've loaded
675 if (!LinkContextIsOurDocument(aAnchor)) {
676 return NS_OK;
679 bool hasPrefetch = linkTypes & nsStyleLinkElement::ePREFETCH;
680 // prefetch href if relation is "next" or "prefetch"
681 if (hasPrefetch || (linkTypes & nsStyleLinkElement::eNEXT)) {
682 PrefetchHref(aHref, mDocument, hasPrefetch);
685 if (!aHref.IsEmpty() && (linkTypes & nsStyleLinkElement::eDNS_PREFETCH)) {
686 PrefetchDNS(aHref);
689 // is it a stylesheet link?
690 if (!(linkTypes & nsStyleLinkElement::eSTYLESHEET)) {
691 return NS_OK;
694 bool isAlternate = linkTypes & nsStyleLinkElement::eALTERNATE;
695 return ProcessStyleLink(nullptr, aHref, isAlternate, aTitle, aType,
696 aMedia);
699 nsresult
700 nsContentSink::ProcessStyleLink(nsIContent* aElement,
701 const nsSubstring& aHref,
702 bool aAlternate,
703 const nsSubstring& aTitle,
704 const nsSubstring& aType,
705 const nsSubstring& aMedia)
707 if (aAlternate && aTitle.IsEmpty()) {
708 // alternates must have title return without error, for now
709 return NS_OK;
712 nsAutoString mimeType;
713 nsAutoString params;
714 nsContentUtils::SplitMimeType(aType, mimeType, params);
716 // see bug 18817
717 if (!mimeType.IsEmpty() && !mimeType.LowerCaseEqualsLiteral("text/css")) {
718 // Unknown stylesheet language
719 return NS_OK;
722 nsCOMPtr<nsIURI> url;
723 nsresult rv = NS_NewURI(getter_AddRefs(url), aHref, nullptr,
724 mDocument->GetDocBaseURI());
726 if (NS_FAILED(rv)) {
727 // The URI is bad, move along, don't propagate the error (for now)
728 return NS_OK;
731 NS_ASSERTION(!aElement ||
732 aElement->NodeType() == nsIDOMNode::PROCESSING_INSTRUCTION_NODE,
733 "We only expect processing instructions here");
735 // If this is a fragment parser, we don't want to observe.
736 // We don't support CORS for processing instructions
737 bool isAlternate;
738 rv = mCSSLoader->LoadStyleLink(aElement, url, aTitle, aMedia, aAlternate,
739 CORS_NONE, mDocument->GetReferrerPolicy(),
740 mRunsToCompletion ? nullptr : this, &isAlternate);
741 NS_ENSURE_SUCCESS(rv, rv);
743 if (!isAlternate && !mRunsToCompletion) {
744 ++mPendingSheetCount;
745 mScriptLoader->AddExecuteBlocker();
748 return NS_OK;
752 nsresult
753 nsContentSink::ProcessMETATag(nsIContent* aContent)
755 NS_ASSERTION(aContent, "missing meta-element");
757 nsresult rv = NS_OK;
759 // set any HTTP-EQUIV data into document's header data as well as url
760 nsAutoString header;
761 aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, header);
762 if (!header.IsEmpty()) {
763 nsAutoString result;
764 aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::content, result);
765 if (!result.IsEmpty()) {
766 nsContentUtils::ASCIIToLower(header);
767 nsCOMPtr<nsIAtom> fieldAtom(do_GetAtom(header));
768 rv = ProcessHeaderData(fieldAtom, result, aContent);
771 NS_ENSURE_SUCCESS(rv, rv);
773 if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
774 nsGkAtoms::handheldFriendly, eIgnoreCase)) {
775 nsAutoString result;
776 aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::content, result);
777 if (!result.IsEmpty()) {
778 nsContentUtils::ASCIIToLower(result);
779 mDocument->SetHeaderData(nsGkAtoms::handheldFriendly, result);
783 return rv;
787 void
788 nsContentSink::PrefetchHref(const nsAString &aHref,
789 nsINode *aSource,
790 bool aExplicit)
793 // SECURITY CHECK: disable prefetching from mailnews!
795 // walk up the docshell tree to see if any containing
796 // docshell are of type MAIL.
798 if (!mDocShell)
799 return;
801 nsCOMPtr<nsIDocShell> docshell = mDocShell;
803 nsCOMPtr<nsIDocShellTreeItem> parentItem;
804 do {
805 uint32_t appType = 0;
806 nsresult rv = docshell->GetAppType(&appType);
807 if (NS_FAILED(rv) || appType == nsIDocShell::APP_TYPE_MAIL)
808 return; // do not prefetch from mailnews
809 docshell->GetParent(getter_AddRefs(parentItem));
810 if (parentItem) {
811 docshell = do_QueryInterface(parentItem);
812 if (!docshell) {
813 NS_ERROR("cannot get a docshell from a treeItem!");
814 return;
817 } while (parentItem);
819 // OK, we passed the security check...
821 nsCOMPtr<nsIPrefetchService> prefetchService(do_GetService(NS_PREFETCHSERVICE_CONTRACTID));
822 if (prefetchService) {
823 // construct URI using document charset
824 const nsACString &charset = mDocument->GetDocumentCharacterSet();
825 nsCOMPtr<nsIURI> uri;
826 NS_NewURI(getter_AddRefs(uri), aHref,
827 charset.IsEmpty() ? nullptr : PromiseFlatCString(charset).get(),
828 mDocument->GetDocBaseURI());
829 if (uri) {
830 nsCOMPtr<nsIDOMNode> domNode = do_QueryInterface(aSource);
831 prefetchService->PrefetchURI(uri, mDocumentURI, domNode, aExplicit);
836 void
837 nsContentSink::PrefetchDNS(const nsAString &aHref)
839 nsAutoString hostname;
841 if (StringBeginsWith(aHref, NS_LITERAL_STRING("//"))) {
842 hostname = Substring(aHref, 2);
844 else {
845 nsCOMPtr<nsIURI> uri;
846 NS_NewURI(getter_AddRefs(uri), aHref);
847 if (!uri) {
848 return;
850 nsAutoCString host;
851 uri->GetHost(host);
852 CopyUTF8toUTF16(host, hostname);
855 if (!hostname.IsEmpty() && nsHTMLDNSPrefetch::IsAllowed(mDocument)) {
856 nsHTMLDNSPrefetch::PrefetchLow(hostname);
860 nsresult
861 nsContentSink::SelectDocAppCache(nsIApplicationCache *aLoadApplicationCache,
862 nsIURI *aManifestURI,
863 bool aFetchedWithHTTPGetOrEquiv,
864 CacheSelectionAction *aAction)
866 nsresult rv;
868 *aAction = CACHE_SELECTION_NONE;
870 nsCOMPtr<nsIApplicationCacheContainer> applicationCacheDocument =
871 do_QueryInterface(mDocument);
872 NS_ASSERTION(applicationCacheDocument,
873 "mDocument must implement nsIApplicationCacheContainer.");
875 if (aLoadApplicationCache) {
876 nsCOMPtr<nsIURI> groupURI;
877 rv = aLoadApplicationCache->GetManifestURI(getter_AddRefs(groupURI));
878 NS_ENSURE_SUCCESS(rv, rv);
880 bool equal = false;
881 rv = groupURI->Equals(aManifestURI, &equal);
882 NS_ENSURE_SUCCESS(rv, rv);
884 if (!equal) {
885 // This is a foreign entry, force a reload to avoid loading the foreign
886 // entry. The entry will be marked as foreign to avoid loading it again.
888 *aAction = CACHE_SELECTION_RELOAD;
890 else {
891 // The http manifest attribute URI is equal to the manifest URI of
892 // the cache the document was loaded from - associate the document with
893 // that cache and invoke the cache update process.
894 #ifdef DEBUG
895 nsAutoCString docURISpec, clientID;
896 mDocumentURI->GetAsciiSpec(docURISpec);
897 aLoadApplicationCache->GetClientID(clientID);
898 SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_CALLS,
899 ("Selection: assigning app cache %s to document %s", clientID.get(), docURISpec.get()));
900 #endif
902 rv = applicationCacheDocument->SetApplicationCache(aLoadApplicationCache);
903 NS_ENSURE_SUCCESS(rv, rv);
905 // Document will be added as implicit entry to the cache as part of
906 // the update process.
907 *aAction = CACHE_SELECTION_UPDATE;
910 else {
911 // The document was not loaded from an application cache
912 // Here we know the manifest has the same origin as the
913 // document. There is call to CheckMayLoad() on it above.
915 if (!aFetchedWithHTTPGetOrEquiv) {
916 // The document was not loaded using HTTP GET or equivalent
917 // method. The spec says to run the cache selection algorithm w/o
918 // the manifest specified.
919 *aAction = CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST;
921 else {
922 // Always do an update in this case
923 *aAction = CACHE_SELECTION_UPDATE;
927 return NS_OK;
930 nsresult
931 nsContentSink::SelectDocAppCacheNoManifest(nsIApplicationCache *aLoadApplicationCache,
932 nsIURI **aManifestURI,
933 CacheSelectionAction *aAction)
935 *aManifestURI = nullptr;
936 *aAction = CACHE_SELECTION_NONE;
938 nsresult rv;
940 if (aLoadApplicationCache) {
941 // The document was loaded from an application cache, use that
942 // application cache as the document's application cache.
943 nsCOMPtr<nsIApplicationCacheContainer> applicationCacheDocument =
944 do_QueryInterface(mDocument);
945 NS_ASSERTION(applicationCacheDocument,
946 "mDocument must implement nsIApplicationCacheContainer.");
948 #ifdef DEBUG
949 nsAutoCString docURISpec, clientID;
950 mDocumentURI->GetAsciiSpec(docURISpec);
951 aLoadApplicationCache->GetClientID(clientID);
952 SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_CALLS,
953 ("Selection, no manifest: assigning app cache %s to document %s", clientID.get(), docURISpec.get()));
954 #endif
956 rv = applicationCacheDocument->SetApplicationCache(aLoadApplicationCache);
957 NS_ENSURE_SUCCESS(rv, rv);
959 // Return the uri and invoke the update process for the selected
960 // application cache.
961 rv = aLoadApplicationCache->GetManifestURI(aManifestURI);
962 NS_ENSURE_SUCCESS(rv, rv);
964 *aAction = CACHE_SELECTION_UPDATE;
967 return NS_OK;
970 void
971 nsContentSink::ProcessOfflineManifest(nsIContent *aElement)
973 // Only check the manifest for root document nodes.
974 if (aElement != mDocument->GetRootElement()) {
975 return;
978 // Don't bother processing offline manifest for documents
979 // without a docshell
980 if (!mDocShell) {
981 return;
984 // Check for a manifest= attribute.
985 nsAutoString manifestSpec;
986 aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::manifest, manifestSpec);
987 ProcessOfflineManifest(manifestSpec);
990 void
991 nsContentSink::ProcessOfflineManifest(const nsAString& aManifestSpec)
993 // Don't bother processing offline manifest for documents
994 // without a docshell
995 if (!mDocShell) {
996 return;
999 // If the docshell's in private browsing mode, we don't want to do any
1000 // manifest processing.
1001 nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(mDocShell);
1002 if (loadContext->UsePrivateBrowsing()) {
1003 return;
1006 nsresult rv;
1008 // Grab the application cache the document was loaded from, if any.
1009 nsCOMPtr<nsIApplicationCache> applicationCache;
1011 nsCOMPtr<nsIApplicationCacheChannel> applicationCacheChannel =
1012 do_QueryInterface(mDocument->GetChannel());
1013 if (applicationCacheChannel) {
1014 bool loadedFromApplicationCache;
1015 rv = applicationCacheChannel->GetLoadedFromApplicationCache(
1016 &loadedFromApplicationCache);
1017 if (NS_FAILED(rv)) {
1018 return;
1021 if (loadedFromApplicationCache) {
1022 rv = applicationCacheChannel->GetApplicationCache(
1023 getter_AddRefs(applicationCache));
1024 if (NS_FAILED(rv)) {
1025 return;
1030 if (aManifestSpec.IsEmpty() && !applicationCache) {
1031 // Not loaded from an application cache, and no manifest
1032 // attribute. Nothing to do here.
1033 return;
1036 CacheSelectionAction action = CACHE_SELECTION_NONE;
1037 nsCOMPtr<nsIURI> manifestURI;
1039 if (aManifestSpec.IsEmpty()) {
1040 action = CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST;
1042 else {
1043 nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(manifestURI),
1044 aManifestSpec, mDocument,
1045 mDocumentURI);
1046 if (!manifestURI) {
1047 return;
1050 // Documents must list a manifest from the same origin
1051 rv = mDocument->NodePrincipal()->CheckMayLoad(manifestURI, true, false);
1052 if (NS_FAILED(rv)) {
1053 action = CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST;
1055 else {
1056 // Only continue if the document has permission to use offline APIs or
1057 // when preferences indicate to permit it automatically.
1058 if (!nsContentUtils::OfflineAppAllowed(mDocument->NodePrincipal()) &&
1059 !nsContentUtils::MaybeAllowOfflineAppByDefault(mDocument->NodePrincipal(), mDocument->GetWindow()) &&
1060 !nsContentUtils::OfflineAppAllowed(mDocument->NodePrincipal())) {
1061 return;
1064 bool fetchedWithHTTPGetOrEquiv = false;
1065 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mDocument->GetChannel()));
1066 if (httpChannel) {
1067 nsAutoCString method;
1068 rv = httpChannel->GetRequestMethod(method);
1069 if (NS_SUCCEEDED(rv))
1070 fetchedWithHTTPGetOrEquiv = method.EqualsLiteral("GET");
1073 rv = SelectDocAppCache(applicationCache, manifestURI,
1074 fetchedWithHTTPGetOrEquiv, &action);
1075 if (NS_FAILED(rv)) {
1076 return;
1081 if (action == CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST) {
1082 rv = SelectDocAppCacheNoManifest(applicationCache,
1083 getter_AddRefs(manifestURI),
1084 &action);
1085 if (NS_FAILED(rv)) {
1086 return;
1090 switch (action)
1092 case CACHE_SELECTION_NONE:
1093 break;
1094 case CACHE_SELECTION_UPDATE: {
1095 nsCOMPtr<nsIOfflineCacheUpdateService> updateService =
1096 do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID);
1098 if (updateService) {
1099 nsCOMPtr<nsIDOMDocument> domdoc = do_QueryInterface(mDocument);
1100 updateService->ScheduleOnDocumentStop(manifestURI, mDocumentURI, domdoc);
1102 break;
1104 case CACHE_SELECTION_RELOAD: {
1105 // This situation occurs only for toplevel documents, see bottom
1106 // of SelectDocAppCache method.
1107 // The document has been loaded from a different offline cache group than
1108 // the manifest it refers to, i.e. this is a foreign entry, mark it as such
1109 // and force a reload to avoid loading it. The next attempt will not
1110 // choose it.
1112 applicationCacheChannel->MarkOfflineCacheEntryAsForeign();
1114 nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(mDocShell);
1116 webNav->Stop(nsIWebNavigation::STOP_ALL);
1117 webNav->Reload(nsIWebNavigation::LOAD_FLAGS_NONE);
1118 break;
1120 default:
1121 NS_ASSERTION(false,
1122 "Cache selection algorithm didn't decide on proper action");
1123 break;
1127 void
1128 nsContentSink::ScrollToRef()
1130 mDocument->ScrollToRef();
1133 void
1134 nsContentSink::StartLayout(bool aIgnorePendingSheets)
1136 if (mLayoutStarted) {
1137 // Nothing to do here
1138 return;
1141 mDeferredLayoutStart = true;
1143 if (!aIgnorePendingSheets && WaitForPendingSheets()) {
1144 // Bail out; we'll start layout when the sheets load
1145 return;
1148 mDeferredLayoutStart = false;
1150 // Notify on all our content. If none of our presshells have started layout
1151 // yet it'll be a no-op except for updating our data structures, a la
1152 // UpdateChildCounts() (because we don't want to double-notify on whatever we
1153 // have right now). If some of them _have_ started layout, we want to make
1154 // sure to flush tags instead of just calling UpdateChildCounts() after we
1155 // loop over the shells.
1156 FlushTags();
1158 mLayoutStarted = true;
1159 mLastNotificationTime = PR_Now();
1161 mDocument->SetMayStartLayout(true);
1162 nsCOMPtr<nsIPresShell> shell = mDocument->GetShell();
1163 // Make sure we don't call Initialize() for a shell that has
1164 // already called it. This can happen when the layout frame for
1165 // an iframe is constructed *between* the Embed() call for the
1166 // docshell in the iframe, and the content sink's call to OpenBody().
1167 // (Bug 153815)
1168 if (shell && !shell->DidInitialize()) {
1169 nsRect r = shell->GetPresContext()->GetVisibleArea();
1170 nsCOMPtr<nsIPresShell> shellGrip = shell;
1171 nsresult rv = shell->Initialize(r.width, r.height);
1172 if (NS_FAILED(rv)) {
1173 return;
1177 // If the document we are loading has a reference or it is a
1178 // frameset document, disable the scroll bars on the views.
1180 mDocument->SetScrollToRef(mDocument->GetDocumentURI());
1183 void
1184 nsContentSink::NotifyAppend(nsIContent* aContainer, uint32_t aStartIndex)
1186 if (aContainer->GetUncomposedDoc() != mDocument) {
1187 // aContainer is not actually in our document anymore.... Just bail out of
1188 // here; notifying on our document for this append would be wrong.
1189 return;
1192 mInNotification++;
1195 // Scope so we call EndUpdate before we decrease mInNotification
1196 MOZ_AUTO_DOC_UPDATE(mDocument, UPDATE_CONTENT_MODEL, !mBeganUpdate);
1197 nsNodeUtils::ContentAppended(aContainer,
1198 aContainer->GetChildAt(aStartIndex),
1199 aStartIndex);
1200 mLastNotificationTime = PR_Now();
1203 mInNotification--;
1206 NS_IMETHODIMP
1207 nsContentSink::Notify(nsITimer *timer)
1209 if (mParsing) {
1210 // We shouldn't interfere with our normal DidProcessAToken logic
1211 mDroppedTimer = true;
1212 return NS_OK;
1215 if (WaitForPendingSheets()) {
1216 mDeferredFlushTags = true;
1217 } else {
1218 FlushTags();
1220 // Now try and scroll to the reference
1221 // XXX Should we scroll unconditionally for history loads??
1222 ScrollToRef();
1225 mNotificationTimer = nullptr;
1226 return NS_OK;
1229 bool
1230 nsContentSink::IsTimeToNotify()
1232 if (!sNotifyOnTimer || !mLayoutStarted || !mBackoffCount ||
1233 mInMonolithicContainer) {
1234 return false;
1237 if (WaitForPendingSheets()) {
1238 mDeferredFlushTags = true;
1239 return false;
1242 PRTime now = PR_Now();
1244 int64_t interval = GetNotificationInterval();
1245 int64_t diff = now - mLastNotificationTime;
1247 if (diff > interval) {
1248 mBackoffCount--;
1249 return true;
1252 return false;
1255 nsresult
1256 nsContentSink::WillInterruptImpl()
1258 nsresult result = NS_OK;
1260 SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_CALLS,
1261 ("nsContentSink::WillInterrupt: this=%p", this));
1262 #ifndef SINK_NO_INCREMENTAL
1263 if (WaitForPendingSheets()) {
1264 mDeferredFlushTags = true;
1265 } else if (sNotifyOnTimer && mLayoutStarted) {
1266 if (mBackoffCount && !mInMonolithicContainer) {
1267 int64_t now = PR_Now();
1268 int64_t interval = GetNotificationInterval();
1269 int64_t diff = now - mLastNotificationTime;
1271 // If it's already time for us to have a notification
1272 if (diff > interval || mDroppedTimer) {
1273 mBackoffCount--;
1274 SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_REFLOW,
1275 ("nsContentSink::WillInterrupt: flushing tags since we've "
1276 "run out time; backoff count: %d", mBackoffCount));
1277 result = FlushTags();
1278 if (mDroppedTimer) {
1279 ScrollToRef();
1280 mDroppedTimer = false;
1282 } else if (!mNotificationTimer) {
1283 interval -= diff;
1284 int32_t delay = interval;
1286 // Convert to milliseconds
1287 delay /= PR_USEC_PER_MSEC;
1289 mNotificationTimer = do_CreateInstance("@mozilla.org/timer;1",
1290 &result);
1291 if (NS_SUCCEEDED(result)) {
1292 SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_REFLOW,
1293 ("nsContentSink::WillInterrupt: setting up timer with "
1294 "delay %d", delay));
1296 result =
1297 mNotificationTimer->InitWithCallback(this, delay,
1298 nsITimer::TYPE_ONE_SHOT);
1299 if (NS_FAILED(result)) {
1300 mNotificationTimer = nullptr;
1305 } else {
1306 SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_REFLOW,
1307 ("nsContentSink::WillInterrupt: flushing tags "
1308 "unconditionally"));
1309 result = FlushTags();
1311 #endif
1313 mParsing = false;
1315 return result;
1318 nsresult
1319 nsContentSink::WillResumeImpl()
1321 SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_CALLS,
1322 ("nsContentSink::WillResume: this=%p", this));
1324 mParsing = true;
1326 return NS_OK;
1329 nsresult
1330 nsContentSink::DidProcessATokenImpl()
1332 if (mRunsToCompletion || !mParser) {
1333 return NS_OK;
1336 // Get the current user event time
1337 nsIPresShell *shell = mDocument->GetShell();
1338 if (!shell) {
1339 // If there's no pres shell in the document, return early since
1340 // we're not laying anything out here.
1341 return NS_OK;
1344 // Increase before comparing to gEventProbeRate
1345 ++mDeflectedCount;
1347 // Check if there's a pending event
1348 if (sPendingEventMode != 0 && !mHasPendingEvent &&
1349 (mDeflectedCount % sEventProbeRate) == 0) {
1350 nsViewManager* vm = shell->GetViewManager();
1351 NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
1352 nsCOMPtr<nsIWidget> widget;
1353 vm->GetRootWidget(getter_AddRefs(widget));
1354 mHasPendingEvent = widget && widget->HasPendingInputEvent();
1357 if (mHasPendingEvent && sPendingEventMode == 2) {
1358 return NS_ERROR_HTMLPARSER_INTERRUPTED;
1361 // Have we processed enough tokens to check time?
1362 if (!mHasPendingEvent &&
1363 mDeflectedCount < uint32_t(mDynamicLowerValue ? sInteractiveDeflectCount :
1364 sPerfDeflectCount)) {
1365 return NS_OK;
1368 mDeflectedCount = 0;
1370 // Check if it's time to return to the main event loop
1371 if (PR_IntervalToMicroseconds(PR_IntervalNow()) > mCurrentParseEndTime) {
1372 return NS_ERROR_HTMLPARSER_INTERRUPTED;
1375 return NS_OK;
1378 //----------------------------------------------------------------------
1380 void
1381 nsContentSink::FavorPerformanceHint(bool perfOverStarvation, uint32_t starvationDelay)
1383 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
1384 nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
1385 if (appShell)
1386 appShell->FavorPerformanceHint(perfOverStarvation, starvationDelay);
1389 void
1390 nsContentSink::BeginUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType)
1392 // Remember nested updates from updates that we started.
1393 if (mInNotification > 0 && mUpdatesInNotification < 2) {
1394 ++mUpdatesInNotification;
1397 // If we're in a script and we didn't do the notification,
1398 // something else in the script processing caused the
1399 // notification to occur. Since this could result in frame
1400 // creation, make sure we've flushed everything before we
1401 // continue.
1403 if (!mInNotification++) {
1404 FlushTags();
1408 void
1409 nsContentSink::EndUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType)
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. Update our notion of how much
1414 // has been flushed to include any new content if ending
1415 // this update leaves us not inside a notification.
1416 if (!--mInNotification) {
1417 UpdateChildCounts();
1421 void
1422 nsContentSink::DidBuildModelImpl(bool aTerminated)
1424 if (mDocument) {
1425 MOZ_ASSERT(aTerminated ||
1426 mDocument->GetReadyStateEnum() ==
1427 nsIDocument::READYSTATE_LOADING, "Bad readyState");
1428 mDocument->SetReadyStateInternal(nsIDocument::READYSTATE_INTERACTIVE);
1431 if (mScriptLoader) {
1432 mScriptLoader->ParsingComplete(aTerminated);
1435 if (!mDocument->HaveFiredDOMTitleChange()) {
1436 mDocument->NotifyPossibleTitleChange(false);
1439 // Cancel a timer if we had one out there
1440 if (mNotificationTimer) {
1441 SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_REFLOW,
1442 ("nsContentSink::DidBuildModel: canceling notification "
1443 "timeout"));
1444 mNotificationTimer->Cancel();
1445 mNotificationTimer = 0;
1449 void
1450 nsContentSink::DropParserAndPerfHint(void)
1452 if (!mParser) {
1453 // Make sure we don't unblock unload too many times
1454 return;
1457 // Ref. Bug 49115
1458 // Do this hack to make sure that the parser
1459 // doesn't get destroyed, accidently, before
1460 // the circularity, between sink & parser, is
1461 // actually broken.
1462 // Drop our reference to the parser to get rid of a circular
1463 // reference.
1464 nsRefPtr<nsParserBase> kungFuDeathGrip(mParser.forget());
1466 if (mDynamicLowerValue) {
1467 // Reset the performance hint which was set to FALSE
1468 // when mDynamicLowerValue was set.
1469 FavorPerformanceHint(true, 0);
1472 if (!mRunsToCompletion) {
1473 mDocument->UnblockOnload(true);
1477 bool
1478 nsContentSink::IsScriptExecutingImpl()
1480 return !!mScriptLoader->GetCurrentScript();
1483 nsresult
1484 nsContentSink::WillParseImpl(void)
1486 if (mRunsToCompletion || !mDocument) {
1487 return NS_OK;
1490 nsIPresShell *shell = mDocument->GetShell();
1491 if (!shell) {
1492 return NS_OK;
1495 uint32_t currentTime = PR_IntervalToMicroseconds(PR_IntervalNow());
1497 if (sEnablePerfMode == 0) {
1498 nsViewManager* vm = shell->GetViewManager();
1499 NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
1500 uint32_t lastEventTime;
1501 vm->GetLastUserEventTime(lastEventTime);
1503 bool newDynLower =
1504 mDocument->IsInBackgroundWindow() ||
1505 ((currentTime - mBeginLoadTime) > uint32_t(sInitialPerfTime) &&
1506 (currentTime - lastEventTime) < uint32_t(sInteractiveTime));
1508 if (mDynamicLowerValue != newDynLower) {
1509 FavorPerformanceHint(!newDynLower, 0);
1510 mDynamicLowerValue = newDynLower;
1514 mDeflectedCount = 0;
1515 mHasPendingEvent = false;
1517 mCurrentParseEndTime = currentTime +
1518 (mDynamicLowerValue ? sInteractiveParseTime : sPerfParseTime);
1520 return NS_OK;
1523 void
1524 nsContentSink::WillBuildModelImpl()
1526 if (!mRunsToCompletion) {
1527 mDocument->BlockOnload();
1529 mBeginLoadTime = PR_IntervalToMicroseconds(PR_IntervalNow());
1532 mDocument->ResetScrolledToRefAlready();
1534 if (mProcessLinkHeaderEvent.get()) {
1535 mProcessLinkHeaderEvent.Revoke();
1537 DoProcessLinkHeader();
1541 /* static */
1542 void
1543 nsContentSink::NotifyDocElementCreated(nsIDocument* aDoc)
1545 nsCOMPtr<nsIObserverService> observerService =
1546 mozilla::services::GetObserverService();
1547 if (observerService) {
1548 nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(aDoc);
1549 observerService->
1550 NotifyObservers(domDoc, "document-element-inserted",
1551 EmptyString().get());