/* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 sw=2 et tw=78: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsCOMPtr.h" #include "nsContentDLF.h" #include "nsGenericHTMLElement.h" #include "nsGkAtoms.h" #include "nsIComponentManager.h" #include "nsIComponentRegistrar.h" #include "nsICategoryManager.h" #include "nsIDocumentLoaderFactory.h" #include "nsIDocument.h" #include "nsIDocumentViewer.h" #include "nsIURL.h" #include "nsICSSStyleSheet.h" #include "nsNodeInfo.h" #include "nsNodeInfoManager.h" #include "nsString.h" #include "nsContentCID.h" #include "prprf.h" #include "nsNetUtil.h" #include "nsICSSLoader.h" #include "nsCRT.h" #include "nsIViewSourceChannel.h" #ifdef MOZ_MEDIA #include "nsHTMLMediaElement.h" #endif #include "imgILoader.h" #include "nsIParser.h" #include "nsMimeTypes.h" // plugins #include "nsIPluginHost.h" static NS_DEFINE_CID(kPluginDocumentCID, NS_PLUGINDOCUMENT_CID); // Factory code for creating variations on html documents #undef NOISY_REGISTRY static NS_DEFINE_IID(kHTMLDocumentCID, NS_HTMLDOCUMENT_CID); static NS_DEFINE_IID(kXMLDocumentCID, NS_XMLDOCUMENT_CID); #ifdef MOZ_SVG static NS_DEFINE_IID(kSVGDocumentCID, NS_SVGDOCUMENT_CID); #endif #ifdef MOZ_MEDIA static NS_DEFINE_IID(kVideoDocumentCID, NS_VIDEODOCUMENT_CID); #endif static NS_DEFINE_IID(kImageDocumentCID, NS_IMAGEDOCUMENT_CID); static NS_DEFINE_IID(kXULDocumentCID, NS_XULDOCUMENT_CID); nsresult NS_NewDocumentViewer(nsIDocumentViewer** aResult); // XXXbz if you change the MIME types here, be sure to update // nsIParser.h and DetermineParseMode in nsParser.cpp accordingly. static const char* const gHTMLTypes[] = { TEXT_HTML, TEXT_PLAIN, TEXT_CSS, TEXT_JAVASCRIPT, TEXT_ECMASCRIPT, APPLICATION_JAVASCRIPT, APPLICATION_ECMASCRIPT, APPLICATION_XJAVASCRIPT, #ifdef MOZ_VIEW_SOURCE VIEWSOURCE_CONTENT_TYPE, #endif APPLICATION_XHTML_XML, 0 }; static const char* const gXMLTypes[] = { TEXT_XML, APPLICATION_XML, #ifdef MOZ_MATHML APPLICATION_MATHML_XML, #endif APPLICATION_RDF_XML, TEXT_RDF, 0 }; #ifdef MOZ_SVG static const char* const gSVGTypes[] = { IMAGE_SVG_XML, 0 }; PRBool NS_SVGEnabled(); #endif static const char* const gXULTypes[] = { TEXT_XUL, APPLICATION_CACHED_XUL, 0 }; nsresult NS_NewContentDocumentLoaderFactory(nsIDocumentLoaderFactory** aResult) { NS_PRECONDITION(aResult, "null OUT ptr"); if (!aResult) { return NS_ERROR_NULL_POINTER; } nsContentDLF* it = new nsContentDLF(); if (!it) { return NS_ERROR_OUT_OF_MEMORY; } return CallQueryInterface(it, aResult); } nsContentDLF::nsContentDLF() { } nsContentDLF::~nsContentDLF() { } NS_IMPL_ISUPPORTS1(nsContentDLF, nsIDocumentLoaderFactory) NS_IMETHODIMP nsContentDLF::CreateInstance(const char* aCommand, nsIChannel* aChannel, nsILoadGroup* aLoadGroup, const char* aContentType, nsISupports* aContainer, nsISupports* aExtraInfo, nsIStreamListener** aDocListener, nsIContentViewer** aDocViewer) { // Declare "type" here. This is because although the variable itself only // needs limited scope, we need to use the raw string memory -- as returned // by "type.get()" farther down in the function. nsCAutoString type; // Are we viewing source? #ifdef MOZ_VIEW_SOURCE nsCOMPtr viewSourceChannel = do_QueryInterface(aChannel); if (viewSourceChannel) { aCommand = "view-source"; // The parser freaks out when it sees the content-type that a // view-source channel normally returns. Get the actual content // type of the data. If it's known, use it; otherwise use // text/plain. viewSourceChannel->GetOriginalContentType(type); PRBool knownType = PR_FALSE; PRInt32 typeIndex; for (typeIndex = 0; gHTMLTypes[typeIndex] && !knownType; ++typeIndex) { if (type.Equals(gHTMLTypes[typeIndex]) && !type.EqualsLiteral(VIEWSOURCE_CONTENT_TYPE)) { knownType = PR_TRUE; } } for (typeIndex = 0; gXMLTypes[typeIndex] && !knownType; ++typeIndex) { if (type.Equals(gXMLTypes[typeIndex])) { knownType = PR_TRUE; } } #ifdef MOZ_SVG if (NS_SVGEnabled()) { for (typeIndex = 0; gSVGTypes[typeIndex] && !knownType; ++typeIndex) { if (type.Equals(gSVGTypes[typeIndex])) { knownType = PR_TRUE; } } } #endif // MOZ_SVG for (typeIndex = 0; gXULTypes[typeIndex] && !knownType; ++typeIndex) { if (type.Equals(gXULTypes[typeIndex])) { knownType = PR_TRUE; } } if (knownType) { viewSourceChannel->SetContentType(type); } else if (IsImageContentType(type.get())) { // If it's an image, we want to display it the same way we normally would. // Also note the lifetime of "type" allows us to safely use "get()" here. aContentType = type.get(); } else { viewSourceChannel->SetContentType(NS_LITERAL_CSTRING(TEXT_PLAIN)); } } else if (0 == PL_strcmp(VIEWSOURCE_CONTENT_TYPE, aContentType)) { aChannel->SetContentType(NS_LITERAL_CSTRING(TEXT_PLAIN)); aContentType = TEXT_PLAIN; } #endif // Try html int typeIndex=0; while(gHTMLTypes[typeIndex]) { if (0 == PL_strcmp(gHTMLTypes[typeIndex++], aContentType)) { return CreateDocument(aCommand, aChannel, aLoadGroup, aContainer, kHTMLDocumentCID, aDocListener, aDocViewer); } } // Try XML typeIndex = 0; while(gXMLTypes[typeIndex]) { if (0== PL_strcmp(gXMLTypes[typeIndex++], aContentType)) { return CreateDocument(aCommand, aChannel, aLoadGroup, aContainer, kXMLDocumentCID, aDocListener, aDocViewer); } } #ifdef MOZ_SVG if (NS_SVGEnabled()) { // Try SVG typeIndex = 0; while(gSVGTypes[typeIndex]) { if (!PL_strcmp(gSVGTypes[typeIndex++], aContentType)) { return CreateDocument(aCommand, aChannel, aLoadGroup, aContainer, kSVGDocumentCID, aDocListener, aDocViewer); } } } #endif // Try XUL typeIndex = 0; while (gXULTypes[typeIndex]) { if (0 == PL_strcmp(gXULTypes[typeIndex++], aContentType)) { return CreateXULDocument(aCommand, aChannel, aLoadGroup, aContentType, aContainer, aExtraInfo, aDocListener, aDocViewer); } } #ifdef MOZ_MEDIA if (nsHTMLMediaElement::ShouldHandleMediaType(aContentType)) { return CreateDocument(aCommand, aChannel, aLoadGroup, aContainer, kVideoDocumentCID, aDocListener, aDocViewer); } #endif // Try image types if (IsImageContentType(aContentType)) { return CreateDocument(aCommand, aChannel, aLoadGroup, aContainer, kImageDocumentCID, aDocListener, aDocViewer); } nsCOMPtr ph (do_GetService(MOZ_PLUGIN_HOST_CONTRACTID)); if(ph && NS_SUCCEEDED(ph->IsPluginEnabledForType(aContentType))) { return CreateDocument(aCommand, aChannel, aLoadGroup, aContainer, kPluginDocumentCID, aDocListener, aDocViewer); } // If we get here, then we weren't able to create anything. Sorry! return NS_ERROR_FAILURE; } NS_IMETHODIMP nsContentDLF::CreateInstanceForDocument(nsISupports* aContainer, nsIDocument* aDocument, const char *aCommand, nsIContentViewer** aDocViewerResult) { nsresult rv = NS_ERROR_FAILURE; do { nsCOMPtr docv; rv = NS_NewDocumentViewer(getter_AddRefs(docv)); if (NS_FAILED(rv)) break; // Bind the document to the Content Viewer nsIContentViewer* cv = static_cast(docv.get()); rv = cv->LoadStart(aDocument); NS_ADDREF(*aDocViewerResult = cv); } while (PR_FALSE); return rv; } NS_IMETHODIMP nsContentDLF::CreateBlankDocument(nsILoadGroup *aLoadGroup, nsIPrincipal* aPrincipal, nsIDocument **aDocument) { *aDocument = nsnull; nsresult rv = NS_ERROR_FAILURE; // create a new blank HTML document nsCOMPtr blankDoc(do_CreateInstance(kHTMLDocumentCID)); if (blankDoc) { // initialize nsCOMPtr uri; NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("about:blank")); if (uri) { blankDoc->ResetToURI(uri, aLoadGroup, aPrincipal); rv = NS_OK; } } // add some simple content structure if (NS_SUCCEEDED(rv)) { rv = NS_ERROR_FAILURE; nsNodeInfoManager *nim = blankDoc->NodeInfoManager(); nsCOMPtr htmlNodeInfo; // generate an html html element htmlNodeInfo = nim->GetNodeInfo(nsGkAtoms::html, 0, kNameSpaceID_XHTML); nsCOMPtr htmlElement = NS_NewHTMLHtmlElement(htmlNodeInfo); // generate an html head element htmlNodeInfo = nim->GetNodeInfo(nsGkAtoms::head, 0, kNameSpaceID_XHTML); nsCOMPtr headElement = NS_NewHTMLHeadElement(htmlNodeInfo); // generate an html body element htmlNodeInfo = nim->GetNodeInfo(nsGkAtoms::body, 0, kNameSpaceID_XHTML); nsCOMPtr bodyElement = NS_NewHTMLBodyElement(htmlNodeInfo); // blat in the structure if (htmlElement && headElement && bodyElement) { NS_ASSERTION(blankDoc->GetChildCount() == 0, "Shouldn't have children"); rv = blankDoc->AppendChildTo(htmlElement, PR_FALSE); if (NS_SUCCEEDED(rv)) { rv = htmlElement->AppendChildTo(headElement, PR_FALSE); if (NS_SUCCEEDED(rv)) { // XXXbz Why not notifying here? htmlElement->AppendChildTo(bodyElement, PR_FALSE); } } } } // add a nice bow if (NS_SUCCEEDED(rv)) { blankDoc->SetDocumentCharacterSetSource(kCharsetFromDocTypeDefault); blankDoc->SetDocumentCharacterSet(NS_LITERAL_CSTRING("UTF-8")); *aDocument = blankDoc; NS_ADDREF(*aDocument); } return rv; } nsresult nsContentDLF::CreateDocument(const char* aCommand, nsIChannel* aChannel, nsILoadGroup* aLoadGroup, nsISupports* aContainer, const nsCID& aDocumentCID, nsIStreamListener** aDocListener, nsIContentViewer** aDocViewer) { nsresult rv = NS_ERROR_FAILURE; nsCOMPtr aURL; rv = aChannel->GetURI(getter_AddRefs(aURL)); if (NS_FAILED(rv)) return rv; #ifdef NOISY_CREATE_DOC if (nsnull != aURL) { nsAutoString tmp; aURL->ToString(tmp); fputs(NS_LossyConvertUTF16toASCII(tmp).get(), stdout); printf(": creating document\n"); } #endif nsCOMPtr doc; nsCOMPtr docv; do { // Create the document doc = do_CreateInstance(aDocumentCID, &rv); if (NS_FAILED(rv)) break; // Create the document viewer XXX: could reuse document viewer here! rv = NS_NewDocumentViewer(getter_AddRefs(docv)); if (NS_FAILED(rv)) break; doc->SetContainer(aContainer); // Initialize the document to begin loading the data. An // nsIStreamListener connected to the parser is returned in // aDocListener. rv = doc->StartDocumentLoad(aCommand, aChannel, aLoadGroup, aContainer, aDocListener, PR_TRUE); if (NS_FAILED(rv)) break; // Bind the document to the Content Viewer rv = docv->LoadStart(doc); *aDocViewer = docv; NS_IF_ADDREF(*aDocViewer); } while (PR_FALSE); return rv; } nsresult nsContentDLF::CreateXULDocument(const char* aCommand, nsIChannel* aChannel, nsILoadGroup* aLoadGroup, const char* aContentType, nsISupports* aContainer, nsISupports* aExtraInfo, nsIStreamListener** aDocListener, nsIContentViewer** aDocViewer) { nsresult rv; nsCOMPtr doc = do_CreateInstance(kXULDocumentCID, &rv); if (NS_FAILED(rv)) return rv; nsCOMPtr docv; rv = NS_NewDocumentViewer(getter_AddRefs(docv)); if (NS_FAILED(rv)) return rv; nsCOMPtr aURL; rv = aChannel->GetURI(getter_AddRefs(aURL)); if (NS_FAILED(rv)) return rv; /* * Initialize the document to begin loading the data... * * An nsIStreamListener connected to the parser is returned in * aDocListener. */ doc->SetContainer(aContainer); rv = doc->StartDocumentLoad(aCommand, aChannel, aLoadGroup, aContainer, aDocListener, PR_TRUE); if (NS_SUCCEEDED(rv)) { /* * Bind the document to the Content Viewer... */ rv = docv->LoadStart(doc); *aDocViewer = docv; NS_IF_ADDREF(*aDocViewer); } return rv; } static nsresult RegisterTypes(nsICategoryManager* aCatMgr, const char* const* aTypes, PRBool aPersist = PR_TRUE) { nsresult rv = NS_OK; while (*aTypes) { const char* contentType = *aTypes++; #ifdef NOISY_REGISTRY printf("Register %s => %s\n", contractid, aPath); #endif // add the MIME types layout can handle to the handlers category. // this allows users of layout's viewers (the docshell for example) // to query the types of viewers layout can create. rv = aCatMgr->AddCategoryEntry("Gecko-Content-Viewers", contentType, "@mozilla.org/content/document-loader-factory;1", aPersist, PR_TRUE, nsnull); if (NS_FAILED(rv)) break; } return rv; } static nsresult UnregisterTypes(nsICategoryManager* aCatMgr, const char* const* aTypes) { nsresult rv = NS_OK; while (*aTypes) { const char* contentType = *aTypes++; rv = aCatMgr->DeleteCategoryEntry("Gecko-Content-Viewers", contentType, PR_TRUE); if (NS_FAILED(rv)) break; } return rv; } #ifdef MOZ_SVG NS_IMETHODIMP nsContentDLF::RegisterSVG() { nsresult rv; nsCOMPtr catmgr(do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv)); if (NS_FAILED(rv)) return rv; return RegisterTypes(catmgr, gSVGTypes, PR_FALSE); } NS_IMETHODIMP nsContentDLF::UnregisterSVG() { nsresult rv; nsCOMPtr catmgr(do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv)); if (NS_FAILED(rv)) return rv; return UnregisterTypes(catmgr, gSVGTypes); } #endif NS_IMETHODIMP nsContentDLF::RegisterDocumentFactories(nsIComponentManager* aCompMgr, nsIFile* aPath, const char *aLocation, const char *aType, const nsModuleComponentInfo* aInfo) { nsresult rv; nsCOMPtr catmgr(do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv)); if (NS_FAILED(rv)) return rv; do { rv = RegisterTypes(catmgr, gHTMLTypes); if (NS_FAILED(rv)) break; rv = RegisterTypes(catmgr, gXMLTypes); if (NS_FAILED(rv)) break; rv = RegisterTypes(catmgr, gXULTypes); if (NS_FAILED(rv)) break; } while (PR_FALSE); return rv; } NS_IMETHODIMP nsContentDLF::UnregisterDocumentFactories(nsIComponentManager* aCompMgr, nsIFile* aPath, const char* aRegistryLocation, const nsModuleComponentInfo* aInfo) { nsresult rv; nsCOMPtr catmgr(do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv)); if (NS_FAILED(rv)) return rv; do { rv = UnregisterTypes(catmgr, gHTMLTypes); if (NS_FAILED(rv)) break; rv = UnregisterTypes(catmgr, gXMLTypes); if (NS_FAILED(rv)) break; #ifdef MOZ_SVG rv = UnregisterTypes(catmgr, gSVGTypes); if (NS_FAILED(rv)) break; #endif rv = UnregisterTypes(catmgr, gXULTypes); if (NS_FAILED(rv)) break; } while (PR_FALSE); return rv; } PRBool nsContentDLF::IsImageContentType(const char* aContentType) { nsCOMPtr loader(do_GetService("@mozilla.org/image/loader;1")); PRBool isDecoderAvailable = PR_FALSE; loader->SupportImageWithMimeType(aContentType, &isDecoderAvailable); return isDecoderAvailable; }