diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /parser/prototype | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'parser/prototype')
-rw-r--r-- | parser/prototype/PrototypeDocumentParser.cpp | 213 | ||||
-rw-r--r-- | parser/prototype/PrototypeDocumentParser.h | 131 | ||||
-rw-r--r-- | parser/prototype/moz.build | 24 |
3 files changed, 368 insertions, 0 deletions
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<PrototypeDocumentContentSink*>(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<nsIParser> parser; + // Get the document's principal + nsCOMPtr<nsIPrincipal> principal = mDocument->NodePrincipal(); + rv = + PrepareToLoadPrototype(mDocumentURI, principal, getter_AddRefs(parser)); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr<nsIStreamListener> 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<PrototypeDocumentParser> 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<PrototypeDocumentContentSink> sink = mOriginalSink; + RefPtr<nsXULPrototypeDocument> 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<XULContentSinkImpl> 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<nsIParser> 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 diff --git a/parser/prototype/PrototypeDocumentParser.h b/parser/prototype/PrototypeDocumentParser.h new file mode 100644 index 0000000000..3e7935bffc --- /dev/null +++ b/parser/prototype/PrototypeDocumentParser.h @@ -0,0 +1,131 @@ +/* -*- 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/. */ + +#ifndef mozilla_parser_PrototypeDocumentParser_h +#define mozilla_parser_PrototypeDocumentParser_h + +#include "nsCycleCollectionParticipant.h" +#include "nsIContentSink.h" +#include "nsIParser.h" +#include "nsXULPrototypeDocument.h" + +class nsIExpatSink; + +namespace mozilla { +namespace dom { +class PrototypeDocumentContentSink; +} // namespace dom +} // namespace mozilla + +namespace mozilla { +namespace parser { + +// The PrototypeDocumentParser is more of a stub than a real parser. It is +// responsible for loading an nsXULPrototypeDocument either from the startup +// cache or creating a new prototype from the original source if a cached +// version does not exist. Once the parser finishes loading the prototype it +// will notify the content sink. +class PrototypeDocumentParser final : public nsIParser, + public nsIStreamListener { + public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(PrototypeDocumentParser, nsIParser) + + explicit PrototypeDocumentParser(nsIURI* aDocumentURI, + dom::Document* aDocument); + + NS_DECL_NSIREQUESTOBSERVER + NS_DECL_NSISTREAMLISTENER + + // Start nsIParser + // Ideally, this would just implement nsBaseParser since most of these are + // stubs, but Document.h expects an nsIParser. + NS_IMETHOD_(void) SetContentSink(nsIContentSink* aSink) override; + + NS_IMETHOD_(nsIContentSink*) GetContentSink() override; + + NS_IMETHOD_(void) GetCommand(nsCString& aCommand) override {} + + NS_IMETHOD_(void) SetCommand(const char* aCommand) override {} + + NS_IMETHOD_(void) SetCommand(eParserCommands aParserCommand) override {} + + virtual void SetDocumentCharset(NotNull<const Encoding*> aEncoding, + int32_t aSource, + bool aForceAutoDetection) override {} + + virtual nsIStreamListener* GetStreamListener() override; + + NS_IMETHOD ContinueInterruptedParsing() override { + return NS_ERROR_NOT_IMPLEMENTED; + } + + NS_IMETHOD_(void) BlockParser() override {} + + NS_IMETHOD_(void) UnblockParser() override {} + + NS_IMETHOD_(void) ContinueInterruptedParsingAsync() override {} + + NS_IMETHOD_(bool) IsParserEnabled() override { return true; } + + NS_IMETHOD_(bool) IsComplete() override; + + NS_IMETHOD Parse(nsIURI* aURL) override; + + NS_IMETHOD Terminate() override { return NS_ERROR_NOT_IMPLEMENTED; } + + virtual bool IsInsertionPointDefined() override { return false; } + + void IncrementScriptNestingLevel() final {} + + void DecrementScriptNestingLevel() final {} + + bool HasNonzeroScriptNestingLevel() const final { return false; } + + virtual bool IsScriptCreated() override { return false; } + + // End nsIParser + + private: + virtual ~PrototypeDocumentParser(); + + protected: + nsresult PrepareToLoadPrototype(nsIURI* aURI, + nsIPrincipal* aDocumentPrincipal, + nsIParser** aResult); + + // This is invoked whenever the prototype for this document is loaded + // and should be walked, regardless of whether the XUL cache is + // disabled, whether the protototype was loaded, whether the + // prototype was loaded from the cache or created by parsing the + // actual XUL source, etc. + nsresult OnPrototypeLoadDone(); + + nsCOMPtr<nsIURI> mDocumentURI; + RefPtr<dom::PrototypeDocumentContentSink> mOriginalSink; + RefPtr<dom::Document> mDocument; + + // The XML parser that data is forwarded to when the prototype does not exist + // and must be parsed from disk. + nsCOMPtr<nsIStreamListener> mStreamListener; + + // The current prototype that we are walking to construct the + // content model. + RefPtr<nsXULPrototypeDocument> mCurrentPrototype; + + // True if there was a prototype in the cache and it finished loading + // already. + bool mPrototypeAlreadyLoaded; + + // True after the parser has notified the content sink that it is done. + bool mIsComplete; +}; + +} // namespace parser +} // namespace mozilla + +#endif // mozilla_parser_PrototypeDocumentParser_h diff --git a/parser/prototype/moz.build b/parser/prototype/moz.build new file mode 100644 index 0000000000..4a2ea437a8 --- /dev/null +++ b/parser/prototype/moz.build @@ -0,0 +1,24 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +with Files("**"): + BUG_COMPONENT = ("Core", "XML") + +EXPORTS.mozilla.parser += [ + "PrototypeDocumentParser.h", +] + + +UNIFIED_SOURCES += [ + "PrototypeDocumentParser.cpp", +] + +FINAL_LIBRARY = "xul" + +LOCAL_INCLUDES += [ + "../htmlparser", + "/dom/xul", +] |