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"
15 #include "mozilla/dom/Document.h"
16 #include "mozilla/dom/URL.h"
17 #include "mozilla/dom/PrototypeDocumentContentSink.h"
19 using namespace mozilla::dom
;
24 PrototypeDocumentParser::PrototypeDocumentParser(nsIURI
* aDocumentURI
,
25 dom::Document
* aDocument
)
26 : mDocumentURI(aDocumentURI
),
28 mPrototypeAlreadyLoaded(false),
31 PrototypeDocumentParser::~PrototypeDocumentParser() {}
33 NS_INTERFACE_TABLE_HEAD(PrototypeDocumentParser
)
34 NS_INTERFACE_TABLE(PrototypeDocumentParser
, nsIParser
, nsIStreamListener
,
36 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(PrototypeDocumentParser
)
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
)
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; }
60 PrototypeDocumentParser::IsComplete() { return mIsComplete
; }
63 PrototypeDocumentParser::Parse(nsIURI
* aURL
) {
64 // Look in the chrome cache: we've got this puppy loaded
66 nsXULPrototypeDocument
* proto
=
67 IsChromeURI(mDocumentURI
)
68 ? nsXULPrototypeCache::GetInstance()->GetPrototype(mDocumentURI
)
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,
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
84 mCurrentPrototype
= proto
;
86 // Set up the right principal on the document.
87 mDocument
->SetPrincipals(proto
->DocumentPrincipal(),
88 proto
->DocumentPrincipal());
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();
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
116 RefPtr
<PrototypeDocumentParser
> self
= this;
117 rv
= mCurrentPrototype
->AwaitLoadDone(
118 [self
]() { self
->OnPrototypeLoadDone(); }, &mPrototypeAlreadyLoaded
);
119 if (NS_FAILED(rv
)) return rv
;
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
133 return NS_ERROR_PARSED_DATA_CACHED
;
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.
149 PrototypeDocumentParser::OnDataAvailable(nsIRequest
* request
,
150 nsIInputStream
* aInStr
,
151 uint64_t aSourceOffset
,
153 if (mStreamListener
) {
154 return mStreamListener
->OnDataAvailable(request
, aInStr
, aSourceOffset
,
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.");
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
) {
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
);
180 mCurrentPrototype
= nullptr;
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
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
);
212 } // namespace parser
213 } // namespace mozilla