michael@0: /* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 sw=2 et tw=78: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: #include "nsCOMPtr.h" michael@0: #include "nsContentDLF.h" michael@0: #include "nsDocShell.h" michael@0: #include "nsGenericHTMLElement.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsIComponentManager.h" michael@0: #include "nsIComponentRegistrar.h" michael@0: #include "nsIContentViewer.h" michael@0: #include "nsICategoryManager.h" michael@0: #include "nsIDocumentLoaderFactory.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIURL.h" michael@0: #include "nsNodeInfo.h" michael@0: #include "nsNodeInfoManager.h" michael@0: #include "nsIScriptSecurityManager.h" michael@0: #include "nsString.h" michael@0: #include "nsContentCID.h" michael@0: #include "prprf.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsCRT.h" michael@0: #include "nsIViewSourceChannel.h" michael@0: #include "nsContentUtils.h" michael@0: #include "imgLoader.h" michael@0: #include "nsCharsetSource.h" michael@0: #include "nsMimeTypes.h" michael@0: #include "DecoderTraits.h" michael@0: michael@0: michael@0: // plugins michael@0: #include "nsIPluginHost.h" michael@0: #include "nsPluginHost.h" michael@0: static NS_DEFINE_CID(kPluginDocumentCID, NS_PLUGINDOCUMENT_CID); michael@0: michael@0: // Factory code for creating variations on html documents michael@0: michael@0: #undef NOISY_REGISTRY michael@0: michael@0: static NS_DEFINE_IID(kHTMLDocumentCID, NS_HTMLDOCUMENT_CID); michael@0: static NS_DEFINE_IID(kXMLDocumentCID, NS_XMLDOCUMENT_CID); michael@0: static NS_DEFINE_IID(kSVGDocumentCID, NS_SVGDOCUMENT_CID); michael@0: static NS_DEFINE_IID(kVideoDocumentCID, NS_VIDEODOCUMENT_CID); michael@0: static NS_DEFINE_IID(kImageDocumentCID, NS_IMAGEDOCUMENT_CID); michael@0: static NS_DEFINE_IID(kXULDocumentCID, NS_XULDOCUMENT_CID); michael@0: michael@0: already_AddRefed NS_NewContentViewer(); michael@0: michael@0: // XXXbz if you change the MIME types here, be sure to update michael@0: // nsIParser.h and DetermineParseMode in nsParser.cpp and michael@0: // nsHTMLDocument::StartDocumentLoad accordingly. michael@0: static const char* const gHTMLTypes[] = { michael@0: TEXT_HTML, michael@0: TEXT_PLAIN, michael@0: TEXT_CACHE_MANIFEST, michael@0: TEXT_CSS, michael@0: TEXT_JAVASCRIPT, michael@0: TEXT_ECMASCRIPT, michael@0: APPLICATION_JAVASCRIPT, michael@0: APPLICATION_ECMASCRIPT, michael@0: APPLICATION_XJAVASCRIPT, michael@0: APPLICATION_JSON, michael@0: VIEWSOURCE_CONTENT_TYPE, michael@0: APPLICATION_XHTML_XML, michael@0: 0 michael@0: }; michael@0: michael@0: static const char* const gXMLTypes[] = { michael@0: TEXT_XML, michael@0: APPLICATION_XML, michael@0: APPLICATION_MATHML_XML, michael@0: APPLICATION_RDF_XML, michael@0: TEXT_RDF, michael@0: 0 michael@0: }; michael@0: michael@0: static const char* const gSVGTypes[] = { michael@0: IMAGE_SVG_XML, michael@0: 0 michael@0: }; michael@0: michael@0: static const char* const gXULTypes[] = { michael@0: TEXT_XUL, michael@0: APPLICATION_CACHED_XUL, michael@0: 0 michael@0: }; michael@0: michael@0: nsresult michael@0: NS_NewContentDocumentLoaderFactory(nsIDocumentLoaderFactory** aResult) michael@0: { michael@0: NS_PRECONDITION(aResult, "null OUT ptr"); michael@0: if (!aResult) { michael@0: return NS_ERROR_NULL_POINTER; michael@0: } michael@0: nsContentDLF* it = new nsContentDLF(); michael@0: if (!it) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: return CallQueryInterface(it, aResult); michael@0: } michael@0: michael@0: nsContentDLF::nsContentDLF() michael@0: { michael@0: } michael@0: michael@0: nsContentDLF::~nsContentDLF() michael@0: { michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsContentDLF, michael@0: nsIDocumentLoaderFactory) michael@0: michael@0: bool michael@0: MayUseXULXBL(nsIChannel* aChannel) michael@0: { michael@0: nsIScriptSecurityManager *securityManager = michael@0: nsContentUtils::GetSecurityManager(); michael@0: if (!securityManager) { michael@0: return false; michael@0: } michael@0: michael@0: nsCOMPtr principal; michael@0: securityManager->GetChannelPrincipal(aChannel, getter_AddRefs(principal)); michael@0: NS_ENSURE_TRUE(principal, false); michael@0: michael@0: return nsContentUtils::AllowXULXBLForPrincipal(principal); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsContentDLF::CreateInstance(const char* aCommand, michael@0: nsIChannel* aChannel, michael@0: nsILoadGroup* aLoadGroup, michael@0: const char* aContentType, michael@0: nsIDocShell* aContainer, michael@0: nsISupports* aExtraInfo, michael@0: nsIStreamListener** aDocListener, michael@0: nsIContentViewer** aDocViewer) michael@0: { michael@0: // Declare "type" here. This is because although the variable itself only michael@0: // needs limited scope, we need to use the raw string memory -- as returned michael@0: // by "type.get()" farther down in the function. michael@0: nsAutoCString type; michael@0: michael@0: // Are we viewing source? michael@0: nsCOMPtr viewSourceChannel = do_QueryInterface(aChannel); michael@0: if (viewSourceChannel) michael@0: { michael@0: aCommand = "view-source"; michael@0: michael@0: // The parser freaks out when it sees the content-type that a michael@0: // view-source channel normally returns. Get the actual content michael@0: // type of the data. If it's known, use it; otherwise use michael@0: // text/plain. michael@0: viewSourceChannel->GetOriginalContentType(type); michael@0: bool knownType = false; michael@0: int32_t typeIndex; michael@0: for (typeIndex = 0; gHTMLTypes[typeIndex] && !knownType; ++typeIndex) { michael@0: if (type.Equals(gHTMLTypes[typeIndex]) && michael@0: !type.EqualsLiteral(VIEWSOURCE_CONTENT_TYPE)) { michael@0: knownType = true; michael@0: } michael@0: } michael@0: michael@0: for (typeIndex = 0; gXMLTypes[typeIndex] && !knownType; ++typeIndex) { michael@0: if (type.Equals(gXMLTypes[typeIndex])) { michael@0: knownType = true; michael@0: } michael@0: } michael@0: michael@0: for (typeIndex = 0; gSVGTypes[typeIndex] && !knownType; ++typeIndex) { michael@0: if (type.Equals(gSVGTypes[typeIndex])) { michael@0: knownType = true; michael@0: } michael@0: } michael@0: michael@0: for (typeIndex = 0; gXULTypes[typeIndex] && !knownType; ++typeIndex) { michael@0: if (type.Equals(gXULTypes[typeIndex])) { michael@0: knownType = true; michael@0: } michael@0: } michael@0: michael@0: if (knownType) { michael@0: viewSourceChannel->SetContentType(type); michael@0: } else if (IsImageContentType(type.get())) { michael@0: // If it's an image, we want to display it the same way we normally would. michael@0: // Also note the lifetime of "type" allows us to safely use "get()" here. michael@0: aContentType = type.get(); michael@0: } else { michael@0: viewSourceChannel->SetContentType(NS_LITERAL_CSTRING(TEXT_PLAIN)); michael@0: } michael@0: } else if (0 == PL_strcmp(VIEWSOURCE_CONTENT_TYPE, aContentType)) { michael@0: aChannel->SetContentType(NS_LITERAL_CSTRING(TEXT_PLAIN)); michael@0: aContentType = TEXT_PLAIN; michael@0: } michael@0: // Try html michael@0: int typeIndex=0; michael@0: while(gHTMLTypes[typeIndex]) { michael@0: if (0 == PL_strcmp(gHTMLTypes[typeIndex++], aContentType)) { michael@0: return CreateDocument(aCommand, michael@0: aChannel, aLoadGroup, michael@0: aContainer, kHTMLDocumentCID, michael@0: aDocListener, aDocViewer); michael@0: } michael@0: } michael@0: michael@0: // Try XML michael@0: typeIndex = 0; michael@0: while(gXMLTypes[typeIndex]) { michael@0: if (0== PL_strcmp(gXMLTypes[typeIndex++], aContentType)) { michael@0: return CreateDocument(aCommand, michael@0: aChannel, aLoadGroup, michael@0: aContainer, kXMLDocumentCID, michael@0: aDocListener, aDocViewer); michael@0: } michael@0: } michael@0: michael@0: // Try SVG michael@0: typeIndex = 0; michael@0: while(gSVGTypes[typeIndex]) { michael@0: if (!PL_strcmp(gSVGTypes[typeIndex++], aContentType)) { michael@0: return CreateDocument(aCommand, michael@0: aChannel, aLoadGroup, michael@0: aContainer, kSVGDocumentCID, michael@0: aDocListener, aDocViewer); michael@0: } michael@0: } michael@0: michael@0: // Try XUL michael@0: typeIndex = 0; michael@0: while (gXULTypes[typeIndex]) { michael@0: if (0 == PL_strcmp(gXULTypes[typeIndex++], aContentType)) { michael@0: if (!MayUseXULXBL(aChannel)) { michael@0: return NS_ERROR_REMOTE_XUL; michael@0: } michael@0: michael@0: return CreateXULDocument(aCommand, michael@0: aChannel, aLoadGroup, michael@0: aContentType, aContainer, michael@0: aExtraInfo, aDocListener, aDocViewer); michael@0: } michael@0: } michael@0: michael@0: if (mozilla::DecoderTraits::ShouldHandleMediaType(aContentType)) { michael@0: return CreateDocument(aCommand, michael@0: aChannel, aLoadGroup, michael@0: aContainer, kVideoDocumentCID, michael@0: aDocListener, aDocViewer); michael@0: } michael@0: michael@0: // Try image types michael@0: if (IsImageContentType(aContentType)) { michael@0: return CreateDocument(aCommand, michael@0: aChannel, aLoadGroup, michael@0: aContainer, kImageDocumentCID, michael@0: aDocListener, aDocViewer); michael@0: } michael@0: michael@0: nsCOMPtr pluginHostCOM(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID)); michael@0: nsPluginHost *pluginHost = static_cast(pluginHostCOM.get()); michael@0: if(pluginHost && michael@0: pluginHost->PluginExistsForType(aContentType)) { michael@0: return CreateDocument(aCommand, michael@0: aChannel, aLoadGroup, michael@0: aContainer, kPluginDocumentCID, michael@0: aDocListener, aDocViewer); michael@0: } michael@0: michael@0: // If we get here, then we weren't able to create anything. Sorry! michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsContentDLF::CreateInstanceForDocument(nsISupports* aContainer, michael@0: nsIDocument* aDocument, michael@0: const char *aCommand, michael@0: nsIContentViewer** aContentViewer) michael@0: { michael@0: MOZ_ASSERT(aDocument); michael@0: michael@0: nsCOMPtr contentViewer = NS_NewContentViewer(); michael@0: michael@0: // Bind the document to the Content Viewer michael@0: contentViewer->LoadStart(aDocument); michael@0: contentViewer.forget(aContentViewer); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsContentDLF::CreateBlankDocument(nsILoadGroup *aLoadGroup, michael@0: nsIPrincipal* aPrincipal, michael@0: nsIDocument **aDocument) michael@0: { michael@0: *aDocument = nullptr; michael@0: michael@0: nsresult rv = NS_ERROR_FAILURE; michael@0: michael@0: // create a new blank HTML document michael@0: nsCOMPtr blankDoc(do_CreateInstance(kHTMLDocumentCID)); michael@0: michael@0: if (blankDoc) { michael@0: // initialize michael@0: nsCOMPtr uri; michael@0: NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("about:blank")); michael@0: if (uri) { michael@0: blankDoc->ResetToURI(uri, aLoadGroup, aPrincipal); michael@0: rv = NS_OK; michael@0: } michael@0: } michael@0: michael@0: // add some simple content structure michael@0: if (NS_SUCCEEDED(rv)) { michael@0: rv = NS_ERROR_FAILURE; michael@0: michael@0: nsNodeInfoManager *nim = blankDoc->NodeInfoManager(); michael@0: michael@0: nsCOMPtr htmlNodeInfo; michael@0: michael@0: // generate an html html element michael@0: htmlNodeInfo = nim->GetNodeInfo(nsGkAtoms::html, 0, kNameSpaceID_XHTML, michael@0: nsIDOMNode::ELEMENT_NODE); michael@0: nsCOMPtr htmlElement = michael@0: NS_NewHTMLHtmlElement(htmlNodeInfo.forget()); michael@0: michael@0: // generate an html head element michael@0: htmlNodeInfo = nim->GetNodeInfo(nsGkAtoms::head, 0, kNameSpaceID_XHTML, michael@0: nsIDOMNode::ELEMENT_NODE); michael@0: nsCOMPtr headElement = michael@0: NS_NewHTMLHeadElement(htmlNodeInfo.forget()); michael@0: michael@0: // generate an html body elemment michael@0: htmlNodeInfo = nim->GetNodeInfo(nsGkAtoms::body, 0, kNameSpaceID_XHTML, michael@0: nsIDOMNode::ELEMENT_NODE); michael@0: nsCOMPtr bodyElement = michael@0: NS_NewHTMLBodyElement(htmlNodeInfo.forget()); michael@0: michael@0: // blat in the structure michael@0: if (htmlElement && headElement && bodyElement) { michael@0: NS_ASSERTION(blankDoc->GetChildCount() == 0, michael@0: "Shouldn't have children"); michael@0: rv = blankDoc->AppendChildTo(htmlElement, false); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: rv = htmlElement->AppendChildTo(headElement, false); michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: // XXXbz Why not notifying here? michael@0: htmlElement->AppendChildTo(bodyElement, false); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: // add a nice bow michael@0: if (NS_SUCCEEDED(rv)) { michael@0: blankDoc->SetDocumentCharacterSetSource(kCharsetFromDocTypeDefault); michael@0: blankDoc->SetDocumentCharacterSet(NS_LITERAL_CSTRING("UTF-8")); michael@0: michael@0: *aDocument = blankDoc; michael@0: NS_ADDREF(*aDocument); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsContentDLF::CreateDocument(const char* aCommand, michael@0: nsIChannel* aChannel, michael@0: nsILoadGroup* aLoadGroup, michael@0: nsIDocShell* aContainer, michael@0: const nsCID& aDocumentCID, michael@0: nsIStreamListener** aDocListener, michael@0: nsIContentViewer** aContentViewer) michael@0: { michael@0: nsresult rv = NS_ERROR_FAILURE; michael@0: michael@0: nsCOMPtr aURL; michael@0: rv = aChannel->GetURI(getter_AddRefs(aURL)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: #ifdef NOISY_CREATE_DOC michael@0: if (nullptr != aURL) { michael@0: nsAutoString tmp; michael@0: aURL->ToString(tmp); michael@0: fputs(NS_LossyConvertUTF16toASCII(tmp).get(), stdout); michael@0: printf(": creating document\n"); michael@0: } michael@0: #endif michael@0: michael@0: // Create the document michael@0: nsCOMPtr doc = do_CreateInstance(aDocumentCID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Create the content viewer XXX: could reuse content viewer here! michael@0: nsCOMPtr contentViewer = NS_NewContentViewer(); michael@0: michael@0: doc->SetContainer(static_cast(aContainer)); michael@0: michael@0: // Initialize the document to begin loading the data. An michael@0: // nsIStreamListener connected to the parser is returned in michael@0: // aDocListener. michael@0: rv = doc->StartDocumentLoad(aCommand, aChannel, aLoadGroup, aContainer, aDocListener, true); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Bind the document to the Content Viewer michael@0: contentViewer->LoadStart(doc); michael@0: contentViewer.forget(aContentViewer); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsContentDLF::CreateXULDocument(const char* aCommand, michael@0: nsIChannel* aChannel, michael@0: nsILoadGroup* aLoadGroup, michael@0: const char* aContentType, michael@0: nsIDocShell* aContainer, michael@0: nsISupports* aExtraInfo, michael@0: nsIStreamListener** aDocListener, michael@0: nsIContentViewer** aContentViewer) michael@0: { michael@0: nsresult rv; michael@0: nsCOMPtr doc = do_CreateInstance(kXULDocumentCID, &rv); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsCOMPtr contentViewer = NS_NewContentViewer(); michael@0: michael@0: nsCOMPtr aURL; michael@0: rv = aChannel->GetURI(getter_AddRefs(aURL)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: /* michael@0: * Initialize the document to begin loading the data... michael@0: * michael@0: * An nsIStreamListener connected to the parser is returned in michael@0: * aDocListener. michael@0: */ michael@0: michael@0: doc->SetContainer(static_cast(aContainer)); michael@0: michael@0: rv = doc->StartDocumentLoad(aCommand, aChannel, aLoadGroup, aContainer, aDocListener, true); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: /* michael@0: * Bind the document to the Content Viewer... michael@0: */ michael@0: contentViewer->LoadStart(doc); michael@0: contentViewer.forget(aContentViewer); michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool nsContentDLF::IsImageContentType(const char* aContentType) { michael@0: return imgLoader::SupportImageWithMimeType(aContentType); michael@0: }