Bug 1829125 - Align the PHC area to the jemalloc chunk size r=glandium
[gecko.git] / parser / prototype / PrototypeDocumentParser.cpp
blobedeb3ed7f13110b7224e432ec916f0dc972b6cc0
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
7 #include "PrototypeDocumentParser.h"
9 #include "nsXULPrototypeCache.h"
10 #include "nsXULContentSink.h"
11 #include "mozilla/Encoding.h"
12 #include "nsCharsetSource.h"
13 #include "nsParser.h"
14 #include "mozilla/dom/Document.h"
15 #include "mozilla/dom/URL.h"
16 #include "mozilla/dom/PrototypeDocumentContentSink.h"
18 using namespace mozilla::dom;
20 namespace mozilla {
21 namespace parser {
23 PrototypeDocumentParser::PrototypeDocumentParser(nsIURI* aDocumentURI,
24 dom::Document* aDocument)
25 : mDocumentURI(aDocumentURI),
26 mDocument(aDocument),
27 mPrototypeAlreadyLoaded(false),
28 mIsComplete(false) {}
30 PrototypeDocumentParser::~PrototypeDocumentParser() {}
32 NS_INTERFACE_TABLE_HEAD(PrototypeDocumentParser)
33 NS_INTERFACE_TABLE(PrototypeDocumentParser, nsIParser, nsIStreamListener,
34 nsIRequestObserver)
35 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(PrototypeDocumentParser)
36 NS_INTERFACE_MAP_END
38 NS_IMPL_CYCLE_COLLECTING_ADDREF(PrototypeDocumentParser)
39 NS_IMPL_CYCLE_COLLECTING_RELEASE(PrototypeDocumentParser)
41 NS_IMPL_CYCLE_COLLECTION(PrototypeDocumentParser, mDocumentURI, mOriginalSink,
42 mDocument, mStreamListener, mCurrentPrototype)
44 NS_IMETHODIMP_(void)
45 PrototypeDocumentParser::SetContentSink(nsIContentSink* aSink) {
46 MOZ_ASSERT(aSink, "sink cannot be null!");
47 mOriginalSink = static_cast<PrototypeDocumentContentSink*>(aSink);
48 MOZ_ASSERT(mOriginalSink);
50 aSink->SetParser(this);
53 NS_IMETHODIMP_(nsIContentSink*)
54 PrototypeDocumentParser::GetContentSink() { return mOriginalSink; }
56 nsIStreamListener* PrototypeDocumentParser::GetStreamListener() { return this; }
58 NS_IMETHODIMP_(bool)
59 PrototypeDocumentParser::IsComplete() { return mIsComplete; }
61 NS_IMETHODIMP
62 PrototypeDocumentParser::Parse(nsIURI* aURL) {
63 // Look in the chrome cache: we've got this puppy loaded
64 // already.
65 nsXULPrototypeDocument* proto =
66 IsChromeURI(mDocumentURI)
67 ? nsXULPrototypeCache::GetInstance()->GetPrototype(mDocumentURI)
68 : nullptr;
70 // We don't abort on failure here because there are too many valid
71 // cases that can return failure, and the null-ness of |proto| is enough
72 // to trigger the fail-safe parse-from-disk solution. Example failure cases
73 // (for reference) include:
75 // NS_ERROR_NOT_AVAILABLE: the URI cannot be found in the startup cache,
76 // parse from disk
77 // other: the startup cache file could not be found, probably
78 // due to being accessed before a profile has been selected (e.g.
79 // loading chrome for the profile manager itself). This must be
80 // parsed from disk.
81 nsresult rv;
82 if (proto) {
83 mCurrentPrototype = proto;
85 // Set up the right principal on the document.
86 mDocument->SetPrincipals(proto->DocumentPrincipal(),
87 proto->DocumentPrincipal());
88 } else {
89 // It's just a vanilla document load. Create a parser to deal
90 // with the stream n' stuff.
92 nsCOMPtr<nsIParser> parser;
93 // Get the document's principal
94 nsCOMPtr<nsIPrincipal> principal = mDocument->NodePrincipal();
95 rv =
96 PrepareToLoadPrototype(mDocumentURI, principal, getter_AddRefs(parser));
97 if (NS_FAILED(rv)) return rv;
99 nsCOMPtr<nsIStreamListener> listener = do_QueryInterface(parser, &rv);
100 NS_ASSERTION(NS_SUCCEEDED(rv), "parser doesn't support nsIStreamListener");
101 if (NS_FAILED(rv)) return rv;
103 mStreamListener = listener;
105 parser->Parse(mDocumentURI);
108 // If we're racing with another document to load proto, wait till the
109 // load has finished loading before trying build the document.
110 // Either the nsXULContentSink finishing to load the XML or
111 // the nsXULPrototypeDocument completing deserialization will trigger the
112 // OnPrototypeLoadDone callback.
113 // If the prototype is already loaded, OnPrototypeLoadDone will be called
114 // in OnStopRequest.
115 RefPtr<PrototypeDocumentParser> self = this;
116 rv = mCurrentPrototype->AwaitLoadDone(
117 [self]() { self->OnPrototypeLoadDone(); }, &mPrototypeAlreadyLoaded);
118 if (NS_FAILED(rv)) return rv;
120 return NS_OK;
123 NS_IMETHODIMP
124 PrototypeDocumentParser::OnStartRequest(nsIRequest* request) {
125 if (mStreamListener) {
126 return mStreamListener->OnStartRequest(request);
128 // There's already a prototype cached, so return cached here so the original
129 // request will be aborted. Either OnStopRequest or the prototype load
130 // finishing will notify the content sink that we're done loading the
131 // prototype.
132 return NS_ERROR_PARSED_DATA_CACHED;
135 NS_IMETHODIMP
136 PrototypeDocumentParser::OnStopRequest(nsIRequest* request, nsresult aStatus) {
137 if (mStreamListener) {
138 return mStreamListener->OnStopRequest(request, aStatus);
140 if (mPrototypeAlreadyLoaded) {
141 return this->OnPrototypeLoadDone();
143 // The prototype will handle calling OnPrototypeLoadDone when it is ready.
144 return NS_OK;
147 NS_IMETHODIMP
148 PrototypeDocumentParser::OnDataAvailable(nsIRequest* request,
149 nsIInputStream* aInStr,
150 uint64_t aSourceOffset,
151 uint32_t aCount) {
152 if (mStreamListener) {
153 return mStreamListener->OnDataAvailable(request, aInStr, aSourceOffset,
154 aCount);
156 MOZ_ASSERT_UNREACHABLE("Cached prototype doesn't receive data");
157 return NS_ERROR_UNEXPECTED;
160 nsresult PrototypeDocumentParser::OnPrototypeLoadDone() {
161 MOZ_ASSERT(!mIsComplete, "Should not be called more than once.");
162 mIsComplete = true;
164 return mOriginalSink->OnPrototypeLoadDone(mCurrentPrototype);
167 nsresult PrototypeDocumentParser::PrepareToLoadPrototype(
168 nsIURI* aURI, nsIPrincipal* aDocumentPrincipal, nsIParser** aResult) {
169 nsresult rv;
171 // Create a new prototype document.
172 rv = NS_NewXULPrototypeDocument(getter_AddRefs(mCurrentPrototype));
173 if (NS_FAILED(rv)) return rv;
175 rv = mCurrentPrototype->InitPrincipal(aURI, aDocumentPrincipal);
176 if (NS_FAILED(rv)) {
177 mCurrentPrototype = nullptr;
178 return rv;
181 // Store the new prototype right away so if there are multiple requests
182 // for the same document they all get the same prototype.
183 if (IsChromeURI(mDocumentURI) &&
184 nsXULPrototypeCache::GetInstance()->IsEnabled()) {
185 nsXULPrototypeCache::GetInstance()->PutPrototype(mCurrentPrototype);
188 mDocument->SetPrincipals(aDocumentPrincipal, aDocumentPrincipal);
190 // Create a XUL content sink, a parser, and kick off a load for
191 // the document.
192 RefPtr<XULContentSinkImpl> sink = new XULContentSinkImpl();
194 rv = sink->Init(mDocument, mCurrentPrototype);
195 NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to initialize datasource sink");
196 if (NS_FAILED(rv)) return rv;
198 nsCOMPtr<nsIParser> parser = new nsParser();
200 parser->SetCommand(eViewNormal);
202 parser->SetDocumentCharset(UTF_8_ENCODING, kCharsetFromDocTypeDefault);
203 parser->SetContentSink(sink); // grabs a reference to the parser
205 parser.forget(aResult);
206 return NS_OK;
209 } // namespace parser
210 } // namespace mozilla