/* -*- 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.
*/
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);
}
}
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;
constbool 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();
}
// Getting the header data and parsing the link header together roughly // implement <https://httpwg.org/specs/rfc8288.html#parse-set>.
mDocument->GetHeaderData(nsGkAtoms::link, value); auto linkHeaders = net::ParseLinkHeader(value);
for (constauto& linkHeader : linkHeaders) {
ProcessLinkFromHeader(linkHeader, 0);
}
}
// 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 (!nsContentUtils::LinkContextIsURI(aHeader.mAnchor,
mDocument->GetDocumentURI())) { return NS_OK;
}
if (nsContentUtils::PrefetchPreloadEnabled(mDocShell)) { // prefetch href if relation is "next" or "prefetch" if ((linkTypes & LinkStyle::eNEXT) || (linkTypes & LinkStyle::ePREFETCH)) {
PrefetchHref(aHeader.mHref, aHeader.mAs, aHeader.mType, aHeader.mMedia);
}
if (!aHeader.mHref.IsEmpty() && (linkTypes & LinkStyle::eDNS_PREFETCH)) {
PrefetchDNS(aHeader.mHref);
}
if (!aHeader.mHref.IsEmpty() && (linkTypes & LinkStyle::ePRECONNECT)) {
Preconnect(aHeader.mHref, aHeader.mCrossOrigin);
}
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);
if (!StaticPrefs::network_modulepreload()) { // Keep behavior from https://phabricator.services.mozilla.com/D149371, // prior to main implementation of modulepreload
moduleLoader->DisallowImportMaps(); return;
}
RefPtr<mozilla::dom::MediaList> mediaList =
mozilla::dom::MediaList::Create(NS_ConvertUTF16toUTF8(aMedia)); if (!mediaList->Matches(*mDocument)) { return;
}
if (aHref.IsEmpty()) { return;
}
if (!net::IsScriptLikeOrInvalid(aAs)) { return;
}
auto encoding = mDocument->GetDocumentCharacterSet();
nsCOMPtr<nsIURI> uri;
NS_NewURI(getter_AddRefs(uri), aHref, encoding, mDocument->GetDocBaseURI()); if (!uri) { return;
}
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();
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.
{ // 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();
}
if (WaitForPendingSheets()) {
mDeferredFlushTags = true; returnfalse;
}
PRTime now = PR_Now();
int64_t interval = GetNotificationInterval();
int64_t diff = now - mLastNotificationTime;
if (diff > interval) {
mBackoffCount--; returntrue;
}
returnfalse;
}
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;
} elseif (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;
}
} elseif (!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
// 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();
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;
}
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();
}
}
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;
// 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;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.