summaryrefslogtreecommitdiffstats
path: root/parser/prototype
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /parser/prototype
parentInitial commit. (diff)
downloadfirefox-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.cpp213
-rw-r--r--parser/prototype/PrototypeDocumentParser.h131
-rw-r--r--parser/prototype/moz.build24
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",
+]