/src/mozilla-central/dom/base/DOMParser.cpp
Line | Count | Source (jump to first uncovered line) |
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 http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "mozilla/dom/DOMParser.h" |
8 | | |
9 | | #include "nsNetUtil.h" |
10 | | #include "nsDOMString.h" |
11 | | #include "MainThreadUtils.h" |
12 | | #include "nsIStreamListener.h" |
13 | | #include "nsStringStream.h" |
14 | | #include "nsIScriptError.h" |
15 | | #include "nsIScriptSecurityManager.h" |
16 | | #include "nsCRT.h" |
17 | | #include "nsStreamUtils.h" |
18 | | #include "nsContentUtils.h" |
19 | | #include "nsDOMJSUtils.h" |
20 | | #include "nsError.h" |
21 | | #include "nsPIDOMWindow.h" |
22 | | #include "mozilla/LoadInfo.h" |
23 | | #include "mozilla/NullPrincipal.h" |
24 | | #include "mozilla/dom/BindingUtils.h" |
25 | | #include "mozilla/dom/ScriptSettings.h" |
26 | | |
27 | | using namespace mozilla; |
28 | | using namespace mozilla::dom; |
29 | | |
30 | | DOMParser::DOMParser(nsIGlobalObject* aOwner, nsIPrincipal* aDocPrincipal, |
31 | | nsIURI* aDocumentURI, nsIURI* aBaseURI) |
32 | | : mOwner(aOwner) |
33 | | , mPrincipal(aDocPrincipal) |
34 | | , mDocumentURI(aDocumentURI) |
35 | | , mBaseURI(aBaseURI) |
36 | | , mForceEnableXULXBL(false) |
37 | 0 | { |
38 | 0 | MOZ_ASSERT(aDocPrincipal); |
39 | 0 | MOZ_ASSERT(aDocumentURI); |
40 | 0 | } |
41 | | |
42 | | DOMParser::~DOMParser() |
43 | 0 | { |
44 | 0 | } |
45 | | |
46 | | // QueryInterface implementation for DOMParser |
47 | 0 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMParser) |
48 | 0 | NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY |
49 | 0 | NS_INTERFACE_MAP_ENTRY(nsISupports) |
50 | 0 | NS_INTERFACE_MAP_END |
51 | | |
52 | | NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DOMParser, mOwner) |
53 | | |
54 | | NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMParser) |
55 | | NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMParser) |
56 | | |
57 | | static const char* |
58 | | StringFromSupportedType(SupportedType aType) |
59 | 0 | { |
60 | 0 | return SupportedTypeValues::strings[static_cast<int>(aType)].value; |
61 | 0 | } |
62 | | |
63 | | already_AddRefed<nsIDocument> |
64 | | DOMParser::ParseFromString(const nsAString& aStr, SupportedType aType, |
65 | | ErrorResult& aRv) |
66 | 0 | { |
67 | 0 | if (aType == SupportedType::Text_html) { |
68 | 0 | nsCOMPtr<nsIDocument> document = SetUpDocument(DocumentFlavorHTML, aRv); |
69 | 0 | if (NS_WARN_IF(aRv.Failed())) { |
70 | 0 | return nullptr; |
71 | 0 | } |
72 | 0 | |
73 | 0 | // Keep the XULXBL state in sync with the XML case. |
74 | 0 | if (mForceEnableXULXBL) { |
75 | 0 | document->ForceEnableXULXBL(); |
76 | 0 | } |
77 | 0 |
|
78 | 0 | nsresult rv = nsContentUtils::ParseDocumentHTML(aStr, document, false); |
79 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
80 | 0 | aRv.Throw(rv); |
81 | 0 | return nullptr; |
82 | 0 | } |
83 | 0 | |
84 | 0 | return document.forget(); |
85 | 0 | } |
86 | 0 | |
87 | 0 | nsAutoCString utf8str; |
88 | 0 | // Convert from UTF16 to UTF8 using fallible allocations |
89 | 0 | if (!AppendUTF16toUTF8(aStr, utf8str, mozilla::fallible)) { |
90 | 0 | aRv.Throw(NS_ERROR_OUT_OF_MEMORY); |
91 | 0 | return nullptr; |
92 | 0 | } |
93 | 0 | |
94 | 0 | // The new stream holds a reference to the buffer |
95 | 0 | nsCOMPtr<nsIInputStream> stream; |
96 | 0 | nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream), |
97 | 0 | utf8str.get(), utf8str.Length(), |
98 | 0 | NS_ASSIGNMENT_DEPEND); |
99 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
100 | 0 | aRv.Throw(rv); |
101 | 0 | return nullptr; |
102 | 0 | } |
103 | 0 | |
104 | 0 | return ParseFromStream(stream, NS_LITERAL_STRING("UTF-8"), |
105 | 0 | utf8str.Length(), aType, aRv); |
106 | 0 | } |
107 | | |
108 | | already_AddRefed<nsIDocument> |
109 | | DOMParser::ParseFromBuffer(const Uint8Array& aBuf, SupportedType aType, |
110 | | ErrorResult& aRv) |
111 | 0 | { |
112 | 0 | aBuf.ComputeLengthAndData(); |
113 | 0 | return ParseFromBuffer(MakeSpan(aBuf.Data(), aBuf.Length()), aType, aRv); |
114 | 0 | } |
115 | | |
116 | | already_AddRefed<nsIDocument> |
117 | | DOMParser::ParseFromBuffer(Span<const uint8_t> aBuf, SupportedType aType, |
118 | | ErrorResult& aRv) |
119 | 0 | { |
120 | 0 | // The new stream holds a reference to the buffer |
121 | 0 | nsCOMPtr<nsIInputStream> stream; |
122 | 0 | nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream), |
123 | 0 | reinterpret_cast<const char *>(aBuf.Elements()), |
124 | 0 | aBuf.Length(), NS_ASSIGNMENT_DEPEND); |
125 | 0 | if (NS_FAILED(rv)) { |
126 | 0 | aRv.Throw(rv); |
127 | 0 | return nullptr; |
128 | 0 | } |
129 | 0 | |
130 | 0 | return ParseFromStream(stream, VoidString(), aBuf.Length(), aType, aRv); |
131 | 0 | } |
132 | | |
133 | | |
134 | | already_AddRefed<nsIDocument> |
135 | | DOMParser::ParseFromStream(nsIInputStream* aStream, |
136 | | const nsAString& aCharset, |
137 | | int32_t aContentLength, |
138 | | SupportedType aType, |
139 | | ErrorResult& aRv) |
140 | 0 | { |
141 | 0 | bool svg = (aType == SupportedType::Image_svg_xml); |
142 | 0 |
|
143 | 0 | // For now, we can only create XML documents. |
144 | 0 | //XXXsmaug Should we create an HTMLDocument (in XHTML mode) |
145 | 0 | // for "application/xhtml+xml"? |
146 | 0 | if (aType != SupportedType::Text_xml && |
147 | 0 | aType != SupportedType::Application_xml && |
148 | 0 | aType != SupportedType::Application_xhtml_xml && |
149 | 0 | !svg) { |
150 | 0 | aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); |
151 | 0 | return nullptr; |
152 | 0 | } |
153 | 0 | |
154 | 0 | // Put the nsCOMPtr out here so we hold a ref to the stream as needed |
155 | 0 | nsCOMPtr<nsIInputStream> stream = aStream; |
156 | 0 | if (!NS_InputStreamIsBuffered(stream)) { |
157 | 0 | nsCOMPtr<nsIInputStream> bufferedStream; |
158 | 0 | nsresult rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), |
159 | 0 | stream.forget(), 4096); |
160 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
161 | 0 | aRv.Throw(rv); |
162 | 0 | return nullptr; |
163 | 0 | } |
164 | 0 | |
165 | 0 | stream = bufferedStream; |
166 | 0 | } |
167 | 0 |
|
168 | 0 | nsCOMPtr<nsIDocument> document = |
169 | 0 | SetUpDocument(svg ? DocumentFlavorSVG : DocumentFlavorLegacyGuess, aRv); |
170 | 0 | if (NS_WARN_IF(aRv.Failed())) { |
171 | 0 | return nullptr; |
172 | 0 | } |
173 | 0 | |
174 | 0 | // Create a fake channel |
175 | 0 | nsCOMPtr<nsIChannel> parserChannel; |
176 | 0 | NS_NewInputStreamChannel(getter_AddRefs(parserChannel), |
177 | 0 | mDocumentURI, |
178 | 0 | nullptr, // aStream |
179 | 0 | mPrincipal, |
180 | 0 | nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL, |
181 | 0 | nsIContentPolicy::TYPE_OTHER, |
182 | 0 | nsDependentCString(StringFromSupportedType(aType))); |
183 | 0 | if (NS_WARN_IF(!parserChannel)) { |
184 | 0 | aRv.Throw(NS_ERROR_UNEXPECTED); |
185 | 0 | return nullptr; |
186 | 0 | } |
187 | 0 | |
188 | 0 | if (!DOMStringIsNull(aCharset)) { |
189 | 0 | parserChannel->SetContentCharset(NS_ConvertUTF16toUTF8(aCharset)); |
190 | 0 | } |
191 | 0 |
|
192 | 0 | // Tell the document to start loading |
193 | 0 | nsCOMPtr<nsIStreamListener> listener; |
194 | 0 |
|
195 | 0 | // Keep the XULXBL state in sync with the HTML case |
196 | 0 | if (mForceEnableXULXBL) { |
197 | 0 | document->ForceEnableXULXBL(); |
198 | 0 | } |
199 | 0 |
|
200 | 0 | // Have to pass false for reset here, else the reset will remove |
201 | 0 | // our event listener. Should that listener addition move to later |
202 | 0 | // than this call? |
203 | 0 | nsresult rv = document->StartDocumentLoad(kLoadAsData, parserChannel, |
204 | 0 | nullptr, nullptr, |
205 | 0 | getter_AddRefs(listener), |
206 | 0 | false); |
207 | 0 |
|
208 | 0 | if (NS_FAILED(rv) || !listener) { |
209 | 0 | aRv.Throw(NS_ERROR_FAILURE); |
210 | 0 | return nullptr; |
211 | 0 | } |
212 | 0 | |
213 | 0 | // Now start pumping data to the listener |
214 | 0 | nsresult status; |
215 | 0 |
|
216 | 0 | rv = listener->OnStartRequest(parserChannel, nullptr); |
217 | 0 | if (NS_FAILED(rv)) |
218 | 0 | parserChannel->Cancel(rv); |
219 | 0 | parserChannel->GetStatus(&status); |
220 | 0 |
|
221 | 0 | if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(status)) { |
222 | 0 | rv = listener->OnDataAvailable(parserChannel, nullptr, stream, 0, |
223 | 0 | aContentLength); |
224 | 0 | if (NS_FAILED(rv)) |
225 | 0 | parserChannel->Cancel(rv); |
226 | 0 | parserChannel->GetStatus(&status); |
227 | 0 | } |
228 | 0 |
|
229 | 0 | rv = listener->OnStopRequest(parserChannel, nullptr, status); |
230 | 0 | // Failure returned from OnStopRequest does not affect the final status of |
231 | 0 | // the channel, so we do not need to call Cancel(rv) as we do above. |
232 | 0 |
|
233 | 0 | if (NS_FAILED(rv)) { |
234 | 0 | aRv.Throw(NS_ERROR_FAILURE); |
235 | 0 | return nullptr; |
236 | 0 | } |
237 | 0 | |
238 | 0 | return document.forget(); |
239 | 0 | } |
240 | | |
241 | | /*static */already_AddRefed<DOMParser> |
242 | | DOMParser::Constructor(const GlobalObject& aOwner, |
243 | | ErrorResult& rv) |
244 | 0 | { |
245 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
246 | 0 | nsCOMPtr<nsIPrincipal> docPrincipal = aOwner.GetSubjectPrincipal(); |
247 | 0 | nsCOMPtr<nsIURI> documentURI; |
248 | 0 | nsIURI* baseURI = nullptr; |
249 | 0 | if (nsContentUtils::IsSystemPrincipal(docPrincipal)) { |
250 | 0 | docPrincipal = NullPrincipal::CreateWithoutOriginAttributes(); |
251 | 0 | docPrincipal->GetURI(getter_AddRefs(documentURI)); |
252 | 0 | } else { |
253 | 0 | // Grab document and base URIs off the window our constructor was |
254 | 0 | // called on. Error out if anything untoward happens. |
255 | 0 | nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aOwner.GetAsSupports()); |
256 | 0 | if (!window) { |
257 | 0 | rv.Throw(NS_ERROR_UNEXPECTED); |
258 | 0 | return nullptr; |
259 | 0 | } |
260 | 0 | |
261 | 0 | baseURI = window->GetDocBaseURI(); |
262 | 0 | documentURI = window->GetDocumentURI(); |
263 | 0 | } |
264 | 0 |
|
265 | 0 | if (!documentURI) { |
266 | 0 | rv.Throw(NS_ERROR_UNEXPECTED); |
267 | 0 | return nullptr; |
268 | 0 | } |
269 | 0 | |
270 | 0 | nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aOwner.GetAsSupports()); |
271 | 0 | MOZ_ASSERT(global); |
272 | 0 | RefPtr<DOMParser> domParser = new DOMParser(global, docPrincipal, |
273 | 0 | documentURI, baseURI); |
274 | 0 | return domParser.forget(); |
275 | 0 | } |
276 | | |
277 | | // static |
278 | | already_AddRefed<DOMParser> |
279 | | DOMParser::CreateWithoutGlobal(ErrorResult& aRv) |
280 | 0 | { |
281 | 0 | nsCOMPtr<nsIPrincipal> docPrincipal = |
282 | 0 | NullPrincipal::CreateWithoutOriginAttributes(); |
283 | 0 | nsCOMPtr<nsIURI> documentURI; |
284 | 0 | docPrincipal->GetURI(getter_AddRefs(documentURI)); |
285 | 0 |
|
286 | 0 | if (!documentURI) { |
287 | 0 | aRv.Throw(NS_ERROR_UNEXPECTED); |
288 | 0 | return nullptr; |
289 | 0 | } |
290 | 0 | |
291 | 0 | RefPtr<DOMParser> domParser = new DOMParser(nullptr, docPrincipal, |
292 | 0 | documentURI, nullptr); |
293 | 0 | return domParser.forget(); |
294 | 0 | } |
295 | | |
296 | | already_AddRefed<nsIDocument> |
297 | | DOMParser::SetUpDocument(DocumentFlavor aFlavor, ErrorResult& aRv) |
298 | 0 | { |
299 | 0 | // We should really just use mOwner here, but nsDocument gets confused |
300 | 0 | // if we pass it a scriptHandlingObject that doesn't QI to |
301 | 0 | // nsIScriptGlobalObject, and test_isequalnode.js (an xpcshell test without |
302 | 0 | // a window global) breaks. The correct solution is just to wean nsDocument |
303 | 0 | // off of nsIScriptGlobalObject, but that's a yak to shave another day. |
304 | 0 | nsCOMPtr<nsIScriptGlobalObject> scriptHandlingObject = |
305 | 0 | do_QueryInterface(mOwner); |
306 | 0 |
|
307 | 0 | // Try to inherit a style backend. |
308 | 0 | NS_ASSERTION(mPrincipal, "Must have principal by now"); |
309 | 0 | NS_ASSERTION(mDocumentURI, "Must have document URI by now"); |
310 | 0 |
|
311 | 0 | nsCOMPtr<nsIDocument> doc; |
312 | 0 | nsresult rv = NS_NewDOMDocument(getter_AddRefs(doc), EmptyString(), EmptyString(), |
313 | 0 | nullptr, mDocumentURI, mBaseURI, mPrincipal, |
314 | 0 | true, scriptHandlingObject, aFlavor); |
315 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
316 | 0 | aRv.Throw(rv); |
317 | 0 | return nullptr; |
318 | 0 | } |
319 | 0 | |
320 | 0 | return doc.forget(); |
321 | 0 | } |