summaryrefslogtreecommitdiffstats
path: root/parser/prototype/PrototypeDocumentParser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'parser/prototype/PrototypeDocumentParser.cpp')
-rw-r--r--parser/prototype/PrototypeDocumentParser.cpp213
1 files changed, 213 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