|
1 /* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim: set ts=2 sw=2 et tw=78: */ |
|
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 #include "nsCOMPtr.h" |
|
7 #include "nsContentDLF.h" |
|
8 #include "nsDocShell.h" |
|
9 #include "nsGenericHTMLElement.h" |
|
10 #include "nsGkAtoms.h" |
|
11 #include "nsIComponentManager.h" |
|
12 #include "nsIComponentRegistrar.h" |
|
13 #include "nsIContentViewer.h" |
|
14 #include "nsICategoryManager.h" |
|
15 #include "nsIDocumentLoaderFactory.h" |
|
16 #include "nsIDocument.h" |
|
17 #include "nsIURL.h" |
|
18 #include "nsNodeInfo.h" |
|
19 #include "nsNodeInfoManager.h" |
|
20 #include "nsIScriptSecurityManager.h" |
|
21 #include "nsString.h" |
|
22 #include "nsContentCID.h" |
|
23 #include "prprf.h" |
|
24 #include "nsNetUtil.h" |
|
25 #include "nsCRT.h" |
|
26 #include "nsIViewSourceChannel.h" |
|
27 #include "nsContentUtils.h" |
|
28 #include "imgLoader.h" |
|
29 #include "nsCharsetSource.h" |
|
30 #include "nsMimeTypes.h" |
|
31 #include "DecoderTraits.h" |
|
32 |
|
33 |
|
34 // plugins |
|
35 #include "nsIPluginHost.h" |
|
36 #include "nsPluginHost.h" |
|
37 static NS_DEFINE_CID(kPluginDocumentCID, NS_PLUGINDOCUMENT_CID); |
|
38 |
|
39 // Factory code for creating variations on html documents |
|
40 |
|
41 #undef NOISY_REGISTRY |
|
42 |
|
43 static NS_DEFINE_IID(kHTMLDocumentCID, NS_HTMLDOCUMENT_CID); |
|
44 static NS_DEFINE_IID(kXMLDocumentCID, NS_XMLDOCUMENT_CID); |
|
45 static NS_DEFINE_IID(kSVGDocumentCID, NS_SVGDOCUMENT_CID); |
|
46 static NS_DEFINE_IID(kVideoDocumentCID, NS_VIDEODOCUMENT_CID); |
|
47 static NS_DEFINE_IID(kImageDocumentCID, NS_IMAGEDOCUMENT_CID); |
|
48 static NS_DEFINE_IID(kXULDocumentCID, NS_XULDOCUMENT_CID); |
|
49 |
|
50 already_AddRefed<nsIContentViewer> NS_NewContentViewer(); |
|
51 |
|
52 // XXXbz if you change the MIME types here, be sure to update |
|
53 // nsIParser.h and DetermineParseMode in nsParser.cpp and |
|
54 // nsHTMLDocument::StartDocumentLoad accordingly. |
|
55 static const char* const gHTMLTypes[] = { |
|
56 TEXT_HTML, |
|
57 TEXT_PLAIN, |
|
58 TEXT_CACHE_MANIFEST, |
|
59 TEXT_CSS, |
|
60 TEXT_JAVASCRIPT, |
|
61 TEXT_ECMASCRIPT, |
|
62 APPLICATION_JAVASCRIPT, |
|
63 APPLICATION_ECMASCRIPT, |
|
64 APPLICATION_XJAVASCRIPT, |
|
65 APPLICATION_JSON, |
|
66 VIEWSOURCE_CONTENT_TYPE, |
|
67 APPLICATION_XHTML_XML, |
|
68 0 |
|
69 }; |
|
70 |
|
71 static const char* const gXMLTypes[] = { |
|
72 TEXT_XML, |
|
73 APPLICATION_XML, |
|
74 APPLICATION_MATHML_XML, |
|
75 APPLICATION_RDF_XML, |
|
76 TEXT_RDF, |
|
77 0 |
|
78 }; |
|
79 |
|
80 static const char* const gSVGTypes[] = { |
|
81 IMAGE_SVG_XML, |
|
82 0 |
|
83 }; |
|
84 |
|
85 static const char* const gXULTypes[] = { |
|
86 TEXT_XUL, |
|
87 APPLICATION_CACHED_XUL, |
|
88 0 |
|
89 }; |
|
90 |
|
91 nsresult |
|
92 NS_NewContentDocumentLoaderFactory(nsIDocumentLoaderFactory** aResult) |
|
93 { |
|
94 NS_PRECONDITION(aResult, "null OUT ptr"); |
|
95 if (!aResult) { |
|
96 return NS_ERROR_NULL_POINTER; |
|
97 } |
|
98 nsContentDLF* it = new nsContentDLF(); |
|
99 if (!it) { |
|
100 return NS_ERROR_OUT_OF_MEMORY; |
|
101 } |
|
102 |
|
103 return CallQueryInterface(it, aResult); |
|
104 } |
|
105 |
|
106 nsContentDLF::nsContentDLF() |
|
107 { |
|
108 } |
|
109 |
|
110 nsContentDLF::~nsContentDLF() |
|
111 { |
|
112 } |
|
113 |
|
114 NS_IMPL_ISUPPORTS(nsContentDLF, |
|
115 nsIDocumentLoaderFactory) |
|
116 |
|
117 bool |
|
118 MayUseXULXBL(nsIChannel* aChannel) |
|
119 { |
|
120 nsIScriptSecurityManager *securityManager = |
|
121 nsContentUtils::GetSecurityManager(); |
|
122 if (!securityManager) { |
|
123 return false; |
|
124 } |
|
125 |
|
126 nsCOMPtr<nsIPrincipal> principal; |
|
127 securityManager->GetChannelPrincipal(aChannel, getter_AddRefs(principal)); |
|
128 NS_ENSURE_TRUE(principal, false); |
|
129 |
|
130 return nsContentUtils::AllowXULXBLForPrincipal(principal); |
|
131 } |
|
132 |
|
133 NS_IMETHODIMP |
|
134 nsContentDLF::CreateInstance(const char* aCommand, |
|
135 nsIChannel* aChannel, |
|
136 nsILoadGroup* aLoadGroup, |
|
137 const char* aContentType, |
|
138 nsIDocShell* aContainer, |
|
139 nsISupports* aExtraInfo, |
|
140 nsIStreamListener** aDocListener, |
|
141 nsIContentViewer** aDocViewer) |
|
142 { |
|
143 // Declare "type" here. This is because although the variable itself only |
|
144 // needs limited scope, we need to use the raw string memory -- as returned |
|
145 // by "type.get()" farther down in the function. |
|
146 nsAutoCString type; |
|
147 |
|
148 // Are we viewing source? |
|
149 nsCOMPtr<nsIViewSourceChannel> viewSourceChannel = do_QueryInterface(aChannel); |
|
150 if (viewSourceChannel) |
|
151 { |
|
152 aCommand = "view-source"; |
|
153 |
|
154 // The parser freaks out when it sees the content-type that a |
|
155 // view-source channel normally returns. Get the actual content |
|
156 // type of the data. If it's known, use it; otherwise use |
|
157 // text/plain. |
|
158 viewSourceChannel->GetOriginalContentType(type); |
|
159 bool knownType = false; |
|
160 int32_t typeIndex; |
|
161 for (typeIndex = 0; gHTMLTypes[typeIndex] && !knownType; ++typeIndex) { |
|
162 if (type.Equals(gHTMLTypes[typeIndex]) && |
|
163 !type.EqualsLiteral(VIEWSOURCE_CONTENT_TYPE)) { |
|
164 knownType = true; |
|
165 } |
|
166 } |
|
167 |
|
168 for (typeIndex = 0; gXMLTypes[typeIndex] && !knownType; ++typeIndex) { |
|
169 if (type.Equals(gXMLTypes[typeIndex])) { |
|
170 knownType = true; |
|
171 } |
|
172 } |
|
173 |
|
174 for (typeIndex = 0; gSVGTypes[typeIndex] && !knownType; ++typeIndex) { |
|
175 if (type.Equals(gSVGTypes[typeIndex])) { |
|
176 knownType = true; |
|
177 } |
|
178 } |
|
179 |
|
180 for (typeIndex = 0; gXULTypes[typeIndex] && !knownType; ++typeIndex) { |
|
181 if (type.Equals(gXULTypes[typeIndex])) { |
|
182 knownType = true; |
|
183 } |
|
184 } |
|
185 |
|
186 if (knownType) { |
|
187 viewSourceChannel->SetContentType(type); |
|
188 } else if (IsImageContentType(type.get())) { |
|
189 // If it's an image, we want to display it the same way we normally would. |
|
190 // Also note the lifetime of "type" allows us to safely use "get()" here. |
|
191 aContentType = type.get(); |
|
192 } else { |
|
193 viewSourceChannel->SetContentType(NS_LITERAL_CSTRING(TEXT_PLAIN)); |
|
194 } |
|
195 } else if (0 == PL_strcmp(VIEWSOURCE_CONTENT_TYPE, aContentType)) { |
|
196 aChannel->SetContentType(NS_LITERAL_CSTRING(TEXT_PLAIN)); |
|
197 aContentType = TEXT_PLAIN; |
|
198 } |
|
199 // Try html |
|
200 int typeIndex=0; |
|
201 while(gHTMLTypes[typeIndex]) { |
|
202 if (0 == PL_strcmp(gHTMLTypes[typeIndex++], aContentType)) { |
|
203 return CreateDocument(aCommand, |
|
204 aChannel, aLoadGroup, |
|
205 aContainer, kHTMLDocumentCID, |
|
206 aDocListener, aDocViewer); |
|
207 } |
|
208 } |
|
209 |
|
210 // Try XML |
|
211 typeIndex = 0; |
|
212 while(gXMLTypes[typeIndex]) { |
|
213 if (0== PL_strcmp(gXMLTypes[typeIndex++], aContentType)) { |
|
214 return CreateDocument(aCommand, |
|
215 aChannel, aLoadGroup, |
|
216 aContainer, kXMLDocumentCID, |
|
217 aDocListener, aDocViewer); |
|
218 } |
|
219 } |
|
220 |
|
221 // Try SVG |
|
222 typeIndex = 0; |
|
223 while(gSVGTypes[typeIndex]) { |
|
224 if (!PL_strcmp(gSVGTypes[typeIndex++], aContentType)) { |
|
225 return CreateDocument(aCommand, |
|
226 aChannel, aLoadGroup, |
|
227 aContainer, kSVGDocumentCID, |
|
228 aDocListener, aDocViewer); |
|
229 } |
|
230 } |
|
231 |
|
232 // Try XUL |
|
233 typeIndex = 0; |
|
234 while (gXULTypes[typeIndex]) { |
|
235 if (0 == PL_strcmp(gXULTypes[typeIndex++], aContentType)) { |
|
236 if (!MayUseXULXBL(aChannel)) { |
|
237 return NS_ERROR_REMOTE_XUL; |
|
238 } |
|
239 |
|
240 return CreateXULDocument(aCommand, |
|
241 aChannel, aLoadGroup, |
|
242 aContentType, aContainer, |
|
243 aExtraInfo, aDocListener, aDocViewer); |
|
244 } |
|
245 } |
|
246 |
|
247 if (mozilla::DecoderTraits::ShouldHandleMediaType(aContentType)) { |
|
248 return CreateDocument(aCommand, |
|
249 aChannel, aLoadGroup, |
|
250 aContainer, kVideoDocumentCID, |
|
251 aDocListener, aDocViewer); |
|
252 } |
|
253 |
|
254 // Try image types |
|
255 if (IsImageContentType(aContentType)) { |
|
256 return CreateDocument(aCommand, |
|
257 aChannel, aLoadGroup, |
|
258 aContainer, kImageDocumentCID, |
|
259 aDocListener, aDocViewer); |
|
260 } |
|
261 |
|
262 nsCOMPtr<nsIPluginHost> pluginHostCOM(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID)); |
|
263 nsPluginHost *pluginHost = static_cast<nsPluginHost*>(pluginHostCOM.get()); |
|
264 if(pluginHost && |
|
265 pluginHost->PluginExistsForType(aContentType)) { |
|
266 return CreateDocument(aCommand, |
|
267 aChannel, aLoadGroup, |
|
268 aContainer, kPluginDocumentCID, |
|
269 aDocListener, aDocViewer); |
|
270 } |
|
271 |
|
272 // If we get here, then we weren't able to create anything. Sorry! |
|
273 return NS_ERROR_FAILURE; |
|
274 } |
|
275 |
|
276 |
|
277 NS_IMETHODIMP |
|
278 nsContentDLF::CreateInstanceForDocument(nsISupports* aContainer, |
|
279 nsIDocument* aDocument, |
|
280 const char *aCommand, |
|
281 nsIContentViewer** aContentViewer) |
|
282 { |
|
283 MOZ_ASSERT(aDocument); |
|
284 |
|
285 nsCOMPtr<nsIContentViewer> contentViewer = NS_NewContentViewer(); |
|
286 |
|
287 // Bind the document to the Content Viewer |
|
288 contentViewer->LoadStart(aDocument); |
|
289 contentViewer.forget(aContentViewer); |
|
290 return NS_OK; |
|
291 } |
|
292 |
|
293 NS_IMETHODIMP |
|
294 nsContentDLF::CreateBlankDocument(nsILoadGroup *aLoadGroup, |
|
295 nsIPrincipal* aPrincipal, |
|
296 nsIDocument **aDocument) |
|
297 { |
|
298 *aDocument = nullptr; |
|
299 |
|
300 nsresult rv = NS_ERROR_FAILURE; |
|
301 |
|
302 // create a new blank HTML document |
|
303 nsCOMPtr<nsIDocument> blankDoc(do_CreateInstance(kHTMLDocumentCID)); |
|
304 |
|
305 if (blankDoc) { |
|
306 // initialize |
|
307 nsCOMPtr<nsIURI> uri; |
|
308 NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("about:blank")); |
|
309 if (uri) { |
|
310 blankDoc->ResetToURI(uri, aLoadGroup, aPrincipal); |
|
311 rv = NS_OK; |
|
312 } |
|
313 } |
|
314 |
|
315 // add some simple content structure |
|
316 if (NS_SUCCEEDED(rv)) { |
|
317 rv = NS_ERROR_FAILURE; |
|
318 |
|
319 nsNodeInfoManager *nim = blankDoc->NodeInfoManager(); |
|
320 |
|
321 nsCOMPtr<nsINodeInfo> htmlNodeInfo; |
|
322 |
|
323 // generate an html html element |
|
324 htmlNodeInfo = nim->GetNodeInfo(nsGkAtoms::html, 0, kNameSpaceID_XHTML, |
|
325 nsIDOMNode::ELEMENT_NODE); |
|
326 nsCOMPtr<nsIContent> htmlElement = |
|
327 NS_NewHTMLHtmlElement(htmlNodeInfo.forget()); |
|
328 |
|
329 // generate an html head element |
|
330 htmlNodeInfo = nim->GetNodeInfo(nsGkAtoms::head, 0, kNameSpaceID_XHTML, |
|
331 nsIDOMNode::ELEMENT_NODE); |
|
332 nsCOMPtr<nsIContent> headElement = |
|
333 NS_NewHTMLHeadElement(htmlNodeInfo.forget()); |
|
334 |
|
335 // generate an html body elemment |
|
336 htmlNodeInfo = nim->GetNodeInfo(nsGkAtoms::body, 0, kNameSpaceID_XHTML, |
|
337 nsIDOMNode::ELEMENT_NODE); |
|
338 nsCOMPtr<nsIContent> bodyElement = |
|
339 NS_NewHTMLBodyElement(htmlNodeInfo.forget()); |
|
340 |
|
341 // blat in the structure |
|
342 if (htmlElement && headElement && bodyElement) { |
|
343 NS_ASSERTION(blankDoc->GetChildCount() == 0, |
|
344 "Shouldn't have children"); |
|
345 rv = blankDoc->AppendChildTo(htmlElement, false); |
|
346 if (NS_SUCCEEDED(rv)) { |
|
347 rv = htmlElement->AppendChildTo(headElement, false); |
|
348 |
|
349 if (NS_SUCCEEDED(rv)) { |
|
350 // XXXbz Why not notifying here? |
|
351 htmlElement->AppendChildTo(bodyElement, false); |
|
352 } |
|
353 } |
|
354 } |
|
355 } |
|
356 |
|
357 // add a nice bow |
|
358 if (NS_SUCCEEDED(rv)) { |
|
359 blankDoc->SetDocumentCharacterSetSource(kCharsetFromDocTypeDefault); |
|
360 blankDoc->SetDocumentCharacterSet(NS_LITERAL_CSTRING("UTF-8")); |
|
361 |
|
362 *aDocument = blankDoc; |
|
363 NS_ADDREF(*aDocument); |
|
364 } |
|
365 return rv; |
|
366 } |
|
367 |
|
368 |
|
369 nsresult |
|
370 nsContentDLF::CreateDocument(const char* aCommand, |
|
371 nsIChannel* aChannel, |
|
372 nsILoadGroup* aLoadGroup, |
|
373 nsIDocShell* aContainer, |
|
374 const nsCID& aDocumentCID, |
|
375 nsIStreamListener** aDocListener, |
|
376 nsIContentViewer** aContentViewer) |
|
377 { |
|
378 nsresult rv = NS_ERROR_FAILURE; |
|
379 |
|
380 nsCOMPtr<nsIURI> aURL; |
|
381 rv = aChannel->GetURI(getter_AddRefs(aURL)); |
|
382 if (NS_FAILED(rv)) return rv; |
|
383 |
|
384 #ifdef NOISY_CREATE_DOC |
|
385 if (nullptr != aURL) { |
|
386 nsAutoString tmp; |
|
387 aURL->ToString(tmp); |
|
388 fputs(NS_LossyConvertUTF16toASCII(tmp).get(), stdout); |
|
389 printf(": creating document\n"); |
|
390 } |
|
391 #endif |
|
392 |
|
393 // Create the document |
|
394 nsCOMPtr<nsIDocument> doc = do_CreateInstance(aDocumentCID, &rv); |
|
395 NS_ENSURE_SUCCESS(rv, rv); |
|
396 |
|
397 // Create the content viewer XXX: could reuse content viewer here! |
|
398 nsCOMPtr<nsIContentViewer> contentViewer = NS_NewContentViewer(); |
|
399 |
|
400 doc->SetContainer(static_cast<nsDocShell*>(aContainer)); |
|
401 |
|
402 // Initialize the document to begin loading the data. An |
|
403 // nsIStreamListener connected to the parser is returned in |
|
404 // aDocListener. |
|
405 rv = doc->StartDocumentLoad(aCommand, aChannel, aLoadGroup, aContainer, aDocListener, true); |
|
406 NS_ENSURE_SUCCESS(rv, rv); |
|
407 |
|
408 // Bind the document to the Content Viewer |
|
409 contentViewer->LoadStart(doc); |
|
410 contentViewer.forget(aContentViewer); |
|
411 return NS_OK; |
|
412 } |
|
413 |
|
414 nsresult |
|
415 nsContentDLF::CreateXULDocument(const char* aCommand, |
|
416 nsIChannel* aChannel, |
|
417 nsILoadGroup* aLoadGroup, |
|
418 const char* aContentType, |
|
419 nsIDocShell* aContainer, |
|
420 nsISupports* aExtraInfo, |
|
421 nsIStreamListener** aDocListener, |
|
422 nsIContentViewer** aContentViewer) |
|
423 { |
|
424 nsresult rv; |
|
425 nsCOMPtr<nsIDocument> doc = do_CreateInstance(kXULDocumentCID, &rv); |
|
426 if (NS_FAILED(rv)) return rv; |
|
427 |
|
428 nsCOMPtr<nsIContentViewer> contentViewer = NS_NewContentViewer(); |
|
429 |
|
430 nsCOMPtr<nsIURI> aURL; |
|
431 rv = aChannel->GetURI(getter_AddRefs(aURL)); |
|
432 if (NS_FAILED(rv)) return rv; |
|
433 |
|
434 /* |
|
435 * Initialize the document to begin loading the data... |
|
436 * |
|
437 * An nsIStreamListener connected to the parser is returned in |
|
438 * aDocListener. |
|
439 */ |
|
440 |
|
441 doc->SetContainer(static_cast<nsDocShell*>(aContainer)); |
|
442 |
|
443 rv = doc->StartDocumentLoad(aCommand, aChannel, aLoadGroup, aContainer, aDocListener, true); |
|
444 if (NS_FAILED(rv)) return rv; |
|
445 |
|
446 /* |
|
447 * Bind the document to the Content Viewer... |
|
448 */ |
|
449 contentViewer->LoadStart(doc); |
|
450 contentViewer.forget(aContentViewer); |
|
451 return NS_OK; |
|
452 } |
|
453 |
|
454 bool nsContentDLF::IsImageContentType(const char* aContentType) { |
|
455 return imgLoader::SupportImageWithMimeType(aContentType); |
|
456 } |