From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- dom/prototype/PrototypeDocumentContentSink.cpp | 1123 ++++++++++++++++++++++++ 1 file changed, 1123 insertions(+) create mode 100644 dom/prototype/PrototypeDocumentContentSink.cpp (limited to 'dom/prototype/PrototypeDocumentContentSink.cpp') diff --git a/dom/prototype/PrototypeDocumentContentSink.cpp b/dom/prototype/PrototypeDocumentContentSink.cpp new file mode 100644 index 0000000000..c19bc3c353 --- /dev/null +++ b/dom/prototype/PrototypeDocumentContentSink.cpp @@ -0,0 +1,1123 @@ +/* -*- 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 https://mozilla.org/MPL/2.0/. */ + +#include "nsCOMPtr.h" +#include "mozilla/dom/PrototypeDocumentContentSink.h" +#include "nsIParser.h" +#include "mozilla/dom/Document.h" +#include "mozilla/dom/URL.h" +#include "nsIContent.h" +#include "nsIURI.h" +#include "nsNetUtil.h" +#include "nsHTMLParts.h" +#include "nsCRT.h" +#include "mozilla/StyleSheetInlines.h" +#include "mozilla/css/Loader.h" +#include "nsGkAtoms.h" +#include "nsContentUtils.h" +#include "nsDocElementCreatedNotificationRunner.h" +#include "nsIScriptContext.h" +#include "nsNameSpaceManager.h" +#include "nsIScriptError.h" +#include "prtime.h" +#include "mozilla/Logging.h" +#include "nsRect.h" +#include "nsIScriptElement.h" +#include "nsReadableUtils.h" +#include "nsUnicharUtils.h" +#include "nsIChannel.h" +#include "nsNodeInfoManager.h" +#include "nsContentCreatorFunctions.h" +#include "nsIContentPolicy.h" +#include "nsContentPolicyUtils.h" +#include "nsError.h" +#include "nsIScriptGlobalObject.h" +#include "mozAutoDocUpdate.h" +#include "nsMimeTypes.h" +#include "nsHtml5SVGLoadDispatcher.h" +#include "nsTextNode.h" +#include "mozilla/dom/AutoEntryScript.h" +#include "mozilla/dom/CDATASection.h" +#include "mozilla/dom/Comment.h" +#include "mozilla/dom/DocumentType.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/HTMLTemplateElement.h" +#include "mozilla/dom/ProcessingInstruction.h" +#include "mozilla/dom/XMLStylesheetProcessingInstruction.h" +#include "mozilla/dom/ScriptLoader.h" +#include "mozilla/LoadInfo.h" +#include "mozilla/PresShell.h" +#include "mozilla/ProfilerLabels.h" +#include "mozilla/RefPtr.h" +#include "mozilla/Try.h" + +#include "nsXULPrototypeCache.h" +#include "nsXULElement.h" +#include "mozilla/CycleCollectedJSContext.h" +#include "js/CompilationAndEvaluation.h" +#include "js/experimental/JSStencil.h" +#include "js/Utility.h" // JS::FreePolicy + +using namespace mozilla; +using namespace mozilla::dom; + +LazyLogModule PrototypeDocumentContentSink::gLog("PrototypeDocument"); + +nsresult NS_NewPrototypeDocumentContentSink(nsIContentSink** aResult, + Document* aDoc, nsIURI* aURI, + nsISupports* aContainer, + nsIChannel* aChannel) { + MOZ_ASSERT(nullptr != aResult, "null ptr"); + if (nullptr == aResult) { + return NS_ERROR_NULL_POINTER; + } + RefPtr it = new PrototypeDocumentContentSink(); + + nsresult rv = it->Init(aDoc, aURI, aContainer, aChannel); + NS_ENSURE_SUCCESS(rv, rv); + + it.forget(aResult); + return NS_OK; +} + +namespace mozilla::dom { + +PrototypeDocumentContentSink::PrototypeDocumentContentSink() + : mNextSrcLoadWaiter(nullptr), + mCurrentScriptProto(nullptr), + mOffThreadCompiling(false), + mStillWalking(false), + mPendingSheets(0) {} + +PrototypeDocumentContentSink::~PrototypeDocumentContentSink() { + NS_ASSERTION( + mNextSrcLoadWaiter == nullptr, + "unreferenced document still waiting for script source to load?"); +} + +nsresult PrototypeDocumentContentSink::Init(Document* aDoc, nsIURI* aURI, + nsISupports* aContainer, + nsIChannel* aChannel) { + MOZ_ASSERT(aDoc, "null ptr"); + MOZ_ASSERT(aURI, "null ptr"); + + mDocument = aDoc; + + mDocument->SetDelayFrameLoaderInitialization(true); + mDocument->SetMayStartLayout(false); + + // Get the URI. this should match the uri used for the OnNewURI call in + // nsDocShell::CreateDocumentViewer. + nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(mDocumentURI)); + NS_ENSURE_SUCCESS(rv, rv); + + mScriptLoader = mDocument->ScriptLoader(); + + return NS_OK; +} + +NS_IMPL_CYCLE_COLLECTION(PrototypeDocumentContentSink, mParser, mDocumentURI, + mDocument, mScriptLoader, mContextStack, + mCurrentPrototype) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PrototypeDocumentContentSink) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentSink) + NS_INTERFACE_MAP_ENTRY(nsIContentSink) + NS_INTERFACE_MAP_ENTRY(nsIStreamLoaderObserver) + NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver) + NS_INTERFACE_MAP_ENTRY(nsIOffThreadScriptReceiver) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(PrototypeDocumentContentSink) +NS_IMPL_CYCLE_COLLECTING_RELEASE(PrototypeDocumentContentSink) + +//---------------------------------------------------------------------- +// +// nsIContentSink interface +// + +void PrototypeDocumentContentSink::SetDocumentCharset( + NotNull aEncoding) { + if (mDocument) { + mDocument->SetDocumentCharacterSet(aEncoding); + } +} + +nsISupports* PrototypeDocumentContentSink::GetTarget() { + return ToSupports(mDocument); +} + +bool PrototypeDocumentContentSink::IsScriptExecuting() { + return !!mScriptLoader->GetCurrentScript(); +} + +NS_IMETHODIMP +PrototypeDocumentContentSink::SetParser(nsParserBase* aParser) { + MOZ_ASSERT(aParser, "Should have a parser here!"); + mParser = aParser; + return NS_OK; +} + +nsIParser* PrototypeDocumentContentSink::GetParser() { + return static_cast(mParser.get()); +} + +void PrototypeDocumentContentSink::ContinueInterruptedParsingIfEnabled() { + if (mParser && mParser->IsParserEnabled()) { + GetParser()->ContinueInterruptedParsing(); + } +} + +void PrototypeDocumentContentSink::ContinueInterruptedParsingAsync() { + nsCOMPtr ev = NewRunnableMethod( + "PrototypeDocumentContentSink::ContinueInterruptedParsingIfEnabled", this, + &PrototypeDocumentContentSink::ContinueInterruptedParsingIfEnabled); + mDocument->Dispatch(ev.forget()); +} + +//---------------------------------------------------------------------- +// +// PrototypeDocumentContentSink::ContextStack +// + +PrototypeDocumentContentSink::ContextStack::ContextStack() + : mTop(nullptr), mDepth(0) {} + +PrototypeDocumentContentSink::ContextStack::~ContextStack() { Clear(); } + +void PrototypeDocumentContentSink::ContextStack::Traverse( + nsCycleCollectionTraversalCallback& aCallback, const char* aName, + uint32_t aFlags) { + aFlags |= CycleCollectionEdgeNameArrayFlag; + Entry* current = mTop; + while (current) { + CycleCollectionNoteChild(aCallback, current->mElement, aName, aFlags); + current = current->mNext; + } +} + +void PrototypeDocumentContentSink::ContextStack::Clear() { + while (mTop) { + Entry* doomed = mTop; + mTop = mTop->mNext; + NS_IF_RELEASE(doomed->mElement); + delete doomed; + } + mDepth = 0; +} + +nsresult PrototypeDocumentContentSink::ContextStack::Push( + nsXULPrototypeElement* aPrototype, nsIContent* aElement) { + Entry* entry = new Entry; + entry->mPrototype = aPrototype; + entry->mElement = aElement; + NS_IF_ADDREF(entry->mElement); + entry->mIndex = 0; + + entry->mNext = mTop; + mTop = entry; + + ++mDepth; + return NS_OK; +} + +nsresult PrototypeDocumentContentSink::ContextStack::Pop() { + if (mDepth == 0) return NS_ERROR_UNEXPECTED; + + Entry* doomed = mTop; + mTop = mTop->mNext; + --mDepth; + + NS_IF_RELEASE(doomed->mElement); + delete doomed; + return NS_OK; +} + +nsresult PrototypeDocumentContentSink::ContextStack::Peek( + nsXULPrototypeElement** aPrototype, nsIContent** aElement, + int32_t* aIndex) { + if (mDepth == 0) return NS_ERROR_UNEXPECTED; + + *aPrototype = mTop->mPrototype; + *aElement = mTop->mElement; + NS_IF_ADDREF(*aElement); + *aIndex = mTop->mIndex; + + return NS_OK; +} + +nsresult PrototypeDocumentContentSink::ContextStack::SetTopIndex( + int32_t aIndex) { + if (mDepth == 0) return NS_ERROR_UNEXPECTED; + + mTop->mIndex = aIndex; + return NS_OK; +} + +//---------------------------------------------------------------------- +// +// Content model walking routines +// + +nsresult PrototypeDocumentContentSink::OnPrototypeLoadDone( + nsXULPrototypeDocument* aPrototype) { + mCurrentPrototype = aPrototype; + mDocument->SetPrototypeDocument(aPrototype); + + nsresult rv = PrepareToWalk(); + NS_ENSURE_SUCCESS(rv, rv); + + rv = ResumeWalk(); + + return rv; +} + +nsresult PrototypeDocumentContentSink::PrepareToWalk() { + MOZ_ASSERT(mCurrentPrototype); + nsresult rv; + + mStillWalking = true; + + // Notify document that the load is beginning + mDocument->BeginLoad(); + + // Get the prototype's root element and initialize the context + // stack for the prototype walk. + nsXULPrototypeElement* proto = mCurrentPrototype->GetRootElement(); + + if (!proto) { + if (MOZ_LOG_TEST(gLog, LogLevel::Error)) { + nsCOMPtr url = mCurrentPrototype->GetURI(); + + nsAutoCString urlspec; + rv = url->GetSpec(urlspec); + if (NS_FAILED(rv)) return rv; + + MOZ_LOG(gLog, LogLevel::Error, + ("prototype: error parsing '%s'", urlspec.get())); + } + + return NS_OK; + } + + nsINode* nodeToInsertBefore = mDocument->GetFirstChild(); + + const nsTArray >& processingInstructions = + mCurrentPrototype->GetProcessingInstructions(); + + uint32_t total = processingInstructions.Length(); + for (uint32_t i = 0; i < total; ++i) { + rv = CreateAndInsertPI(processingInstructions[i], mDocument, + nodeToInsertBefore); + if (NS_FAILED(rv)) return rv; + } + + // Do one-time initialization. + RefPtr root; + + // Add the root element + rv = CreateElementFromPrototype(proto, getter_AddRefs(root), nullptr); + if (NS_FAILED(rv)) return rv; + + ErrorResult error; + mDocument->AppendChildTo(root, false, error); + if (error.Failed()) { + return error.StealNSResult(); + } + + // TODO(emilio): Should this really notify? We don't notify of appends anyhow, + // and we just appended the root so no styles can possibly depend on it. + mDocument->UpdateDocumentStates(DocumentState::RTL_LOCALE, true); + + nsContentUtils::AddScriptRunner( + new nsDocElementCreatedNotificationRunner(mDocument)); + + // There'd better not be anything on the context stack at this + // point! This is the basis case for our "induction" in + // ResumeWalk(), below, which'll assume that there's always a + // content element on the context stack if we're in the document. + NS_ASSERTION(mContextStack.Depth() == 0, + "something's on the context stack already"); + if (mContextStack.Depth() != 0) return NS_ERROR_UNEXPECTED; + + rv = mContextStack.Push(proto, root); + if (NS_FAILED(rv)) return rv; + + return NS_OK; +} + +nsresult PrototypeDocumentContentSink::CreateAndInsertPI( + const nsXULPrototypePI* aProtoPI, nsINode* aParent, nsINode* aBeforeThis) { + MOZ_ASSERT(aProtoPI, "null ptr"); + MOZ_ASSERT(aParent, "null ptr"); + + RefPtr node = + NS_NewXMLProcessingInstruction(aParent->OwnerDoc()->NodeInfoManager(), + aProtoPI->mTarget, aProtoPI->mData); + + nsresult rv; + if (aProtoPI->mTarget.EqualsLiteral("xml-stylesheet")) { + MOZ_ASSERT(LinkStyle::FromNode(*node), + "XML Stylesheet node does not implement LinkStyle!"); + auto* pi = static_cast(node.get()); + rv = InsertXMLStylesheetPI(aProtoPI, aParent, aBeforeThis, pi); + } else { + // No special processing, just add the PI to the document. + ErrorResult error; + aParent->InsertChildBefore(node->AsContent(), + aBeforeThis ? aBeforeThis->AsContent() : nullptr, + false, error); + rv = error.StealNSResult(); + } + + return rv; +} + +nsresult PrototypeDocumentContentSink::InsertXMLStylesheetPI( + const nsXULPrototypePI* aProtoPI, nsINode* aParent, nsINode* aBeforeThis, + XMLStylesheetProcessingInstruction* aPINode) { + // We want to be notified when the style sheet finishes loading, so + // disable style sheet loading for now. + aPINode->DisableUpdates(); + aPINode->OverrideBaseURI(mCurrentPrototype->GetURI()); + + ErrorResult rv; + aParent->InsertChildBefore( + aPINode, aBeforeThis ? aBeforeThis->AsContent() : nullptr, false, rv); + if (rv.Failed()) { + return rv.StealNSResult(); + } + + // load the stylesheet if necessary, passing ourselves as + // nsICSSObserver + auto result = aPINode->EnableUpdatesAndUpdateStyleSheet(this); + if (result.isErr()) { + // Ignore errors from UpdateStyleSheet; we don't want failure to + // do that to break the XUL document load. But do propagate out + // NS_ERROR_OUT_OF_MEMORY. + if (result.unwrapErr() == NS_ERROR_OUT_OF_MEMORY) { + return result.unwrapErr(); + } + return NS_OK; + } + + auto update = result.unwrap(); + if (update.ShouldBlock()) { + ++mPendingSheets; + } + + return NS_OK; +} + +void PrototypeDocumentContentSink::CloseElement(Element* aElement, + bool aHadChildren) { + if (nsIContent::RequiresDoneAddingChildren( + aElement->NodeInfo()->NamespaceID(), + aElement->NodeInfo()->NameAtom())) { + aElement->DoneAddingChildren(false); + } + + if (auto* linkStyle = LinkStyle::FromNode(*aElement)) { + auto result = linkStyle->EnableUpdatesAndUpdateStyleSheet(this); + if (result.isOk() && result.unwrap().ShouldBlock()) { + ++mPendingSheets; + } + return; + } + + if (!aHadChildren) { + return; + } + + // See bug 370111 and bug 1495946. We don't cache inline styles nor module + // scripts in the prototype cache, and we don't notify on node insertion, so + // we need to do this for the stylesheet / script to be properly processed. + // This kinda sucks, but notifying was a pretty sizeable perf regression so... + if (aElement->IsHTMLElement(nsGkAtoms::script) || + aElement->IsSVGElement(nsGkAtoms::script)) { + nsCOMPtr sele = do_QueryInterface(aElement); + MOZ_ASSERT(sele, "Node didn't QI to script."); + if (sele->GetScriptIsModule()) { + DebugOnly block = sele->AttemptToExecute(); + MOZ_ASSERT(!block, "