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 --- parser/prototype/PrototypeDocumentParser.cpp | 213 +++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 parser/prototype/PrototypeDocumentParser.cpp (limited to 'parser/prototype/PrototypeDocumentParser.cpp') diff --git a/parser/prototype/PrototypeDocumentParser.cpp b/parser/prototype/PrototypeDocumentParser.cpp new file mode 100644 index 0000000000..c07a35b892 --- /dev/null +++ b/parser/prototype/PrototypeDocumentParser.cpp @@ -0,0 +1,213 @@ +/* -*- 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 "PrototypeDocumentParser.h" + +#include "nsXULPrototypeCache.h" +#include "nsXULContentSink.h" +#include "nsXULPrototypeDocument.h" +#include "mozilla/Encoding.h" +#include "nsCharsetSource.h" +#include "nsParser.h" +#include "mozilla/dom/Document.h" +#include "mozilla/dom/URL.h" +#include "mozilla/dom/PrototypeDocumentContentSink.h" + +using namespace mozilla::dom; + +namespace mozilla { +namespace parser { + +PrototypeDocumentParser::PrototypeDocumentParser(nsIURI* aDocumentURI, + dom::Document* aDocument) + : mDocumentURI(aDocumentURI), + mDocument(aDocument), + mPrototypeAlreadyLoaded(false), + mIsComplete(false) {} + +PrototypeDocumentParser::~PrototypeDocumentParser() {} + +NS_INTERFACE_TABLE_HEAD(PrototypeDocumentParser) + NS_INTERFACE_TABLE(PrototypeDocumentParser, nsIParser, nsIStreamListener, + nsIRequestObserver) + NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(PrototypeDocumentParser) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(PrototypeDocumentParser) +NS_IMPL_CYCLE_COLLECTING_RELEASE(PrototypeDocumentParser) + +NS_IMPL_CYCLE_COLLECTION(PrototypeDocumentParser, mDocumentURI, mOriginalSink, + mDocument, mStreamListener, mCurrentPrototype) + +NS_IMETHODIMP_(void) +PrototypeDocumentParser::SetContentSink(nsIContentSink* aSink) { + MOZ_ASSERT(aSink, "sink cannot be null!"); + mOriginalSink = static_cast(aSink); + MOZ_ASSERT(mOriginalSink); + + aSink->SetParser(this); +} + +NS_IMETHODIMP_(nsIContentSink*) +PrototypeDocumentParser::GetContentSink() { return mOriginalSink; } + +nsIStreamListener* PrototypeDocumentParser::GetStreamListener() { return this; } + +NS_IMETHODIMP_(bool) +PrototypeDocumentParser::IsComplete() { return mIsComplete; } + +NS_IMETHODIMP +PrototypeDocumentParser::Parse(nsIURI* aURL) { + // Look in the chrome cache: we've got this puppy loaded + // already. + nsXULPrototypeDocument* proto = + IsChromeURI(mDocumentURI) + ? nsXULPrototypeCache::GetInstance()->GetPrototype(mDocumentURI) + : nullptr; + + // We don't abort on failure here because there are too many valid + // cases that can return failure, and the null-ness of |proto| is enough + // to trigger the fail-safe parse-from-disk solution. Example failure cases + // (for reference) include: + // + // NS_ERROR_NOT_AVAILABLE: the URI cannot be found in the startup cache, + // parse from disk + // other: the startup cache file could not be found, probably + // due to being accessed before a profile has been selected (e.g. + // loading chrome for the profile manager itself). This must be + // parsed from disk. + nsresult rv; + if (proto) { + mCurrentPrototype = proto; + + // Set up the right principal on the document. + mDocument->SetPrincipals(proto->DocumentPrincipal(), + proto->DocumentPrincipal()); + } else { + // It's just a vanilla document load. Create a parser to deal + // with the stream n' stuff. + + nsCOMPtr parser; + // Get the document's principal + nsCOMPtr principal = mDocument->NodePrincipal(); + rv = + PrepareToLoadPrototype(mDocumentURI, principal, getter_AddRefs(parser)); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr listener = do_QueryInterface(parser, &rv); + NS_ASSERTION(NS_SUCCEEDED(rv), "parser doesn't support nsIStreamListener"); + if (NS_FAILED(rv)) return rv; + + mStreamListener = listener; + + parser->Parse(mDocumentURI); + } + + // If we're racing with another document to load proto, wait till the + // load has finished loading before trying build the document. + // Either the nsXULContentSink finishing to load the XML or + // the nsXULPrototypeDocument completing deserialization will trigger the + // OnPrototypeLoadDone callback. + // If the prototype is already loaded, OnPrototypeLoadDone will be called + // in OnStopRequest. + RefPtr self = this; + rv = mCurrentPrototype->AwaitLoadDone( + [self]() { self->OnPrototypeLoadDone(); }, &mPrototypeAlreadyLoaded); + if (NS_FAILED(rv)) return rv; + + return NS_OK; +} + +NS_IMETHODIMP +PrototypeDocumentParser::OnStartRequest(nsIRequest* request) { + if (mStreamListener) { + return mStreamListener->OnStartRequest(request); + } + // There's already a prototype cached, so return cached here so the original + // request will be aborted. Either OnStopRequest or the prototype load + // finishing will notify the content sink that we're done loading the + // prototype. + return NS_ERROR_PARSED_DATA_CACHED; +} + +NS_IMETHODIMP +PrototypeDocumentParser::OnStopRequest(nsIRequest* request, nsresult aStatus) { + if (mStreamListener) { + return mStreamListener->OnStopRequest(request, aStatus); + } + if (mPrototypeAlreadyLoaded) { + return this->OnPrototypeLoadDone(); + } + // The prototype will handle calling OnPrototypeLoadDone when it is ready. + return NS_OK; +} + +NS_IMETHODIMP +PrototypeDocumentParser::OnDataAvailable(nsIRequest* request, + nsIInputStream* aInStr, + uint64_t aSourceOffset, + uint32_t aCount) { + if (mStreamListener) { + return mStreamListener->OnDataAvailable(request, aInStr, aSourceOffset, + aCount); + } + MOZ_ASSERT_UNREACHABLE("Cached prototype doesn't receive data"); + return NS_ERROR_UNEXPECTED; +} + +nsresult PrototypeDocumentParser::OnPrototypeLoadDone() { + MOZ_ASSERT(!mIsComplete, "Should not be called more than once."); + mIsComplete = true; + + RefPtr sink = mOriginalSink; + RefPtr prototype = mCurrentPrototype; + return sink->OnPrototypeLoadDone(prototype); +} + +nsresult PrototypeDocumentParser::PrepareToLoadPrototype( + nsIURI* aURI, nsIPrincipal* aDocumentPrincipal, nsIParser** aResult) { + nsresult rv; + + // Create a new prototype document. + rv = NS_NewXULPrototypeDocument(getter_AddRefs(mCurrentPrototype)); + if (NS_FAILED(rv)) return rv; + + rv = mCurrentPrototype->InitPrincipal(aURI, aDocumentPrincipal); + if (NS_FAILED(rv)) { + mCurrentPrototype = nullptr; + return rv; + } + + // Store the new prototype right away so if there are multiple requests + // for the same document they all get the same prototype. + if (IsChromeURI(mDocumentURI) && + nsXULPrototypeCache::GetInstance()->IsEnabled()) { + nsXULPrototypeCache::GetInstance()->PutPrototype(mCurrentPrototype); + } + + mDocument->SetPrincipals(aDocumentPrincipal, aDocumentPrincipal); + + // Create a XUL content sink, a parser, and kick off a load for + // the document. + RefPtr sink = new XULContentSinkImpl(); + + rv = sink->Init(mDocument, mCurrentPrototype); + NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to initialize datasource sink"); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr parser = new nsParser(); + + parser->SetCommand(eViewNormal); + + parser->SetDocumentCharset(UTF_8_ENCODING, kCharsetFromDocTypeDefault); + parser->SetContentSink(sink); // grabs a reference to the parser + + parser.forget(aResult); + return NS_OK; +} + +} // namespace parser +} // namespace mozilla -- cgit v1.2.3