Bug 1839316: part 5) Guard the "fetchpriority" attribute behind a pref. r=kershaw...
[gecko.git] / parser / prototype / PrototypeDocumentParser.cpp
blobc07a35b892d0f7e6663e95c2cbd370aa84a5a0e4
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 "nsXULPrototypeDocument.h"
12 #include "mozilla/Encoding.h"
13 #include "nsCharsetSource.h"
14 #include "nsParser.h"
15 #include "mozilla/dom/Document.h"
16 #include "mozilla/dom/URL.h"
17 #include "mozilla/dom/PrototypeDocumentContentSink.h"
19 using namespace mozilla::dom;
21 namespace mozilla {
22 namespace parser {
24 PrototypeDocumentParser::PrototypeDocumentParser(nsIURI* aDocumentURI,
25 dom::Document* aDocument)
26 : mDocumentURI(aDocumentURI),
27 mDocument(aDocument),
28 mPrototypeAlreadyLoaded(false),
29 mIsComplete(false) {}
31 PrototypeDocumentParser::~PrototypeDocumentParser() {}
33 NS_INTERFACE_TABLE_HEAD(PrototypeDocumentParser)
34 NS_INTERFACE_TABLE(PrototypeDocumentParser, nsIParser, nsIStreamListener,
35 nsIRequestObserver)
36 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(PrototypeDocumentParser)
37 NS_INTERFACE_MAP_END
39 NS_IMPL_CYCLE_COLLECTING_ADDREF(PrototypeDocumentParser)
40 NS_IMPL_CYCLE_COLLECTING_RELEASE(PrototypeDocumentParser)
42 NS_IMPL_CYCLE_COLLECTION(PrototypeDocumentParser, mDocumentURI, mOriginalSink,
43 mDocument, mStreamListener, mCurrentPrototype)
45 NS_IMETHODIMP_(void)
46 PrototypeDocumentParser::SetContentSink(nsIContentSink* aSink) {
47 MOZ_ASSERT(aSink, "sink cannot be null!");
48 mOriginalSink = static_cast<PrototypeDocumentContentSink*>(aSink);
49 MOZ_ASSERT(mOriginalSink);
51 aSink->SetParser(this);
54 NS_IMETHODIMP_(nsIContentSink*)
55 PrototypeDocumentParser::GetContentSink() { return mOriginalSink; }
57 nsIStreamListener* PrototypeDocumentParser::GetStreamListener() { return this; }
59 NS_IMETHODIMP_(bool)
60 PrototypeDocumentParser::IsComplete() { return mIsComplete; }
62 NS_IMETHODIMP
63 PrototypeDocumentParser::Parse(nsIURI* aURL) {
64 // Look in the chrome cache: we've got this puppy loaded
65 // already.
66 nsXULPrototypeDocument* proto =
67 IsChromeURI(mDocumentURI)
68 ? nsXULPrototypeCache::GetInstance()->GetPrototype(mDocumentURI)
69 : nullptr;
71 // We don't abort on failure here because there are too many valid
72 // cases that can return failure, and the null-ness of |proto| is enough
73 // to trigger the fail-safe parse-from-disk solution. Example failure cases
74 // (for reference) include:
76 // NS_ERROR_NOT_AVAILABLE: the URI cannot be found in the startup cache,
77 // parse from disk
78 // other: the startup cache file could not be found, probably
79 // due to being accessed before a profile has been selected (e.g.
80 // loading chrome for the profile manager itself). This must be
81 // parsed from disk.
82 nsresult rv;
83 if (proto) {
84 mCurrentPrototype = proto;
86 // Set up the right principal on the document.
87 mDocument->SetPrincipals(proto->DocumentPrincipal(),
88 proto->DocumentPrincipal());
89 } else {
90 // It's just a vanilla document load. Create a parser to deal
91 // with the stream n' stuff.
93 nsCOMPtr<nsIParser> parser;
94 // Get the document's principal
95 nsCOMPtr<nsIPrincipal> principal = mDocument->NodePrincipal();
96 rv =
97 PrepareToLoadPrototype(mDocumentURI, principal, getter_AddRefs(parser));
98 if (NS_FAILED(rv)) return rv;
100 nsCOMPtr<nsIStreamListener> listener = do_QueryInterface(parser, &rv);
101 NS_ASSERTION(NS_SUCCEEDED(rv), "parser doesn't support nsIStreamListener");
102 if (NS_FAILED(rv)) return rv;
104 mStreamListener = listener;
106 parser->Parse(mDocumentURI);
109 // If we're racing with another document to load proto, wait till the
110 // load has finished loading before trying build the document.
111 // Either the nsXULContentSink finishing to load the XML or
112 // the nsXULPrototypeDocument completing deserialization will trigger the
113 // OnPrototypeLoadDone callback.
114 // If the prototype is already loaded, OnPrototypeLoadDone will be called
115 // in OnStopRequest.
116 RefPtr<PrototypeDocumentParser> self = this;
117 rv = mCurrentPrototype->AwaitLoadDone(
118 [self]() { self->OnPrototypeLoadDone(); }, &mPrototypeAlreadyLoaded);
119 if (NS_FAILED(rv)) return rv;
121 return NS_OK;
124 NS_IMETHODIMP
125 PrototypeDocumentParser::OnStartRequest(nsIRequest* request) {
126 if (mStreamListener) {
127 return mStreamListener->OnStartRequest(request);
129 // There's already a prototype cached, so return cached here so the original
130 // request will be aborted. Either OnStopRequest or the prototype load
131 // finishing will notify the content sink that we're done loading the
132 // prototype.
133 return NS_ERROR_PARSED_DATA_CACHED;
136 NS_IMETHODIMP
137 PrototypeDocumentParser::OnStopRequest(nsIRequest* request, nsresult aStatus) {
138 if (mStreamListener) {
139 return mStreamListener->OnStopRequest(request, aStatus);
141 if (mPrototypeAlreadyLoaded) {
142 return this->OnPrototypeLoadDone();
144 // The prototype will handle calling OnPrototypeLoadDone when it is ready.
145 return NS_OK;
148 NS_IMETHODIMP
149 PrototypeDocumentParser::OnDataAvailable(nsIRequest* request,
150 nsIInputStream* aInStr,
151 uint64_t aSourceOffset,
152 uint32_t aCount) {
153 if (mStreamListener) {
154 return mStreamListener->OnDataAvailable(request, aInStr, aSourceOffset,
155 aCount);
157 MOZ_ASSERT_UNREACHABLE("Cached prototype doesn't receive data");
158 return NS_ERROR_UNEXPECTED;
161 nsresult PrototypeDocumentParser::OnPrototypeLoadDone() {
162 MOZ_ASSERT(!mIsComplete, "Should not be called more than once.");
163 mIsComplete = true;
165 RefPtr<PrototypeDocumentContentSink> sink = mOriginalSink;
166 RefPtr<nsXULPrototypeDocument> prototype = mCurrentPrototype;
167 return sink->OnPrototypeLoadDone(prototype);
170 nsresult PrototypeDocumentParser::PrepareToLoadPrototype(
171 nsIURI* aURI, nsIPrincipal* aDocumentPrincipal, nsIParser** aResult) {
172 nsresult rv;
174 // Create a new prototype document.
175 rv = NS_NewXULPrototypeDocument(getter_AddRefs(mCurrentPrototype));
176 if (NS_FAILED(rv)) return rv;
178 rv = mCurrentPrototype->InitPrincipal(aURI, aDocumentPrincipal);
179 if (NS_FAILED(rv)) {
180 mCurrentPrototype = nullptr;
181 return rv;
184 // Store the new prototype right away so if there are multiple requests
185 // for the same document they all get the same prototype.
186 if (IsChromeURI(mDocumentURI) &&
187 nsXULPrototypeCache::GetInstance()->IsEnabled()) {
188 nsXULPrototypeCache::GetInstance()->PutPrototype(mCurrentPrototype);
191 mDocument->SetPrincipals(aDocumentPrincipal, aDocumentPrincipal);
193 // Create a XUL content sink, a parser, and kick off a load for
194 // the document.
195 RefPtr<XULContentSinkImpl> sink = new XULContentSinkImpl();
197 rv = sink->Init(mDocument, mCurrentPrototype);
198 NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to initialize datasource sink");
199 if (NS_FAILED(rv)) return rv;
201 nsCOMPtr<nsIParser> parser = new nsParser();
203 parser->SetCommand(eViewNormal);
205 parser->SetDocumentCharset(UTF_8_ENCODING, kCharsetFromDocTypeDefault);
206 parser->SetContentSink(sink); // grabs a reference to the parser
208 parser.forget(aResult);
209 return NS_OK;
212 } // namespace parser
213 } // namespace mozilla