Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
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.
10 */
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);
72 }
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()
87 {
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");
102 }
103 #endif
104 }
106 nsContentSink::~nsContentSink()
107 {
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);
112 }
113 }
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()
130 {
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);
163 }
165 nsresult
166 nsContentSink::Init(nsIDocument* aDoc,
167 nsIURI* aURI,
168 nsISupports* aContainer,
169 nsIChannel* aChannel)
170 {
171 NS_PRECONDITION(aDoc, "null ptr");
172 NS_PRECONDITION(aURI, "null ptr");
174 if (!aDoc || !aURI) {
175 return NS_ERROR_NULL_POINTER;
176 }
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);
190 }
192 ProcessHTTPHeaders(aChannel);
193 }
195 mCSSLoader = aDoc->CSSLoader();
197 mNodeInfoManager = aDoc->NodeInfoManager();
199 mBackoffCount = sBackoffCount;
201 if (sEnablePerfMode != 0) {
202 mDynamicLowerValue = sEnablePerfMode == 1;
203 FavorPerformanceHint(!mDynamicLowerValue, 0);
204 }
206 return NS_OK;
207 }
209 NS_IMETHODIMP
210 nsContentSink::StyleSheetLoaded(nsCSSStyleSheet* aSheet,
211 bool aWasAlternate,
212 nsresult aStatus)
213 {
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();
223 }
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);
232 }
234 // Go ahead and try to scroll to our ref if we have one
235 ScrollToRef();
236 }
238 mScriptLoader->RemoveExecuteBlocker();
239 }
241 return NS_OK;
242 }
244 nsresult
245 nsContentSink::ProcessHTTPHeaders(nsIChannel* aChannel)
246 {
247 nsCOMPtr<nsIHttpChannel> httpchannel(do_QueryInterface(aChannel));
249 if (!httpchannel) {
250 return NS_OK;
251 }
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();
273 }
274 }
276 return NS_OK;
277 }
279 nsresult
280 nsContentSink::ProcessHeaderData(nsIAtom* aHeader, const nsAString& aValue,
281 nsIContent* aContent)
282 {
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;
296 }
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));
312 }
314 rv = cookieServ->SetCookieString(codebaseURI,
315 nullptr,
316 NS_ConvertUTF16toUTF8(aValue).get(),
317 channel);
318 if (NS_FAILED(rv)) {
319 return rv;
320 }
321 }
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();
330 }
331 }
332 }
334 return rv;
335 }
338 void
339 nsContentSink::DoProcessLinkHeader()
340 {
341 nsAutoString value;
342 mDocument->GetHeaderData(nsGkAtoms::link, value);
343 ProcessLinkHeader(value);
344 }
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)
351 {
352 if (aAnchor.IsEmpty()) {
353 // anchor parameter not present or empty -> same document reference
354 return true;
355 }
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;
368 }
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;
378 }
380 bool same;
381 rv = contextUri->Equals(resolvedUri, &same);
382 if (NS_FAILED(rv)) {
383 // comparison failed
384 return false;
385 }
387 return same;
388 }
390 // Decode a parameter value using the encoding defined in RFC 5987 (in place)
391 //
392 // charset "'" [ language ] "'" value-chars
393 //
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;
417 }
418 encstart++;
419 }
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;
430 }
432 nsresult
433 nsContentSink::ProcessLinkHeader(const nsAString& aLinkData)
434 {
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;
464 }
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;
481 }
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;
492 }
494 ++closeQuote;
495 }
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;
518 }
519 }
520 }
521 }
523 ++end;
524 ++last;
525 }
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();
541 }
542 } else {
543 char16_t* equals = start;
544 seenParameters = true;
546 while ((*equals != kNullCh) && (*equals != kEqual)) {
547 equals++;
548 }
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++;
558 }
560 if ((*value == kQuote) && (*value == *last)) {
561 *last = kNullCh;
562 value++;
563 }
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++;
573 }
574 *unescaped++ = *src++;
575 }
577 *unescaped = kNullCh;
578 }
580 if (attr.LowerCaseEqualsLiteral("rel")) {
581 if (rel.IsEmpty()) {
582 rel = value;
583 rel.CompressWhitespace();
584 }
585 } else if (attr.LowerCaseEqualsLiteral("title")) {
586 if (title.IsEmpty()) {
587 title = value;
588 title.CompressWhitespace();
589 }
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();
602 }
603 }
604 } else if (attr.LowerCaseEqualsLiteral("type")) {
605 if (type.IsEmpty()) {
606 type = value;
607 type.StripWhitespace();
608 }
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);
616 }
617 } else if (attr.LowerCaseEqualsLiteral("anchor")) {
618 if (anchor.IsEmpty()) {
619 anchor = value;
620 anchor.StripWhitespace();
621 }
622 }
623 }
624 }
625 }
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);
636 }
638 href.Truncate();
639 rel.Truncate();
640 title.Truncate();
641 type.Truncate();
642 media.Truncate();
643 anchor.Truncate();
645 seenParameters = false;
646 }
648 start = ++end;
649 }
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);
657 }
659 return rv;
660 }
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)
667 {
668 uint32_t linkTypes = nsStyleLinkElement::ParseLinkTypes(aRel);
670 // The link relation may apply to a different resource, specified
671 // in the anchor parameter. For the link relations supported so far,
672 // we simply abort if the link applies to a resource different to the
673 // one we've loaded
674 if (!LinkContextIsOurDocument(aAnchor)) {
675 return NS_OK;
676 }
678 bool hasPrefetch = linkTypes & nsStyleLinkElement::ePREFETCH;
679 // prefetch href if relation is "next" or "prefetch"
680 if (hasPrefetch || (linkTypes & nsStyleLinkElement::eNEXT)) {
681 PrefetchHref(aHref, mDocument, hasPrefetch);
682 }
684 if (!aHref.IsEmpty() && (linkTypes & nsStyleLinkElement::eDNS_PREFETCH)) {
685 PrefetchDNS(aHref);
686 }
688 // is it a stylesheet link?
689 if (!(linkTypes & nsStyleLinkElement::eSTYLESHEET)) {
690 return NS_OK;
691 }
693 bool isAlternate = linkTypes & nsStyleLinkElement::eALTERNATE;
694 return ProcessStyleLink(nullptr, aHref, isAlternate, aTitle, aType,
695 aMedia);
696 }
698 nsresult
699 nsContentSink::ProcessStyleLink(nsIContent* aElement,
700 const nsSubstring& aHref,
701 bool aAlternate,
702 const nsSubstring& aTitle,
703 const nsSubstring& aType,
704 const nsSubstring& aMedia)
705 {
706 if (aAlternate && aTitle.IsEmpty()) {
707 // alternates must have title return without error, for now
708 return NS_OK;
709 }
711 nsAutoString mimeType;
712 nsAutoString params;
713 nsContentUtils::SplitMimeType(aType, mimeType, params);
715 // see bug 18817
716 if (!mimeType.IsEmpty() && !mimeType.LowerCaseEqualsLiteral("text/css")) {
717 // Unknown stylesheet language
718 return NS_OK;
719 }
721 nsCOMPtr<nsIURI> url;
722 nsresult rv = NS_NewURI(getter_AddRefs(url), aHref, nullptr,
723 mDocument->GetDocBaseURI());
725 if (NS_FAILED(rv)) {
726 // The URI is bad, move along, don't propagate the error (for now)
727 return NS_OK;
728 }
730 NS_ASSERTION(!aElement ||
731 aElement->NodeType() == nsIDOMNode::PROCESSING_INSTRUCTION_NODE,
732 "We only expect processing instructions here");
734 // If this is a fragment parser, we don't want to observe.
735 // We don't support CORS for processing instructions
736 bool isAlternate;
737 rv = mCSSLoader->LoadStyleLink(aElement, url, aTitle, aMedia, aAlternate,
738 CORS_NONE,
739 mRunsToCompletion ? nullptr : this, &isAlternate);
740 NS_ENSURE_SUCCESS(rv, rv);
742 if (!isAlternate && !mRunsToCompletion) {
743 ++mPendingSheetCount;
744 mScriptLoader->AddExecuteBlocker();
745 }
747 return NS_OK;
748 }
751 nsresult
752 nsContentSink::ProcessMETATag(nsIContent* aContent)
753 {
754 NS_ASSERTION(aContent, "missing meta-element");
756 nsresult rv = NS_OK;
758 // set any HTTP-EQUIV data into document's header data as well as url
759 nsAutoString header;
760 aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, header);
761 if (!header.IsEmpty()) {
762 nsAutoString result;
763 aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::content, result);
764 if (!result.IsEmpty()) {
765 nsContentUtils::ASCIIToLower(header);
766 nsCOMPtr<nsIAtom> fieldAtom(do_GetAtom(header));
767 rv = ProcessHeaderData(fieldAtom, result, aContent);
768 }
769 }
770 NS_ENSURE_SUCCESS(rv, rv);
772 if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
773 nsGkAtoms::handheldFriendly, eIgnoreCase)) {
774 nsAutoString result;
775 aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::content, result);
776 if (!result.IsEmpty()) {
777 nsContentUtils::ASCIIToLower(result);
778 mDocument->SetHeaderData(nsGkAtoms::handheldFriendly, result);
779 }
780 }
782 return rv;
783 }
786 void
787 nsContentSink::PrefetchHref(const nsAString &aHref,
788 nsINode *aSource,
789 bool aExplicit)
790 {
791 //
792 // SECURITY CHECK: disable prefetching from mailnews!
793 //
794 // walk up the docshell tree to see if any containing
795 // docshell are of type MAIL.
796 //
797 if (!mDocShell)
798 return;
800 nsCOMPtr<nsIDocShell> docshell = mDocShell;
802 nsCOMPtr<nsIDocShellTreeItem> parentItem;
803 do {
804 uint32_t appType = 0;
805 nsresult rv = docshell->GetAppType(&appType);
806 if (NS_FAILED(rv) || appType == nsIDocShell::APP_TYPE_MAIL)
807 return; // do not prefetch from mailnews
808 docshell->GetParent(getter_AddRefs(parentItem));
809 if (parentItem) {
810 docshell = do_QueryInterface(parentItem);
811 if (!docshell) {
812 NS_ERROR("cannot get a docshell from a treeItem!");
813 return;
814 }
815 }
816 } while (parentItem);
818 // OK, we passed the security check...
820 nsCOMPtr<nsIPrefetchService> prefetchService(do_GetService(NS_PREFETCHSERVICE_CONTRACTID));
821 if (prefetchService) {
822 // construct URI using document charset
823 const nsACString &charset = mDocument->GetDocumentCharacterSet();
824 nsCOMPtr<nsIURI> uri;
825 NS_NewURI(getter_AddRefs(uri), aHref,
826 charset.IsEmpty() ? nullptr : PromiseFlatCString(charset).get(),
827 mDocument->GetDocBaseURI());
828 if (uri) {
829 nsCOMPtr<nsIDOMNode> domNode = do_QueryInterface(aSource);
830 prefetchService->PrefetchURI(uri, mDocumentURI, domNode, aExplicit);
831 }
832 }
833 }
835 void
836 nsContentSink::PrefetchDNS(const nsAString &aHref)
837 {
838 nsAutoString hostname;
840 if (StringBeginsWith(aHref, NS_LITERAL_STRING("//"))) {
841 hostname = Substring(aHref, 2);
842 }
843 else {
844 nsCOMPtr<nsIURI> uri;
845 NS_NewURI(getter_AddRefs(uri), aHref);
846 if (!uri) {
847 return;
848 }
849 nsAutoCString host;
850 uri->GetHost(host);
851 CopyUTF8toUTF16(host, hostname);
852 }
854 if (!hostname.IsEmpty() && nsHTMLDNSPrefetch::IsAllowed(mDocument)) {
855 nsHTMLDNSPrefetch::PrefetchLow(hostname);
856 }
857 }
859 nsresult
860 nsContentSink::SelectDocAppCache(nsIApplicationCache *aLoadApplicationCache,
861 nsIURI *aManifestURI,
862 bool aFetchedWithHTTPGetOrEquiv,
863 CacheSelectionAction *aAction)
864 {
865 nsresult rv;
867 *aAction = CACHE_SELECTION_NONE;
869 nsCOMPtr<nsIApplicationCacheContainer> applicationCacheDocument =
870 do_QueryInterface(mDocument);
871 NS_ASSERTION(applicationCacheDocument,
872 "mDocument must implement nsIApplicationCacheContainer.");
874 if (aLoadApplicationCache) {
875 nsCOMPtr<nsIURI> groupURI;
876 rv = aLoadApplicationCache->GetManifestURI(getter_AddRefs(groupURI));
877 NS_ENSURE_SUCCESS(rv, rv);
879 bool equal = false;
880 rv = groupURI->Equals(aManifestURI, &equal);
881 NS_ENSURE_SUCCESS(rv, rv);
883 if (!equal) {
884 // This is a foreign entry, force a reload to avoid loading the foreign
885 // entry. The entry will be marked as foreign to avoid loading it again.
887 *aAction = CACHE_SELECTION_RELOAD;
888 }
889 else {
890 // The http manifest attribute URI is equal to the manifest URI of
891 // the cache the document was loaded from - associate the document with
892 // that cache and invoke the cache update process.
893 #ifdef DEBUG
894 nsAutoCString docURISpec, clientID;
895 mDocumentURI->GetAsciiSpec(docURISpec);
896 aLoadApplicationCache->GetClientID(clientID);
897 SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_CALLS,
898 ("Selection: assigning app cache %s to document %s", clientID.get(), docURISpec.get()));
899 #endif
901 rv = applicationCacheDocument->SetApplicationCache(aLoadApplicationCache);
902 NS_ENSURE_SUCCESS(rv, rv);
904 // Document will be added as implicit entry to the cache as part of
905 // the update process.
906 *aAction = CACHE_SELECTION_UPDATE;
907 }
908 }
909 else {
910 // The document was not loaded from an application cache
911 // Here we know the manifest has the same origin as the
912 // document. There is call to CheckMayLoad() on it above.
914 if (!aFetchedWithHTTPGetOrEquiv) {
915 // The document was not loaded using HTTP GET or equivalent
916 // method. The spec says to run the cache selection algorithm w/o
917 // the manifest specified.
918 *aAction = CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST;
919 }
920 else {
921 // Always do an update in this case
922 *aAction = CACHE_SELECTION_UPDATE;
923 }
924 }
926 return NS_OK;
927 }
929 nsresult
930 nsContentSink::SelectDocAppCacheNoManifest(nsIApplicationCache *aLoadApplicationCache,
931 nsIURI **aManifestURI,
932 CacheSelectionAction *aAction)
933 {
934 *aManifestURI = nullptr;
935 *aAction = CACHE_SELECTION_NONE;
937 nsresult rv;
939 if (aLoadApplicationCache) {
940 // The document was loaded from an application cache, use that
941 // application cache as the document's application cache.
942 nsCOMPtr<nsIApplicationCacheContainer> applicationCacheDocument =
943 do_QueryInterface(mDocument);
944 NS_ASSERTION(applicationCacheDocument,
945 "mDocument must implement nsIApplicationCacheContainer.");
947 #ifdef DEBUG
948 nsAutoCString docURISpec, clientID;
949 mDocumentURI->GetAsciiSpec(docURISpec);
950 aLoadApplicationCache->GetClientID(clientID);
951 SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_CALLS,
952 ("Selection, no manifest: assigning app cache %s to document %s", clientID.get(), docURISpec.get()));
953 #endif
955 rv = applicationCacheDocument->SetApplicationCache(aLoadApplicationCache);
956 NS_ENSURE_SUCCESS(rv, rv);
958 // Return the uri and invoke the update process for the selected
959 // application cache.
960 rv = aLoadApplicationCache->GetManifestURI(aManifestURI);
961 NS_ENSURE_SUCCESS(rv, rv);
963 *aAction = CACHE_SELECTION_UPDATE;
964 }
966 return NS_OK;
967 }
969 void
970 nsContentSink::ProcessOfflineManifest(nsIContent *aElement)
971 {
972 // Only check the manifest for root document nodes.
973 if (aElement != mDocument->GetRootElement()) {
974 return;
975 }
977 // Don't bother processing offline manifest for documents
978 // without a docshell
979 if (!mDocShell) {
980 return;
981 }
983 // Check for a manifest= attribute.
984 nsAutoString manifestSpec;
985 aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::manifest, manifestSpec);
986 ProcessOfflineManifest(manifestSpec);
987 }
989 void
990 nsContentSink::ProcessOfflineManifest(const nsAString& aManifestSpec)
991 {
992 // Don't bother processing offline manifest for documents
993 // without a docshell
994 if (!mDocShell) {
995 return;
996 }
998 // If the docshell's in private browsing mode, we don't want to do any
999 // manifest processing.
1000 nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(mDocShell);
1001 if (loadContext->UsePrivateBrowsing()) {
1002 return;
1003 }
1005 nsresult rv;
1007 // Grab the application cache the document was loaded from, if any.
1008 nsCOMPtr<nsIApplicationCache> applicationCache;
1010 nsCOMPtr<nsIApplicationCacheChannel> applicationCacheChannel =
1011 do_QueryInterface(mDocument->GetChannel());
1012 if (applicationCacheChannel) {
1013 bool loadedFromApplicationCache;
1014 rv = applicationCacheChannel->GetLoadedFromApplicationCache(
1015 &loadedFromApplicationCache);
1016 if (NS_FAILED(rv)) {
1017 return;
1018 }
1020 if (loadedFromApplicationCache) {
1021 rv = applicationCacheChannel->GetApplicationCache(
1022 getter_AddRefs(applicationCache));
1023 if (NS_FAILED(rv)) {
1024 return;
1025 }
1026 }
1027 }
1029 if (aManifestSpec.IsEmpty() && !applicationCache) {
1030 // Not loaded from an application cache, and no manifest
1031 // attribute. Nothing to do here.
1032 return;
1033 }
1035 CacheSelectionAction action = CACHE_SELECTION_NONE;
1036 nsCOMPtr<nsIURI> manifestURI;
1038 if (aManifestSpec.IsEmpty()) {
1039 action = CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST;
1040 }
1041 else {
1042 nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(manifestURI),
1043 aManifestSpec, mDocument,
1044 mDocumentURI);
1045 if (!manifestURI) {
1046 return;
1047 }
1049 // Documents must list a manifest from the same origin
1050 rv = mDocument->NodePrincipal()->CheckMayLoad(manifestURI, true, false);
1051 if (NS_FAILED(rv)) {
1052 action = CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST;
1053 }
1054 else {
1055 // Only continue if the document has permission to use offline APIs or
1056 // when preferences indicate to permit it automatically.
1057 if (!nsContentUtils::OfflineAppAllowed(mDocument->NodePrincipal()) &&
1058 !nsContentUtils::MaybeAllowOfflineAppByDefault(mDocument->NodePrincipal(), mDocument->GetWindow()) &&
1059 !nsContentUtils::OfflineAppAllowed(mDocument->NodePrincipal())) {
1060 return;
1061 }
1063 bool fetchedWithHTTPGetOrEquiv = false;
1064 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mDocument->GetChannel()));
1065 if (httpChannel) {
1066 nsAutoCString method;
1067 rv = httpChannel->GetRequestMethod(method);
1068 if (NS_SUCCEEDED(rv))
1069 fetchedWithHTTPGetOrEquiv = method.Equals("GET");
1070 }
1072 rv = SelectDocAppCache(applicationCache, manifestURI,
1073 fetchedWithHTTPGetOrEquiv, &action);
1074 if (NS_FAILED(rv)) {
1075 return;
1076 }
1077 }
1078 }
1080 if (action == CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST) {
1081 rv = SelectDocAppCacheNoManifest(applicationCache,
1082 getter_AddRefs(manifestURI),
1083 &action);
1084 if (NS_FAILED(rv)) {
1085 return;
1086 }
1087 }
1089 switch (action)
1090 {
1091 case CACHE_SELECTION_NONE:
1092 break;
1093 case CACHE_SELECTION_UPDATE: {
1094 nsCOMPtr<nsIOfflineCacheUpdateService> updateService =
1095 do_GetService(NS_OFFLINECACHEUPDATESERVICE_CONTRACTID);
1097 if (updateService) {
1098 nsCOMPtr<nsIDOMDocument> domdoc = do_QueryInterface(mDocument);
1099 updateService->ScheduleOnDocumentStop(manifestURI, mDocumentURI, domdoc);
1100 }
1101 break;
1102 }
1103 case CACHE_SELECTION_RELOAD: {
1104 // This situation occurs only for toplevel documents, see bottom
1105 // of SelectDocAppCache method.
1106 // The document has been loaded from a different offline cache group than
1107 // the manifest it refers to, i.e. this is a foreign entry, mark it as such
1108 // and force a reload to avoid loading it. The next attempt will not
1109 // choose it.
1111 applicationCacheChannel->MarkOfflineCacheEntryAsForeign();
1113 nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(mDocShell);
1115 webNav->Stop(nsIWebNavigation::STOP_ALL);
1116 webNav->Reload(nsIWebNavigation::LOAD_FLAGS_NONE);
1117 break;
1118 }
1119 default:
1120 NS_ASSERTION(false,
1121 "Cache selection algorithm didn't decide on proper action");
1122 break;
1123 }
1124 }
1126 void
1127 nsContentSink::ScrollToRef()
1128 {
1129 mDocument->ScrollToRef();
1130 }
1132 void
1133 nsContentSink::StartLayout(bool aIgnorePendingSheets)
1134 {
1135 if (mLayoutStarted) {
1136 // Nothing to do here
1137 return;
1138 }
1140 mDeferredLayoutStart = true;
1142 if (!aIgnorePendingSheets && WaitForPendingSheets()) {
1143 // Bail out; we'll start layout when the sheets load
1144 return;
1145 }
1147 mDeferredLayoutStart = false;
1149 // Notify on all our content. If none of our presshells have started layout
1150 // yet it'll be a no-op except for updating our data structures, a la
1151 // UpdateChildCounts() (because we don't want to double-notify on whatever we
1152 // have right now). If some of them _have_ started layout, we want to make
1153 // sure to flush tags instead of just calling UpdateChildCounts() after we
1154 // loop over the shells.
1155 FlushTags();
1157 mLayoutStarted = true;
1158 mLastNotificationTime = PR_Now();
1160 mDocument->SetMayStartLayout(true);
1161 nsCOMPtr<nsIPresShell> shell = mDocument->GetShell();
1162 // Make sure we don't call Initialize() for a shell that has
1163 // already called it. This can happen when the layout frame for
1164 // an iframe is constructed *between* the Embed() call for the
1165 // docshell in the iframe, and the content sink's call to OpenBody().
1166 // (Bug 153815)
1167 if (shell && !shell->DidInitialize()) {
1168 nsRect r = shell->GetPresContext()->GetVisibleArea();
1169 nsCOMPtr<nsIPresShell> shellGrip = shell;
1170 nsresult rv = shell->Initialize(r.width, r.height);
1171 if (NS_FAILED(rv)) {
1172 return;
1173 }
1174 }
1176 // If the document we are loading has a reference or it is a
1177 // frameset document, disable the scroll bars on the views.
1179 mDocument->SetScrollToRef(mDocumentURI);
1180 }
1182 void
1183 nsContentSink::NotifyAppend(nsIContent* aContainer, uint32_t aStartIndex)
1184 {
1185 if (aContainer->GetCurrentDoc() != mDocument) {
1186 // aContainer is not actually in our document anymore.... Just bail out of
1187 // here; notifying on our document for this append would be wrong.
1188 return;
1189 }
1191 mInNotification++;
1193 {
1194 // Scope so we call EndUpdate before we decrease mInNotification
1195 MOZ_AUTO_DOC_UPDATE(mDocument, UPDATE_CONTENT_MODEL, !mBeganUpdate);
1196 nsNodeUtils::ContentAppended(aContainer,
1197 aContainer->GetChildAt(aStartIndex),
1198 aStartIndex);
1199 mLastNotificationTime = PR_Now();
1200 }
1202 mInNotification--;
1203 }
1205 NS_IMETHODIMP
1206 nsContentSink::Notify(nsITimer *timer)
1207 {
1208 if (mParsing) {
1209 // We shouldn't interfere with our normal DidProcessAToken logic
1210 mDroppedTimer = true;
1211 return NS_OK;
1212 }
1214 if (WaitForPendingSheets()) {
1215 mDeferredFlushTags = true;
1216 } else {
1217 FlushTags();
1219 // Now try and scroll to the reference
1220 // XXX Should we scroll unconditionally for history loads??
1221 ScrollToRef();
1222 }
1224 mNotificationTimer = nullptr;
1225 return NS_OK;
1226 }
1228 bool
1229 nsContentSink::IsTimeToNotify()
1230 {
1231 if (!sNotifyOnTimer || !mLayoutStarted || !mBackoffCount ||
1232 mInMonolithicContainer) {
1233 return false;
1234 }
1236 if (WaitForPendingSheets()) {
1237 mDeferredFlushTags = true;
1238 return false;
1239 }
1241 PRTime now = PR_Now();
1243 int64_t interval = GetNotificationInterval();
1244 int64_t diff = now - mLastNotificationTime;
1246 if (diff > interval) {
1247 mBackoffCount--;
1248 return true;
1249 }
1251 return false;
1252 }
1254 nsresult
1255 nsContentSink::WillInterruptImpl()
1256 {
1257 nsresult result = NS_OK;
1259 SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_CALLS,
1260 ("nsContentSink::WillInterrupt: this=%p", this));
1261 #ifndef SINK_NO_INCREMENTAL
1262 if (WaitForPendingSheets()) {
1263 mDeferredFlushTags = true;
1264 } else if (sNotifyOnTimer && mLayoutStarted) {
1265 if (mBackoffCount && !mInMonolithicContainer) {
1266 int64_t now = PR_Now();
1267 int64_t interval = GetNotificationInterval();
1268 int64_t diff = now - mLastNotificationTime;
1270 // If it's already time for us to have a notification
1271 if (diff > interval || mDroppedTimer) {
1272 mBackoffCount--;
1273 SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_REFLOW,
1274 ("nsContentSink::WillInterrupt: flushing tags since we've "
1275 "run out time; backoff count: %d", mBackoffCount));
1276 result = FlushTags();
1277 if (mDroppedTimer) {
1278 ScrollToRef();
1279 mDroppedTimer = false;
1280 }
1281 } else if (!mNotificationTimer) {
1282 interval -= diff;
1283 int32_t delay = interval;
1285 // Convert to milliseconds
1286 delay /= PR_USEC_PER_MSEC;
1288 mNotificationTimer = do_CreateInstance("@mozilla.org/timer;1",
1289 &result);
1290 if (NS_SUCCEEDED(result)) {
1291 SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_REFLOW,
1292 ("nsContentSink::WillInterrupt: setting up timer with "
1293 "delay %d", delay));
1295 result =
1296 mNotificationTimer->InitWithCallback(this, delay,
1297 nsITimer::TYPE_ONE_SHOT);
1298 if (NS_FAILED(result)) {
1299 mNotificationTimer = nullptr;
1300 }
1301 }
1302 }
1303 }
1304 } else {
1305 SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_REFLOW,
1306 ("nsContentSink::WillInterrupt: flushing tags "
1307 "unconditionally"));
1308 result = FlushTags();
1309 }
1310 #endif
1312 mParsing = false;
1314 return result;
1315 }
1317 nsresult
1318 nsContentSink::WillResumeImpl()
1319 {
1320 SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_CALLS,
1321 ("nsContentSink::WillResume: this=%p", this));
1323 mParsing = true;
1325 return NS_OK;
1326 }
1328 nsresult
1329 nsContentSink::DidProcessATokenImpl()
1330 {
1331 if (mRunsToCompletion || !mParser) {
1332 return NS_OK;
1333 }
1335 // Get the current user event time
1336 nsIPresShell *shell = mDocument->GetShell();
1337 if (!shell) {
1338 // If there's no pres shell in the document, return early since
1339 // we're not laying anything out here.
1340 return NS_OK;
1341 }
1343 // Increase before comparing to gEventProbeRate
1344 ++mDeflectedCount;
1346 // Check if there's a pending event
1347 if (sPendingEventMode != 0 && !mHasPendingEvent &&
1348 (mDeflectedCount % sEventProbeRate) == 0) {
1349 nsViewManager* vm = shell->GetViewManager();
1350 NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
1351 nsCOMPtr<nsIWidget> widget;
1352 vm->GetRootWidget(getter_AddRefs(widget));
1353 mHasPendingEvent = widget && widget->HasPendingInputEvent();
1354 }
1356 if (mHasPendingEvent && sPendingEventMode == 2) {
1357 return NS_ERROR_HTMLPARSER_INTERRUPTED;
1358 }
1360 // Have we processed enough tokens to check time?
1361 if (!mHasPendingEvent &&
1362 mDeflectedCount < uint32_t(mDynamicLowerValue ? sInteractiveDeflectCount :
1363 sPerfDeflectCount)) {
1364 return NS_OK;
1365 }
1367 mDeflectedCount = 0;
1369 // Check if it's time to return to the main event loop
1370 if (PR_IntervalToMicroseconds(PR_IntervalNow()) > mCurrentParseEndTime) {
1371 return NS_ERROR_HTMLPARSER_INTERRUPTED;
1372 }
1374 return NS_OK;
1375 }
1377 //----------------------------------------------------------------------
1379 void
1380 nsContentSink::FavorPerformanceHint(bool perfOverStarvation, uint32_t starvationDelay)
1381 {
1382 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
1383 nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
1384 if (appShell)
1385 appShell->FavorPerformanceHint(perfOverStarvation, starvationDelay);
1386 }
1388 void
1389 nsContentSink::BeginUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType)
1390 {
1391 // Remember nested updates from updates that we started.
1392 if (mInNotification > 0 && mUpdatesInNotification < 2) {
1393 ++mUpdatesInNotification;
1394 }
1396 // If we're in a script and we didn't do the notification,
1397 // something else in the script processing caused the
1398 // notification to occur. Since this could result in frame
1399 // creation, make sure we've flushed everything before we
1400 // continue.
1402 if (!mInNotification++) {
1403 FlushTags();
1404 }
1405 }
1407 void
1408 nsContentSink::EndUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType)
1409 {
1410 // If we're in a script and we didn't do the notification,
1411 // something else in the script processing caused the
1412 // notification to occur. Update our notion of how much
1413 // has been flushed to include any new content if ending
1414 // this update leaves us not inside a notification.
1415 if (!--mInNotification) {
1416 UpdateChildCounts();
1417 }
1418 }
1420 void
1421 nsContentSink::DidBuildModelImpl(bool aTerminated)
1422 {
1423 if (mDocument) {
1424 MOZ_ASSERT(aTerminated ||
1425 mDocument->GetReadyStateEnum() ==
1426 nsIDocument::READYSTATE_LOADING, "Bad readyState");
1427 mDocument->SetReadyStateInternal(nsIDocument::READYSTATE_INTERACTIVE);
1428 }
1430 if (mScriptLoader) {
1431 mScriptLoader->ParsingComplete(aTerminated);
1432 }
1434 if (!mDocument->HaveFiredDOMTitleChange()) {
1435 mDocument->NotifyPossibleTitleChange(false);
1436 }
1438 // Cancel a timer if we had one out there
1439 if (mNotificationTimer) {
1440 SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_REFLOW,
1441 ("nsContentSink::DidBuildModel: canceling notification "
1442 "timeout"));
1443 mNotificationTimer->Cancel();
1444 mNotificationTimer = 0;
1445 }
1446 }
1448 void
1449 nsContentSink::DropParserAndPerfHint(void)
1450 {
1451 if (!mParser) {
1452 // Make sure we don't unblock unload too many times
1453 return;
1454 }
1456 // Ref. Bug 49115
1457 // Do this hack to make sure that the parser
1458 // doesn't get destroyed, accidently, before
1459 // the circularity, between sink & parser, is
1460 // actually broken.
1461 // Drop our reference to the parser to get rid of a circular
1462 // reference.
1463 nsRefPtr<nsParserBase> kungFuDeathGrip(mParser.forget());
1465 if (mDynamicLowerValue) {
1466 // Reset the performance hint which was set to FALSE
1467 // when mDynamicLowerValue was set.
1468 FavorPerformanceHint(true, 0);
1469 }
1471 if (!mRunsToCompletion) {
1472 mDocument->UnblockOnload(true);
1473 }
1474 }
1476 bool
1477 nsContentSink::IsScriptExecutingImpl()
1478 {
1479 return !!mScriptLoader->GetCurrentScript();
1480 }
1482 nsresult
1483 nsContentSink::WillParseImpl(void)
1484 {
1485 if (mRunsToCompletion || !mDocument) {
1486 return NS_OK;
1487 }
1489 nsIPresShell *shell = mDocument->GetShell();
1490 if (!shell) {
1491 return NS_OK;
1492 }
1494 uint32_t currentTime = PR_IntervalToMicroseconds(PR_IntervalNow());
1496 if (sEnablePerfMode == 0) {
1497 nsViewManager* vm = shell->GetViewManager();
1498 NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
1499 uint32_t lastEventTime;
1500 vm->GetLastUserEventTime(lastEventTime);
1502 bool newDynLower =
1503 mDocument->IsInBackgroundWindow() ||
1504 ((currentTime - mBeginLoadTime) > uint32_t(sInitialPerfTime) &&
1505 (currentTime - lastEventTime) < uint32_t(sInteractiveTime));
1507 if (mDynamicLowerValue != newDynLower) {
1508 FavorPerformanceHint(!newDynLower, 0);
1509 mDynamicLowerValue = newDynLower;
1510 }
1511 }
1513 mDeflectedCount = 0;
1514 mHasPendingEvent = false;
1516 mCurrentParseEndTime = currentTime +
1517 (mDynamicLowerValue ? sInteractiveParseTime : sPerfParseTime);
1519 return NS_OK;
1520 }
1522 void
1523 nsContentSink::WillBuildModelImpl()
1524 {
1525 if (!mRunsToCompletion) {
1526 mDocument->BlockOnload();
1528 mBeginLoadTime = PR_IntervalToMicroseconds(PR_IntervalNow());
1529 }
1531 mDocument->ResetScrolledToRefAlready();
1533 if (mProcessLinkHeaderEvent.get()) {
1534 mProcessLinkHeaderEvent.Revoke();
1536 DoProcessLinkHeader();
1537 }
1538 }
1540 /* static */
1541 void
1542 nsContentSink::NotifyDocElementCreated(nsIDocument* aDoc)
1543 {
1544 nsCOMPtr<nsIObserverService> observerService =
1545 mozilla::services::GetObserverService();
1546 if (observerService) {
1547 nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(aDoc);
1548 observerService->
1549 NotifyObservers(domDoc, "document-element-inserted",
1550 EmptyString().get());
1551 }
1552 }