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"
14 #include "mozilla/dom/Document.h"
15 #include "mozilla/dom/URL.h"
16 #include "mozilla/dom/PrototypeDocumentContentSink.h"
18 using namespace mozilla::dom
;
23 PrototypeDocumentParser::PrototypeDocumentParser(nsIURI
* aDocumentURI
,
24 dom::Document
* aDocument
)
25 : mDocumentURI(aDocumentURI
),
27 mPrototypeAlreadyLoaded(false),
30 PrototypeDocumentParser::~PrototypeDocumentParser() {}
32 NS_INTERFACE_TABLE_HEAD(PrototypeDocumentParser
)
33 NS_INTERFACE_TABLE(PrototypeDocumentParser
, nsIParser
, nsIStreamListener
,
35 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(PrototypeDocumentParser
)
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
)
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; }
59 PrototypeDocumentParser::IsComplete() { return mIsComplete
; }
62 PrototypeDocumentParser::Parse(nsIURI
* aURL
) {
63 // Look in the chrome cache: we've got this puppy loaded
65 nsXULPrototypeDocument
* proto
=
66 IsChromeURI(mDocumentURI
)
67 ? nsXULPrototypeCache::GetInstance()->GetPrototype(mDocumentURI
)
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,
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
83 mCurrentPrototype
= proto
;
85 // Set up the right principal on the document.
86 mDocument
->SetPrincipals(proto
->DocumentPrincipal(),
87 proto
->DocumentPrincipal());
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();
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
115 RefPtr
<PrototypeDocumentParser
> self
= this;
116 rv
= mCurrentPrototype
->AwaitLoadDone(
117 [self
]() { self
->OnPrototypeLoadDone(); }, &mPrototypeAlreadyLoaded
);
118 if (NS_FAILED(rv
)) return rv
;
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
132 return NS_ERROR_PARSED_DATA_CACHED
;
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.
148 PrototypeDocumentParser::OnDataAvailable(nsIRequest
* request
,
149 nsIInputStream
* aInStr
,
150 uint64_t aSourceOffset
,
152 if (mStreamListener
) {
153 return mStreamListener
->OnDataAvailable(request
, aInStr
, aSourceOffset
,
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.");
164 return mOriginalSink
->OnPrototypeLoadDone(mCurrentPrototype
);
167 nsresult
PrototypeDocumentParser::PrepareToLoadPrototype(
168 nsIURI
* aURI
, nsIPrincipal
* aDocumentPrincipal
, nsIParser
** aResult
) {
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
);
177 mCurrentPrototype
= nullptr;
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
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
);
209 } // namespace parser
210 } // namespace mozilla