diff options
Diffstat (limited to 'dom/base/nsContentSink.cpp')
-rw-r--r-- | dom/base/nsContentSink.cpp | 1518 |
1 files changed, 1518 insertions, 0 deletions
diff --git a/dom/base/nsContentSink.cpp b/dom/base/nsContentSink.cpp new file mode 100644 index 0000000000..90dfafb2b8 --- /dev/null +++ b/dom/base/nsContentSink.cpp @@ -0,0 +1,1518 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * Base class for the XML and HTML content sinks, which construct a + * DOM based on information from the parser. + */ + +#include "nsContentSink.h" +#include "mozilla/Components.h" +#include "mozilla/PresShell.h" +#include "mozilla/StaticPrefs_browser.h" +#include "mozilla/StaticPrefs_content.h" +#include "mozilla/dom/Document.h" +#include "mozilla/dom/LinkStyle.h" +#include "mozilla/css/Loader.h" +#include "mozilla/dom/MutationObservers.h" +#include "mozilla/dom/SRILogHelper.h" +#include "mozilla/StoragePrincipalHelper.h" +#include "nsIDocShell.h" +#include "nsILoadContext.h" +#include "nsIPrefetchService.h" +#include "nsIURI.h" +#include "nsNetUtil.h" +#include "nsIMIMEHeaderParam.h" +#include "nsIProtocolHandler.h" +#include "nsIHttpChannel.h" +#include "nsIContent.h" +#include "nsPresContext.h" +#include "nsViewManager.h" +#include "nsAtom.h" +#include "nsGkAtoms.h" +#include "nsGlobalWindowInner.h" +#include "nsNetCID.h" +#include "nsIOfflineCacheUpdate.h" +#include "nsIApplicationCache.h" +#include "nsIApplicationCacheChannel.h" +#include "nsICookieService.h" +#include "nsContentUtils.h" +#include "nsNodeInfoManager.h" +#include "nsIAppShell.h" +#include "nsIWidget.h" +#include "nsWidgetsCID.h" +#include "mozAutoDocUpdate.h" +#include "nsIWebNavigation.h" +#include "nsGenericHTMLElement.h" +#include "nsHTMLDNSPrefetch.h" +#include "nsIObserverService.h" +#include "mozilla/Preferences.h" +#include "mozilla/dom/ServiceWorkerDescriptor.h" +#include "mozilla/dom/ScriptLoader.h" +#include "nsParserConstants.h" +#include "nsSandboxFlags.h" +#include "Link.h" +#include "HTMLLinkElement.h" +using namespace mozilla; +using namespace mozilla::css; +using namespace mozilla::dom; + +LazyLogModule gContentSinkLogModuleInfo("nscontentsink"); + +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsContentSink) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsContentSink) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsContentSink) + NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver) + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) + NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver) + NS_INTERFACE_MAP_ENTRY(nsIMutationObserver) + NS_INTERFACE_MAP_ENTRY(nsITimerCallback) + NS_INTERFACE_MAP_ENTRY(nsINamed) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDocumentObserver) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTION_CLASS(nsContentSink) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsContentSink) + if (tmp->mDocument) { + tmp->mDocument->RemoveObserver(tmp); + } + NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mParser) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mCSSLoader) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mNodeInfoManager) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mScriptLoader) + NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE +NS_IMPL_CYCLE_COLLECTION_UNLINK_END +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsContentSink) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCSSLoader) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfoManager) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +nsContentSink::nsContentSink() + : mBackoffCount(0), + mLastNotificationTime(0), + mLayoutStarted(0), + mDynamicLowerValue(0), + mParsing(0), + mDroppedTimer(0), + mDeferredLayoutStart(0), + mDeferredFlushTags(0), + mIsDocumentObserver(0), + mRunsToCompletion(0), + mIsBlockingOnload(false), + mDeflectedCount(0), + mHasPendingEvent(false), + mCurrentParseEndTime(0), + mBeginLoadTime(0), + mLastSampledUserEventTime(0), + mInMonolithicContainer(0), + mInNotification(0), + mUpdatesInNotification(0), + mPendingSheetCount(0) { + NS_ASSERTION(!mLayoutStarted, "What?"); + NS_ASSERTION(!mDynamicLowerValue, "What?"); + NS_ASSERTION(!mParsing, "What?"); + NS_ASSERTION(mLastSampledUserEventTime == 0, "What?"); + NS_ASSERTION(mDeflectedCount == 0, "What?"); + NS_ASSERTION(!mDroppedTimer, "What?"); + NS_ASSERTION(mInMonolithicContainer == 0, "What?"); + NS_ASSERTION(mInNotification == 0, "What?"); + NS_ASSERTION(!mDeferredLayoutStart, "What?"); +} + +nsContentSink::~nsContentSink() { + if (mDocument) { + // Remove ourselves just to be safe, though we really should have + // been removed in DidBuildModel if everything worked right. + mDocument->RemoveObserver(this); + } +} + +nsresult nsContentSink::Init(Document* aDoc, nsIURI* aURI, + nsISupports* aContainer, nsIChannel* aChannel) { + MOZ_ASSERT(aDoc, "null ptr"); + MOZ_ASSERT(aURI, "null ptr"); + + if (!aDoc || !aURI) { + return NS_ERROR_NULL_POINTER; + } + + mDocument = aDoc; + + mDocumentURI = aURI; + mDocShell = do_QueryInterface(aContainer); + mScriptLoader = mDocument->ScriptLoader(); + + if (!mRunsToCompletion) { + if (mDocShell) { + uint32_t loadType = 0; + mDocShell->GetLoadType(&loadType); + mDocument->SetChangeScrollPosWhenScrollingToRef( + (loadType & nsIDocShell::LOAD_CMD_HISTORY) == 0); + } + + ProcessHTTPHeaders(aChannel); + } + + mCSSLoader = aDoc->CSSLoader(); + + mNodeInfoManager = aDoc->NodeInfoManager(); + + mBackoffCount = StaticPrefs::content_notify_backoffcount(); + + if (StaticPrefs::content_sink_enable_perf_mode() != 0) { + mDynamicLowerValue = StaticPrefs::content_sink_enable_perf_mode() == 1; + FavorPerformanceHint(!mDynamicLowerValue, 0); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsContentSink::StyleSheetLoaded(StyleSheet* aSheet, bool aWasDeferred, + nsresult aStatus) { + MOZ_ASSERT(!mRunsToCompletion, "How come a fragment parser observed sheets?"); + if (aWasDeferred) { + return NS_OK; + } + MOZ_ASSERT(mPendingSheetCount > 0, "How'd that happen?"); + --mPendingSheetCount; + + const bool loadedAllSheets = !mPendingSheetCount; + if (loadedAllSheets && (mDeferredLayoutStart || mDeferredFlushTags)) { + if (mDeferredFlushTags) { + FlushTags(); + } + if (mDeferredLayoutStart) { + // We might not have really started layout, since this sheet was still + // loading. Do it now. Probably doesn't matter whether we do this + // before or after we unblock scripts, but before feels saner. Note + // that if mDeferredLayoutStart is true, that means any subclass + // StartLayout() stuff that needs to happen has already happened, so + // we don't need to worry about it. + StartLayout(false); + } + + // Go ahead and try to scroll to our ref if we have one + ScrollToRef(); + } + + mScriptLoader->RemoveParserBlockingScriptExecutionBlocker(); + + if (loadedAllSheets && + mDocument->GetReadyStateEnum() >= Document::READYSTATE_INTERACTIVE) { + mScriptLoader->DeferCheckpointReached(); + } + + return NS_OK; +} + +nsresult nsContentSink::ProcessHTTPHeaders(nsIChannel* aChannel) { + nsCOMPtr<nsIHttpChannel> httpchannel(do_QueryInterface(aChannel)); + + if (!httpchannel) { + return NS_OK; + } + + // Note that the only header we care about is the "link" header, since we + // have all the infrastructure for kicking off stylesheet loads. + + nsAutoCString linkHeader; + + nsresult rv = httpchannel->GetResponseHeader("link"_ns, linkHeader); + if (NS_SUCCEEDED(rv) && !linkHeader.IsEmpty()) { + mDocument->SetHeaderData(nsGkAtoms::link, + NS_ConvertASCIItoUTF16(linkHeader)); + + NS_ASSERTION(!mProcessLinkHeaderEvent.get(), + "Already dispatched an event?"); + + mProcessLinkHeaderEvent = + NewNonOwningRunnableMethod("nsContentSink::DoProcessLinkHeader", this, + &nsContentSink::DoProcessLinkHeader); + rv = NS_DispatchToCurrentThread(mProcessLinkHeaderEvent.get()); + if (NS_FAILED(rv)) { + mProcessLinkHeaderEvent.Forget(); + } + } + + return NS_OK; +} + +void nsContentSink::DoProcessLinkHeader() { + nsAutoString value; + mDocument->GetHeaderData(nsGkAtoms::link, value); + ProcessLinkHeader(value); +} + +// check whether the Link header field applies to the context resource +// see <http://tools.ietf.org/html/rfc5988#section-5.2> + +bool nsContentSink::LinkContextIsOurDocument(const nsAString& aAnchor) { + if (aAnchor.IsEmpty()) { + // anchor parameter not present or empty -> same document reference + return true; + } + + nsIURI* docUri = mDocument->GetDocumentURI(); + + // the document URI might contain a fragment identifier ("#...') + // we want to ignore that because it's invisible to the server + // and just affects the local interpretation in the recipient + nsCOMPtr<nsIURI> contextUri; + nsresult rv = NS_GetURIWithoutRef(docUri, getter_AddRefs(contextUri)); + + if (NS_FAILED(rv)) { + // copying failed + return false; + } + + // resolve anchor against context + nsCOMPtr<nsIURI> resolvedUri; + rv = NS_NewURI(getter_AddRefs(resolvedUri), aAnchor, nullptr, contextUri); + + if (NS_FAILED(rv)) { + // resolving failed + return false; + } + + bool same; + rv = contextUri->Equals(resolvedUri, &same); + if (NS_FAILED(rv)) { + // comparison failed + return false; + } + + return same; +} + +// Decode a parameter value using the encoding defined in RFC 5987 (in place) +// +// charset "'" [ language ] "'" value-chars +// +// returns true when decoding happened successfully (otherwise leaves +// passed value alone) +bool nsContentSink::Decode5987Format(nsAString& aEncoded) { + nsresult rv; + nsCOMPtr<nsIMIMEHeaderParam> mimehdrpar = + do_GetService(NS_MIMEHEADERPARAM_CONTRACTID, &rv); + if (NS_FAILED(rv)) return false; + + nsAutoCString asciiValue; + + const char16_t* encstart = aEncoded.BeginReading(); + const char16_t* encend = aEncoded.EndReading(); + + // create a plain ASCII string, aborting if we can't do that + // converted form is always shorter than input + while (encstart != encend) { + if (*encstart > 0 && *encstart < 128) { + asciiValue.Append((char)*encstart); + } else { + return false; + } + encstart++; + } + + nsAutoString decoded; + nsAutoCString language; + + rv = mimehdrpar->DecodeRFC5987Param(asciiValue, language, decoded); + if (NS_FAILED(rv)) return false; + + aEncoded = decoded; + return true; +} + +nsresult nsContentSink::ProcessLinkHeader(const nsAString& aLinkData) { + nsresult rv = NS_OK; + + // keep track where we are within the header field + bool seenParameters = false; + + // parse link content and call process style link + nsAutoString href; + nsAutoString rel; + nsAutoString title; + nsAutoString titleStar; + nsAutoString integrity; + nsAutoString srcset; + nsAutoString sizes; + nsAutoString type; + nsAutoString media; + nsAutoString anchor; + nsAutoString crossOrigin; + nsAutoString referrerPolicy; + nsAutoString as; + + crossOrigin.SetIsVoid(true); + + // copy to work buffer + nsAutoString stringList(aLinkData); + + // put an extra null at the end + stringList.Append(kNullCh); + + char16_t* start = stringList.BeginWriting(); + char16_t* end = start; + char16_t* last = start; + char16_t endCh; + + while (*start != kNullCh) { + // skip leading space + while ((*start != kNullCh) && nsCRT::IsAsciiSpace(*start)) { + ++start; + } + + end = start; + last = end - 1; + + bool wasQuotedString = false; + + // look for semicolon or comma + while (*end != kNullCh && *end != kSemicolon && *end != kComma) { + char16_t ch = *end; + + if (ch == kQuote || ch == kLessThan) { + // quoted string + + char16_t quote = ch; + if (quote == kLessThan) { + quote = kGreaterThan; + } + + wasQuotedString = (ch == kQuote); + + char16_t* closeQuote = (end + 1); + + // seek closing quote + while (*closeQuote != kNullCh && quote != *closeQuote) { + // in quoted-string, "\" is an escape character + if (wasQuotedString && *closeQuote == kBackSlash && + *(closeQuote + 1) != kNullCh) { + ++closeQuote; + } + + ++closeQuote; + } + + if (quote == *closeQuote) { + // found closer + + // skip to close quote + end = closeQuote; + + last = end - 1; + + ch = *(end + 1); + + if (ch != kNullCh && ch != kSemicolon && ch != kComma) { + // end string here + *(++end) = kNullCh; + + ch = *(end + 1); + + // keep going until semi or comma + while (ch != kNullCh && ch != kSemicolon && ch != kComma) { + ++end; + + ch = *(end + 1); + } + } + } + } + + ++end; + ++last; + } + + endCh = *end; + + // end string here + *end = kNullCh; + + if (start < end) { + if ((*start == kLessThan) && (*last == kGreaterThan)) { + *last = kNullCh; + + // first instance of <...> wins + // also, do not allow hrefs after the first param was seen + if (href.IsEmpty() && !seenParameters) { + href = (start + 1); + href.StripWhitespace(); + } + } else { + char16_t* equals = start; + seenParameters = true; + + while ((*equals != kNullCh) && (*equals != kEqual)) { + equals++; + } + + if (*equals != kNullCh) { + *equals = kNullCh; + nsAutoString attr(start); + attr.StripWhitespace(); + + char16_t* value = ++equals; + while (nsCRT::IsAsciiSpace(*value)) { + value++; + } + + if ((*value == kQuote) && (*value == *last)) { + *last = kNullCh; + value++; + } + + if (wasQuotedString) { + // unescape in-place + char16_t* unescaped = value; + char16_t* src = value; + + while (*src != kNullCh) { + if (*src == kBackSlash && *(src + 1) != kNullCh) { + src++; + } + *unescaped++ = *src++; + } + + *unescaped = kNullCh; + } + + if (attr.LowerCaseEqualsLiteral("rel")) { + if (rel.IsEmpty()) { + rel = value; + rel.CompressWhitespace(); + } + } else if (attr.LowerCaseEqualsLiteral("title")) { + if (title.IsEmpty()) { + title = value; + title.CompressWhitespace(); + } + } else if (attr.LowerCaseEqualsLiteral("title*")) { + if (titleStar.IsEmpty() && !wasQuotedString) { + // RFC 5987 encoding; uses token format only, so skip if we get + // here with a quoted-string + nsAutoString tmp; + tmp = value; + if (Decode5987Format(tmp)) { + titleStar = tmp; + titleStar.CompressWhitespace(); + } else { + // header value did not parse, throw it away + titleStar.Truncate(); + } + } + } else if (attr.LowerCaseEqualsLiteral("type")) { + if (type.IsEmpty()) { + type = value; + type.StripWhitespace(); + } + } else if (attr.LowerCaseEqualsLiteral("media")) { + if (media.IsEmpty()) { + media = value; + + // The HTML5 spec is formulated in terms of the CSS3 spec, + // which specifies that media queries are case insensitive. + nsContentUtils::ASCIIToLower(media); + } + } else if (attr.LowerCaseEqualsLiteral("anchor")) { + if (anchor.IsEmpty()) { + anchor = value; + anchor.StripWhitespace(); + } + } else if (attr.LowerCaseEqualsLiteral("crossorigin")) { + if (crossOrigin.IsVoid()) { + crossOrigin.SetIsVoid(false); + crossOrigin = value; + crossOrigin.StripWhitespace(); + } + } else if (attr.LowerCaseEqualsLiteral("as")) { + if (as.IsEmpty()) { + as = value; + as.CompressWhitespace(); + } + } else if (attr.LowerCaseEqualsLiteral("referrerpolicy")) { + // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#referrer-policy-attribute + // Specs says referrer policy attribute is an enumerated attribute, + // case insensitive and includes the empty string + // We will parse the value with AttributeReferrerPolicyFromString + // later, which will handle parsing it as an enumerated attribute. + if (referrerPolicy.IsEmpty()) { + referrerPolicy = value; + } + } else if (attr.LowerCaseEqualsLiteral("integrity")) { + if (integrity.IsEmpty()) { + integrity = value; + } + } else if (attr.LowerCaseEqualsLiteral("imagesrcset")) { + if (srcset.IsEmpty()) { + srcset = value; + } + } else if (attr.LowerCaseEqualsLiteral("imagesizes")) { + if (sizes.IsEmpty()) { + sizes = value; + } + } + } + } + } + + if (endCh == kComma) { + // hit a comma, process what we've got so far + + href.Trim(" \t\n\r\f"); // trim HTML5 whitespace + if (!href.IsEmpty() && !rel.IsEmpty()) { + rv = ProcessLinkFromHeader( + anchor, href, rel, + // prefer RFC 5987 variant over non-I18zed version + titleStar.IsEmpty() ? title : titleStar, integrity, srcset, sizes, + type, media, crossOrigin, referrerPolicy, as); + } + + href.Truncate(); + rel.Truncate(); + title.Truncate(); + type.Truncate(); + integrity.Truncate(); + srcset.Truncate(); + sizes.Truncate(); + media.Truncate(); + anchor.Truncate(); + referrerPolicy.Truncate(); + crossOrigin.SetIsVoid(true); + as.Truncate(); + + seenParameters = false; + } + + start = ++end; + } + + href.Trim(" \t\n\r\f"); // trim HTML5 whitespace + if (!href.IsEmpty() && !rel.IsEmpty()) { + rv = ProcessLinkFromHeader( + anchor, href, rel, + // prefer RFC 5987 variant over non-I18zed version + titleStar.IsEmpty() ? title : titleStar, integrity, srcset, sizes, type, + media, crossOrigin, referrerPolicy, as); + } + + return rv; +} + +nsresult nsContentSink::ProcessLinkFromHeader( + const nsAString& aAnchor, const nsAString& aHref, const nsAString& aRel, + const nsAString& aTitle, const nsAString& aIntegrity, + const nsAString& aSrcset, const nsAString& aSizes, const nsAString& aType, + const nsAString& aMedia, const nsAString& aCrossOrigin, + const nsAString& aReferrerPolicy, const nsAString& aAs) { + uint32_t linkTypes = LinkStyle::ParseLinkTypes(aRel); + + // The link relation may apply to a different resource, specified + // in the anchor parameter. For the link relations supported so far, + // we simply abort if the link applies to a resource different to the + // one we've loaded + if (!LinkContextIsOurDocument(aAnchor)) { + return NS_OK; + } + + if (nsContentUtils::PrefetchPreloadEnabled(mDocShell)) { + // prefetch href if relation is "next" or "prefetch" + if ((linkTypes & LinkStyle::eNEXT) || (linkTypes & LinkStyle::ePREFETCH)) { + PrefetchHref(aHref, aAs, aType, aMedia); + } + + if (!aHref.IsEmpty() && (linkTypes & LinkStyle::eDNS_PREFETCH)) { + PrefetchDNS(aHref); + } + + if (!aHref.IsEmpty() && (linkTypes & LinkStyle::ePRECONNECT)) { + Preconnect(aHref, aCrossOrigin); + } + + if (linkTypes & LinkStyle::ePRELOAD) { + PreloadHref(aHref, aAs, aType, aMedia, aIntegrity, aSrcset, aSizes, + aCrossOrigin, aReferrerPolicy); + } + } + + // is it a stylesheet link? + if (!(linkTypes & LinkStyle::eSTYLESHEET)) { + return NS_OK; + } + + bool isAlternate = linkTypes & LinkStyle::eALTERNATE; + return ProcessStyleLinkFromHeader(aHref, isAlternate, aTitle, aIntegrity, + aType, aMedia, aReferrerPolicy); +} + +nsresult nsContentSink::ProcessStyleLinkFromHeader( + const nsAString& aHref, bool aAlternate, const nsAString& aTitle, + const nsAString& aIntegrity, const nsAString& aType, + const nsAString& aMedia, const nsAString& aReferrerPolicy) { + if (aAlternate && aTitle.IsEmpty()) { + // alternates must have title return without error, for now + return NS_OK; + } + + nsAutoString mimeType; + nsAutoString params; + nsContentUtils::SplitMimeType(aType, mimeType, params); + + // see bug 18817 + if (!mimeType.IsEmpty() && !mimeType.LowerCaseEqualsLiteral("text/css")) { + // Unknown stylesheet language + return NS_OK; + } + + nsCOMPtr<nsIURI> url; + nsresult rv = NS_NewURI(getter_AddRefs(url), aHref, nullptr, + mDocument->GetDocBaseURI()); + + if (NS_FAILED(rv)) { + // The URI is bad, move along, don't propagate the error (for now) + return NS_OK; + } + + // Link header is working like a <link> node, so referrerPolicy attr should + // have higher priority than referrer policy from document. + ReferrerPolicy policy = + ReferrerInfo::ReferrerPolicyAttributeFromString(aReferrerPolicy); + nsCOMPtr<nsIReferrerInfo> referrerInfo = + ReferrerInfo::CreateFromDocumentAndPolicyOverride(mDocument, policy); + + Loader::SheetInfo info{ + *mDocument, + nullptr, + url.forget(), + nullptr, + referrerInfo.forget(), + CORS_NONE, + aTitle, + aMedia, + aIntegrity, + /* nonce = */ u""_ns, + aAlternate ? Loader::HasAlternateRel::Yes : Loader::HasAlternateRel::No, + Loader::IsInline::No, + Loader::IsExplicitlyEnabled::No, + }; + + auto loadResultOrErr = + mCSSLoader->LoadStyleLink(info, mRunsToCompletion ? nullptr : this); + if (loadResultOrErr.isErr()) { + return loadResultOrErr.unwrapErr(); + } + + if (loadResultOrErr.inspect().ShouldBlock() && !mRunsToCompletion) { + ++mPendingSheetCount; + mScriptLoader->AddParserBlockingScriptExecutionBlocker(); + } + + return NS_OK; +} + +void nsContentSink::PrefetchHref(const nsAString& aHref, const nsAString& aAs, + const nsAString& aType, + const nsAString& aMedia) { + nsCOMPtr<nsIPrefetchService> prefetchService(components::Prefetch::Service()); + if (prefetchService) { + // construct URI using document charset + auto encoding = mDocument->GetDocumentCharacterSet(); + nsCOMPtr<nsIURI> uri; + NS_NewURI(getter_AddRefs(uri), aHref, encoding, mDocument->GetDocBaseURI()); + if (uri) { + auto referrerInfo = MakeRefPtr<ReferrerInfo>(*mDocument); + referrerInfo = referrerInfo->CloneWithNewOriginalReferrer(mDocumentURI); + + prefetchService->PrefetchURI(uri, referrerInfo, mDocument, true); + } + } +} + +void nsContentSink::PreloadHref(const nsAString& aHref, const nsAString& aAs, + const nsAString& aType, const nsAString& aMedia, + const nsAString& aIntegrity, + const nsAString& aSrcset, + const nsAString& aSizes, const nsAString& aCORS, + const nsAString& aReferrerPolicy) { + auto encoding = mDocument->GetDocumentCharacterSet(); + nsCOMPtr<nsIURI> uri; + NS_NewURI(getter_AddRefs(uri), aHref, encoding, mDocument->GetDocBaseURI()); + if (!uri) { + // URL parsing failed. + return; + } + + nsAttrValue asAttr; + HTMLLinkElement::ParseAsValue(aAs, asAttr); + + nsAutoString mimeType; + nsAutoString notUsed; + nsContentUtils::SplitMimeType(aType, mimeType, notUsed); + + auto policyType = HTMLLinkElement::AsValueToContentPolicy(asAttr); + if (policyType == nsIContentPolicy::TYPE_INVALID || + !HTMLLinkElement::CheckPreloadAttrs(asAttr, mimeType, aMedia, + mDocument)) { + // Ignore preload wrong or empty attributes. + HTMLLinkElement::WarnIgnoredPreload(*mDocument, *uri); + return; + } + + mDocument->Preloads().PreloadLinkHeader(uri, aHref, policyType, aAs, aType, + aIntegrity, aSrcset, aSizes, aCORS, + aReferrerPolicy); +} + +void nsContentSink::PrefetchDNS(const nsAString& aHref) { + nsAutoString hostname; + bool isHttps = false; + + if (StringBeginsWith(aHref, u"//"_ns)) { + hostname = Substring(aHref, 2); + } else { + nsCOMPtr<nsIURI> uri; + NS_NewURI(getter_AddRefs(uri), aHref); + if (!uri) { + return; + } + nsresult rv; + bool isLocalResource = false; + rv = NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE, + &isLocalResource); + if (NS_SUCCEEDED(rv) && !isLocalResource) { + nsAutoCString host; + uri->GetHost(host); + CopyUTF8toUTF16(host, hostname); + } + isHttps = uri->SchemeIs("https"); + } + + if (!hostname.IsEmpty() && nsHTMLDNSPrefetch::IsAllowed(mDocument)) { + OriginAttributes oa; + StoragePrincipalHelper::GetOriginAttributesForNetworkState(mDocument, oa); + + nsHTMLDNSPrefetch::Prefetch(hostname, isHttps, oa, + mDocument->GetChannel()->GetTRRMode(), + nsHTMLDNSPrefetch::Priority::Low); + } +} + +void nsContentSink::Preconnect(const nsAString& aHref, + const nsAString& aCrossOrigin) { + // construct URI using document charset + auto encoding = mDocument->GetDocumentCharacterSet(); + nsCOMPtr<nsIURI> uri; + NS_NewURI(getter_AddRefs(uri), aHref, encoding, mDocument->GetDocBaseURI()); + + if (uri && mDocument) { + mDocument->MaybePreconnect(uri, + dom::Element::StringToCORSMode(aCrossOrigin)); + } +} + +nsresult nsContentSink::SelectDocAppCache( + nsIApplicationCache* aLoadApplicationCache, nsIURI* aManifestURI, + bool aFetchedWithHTTPGetOrEquiv, CacheSelectionAction* aAction) { + nsresult rv; + + *aAction = CACHE_SELECTION_NONE; + + if (aLoadApplicationCache) { + nsCOMPtr<nsIURI> groupURI; + rv = aLoadApplicationCache->GetManifestURI(getter_AddRefs(groupURI)); + NS_ENSURE_SUCCESS(rv, rv); + + bool equal = false; + rv = groupURI->Equals(aManifestURI, &equal); + NS_ENSURE_SUCCESS(rv, rv); + + if (!equal) { + // This is a foreign entry, force a reload to avoid loading the foreign + // entry. The entry will be marked as foreign to avoid loading it again. + + *aAction = CACHE_SELECTION_RELOAD; + } else { + // The http manifest attribute URI is equal to the manifest URI of + // the cache the document was loaded from - associate the document with + // that cache and invoke the cache update process. +#ifdef DEBUG + nsAutoCString docURISpec, clientID; + mDocumentURI->GetAsciiSpec(docURISpec); + aLoadApplicationCache->GetClientID(clientID); + SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo), + SINK_TRACE_CALLS, + ("Selection: assigning app cache %s to document %s", + clientID.get(), docURISpec.get())); +#endif + + rv = mDocument->SetApplicationCache(aLoadApplicationCache); + NS_ENSURE_SUCCESS(rv, rv); + + // Document will be added as implicit entry to the cache as part of + // the update process. + *aAction = CACHE_SELECTION_UPDATE; + } + } else { + // The document was not loaded from an application cache + // Here we know the manifest has the same origin as the + // document. There is call to CheckMayLoadWithReporting() on it above. + + if (!aFetchedWithHTTPGetOrEquiv) { + // The document was not loaded using HTTP GET or equivalent + // method. The spec says to run the cache selection algorithm w/o + // the manifest specified. + *aAction = CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST; + } else { + // Always do an update in this case + *aAction = CACHE_SELECTION_UPDATE; + } + } + + return NS_OK; +} + +nsresult nsContentSink::SelectDocAppCacheNoManifest( + nsIApplicationCache* aLoadApplicationCache, nsIURI** aManifestURI, + CacheSelectionAction* aAction) { + *aManifestURI = nullptr; + *aAction = CACHE_SELECTION_NONE; + + nsresult rv; + + if (aLoadApplicationCache) { + // The document was loaded from an application cache, use that + // application cache as the document's application cache. +#ifdef DEBUG + nsAutoCString docURISpec, clientID; + mDocumentURI->GetAsciiSpec(docURISpec); + aLoadApplicationCache->GetClientID(clientID); + SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo), + SINK_TRACE_CALLS, + ("Selection, no manifest: assigning app cache %s to document %s", + clientID.get(), docURISpec.get())); +#endif + + rv = mDocument->SetApplicationCache(aLoadApplicationCache); + NS_ENSURE_SUCCESS(rv, rv); + + // Return the uri and invoke the update process for the selected + // application cache. + rv = aLoadApplicationCache->GetManifestURI(aManifestURI); + NS_ENSURE_SUCCESS(rv, rv); + + *aAction = CACHE_SELECTION_UPDATE; + } + + return NS_OK; +} + +void nsContentSink::ProcessOfflineManifest(nsIContent* aElement) { + // Only check the manifest for root document nodes. + if (aElement != mDocument->GetRootElement()) { + return; + } + + // Don't bother processing offline manifest for documents + // without a docshell + if (!mDocShell) { + return; + } + + // Check for a manifest= attribute. + nsAutoString manifestSpec; + aElement->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::manifest, + manifestSpec); + ProcessOfflineManifest(manifestSpec); +} + +void nsContentSink::ProcessOfflineManifest(const nsAString& aManifestSpec) { + // Don't bother processing offline manifest for documents + // without a docshell + if (!mDocShell) { + return; + } + + // If offline storage is disabled skip processing + if (!StaticPrefs::browser_cache_offline_storage_enable()) { + return; + } + + // If this document has been interecepted, let's skip the processing of the + // manifest. + if (mDocument->GetController().isSome()) { + return; + } + + // If the docshell's in private browsing mode, we don't want to do any + // manifest processing. + nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(mDocShell); + if (loadContext->UsePrivateBrowsing()) { + return; + } + + nsresult rv; + + // Grab the application cache the document was loaded from, if any. + nsCOMPtr<nsIApplicationCache> applicationCache; + + nsCOMPtr<nsIApplicationCacheChannel> applicationCacheChannel = + do_QueryInterface(mDocument->GetChannel()); + if (applicationCacheChannel) { + bool loadedFromApplicationCache; + rv = applicationCacheChannel->GetLoadedFromApplicationCache( + &loadedFromApplicationCache); + if (NS_FAILED(rv)) { + return; + } + + if (loadedFromApplicationCache) { + rv = applicationCacheChannel->GetApplicationCache( + getter_AddRefs(applicationCache)); + if (NS_FAILED(rv)) { + return; + } + } + } + + if (aManifestSpec.IsEmpty() && !applicationCache) { + // Not loaded from an application cache, and no manifest + // attribute. Nothing to do here. + return; + } + + CacheSelectionAction action = CACHE_SELECTION_NONE; + nsCOMPtr<nsIURI> manifestURI; + + if (aManifestSpec.IsEmpty()) { + action = CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST; + } else { + nsContentUtils::NewURIWithDocumentCharset( + getter_AddRefs(manifestURI), aManifestSpec, mDocument, mDocumentURI); + if (!manifestURI) { + return; + } + + // Documents must list a manifest from the same origin + rv = mDocument->NodePrincipal()->CheckMayLoadWithReporting( + manifestURI, false, mDocument->InnerWindowID()); + if (NS_FAILED(rv)) { + action = CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST; + } else { + if (!nsContentUtils::OfflineAppAllowed(mDocument->NodePrincipal())) { + nsCOMPtr<nsIOfflineCacheUpdateService> updateService = + components::OfflineCacheUpdate::Service(); + if (!updateService) { + return; + } + rv = updateService->AllowOfflineApp(mDocument->NodePrincipal()); + if (NS_FAILED(rv)) { + return; + } + } + + bool fetchedWithHTTPGetOrEquiv = false; + nsCOMPtr<nsIHttpChannel> httpChannel( + do_QueryInterface(mDocument->GetChannel())); + if (httpChannel) { + nsAutoCString method; + rv = httpChannel->GetRequestMethod(method); + if (NS_SUCCEEDED(rv)) + fetchedWithHTTPGetOrEquiv = method.EqualsLiteral("GET"); + } + + rv = SelectDocAppCache(applicationCache, manifestURI, + fetchedWithHTTPGetOrEquiv, &action); + if (NS_FAILED(rv)) { + return; + } + } + } + + if (action == CACHE_SELECTION_RESELECT_WITHOUT_MANIFEST) { + rv = SelectDocAppCacheNoManifest(applicationCache, + getter_AddRefs(manifestURI), &action); + if (NS_FAILED(rv)) { + return; + } + } + + switch (action) { + case CACHE_SELECTION_NONE: + break; + case CACHE_SELECTION_UPDATE: { + nsCOMPtr<nsIOfflineCacheUpdateService> updateService = + components::OfflineCacheUpdate::Service(); + + if (updateService) { + updateService->ScheduleOnDocumentStop( + manifestURI, mDocumentURI, mDocument->NodePrincipal(), mDocument); + } + break; + } + case CACHE_SELECTION_RELOAD: { + // This situation occurs only for toplevel documents, see bottom + // of SelectDocAppCache method. + // The document has been loaded from a different offline cache group than + // the manifest it refers to, i.e. this is a foreign entry, mark it as + // such and force a reload to avoid loading it. The next attempt will not + // choose it. + + applicationCacheChannel->MarkOfflineCacheEntryAsForeign(); + + nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(mDocShell); + + webNav->Stop(nsIWebNavigation::STOP_ALL); + webNav->Reload(nsIWebNavigation::LOAD_FLAGS_NONE); + break; + } + default: + NS_ASSERTION(false, + "Cache selection algorithm didn't decide on proper action"); + break; + } +} + +void nsContentSink::ScrollToRef() { + RefPtr<Document> document = mDocument; + document->ScrollToRef(); +} + +void nsContentSink::StartLayout(bool aIgnorePendingSheets) { + AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("nsContentSink::StartLayout", LAYOUT, + mDocumentURI->GetSpecOrDefault()); + + if (mLayoutStarted) { + // Nothing to do here + return; + } + + mDeferredLayoutStart = true; + + if (!aIgnorePendingSheets && + (WaitForPendingSheets() || mDocument->HasPendingInitialTranslation())) { + // Bail out; we'll start layout when the sheets and l10n load + return; + } + + mDeferredLayoutStart = false; + + if (aIgnorePendingSheets) { + nsContentUtils::ReportToConsole( + nsIScriptError::warningFlag, "Layout"_ns, mDocument, + nsContentUtils::eLAYOUT_PROPERTIES, "ForcedLayoutStart"); + } + + // Notify on all our content. If none of our presshells have started layout + // yet it'll be a no-op except for updating our data structures, a la + // UpdateChildCounts() (because we don't want to double-notify on whatever we + // have right now). If some of them _have_ started layout, we want to make + // sure to flush tags instead of just calling UpdateChildCounts() after we + // loop over the shells. + FlushTags(); + + mLayoutStarted = true; + mLastNotificationTime = PR_Now(); + + mDocument->SetMayStartLayout(true); + RefPtr<PresShell> presShell = mDocument->GetPresShell(); + // Make sure we don't call Initialize() for a shell that has + // already called it. This can happen when the layout frame for + // an iframe is constructed *between* the Embed() call for the + // docshell in the iframe, and the content sink's call to OpenBody(). + // (Bug 153815) + if (presShell && !presShell->DidInitialize()) { + nsresult rv = presShell->Initialize(); + if (NS_FAILED(rv)) { + return; + } + } + + // If the document we are loading has a reference or it is a + // frameset document, disable the scroll bars on the views. + + mDocument->SetScrollToRef(mDocument->GetDocumentURI()); +} + +void nsContentSink::NotifyAppend(nsIContent* aContainer, uint32_t aStartIndex) { + mInNotification++; + + { + // Scope so we call EndUpdate before we decrease mInNotification + // + // Note that aContainer->OwnerDoc() may not be mDocument. + MOZ_AUTO_DOC_UPDATE(aContainer->OwnerDoc(), true); + MutationObservers::NotifyContentAppended( + aContainer, aContainer->GetChildAt_Deprecated(aStartIndex)); + mLastNotificationTime = PR_Now(); + } + + mInNotification--; +} + +NS_IMETHODIMP +nsContentSink::Notify(nsITimer* timer) { + if (mParsing) { + // We shouldn't interfere with our normal DidProcessAToken logic + mDroppedTimer = true; + return NS_OK; + } + + if (WaitForPendingSheets()) { + mDeferredFlushTags = true; + } else { + FlushTags(); + + // Now try and scroll to the reference + // XXX Should we scroll unconditionally for history loads?? + ScrollToRef(); + } + + mNotificationTimer = nullptr; + return NS_OK; +} + +bool nsContentSink::IsTimeToNotify() { + if (!StaticPrefs::content_notify_ontimer() || !mLayoutStarted || + !mBackoffCount || mInMonolithicContainer) { + return false; + } + + if (WaitForPendingSheets()) { + mDeferredFlushTags = true; + return false; + } + + PRTime now = PR_Now(); + + int64_t interval = GetNotificationInterval(); + int64_t diff = now - mLastNotificationTime; + + if (diff > interval) { + mBackoffCount--; + return true; + } + + return false; +} + +nsresult nsContentSink::WillInterruptImpl() { + nsresult result = NS_OK; + + SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo), + SINK_TRACE_CALLS, ("nsContentSink::WillInterrupt: this=%p", this)); +#ifndef SINK_NO_INCREMENTAL + if (WaitForPendingSheets()) { + mDeferredFlushTags = true; + } else if (StaticPrefs::content_notify_ontimer() && mLayoutStarted) { + if (mBackoffCount && !mInMonolithicContainer) { + int64_t now = PR_Now(); + int64_t interval = GetNotificationInterval(); + int64_t diff = now - mLastNotificationTime; + + // If it's already time for us to have a notification + if (diff > interval || mDroppedTimer) { + mBackoffCount--; + SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo), + SINK_TRACE_REFLOW, + ("nsContentSink::WillInterrupt: flushing tags since we've " + "run out time; backoff count: %d", + mBackoffCount)); + result = FlushTags(); + if (mDroppedTimer) { + ScrollToRef(); + mDroppedTimer = false; + } + } else if (!mNotificationTimer) { + interval -= diff; + int32_t delay = interval; + + // Convert to milliseconds + delay /= PR_USEC_PER_MSEC; + + NS_NewTimerWithCallback(getter_AddRefs(mNotificationTimer), this, delay, + nsITimer::TYPE_ONE_SHOT); + if (mNotificationTimer) { + SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo), + SINK_TRACE_REFLOW, + ("nsContentSink::WillInterrupt: setting up timer with " + "delay %d", + delay)); + } + } + } + } else { + SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo), + SINK_TRACE_REFLOW, + ("nsContentSink::WillInterrupt: flushing tags " + "unconditionally")); + result = FlushTags(); + } +#endif + + mParsing = false; + + return result; +} + +nsresult nsContentSink::WillResumeImpl() { + SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo), + SINK_TRACE_CALLS, ("nsContentSink::WillResume: this=%p", this)); + + mParsing = true; + + return NS_OK; +} + +nsresult nsContentSink::DidProcessATokenImpl() { + if (mRunsToCompletion || !mParser) { + return NS_OK; + } + + // Get the current user event time + PresShell* presShell = mDocument->GetPresShell(); + if (!presShell) { + // If there's no pres shell in the document, return early since + // we're not laying anything out here. + return NS_OK; + } + + // Increase before comparing to gEventProbeRate + ++mDeflectedCount; + + // Check if there's a pending event + if (StaticPrefs::content_sink_pending_event_mode() != 0 && + !mHasPendingEvent && + (mDeflectedCount % StaticPrefs::content_sink_event_probe_rate()) == 0) { + nsViewManager* vm = presShell->GetViewManager(); + NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE); + nsCOMPtr<nsIWidget> widget; + vm->GetRootWidget(getter_AddRefs(widget)); + mHasPendingEvent = widget && widget->HasPendingInputEvent(); + } + + if (mHasPendingEvent && StaticPrefs::content_sink_pending_event_mode() == 2) { + return NS_ERROR_HTMLPARSER_INTERRUPTED; + } + + // Have we processed enough tokens to check time? + if (!mHasPendingEvent && + mDeflectedCount < + uint32_t(mDynamicLowerValue + ? StaticPrefs::content_sink_interactive_deflect_count() + : StaticPrefs::content_sink_perf_deflect_count())) { + return NS_OK; + } + + mDeflectedCount = 0; + + // Check if it's time to return to the main event loop + if (PR_IntervalToMicroseconds(PR_IntervalNow()) > mCurrentParseEndTime) { + return NS_ERROR_HTMLPARSER_INTERRUPTED; + } + + return NS_OK; +} + +//---------------------------------------------------------------------- + +void nsContentSink::FavorPerformanceHint(bool perfOverStarvation, + uint32_t starvationDelay) { + static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); + nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID); + if (appShell) + appShell->FavorPerformanceHint(perfOverStarvation, starvationDelay); +} + +void nsContentSink::BeginUpdate(Document* aDocument) { + // Remember nested updates from updates that we started. + if (mInNotification > 0 && mUpdatesInNotification < 2) { + ++mUpdatesInNotification; + } + + // If we're in a script and we didn't do the notification, + // something else in the script processing caused the + // notification to occur. Since this could result in frame + // creation, make sure we've flushed everything before we + // continue. + + if (!mInNotification++) { + FlushTags(); + } +} + +void nsContentSink::EndUpdate(Document* aDocument) { + // If we're in a script and we didn't do the notification, + // something else in the script processing caused the + // notification to occur. Update our notion of how much + // has been flushed to include any new content if ending + // this update leaves us not inside a notification. + if (!--mInNotification) { + UpdateChildCounts(); + } +} + +void nsContentSink::DidBuildModelImpl(bool aTerminated) { + MOZ_ASSERT(aTerminated || + mDocument->GetReadyStateEnum() == Document::READYSTATE_LOADING, + "Bad readyState"); + mDocument->SetReadyStateInternal(Document::READYSTATE_INTERACTIVE); + + if (mScriptLoader) { + mScriptLoader->ParsingComplete(aTerminated); + if (!mPendingSheetCount) { + mScriptLoader->DeferCheckpointReached(); + } + } + + if (!mDocument->HaveFiredDOMTitleChange()) { + mDocument->NotifyPossibleTitleChange(false); + } + + // Cancel a timer if we had one out there + if (mNotificationTimer) { + SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo), + SINK_TRACE_REFLOW, + ("nsContentSink::DidBuildModel: canceling notification " + "timeout")); + mNotificationTimer->Cancel(); + mNotificationTimer = nullptr; + } +} + +void nsContentSink::DropParserAndPerfHint(void) { + if (!mParser) { + // Make sure we don't unblock unload too many times + return; + } + + // Ref. Bug 49115 + // Do this hack to make sure that the parser + // doesn't get destroyed, accidently, before + // the circularity, between sink & parser, is + // actually broken. + // Drop our reference to the parser to get rid of a circular + // reference. + RefPtr<nsParserBase> kungFuDeathGrip = std::move(mParser); + mozilla::Unused << kungFuDeathGrip; + + if (mDynamicLowerValue) { + // Reset the performance hint which was set to FALSE + // when mDynamicLowerValue was set. + FavorPerformanceHint(true, 0); + } + + // Call UnblockOnload only if mRunsToComletion is false and if + // we have already started loading because it's possible that this function + // is called (i.e. the parser is terminated) before we start loading due to + // destroying the window inside unload event callbacks for the previous + // document. + if (!mRunsToCompletion && mIsBlockingOnload) { + mDocument->UnblockOnload(true); + mIsBlockingOnload = false; + } +} + +bool nsContentSink::IsScriptExecutingImpl() { + return !!mScriptLoader->GetCurrentScript(); +} + +nsresult nsContentSink::WillParseImpl(void) { + if (mRunsToCompletion || !mDocument) { + return NS_OK; + } + + PresShell* presShell = mDocument->GetPresShell(); + if (!presShell) { + return NS_OK; + } + + uint32_t currentTime = PR_IntervalToMicroseconds(PR_IntervalNow()); + + if (StaticPrefs::content_sink_enable_perf_mode() == 0) { + nsViewManager* vm = presShell->GetViewManager(); + NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE); + uint32_t lastEventTime; + vm->GetLastUserEventTime(lastEventTime); + + bool newDynLower = mDocument->IsInBackgroundWindow() || + ((currentTime - mBeginLoadTime) > + StaticPrefs::content_sink_initial_perf_time() && + (currentTime - lastEventTime) < + StaticPrefs::content_sink_interactive_time()); + + if (mDynamicLowerValue != newDynLower) { + FavorPerformanceHint(!newDynLower, 0); + mDynamicLowerValue = newDynLower; + } + } + + mDeflectedCount = 0; + mHasPendingEvent = false; + + mCurrentParseEndTime = + currentTime + (mDynamicLowerValue + ? StaticPrefs::content_sink_interactive_parse_time() + : StaticPrefs::content_sink_perf_parse_time()); + + return NS_OK; +} + +void nsContentSink::WillBuildModelImpl() { + if (!mRunsToCompletion) { + mDocument->BlockOnload(); + mIsBlockingOnload = true; + + mBeginLoadTime = PR_IntervalToMicroseconds(PR_IntervalNow()); + } + + mDocument->ResetScrolledToRefAlready(); + + if (mProcessLinkHeaderEvent.get()) { + mProcessLinkHeaderEvent.Revoke(); + + DoProcessLinkHeader(); + } +} + +/* static */ +void nsContentSink::NotifyDocElementCreated(Document* aDoc) { + MOZ_ASSERT(nsContentUtils::IsSafeToRunScript()); + + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + MOZ_ASSERT(observerService); + + auto* win = nsGlobalWindowInner::Cast(aDoc->GetInnerWindow()); + bool fireInitialInsertion = !win || !win->DidFireDocElemInserted(); + if (win) { + win->SetDidFireDocElemInserted(); + } + if (fireInitialInsertion) { + observerService->NotifyObservers(ToSupports(aDoc), + "initial-document-element-inserted", u""); + } + observerService->NotifyObservers(ToSupports(aDoc), + "document-element-inserted", u""); + + nsContentUtils::DispatchChromeEvent(aDoc, ToSupports(aDoc), + u"DOMDocElementInserted"_ns, + CanBubble::eYes, Cancelable::eNo); +} + +NS_IMETHODIMP +nsContentSink::GetName(nsACString& aName) { + aName.AssignLiteral("nsContentSink_timer"); + return NS_OK; +} |