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/. */
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"
22 #include "nsNetUtil.h"
23 #include "nsIHttpChannel.h"
24 #include "nsIContent.h"
25 #include "nsIPresShell.h"
26 #include "nsPresContext.h"
27 #include "nsViewManager.h"
29 #include "nsGkAtoms.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
)
67 NS_IMPL_CYCLE_COLLECTION_CLASS(nsContentSink
)
69 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsContentSink
)
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?");
100 if (!gContentSinkLogModuleInfo
) {
101 gContentSinkLogModuleInfo
= PR_NewLogModule("nscontentsink");
106 nsContentSink::~nsContentSink()
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
;
129 nsContentSink::InitializeStatics()
131 Preferences::AddBoolVarCache(&sNotifyOnTimer
,
132 "content.notify.ontimer", true);
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);
166 nsContentSink::Init(nsIDocument
* aDoc
,
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
;
181 mDocShell
= do_QueryInterface(aContainer
);
182 mScriptLoader
= mDocument
->ScriptLoader();
184 if (!mRunsToCompletion
) {
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);
210 nsContentSink::StyleSheetLoaded(CSSStyleSheet
* aSheet
,
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
) {
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.
234 // Go ahead and try to scroll to our ref if we have one
238 mScriptLoader
->RemoveExecuteBlocker();
245 nsContentSink::ProcessHTTPHeaders(nsIChannel
* aChannel
)
247 nsCOMPtr
<nsIHttpChannel
> httpchannel(do_QueryInterface(aChannel
));
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"),
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());
272 mProcessLinkHeaderEvent
.Forget();
280 nsContentSink::ProcessHeaderData(nsIAtom
* aHeader
, const nsAString
& aValue
,
281 nsIContent
* aContent
)
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
292 nsCOMPtr
<nsICookieService
> cookieServ
=
293 do_GetService(NS_COOKIESERVICE_CONTRACTID
, &rv
);
298 // Get a URI from the document principal
300 // We use the original codebase in case the codebase was changed
303 // Note that a non-codebase principal (eg the system principal) will return
305 nsCOMPtr
<nsIURI
> codebaseURI
;
306 rv
= mDocument
->NodePrincipal()->GetURI(getter_AddRefs(codebaseURI
));
307 NS_ENSURE_TRUE(codebaseURI
, rv
);
309 nsCOMPtr
<nsIChannel
> channel
;
311 mParser
->GetChannel(getter_AddRefs(channel
));
314 rv
= cookieServ
->SetCookieString(codebaseURI
,
316 NS_ConvertUTF16toUTF8(aValue
).get(),
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();
329 shell
->DisableThemeSupport();
339 nsContentSink::DoProcessLinkHeader()
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>
350 nsContentSink::LinkContextIsOurDocument(const nsSubstring
& aAnchor
)
352 if (aAnchor
.IsEmpty()) {
353 // anchor parameter not present or empty -> same document reference
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
));
370 // resolve anchor against context
371 nsCOMPtr
<nsIURI
> resolvedUri
;
372 rv
= NS_NewURI(getter_AddRefs(resolvedUri
), aAnchor
,
373 nullptr, contextUri
);
381 rv
= contextUri
->Equals(resolvedUri
, &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)
397 nsContentSink::Decode5987Format(nsAString
& aEncoded
) {
400 nsCOMPtr
<nsIMIMEHeaderParam
> mimehdrpar
=
401 do_GetService(NS_MIMEHEADERPARAM_CONTRACTID
, &rv
);
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
);
421 nsAutoString decoded
;
422 nsAutoCString language
;
424 rv
= mimehdrpar
->DecodeRFC5987Param(asciiValue
, language
, decoded
);
433 nsContentSink::ProcessLinkHeader(const nsAString
& aLinkData
)
437 // keep track where we are within the header field
438 bool seenParameters
= false;
440 // parse link content and call process style link
444 nsAutoString titleStar
;
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
;
460 while (*start
!= kNullCh
) {
461 // skip leading space
462 while ((*start
!= kNullCh
) && nsCRT::IsAsciiSpace(*start
)) {
469 bool wasQuotedString
= false;
471 // look for semicolon or comma
472 while (*end
!= kNullCh
&& *end
!= kSemicolon
&& *end
!= kComma
) {
475 if (ch
== kQuote
|| ch
== kLessThan
) {
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
) {
497 if (quote
== *closeQuote
) {
500 // skip to close quote
507 if (ch
!= kNullCh
&& ch
!= kSemicolon
&& ch
!= kComma
) {
513 // keep going until semi or comma
514 while (ch
!= kNullCh
&& ch
!= kSemicolon
&& ch
!= kComma
) {
533 if ((*start
== kLessThan
) && (*last
== kGreaterThan
)) {
536 // first instance of <...> wins
537 // also, do not allow hrefs after the first param was seen
538 if (href
.IsEmpty() && !seenParameters
) {
540 href
.StripWhitespace();
543 char16_t
* equals
= start
;
544 seenParameters
= true;
546 while ((*equals
!= kNullCh
) && (*equals
!= kEqual
)) {
550 if (*equals
!= kNullCh
) {
552 nsAutoString
attr(start
);
553 attr
.StripWhitespace();
555 char16_t
* value
= ++equals
;
556 while (nsCRT::IsAsciiSpace(*value
)) {
560 if ((*value
== kQuote
) && (*value
== *last
)) {
565 if (wasQuotedString
) {
567 char16_t
* unescaped
= value
;
568 char16_t
*src
= value
;
570 while (*src
!= kNullCh
) {
571 if (*src
== kBackSlash
&& *(src
+ 1) != kNullCh
) {
574 *unescaped
++ = *src
++;
577 *unescaped
= kNullCh
;
580 if (attr
.LowerCaseEqualsLiteral("rel")) {
583 rel
.CompressWhitespace();
585 } else if (attr
.LowerCaseEqualsLiteral("title")) {
586 if (title
.IsEmpty()) {
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
596 if (Decode5987Format(tmp
)) {
598 titleStar
.CompressWhitespace();
600 // header value did not parse, throw it away
601 titleStar
.Truncate();
604 } else if (attr
.LowerCaseEqualsLiteral("type")) {
605 if (type
.IsEmpty()) {
607 type
.StripWhitespace();
609 } else if (attr
.LowerCaseEqualsLiteral("media")) {
610 if (media
.IsEmpty()) {
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()) {
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
,
645 seenParameters
= false;
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
,
664 nsContentSink::ProcessLink(const nsSubstring
& aAnchor
, const nsSubstring
& aHref
,
665 const nsSubstring
& aRel
, const nsSubstring
& aTitle
,
666 const nsSubstring
& aType
, const nsSubstring
& aMedia
)
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
675 if (!LinkContextIsOurDocument(aAnchor
)) {
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
)) {
689 // is it a stylesheet link?
690 if (!(linkTypes
& nsStyleLinkElement::eSTYLESHEET
)) {
694 bool isAlternate
= linkTypes
& nsStyleLinkElement::eALTERNATE
;
695 return ProcessStyleLink(nullptr, aHref
, isAlternate
, aTitle
, aType
,
700 nsContentSink::ProcessStyleLink(nsIContent
* aElement
,
701 const nsSubstring
& aHref
,
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
712 nsAutoString mimeType
;
714 nsContentUtils::SplitMimeType(aType
, mimeType
, params
);
717 if (!mimeType
.IsEmpty() && !mimeType
.LowerCaseEqualsLiteral("text/css")) {
718 // Unknown stylesheet language
722 nsCOMPtr
<nsIURI
> url
;
723 nsresult rv
= NS_NewURI(getter_AddRefs(url
), aHref
, nullptr,
724 mDocument
->GetDocBaseURI());
727 // The URI is bad, move along, don't propagate the error (for now)
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
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();
753 nsContentSink::ProcessMETATag(nsIContent
* aContent
)
755 NS_ASSERTION(aContent
, "missing meta-element");
759 // set any HTTP-EQUIV data into document's header data as well as url
761 aContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::httpEquiv
, header
);
762 if (!header
.IsEmpty()) {
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
)) {
776 aContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::content
, result
);
777 if (!result
.IsEmpty()) {
778 nsContentUtils::ASCIIToLower(result
);
779 mDocument
->SetHeaderData(nsGkAtoms::handheldFriendly
, result
);
788 nsContentSink::PrefetchHref(const nsAString
&aHref
,
793 // SECURITY CHECK: disable prefetching from mailnews!
795 // walk up the docshell tree to see if any containing
796 // docshell are of type MAIL.
801 nsCOMPtr
<nsIDocShell
> docshell
= mDocShell
;
803 nsCOMPtr
<nsIDocShellTreeItem
> parentItem
;
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
));
811 docshell
= do_QueryInterface(parentItem
);
813 NS_ERROR("cannot get a docshell from a treeItem!");
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());
830 nsCOMPtr
<nsIDOMNode
> domNode
= do_QueryInterface(aSource
);
831 prefetchService
->PrefetchURI(uri
, mDocumentURI
, domNode
, aExplicit
);
837 nsContentSink::PrefetchDNS(const nsAString
&aHref
)
839 nsAutoString hostname
;
841 if (StringBeginsWith(aHref
, NS_LITERAL_STRING("//"))) {
842 hostname
= Substring(aHref
, 2);
845 nsCOMPtr
<nsIURI
> uri
;
846 NS_NewURI(getter_AddRefs(uri
), aHref
);
852 CopyUTF8toUTF16(host
, hostname
);
855 if (!hostname
.IsEmpty() && nsHTMLDNSPrefetch::IsAllowed(mDocument
)) {
856 nsHTMLDNSPrefetch::PrefetchLow(hostname
);
861 nsContentSink::SelectDocAppCache(nsIApplicationCache
*aLoadApplicationCache
,
862 nsIURI
*aManifestURI
,
863 bool aFetchedWithHTTPGetOrEquiv
,
864 CacheSelectionAction
*aAction
)
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
);
881 rv
= groupURI
->Equals(aManifestURI
, &equal
);
882 NS_ENSURE_SUCCESS(rv
, rv
);
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
;
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.
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()));
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
;
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
;
922 // Always do an update in this case
923 *aAction
= CACHE_SELECTION_UPDATE
;
931 nsContentSink::SelectDocAppCacheNoManifest(nsIApplicationCache
*aLoadApplicationCache
,
932 nsIURI
**aManifestURI
,
933 CacheSelectionAction
*aAction
)
935 *aManifestURI
= nullptr;
936 *aAction
= CACHE_SELECTION_NONE
;
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.");
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()));
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
;
971 nsContentSink::ProcessOfflineManifest(nsIContent
*aElement
)
973 // Only check the manifest for root document nodes.
974 if (aElement
!= mDocument
->GetRootElement()) {
978 // Don't bother processing offline manifest for documents
979 // without a docshell
984 // Check for a manifest= attribute.
985 nsAutoString manifestSpec
;
986 aElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::manifest
, manifestSpec
);
987 ProcessOfflineManifest(manifestSpec
);
991 nsContentSink::ProcessOfflineManifest(const nsAString
& aManifestSpec
)
993 // Don't bother processing offline manifest for documents
994 // without a docshell
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()) {
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
)) {
1021 if (loadedFromApplicationCache
) {
1022 rv
= applicationCacheChannel
->GetApplicationCache(
1023 getter_AddRefs(applicationCache
));
1024 if (NS_FAILED(rv
)) {
1030 if (aManifestSpec
.IsEmpty() && !applicationCache
) {
1031 // Not loaded from an application cache, and no manifest
1032 // attribute. Nothing to do here.
1036 CacheSelectionAction action
= CACHE_SELECTION_NONE
;
1037 nsCOMPtr
<nsIURI
> manifestURI
;
1039 if (aManifestSpec
.IsEmpty()) {
1040 action
= CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST
;
1043 nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(manifestURI
),
1044 aManifestSpec
, mDocument
,
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
;
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())) {
1064 bool fetchedWithHTTPGetOrEquiv
= false;
1065 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(mDocument
->GetChannel()));
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
)) {
1081 if (action
== CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST
) {
1082 rv
= SelectDocAppCacheNoManifest(applicationCache
,
1083 getter_AddRefs(manifestURI
),
1085 if (NS_FAILED(rv
)) {
1092 case CACHE_SELECTION_NONE
:
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
);
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
1112 applicationCacheChannel
->MarkOfflineCacheEntryAsForeign();
1114 nsCOMPtr
<nsIWebNavigation
> webNav
= do_QueryInterface(mDocShell
);
1116 webNav
->Stop(nsIWebNavigation::STOP_ALL
);
1117 webNav
->Reload(nsIWebNavigation::LOAD_FLAGS_NONE
);
1122 "Cache selection algorithm didn't decide on proper action");
1128 nsContentSink::ScrollToRef()
1130 mDocument
->ScrollToRef();
1134 nsContentSink::StartLayout(bool aIgnorePendingSheets
)
1136 if (mLayoutStarted
) {
1137 // Nothing to do here
1141 mDeferredLayoutStart
= true;
1143 if (!aIgnorePendingSheets
&& WaitForPendingSheets()) {
1144 // Bail out; we'll start layout when the sheets load
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.
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().
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
)) {
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());
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.
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
),
1200 mLastNotificationTime
= PR_Now();
1207 nsContentSink::Notify(nsITimer
*timer
)
1210 // We shouldn't interfere with our normal DidProcessAToken logic
1211 mDroppedTimer
= true;
1215 if (WaitForPendingSheets()) {
1216 mDeferredFlushTags
= true;
1220 // Now try and scroll to the reference
1221 // XXX Should we scroll unconditionally for history loads??
1225 mNotificationTimer
= nullptr;
1230 nsContentSink::IsTimeToNotify()
1232 if (!sNotifyOnTimer
|| !mLayoutStarted
|| !mBackoffCount
||
1233 mInMonolithicContainer
) {
1237 if (WaitForPendingSheets()) {
1238 mDeferredFlushTags
= true;
1242 PRTime now
= PR_Now();
1244 int64_t interval
= GetNotificationInterval();
1245 int64_t diff
= now
- mLastNotificationTime
;
1247 if (diff
> interval
) {
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
) {
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
) {
1280 mDroppedTimer
= false;
1282 } else if (!mNotificationTimer
) {
1284 int32_t delay
= interval
;
1286 // Convert to milliseconds
1287 delay
/= PR_USEC_PER_MSEC
;
1289 mNotificationTimer
= do_CreateInstance("@mozilla.org/timer;1",
1291 if (NS_SUCCEEDED(result
)) {
1292 SINK_TRACE(gContentSinkLogModuleInfo
, SINK_TRACE_REFLOW
,
1293 ("nsContentSink::WillInterrupt: setting up timer with "
1294 "delay %d", delay
));
1297 mNotificationTimer
->InitWithCallback(this, delay
,
1298 nsITimer::TYPE_ONE_SHOT
);
1299 if (NS_FAILED(result
)) {
1300 mNotificationTimer
= nullptr;
1306 SINK_TRACE(gContentSinkLogModuleInfo
, SINK_TRACE_REFLOW
,
1307 ("nsContentSink::WillInterrupt: flushing tags "
1308 "unconditionally"));
1309 result
= FlushTags();
1319 nsContentSink::WillResumeImpl()
1321 SINK_TRACE(gContentSinkLogModuleInfo
, SINK_TRACE_CALLS
,
1322 ("nsContentSink::WillResume: this=%p", this));
1330 nsContentSink::DidProcessATokenImpl()
1332 if (mRunsToCompletion
|| !mParser
) {
1336 // Get the current user event time
1337 nsIPresShell
*shell
= mDocument
->GetShell();
1339 // If there's no pres shell in the document, return early since
1340 // we're not laying anything out here.
1344 // Increase before comparing to gEventProbeRate
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
)) {
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
;
1378 //----------------------------------------------------------------------
1381 nsContentSink::FavorPerformanceHint(bool perfOverStarvation
, uint32_t starvationDelay
)
1383 static NS_DEFINE_CID(kAppShellCID
, NS_APPSHELL_CID
);
1384 nsCOMPtr
<nsIAppShell
> appShell
= do_GetService(kAppShellCID
);
1386 appShell
->FavorPerformanceHint(perfOverStarvation
, starvationDelay
);
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
1403 if (!mInNotification
++) {
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();
1422 nsContentSink::DidBuildModelImpl(bool aTerminated
)
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 "
1444 mNotificationTimer
->Cancel();
1445 mNotificationTimer
= 0;
1450 nsContentSink::DropParserAndPerfHint(void)
1453 // Make sure we don't unblock unload too many times
1458 // Do this hack to make sure that the parser
1459 // doesn't get destroyed, accidently, before
1460 // the circularity, between sink & parser, is
1462 // Drop our reference to the parser to get rid of a circular
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);
1478 nsContentSink::IsScriptExecutingImpl()
1480 return !!mScriptLoader
->GetCurrentScript();
1484 nsContentSink::WillParseImpl(void)
1486 if (mRunsToCompletion
|| !mDocument
) {
1490 nsIPresShell
*shell
= mDocument
->GetShell();
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
);
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
);
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();
1543 nsContentSink::NotifyDocElementCreated(nsIDocument
* aDoc
)
1545 nsCOMPtr
<nsIObserverService
> observerService
=
1546 mozilla::services::GetObserverService();
1547 if (observerService
) {
1548 nsCOMPtr
<nsIDOMDocument
> domDoc
= do_QueryInterface(aDoc
);
1550 NotifyObservers(domDoc
, "document-element-inserted",
1551 EmptyString().get());