Mozilla/mozilla/content/xml/document/src/nsXMLDocument.cpp
dougt%netscape.com 175245e2de Relanding Necko Changes.
Revising nsIChannel to allow for overlapped i/o. This consists of three parts:

1. Factoring nsIChannel into a protocol specific part, the nsIChannel, and a socket specific, the nsITransport.
2. Derive the nsIChannel from a nsIRequest.
2. Changes the notification system from necko and the URILoader to pass the nsIRequest interface instead of nsIChannel interface.

This goal stems from wanting to be able to have active AsyncRead and AsyncWrite operations on nsSocketTransport.
This is desired because it would greatly simplify the task of maintaining persistent/reusable socket connections
for FTP, HTTP, and Imap (and potentially other protocols). The problem with the existing nsIChannel interface is
that it does not allow one to selectively suspend just one of the read or write operations while keeping the other active.

r=darin@netscape.com
sr=rpotts@netscape.com


git-svn-id: svn://10.0.0.236/trunk@87587 18797224-902f-48f8-a5cc-f745e15eee43
2001-02-21 20:38:08 +00:00

1094 lines
32 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Pierre Phaneuf <pp@ludusdesign.com>
*/
#include "nsXMLDocument.h"
#include "nsWellFormedDTD.h"
#include "nsParserCIID.h"
#include "nsIParser.h"
#include "nsIXMLContent.h"
#include "nsIXMLContentSink.h"
#include "nsIPresShell.h"
#include "nsIPresContext.h"
#include "nsIContent.h"
#include "nsIContentViewerContainer.h"
#include "nsIContentViewer.h"
#include "nsIWebShell.h"
#include "nsIDocShell.h"
#include "nsIMarkupDocumentViewer.h"
#include "nsIDocumentLoader.h"
#include "nsIHTMLContent.h"
#include "nsHTMLParts.h"
#include "nsIHTMLStyleSheet.h"
#include "nsIHTMLCSSStyleSheet.h"
#include "nsIStyleSet.h"
#include "nsIComponentManager.h"
#include "nsIDOMComment.h"
#include "nsIDOMElement.h"
#include "nsIDOMText.h"
#include "nsIBaseWindow.h"
#include "nsIDOMCDATASection.h"
#include "nsIDOMProcessingInstruction.h"
#include "nsIDOMDocumentType.h"
#include "nsExpatDTD.h"
#include "nsINameSpaceManager.h"
#include "nsICSSLoader.h"
#include "nsCOMPtr.h"
#include "nsIURI.h"
#include "nsXPIDLString.h"
#include "nsIHTTPChannel.h"
#include "nsIServiceManager.h"
#include "nsICharsetAlias.h"
#include "nsIPref.h"
#include "nsICharsetDetector.h"
#include "prmem.h"
#include "prtime.h"
#include "nsIWebShellServices.h"
#include "nsICharsetDetectionAdaptor.h"
#include "nsCharsetDetectionAdaptorCID.h"
#include "nsICharsetAlias.h"
#include "nsIParserFilter.h"
#include "nsNetUtil.h"
#include "nsDOMError.h"
#include "nsScriptSecurityManager.h"
#include "nsIPrincipal.h"
#include "nsIAggregatePrincipal.h"
#include "nsLayoutCID.h"
#include "nsContentCID.h"
static NS_DEFINE_CID(kHTMLStyleSheetCID,NS_HTMLSTYLESHEET_CID);
#include "nsCExternalHandlerService.h"
#include "nsIMIMEService.h"
#include "nsNetUtil.h"
#include "nsMimeTypes.h"
// XXX The XML world depends on the html atoms
#include "nsHTMLAtoms.h"
#define DETECTOR_CONTRACTID_MAX 127
static char g_detector_contractid[DETECTOR_CONTRACTID_MAX + 1];
static PRBool gInitDetector = PR_FALSE;
static PRBool gPlugDetector = PR_FALSE;
static const char* kLoadAsData = "loadAsData";
// ==================================================================
// =
// ==================================================================
static int PR_CALLBACK
MyPrefChangedCallback(const char*aPrefName, void* instance_data)
{
nsresult rv;
NS_WITH_SERVICE(nsIPref, prefs, "@mozilla.org/preferences;1", &rv);
PRUnichar* detector_name = nsnull;
if(NS_SUCCEEDED(rv) && NS_SUCCEEDED(
rv = prefs->GetLocalizedUnicharPref("intl.charset.detector",
&detector_name)))
{
if(nsCRT::strlen(detector_name) > 0) {
PL_strncpy(g_detector_contractid, NS_CHARSET_DETECTOR_CONTRACTID_BASE,DETECTOR_CONTRACTID_MAX);
PL_strncat(g_detector_contractid, NS_ConvertUCS2toUTF8(detector_name),DETECTOR_CONTRACTID_MAX);
gPlugDetector = PR_TRUE;
} else {
g_detector_contractid[0]=0;
gPlugDetector = PR_FALSE;
}
PR_FREEIF(detector_name);
}
return 0;
}
NS_LAYOUT nsresult
NS_NewDOMDocument(nsIDOMDocument** aInstancePtrResult,
const nsAReadableString& aNamespaceURI,
const nsAReadableString& aQualifiedName,
nsIDOMDocumentType* aDoctype,
nsIURI* aBaseURI)
{
nsresult rv;
*aInstancePtrResult = nsnull;
nsXMLDocument* doc = new nsXMLDocument();
if (doc == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
nsCOMPtr<nsIDOMDocument> kungFuDeathGrip(doc);
rv = doc->Reset(nsnull, nsnull);
NS_ENSURE_SUCCESS(rv, rv);
doc->SetDocumentURL(aBaseURI);
if (aDoctype) {
nsCOMPtr<nsIDOMNode> tmpNode;
rv = doc->AppendChild(aDoctype, getter_AddRefs(tmpNode));
NS_ENSURE_SUCCESS(rv, rv);
}
if (aQualifiedName.Length() > 0) {
nsCOMPtr<nsIDOMElement> root;
rv = doc->CreateElementNS(aNamespaceURI, aQualifiedName,
getter_AddRefs(root));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMNode> tmpNode;
rv = doc->AppendChild(root, getter_AddRefs(tmpNode));
NS_ENSURE_SUCCESS(rv, rv);
}
*aInstancePtrResult = doc;
NS_ADDREF(*aInstancePtrResult);
return NS_OK;
}
NS_LAYOUT nsresult
NS_NewXMLDocument(nsIDocument** aInstancePtrResult)
{
nsXMLDocument* doc = new nsXMLDocument();
if (doc == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
return doc->QueryInterface(NS_GET_IID(nsIDocument), (void**) aInstancePtrResult);
}
nsXMLDocument::nsXMLDocument()
{
mParser = nsnull;
mAttrStyleSheet = nsnull;
mInlineStyleSheet = nsnull;
mCSSLoader = nsnull;
}
nsXMLDocument::~nsXMLDocument()
{
NS_IF_RELEASE(mParser);
if (nsnull != mAttrStyleSheet) {
mAttrStyleSheet->SetOwningDocument(nsnull);
NS_RELEASE(mAttrStyleSheet);
}
if (nsnull != mInlineStyleSheet) {
mInlineStyleSheet->SetOwningDocument(nsnull);
NS_RELEASE(mInlineStyleSheet);
}
if (mCSSLoader) {
mCSSLoader->DropDocumentReference();
NS_RELEASE(mCSSLoader);
}
}
NS_IMETHODIMP
nsXMLDocument::QueryInterface(REFNSIID aIID,
void** aInstancePtr)
{
NS_ENSURE_ARG_POINTER(aInstancePtr);
nsISupports *inst = nsnull;
if (aIID.Equals(NS_GET_IID(nsIXMLDocument))) {
inst = NS_STATIC_CAST(nsIXMLDocument *, this);
} else if (aIID.Equals(NS_GET_IID(nsIHTMLContentContainer))) {
inst = NS_STATIC_CAST(nsIHTMLContentContainer *, this);
} else if (aIID.Equals(NS_GET_IID(nsIInterfaceRequestor))) {
inst = NS_STATIC_CAST(nsIInterfaceRequestor *, this);
} else if (aIID.Equals(NS_GET_IID(nsIHTTPEventSink))) {
inst = NS_STATIC_CAST(nsIHTTPEventSink *, this);
} else {
return nsDocument::QueryInterface(aIID, aInstancePtr);
}
NS_ADDREF(inst);
*aInstancePtr = inst;
return NS_OK;
}
nsrefcnt nsXMLDocument::AddRef()
{
return nsDocument::AddRef();
}
nsrefcnt nsXMLDocument::Release()
{
return nsDocument::Release();
}
nsresult
nsXMLDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup)
{
nsresult result = nsDocument::Reset(aChannel, aLoadGroup);
if (NS_FAILED(result)) return result;
nsCOMPtr<nsIURI> url;
if (aChannel) {
result = aChannel->GetURI(getter_AddRefs(url));
if (NS_FAILED(result)) return result;
}
if (nsnull != mAttrStyleSheet) {
mAttrStyleSheet->SetOwningDocument(nsnull);
NS_RELEASE(mAttrStyleSheet);
}
if (nsnull != mInlineStyleSheet) {
mInlineStyleSheet->SetOwningDocument(nsnull);
NS_RELEASE(mInlineStyleSheet);
}
result = SetDefaultStylesheets(url);
return result;
}
NS_IMETHODIMP
nsXMLDocument::GetContentType(nsAWritableString& aContentType) const
{
// XXX Should get document type from incoming stream
aContentType.Assign(NS_LITERAL_STRING("text/xml"));
return NS_OK;
}
NS_IMETHODIMP
nsXMLDocument::GetInterface(const nsIID& aIID, void** aSink)
{
// Since we implement all the interfaces that you can get with
// GetInterface() we can simply call QueryInterface() here and let
// it do all the work.
return QueryInterface(aIID, aSink);
}
// nsIHTTPEventSink
NS_IMETHODIMP
nsXMLDocument::OnHeadersAvailable(nsISupports *aContext)
{
return NS_OK;
}
NS_IMETHODIMP
nsXMLDocument::OnRedirect(nsISupports *aContext, nsIURI *aNewLocation)
{
nsresult rv;
NS_WITH_SERVICE(nsIScriptSecurityManager, securityManager,
NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
if (NS_FAILED(rv))
return NS_ERROR_FAILURE;
nsCOMPtr<nsIPrincipal> newCodebase;
rv = securityManager->GetCodebasePrincipal(aNewLocation,
getter_AddRefs(newCodebase));
if (NS_FAILED(rv))
return NS_ERROR_FAILURE;
nsCOMPtr<nsIAggregatePrincipal> agg = do_QueryInterface(mPrincipal, &rv);
NS_ASSERTION(NS_SUCCEEDED(rv), "Principal not an aggregate.");
if (NS_FAILED(rv))
return NS_ERROR_FAILURE;
rv = agg->SetCodebase(newCodebase);
return rv;
}
NS_IMETHODIMP
nsXMLDocument::Load(const nsAReadableString& aUrl)
{
nsCOMPtr<nsIChannel> channel;
nsCOMPtr<nsIURI> uri;
nsresult rv;
// Create a new URI
rv = NS_NewURI(getter_AddRefs(uri), aUrl, mDocumentURL);
if (NS_FAILED(rv)) return rv;
// Get security manager, check to see if we're allowed to load this URI
NS_WITH_SERVICE(nsIScriptSecurityManager, secMan, NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
if (NS_FAILED(rv)) return rv;
if (NS_FAILED(secMan->CheckLoadURIFromScript(nsnull, uri)))
return NS_ERROR_FAILURE;
// Set a principal for this document
NS_IF_RELEASE(mPrincipal);
rv = secMan->GetCodebasePrincipal(uri, &mPrincipal);
if (!mPrincipal) return rv;
// Create a channel
rv = NS_OpenURI(getter_AddRefs(channel), uri, nsnull, nsnull, this);
if (NS_FAILED(rv)) return rv;
// Prepare for loading the XML document "into oneself"
nsCOMPtr<nsIStreamListener> listener;
if (NS_FAILED(rv = StartDocumentLoad(kLoadAsData, channel,
nsnull, nsnull,
getter_AddRefs(listener),
PR_FALSE))) {
NS_ERROR("nsXMLDocument::Load: Failed to start the document load.");
return rv;
}
// Start an asynchronous read of the XML document
rv = channel->AsyncOpen(listener, nsnull);
return rv;
}
NS_IMETHODIMP
nsXMLDocument::StartDocumentLoad(const char* aCommand,
nsIChannel* aChannel,
nsILoadGroup* aLoadGroup,
nsISupports* aContainer,
nsIStreamListener **aDocListener,
PRBool aReset)
{
nsresult rv = nsDocument::StartDocumentLoad(aCommand,
aChannel, aLoadGroup,
aContainer,
aDocListener, aReset);
if (NS_FAILED(rv)) {
return rv;
}
nsAutoString charset; charset.AssignWithConversion("UTF-8");
PRBool bIsHTML = PR_FALSE;
char* aContentType;
nsCharsetSource charsetSource = kCharsetFromDocTypeDefault;
nsCOMPtr<nsIURI> aUrl;
rv = aChannel->GetURI(getter_AddRefs(aUrl));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIMIMEService> MIMEService (do_GetService(NS_MIMESERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv)) return rv;
rv = MIMEService->GetTypeFromURI(aUrl, &aContentType);
if (NS_SUCCEEDED(rv)) {
if ( 0 == PL_strcmp(aContentType, "text/html")) {
bIsHTML = PR_TRUE;
}
Recycle(aContentType);
aContentType = nsnull;
}
nsCOMPtr<nsIHTTPChannel> httpChannel = do_QueryInterface(aChannel);
if(httpChannel) {
nsIAtom* contentTypeKey = NS_NewAtom("content-type");
nsXPIDLCString contenttypeheader;
rv = httpChannel->GetResponseHeader(contentTypeKey, getter_Copies(contenttypeheader));
NS_RELEASE(contentTypeKey);
if (NS_SUCCEEDED(rv)) {
nsAutoString contentType;
contentType.AssignWithConversion( NS_STATIC_CAST(const char*, contenttypeheader) );
PRInt32 start = contentType.RFind("charset=", PR_TRUE ) ;
if(kNotFound != start)
{
start += 8; // 8 = "charset=".length
PRInt32 end = 0;
if(PRUnichar('"') == contentType.CharAt(start)) {
start++;
end = contentType.FindCharInSet("\"", start );
if(kNotFound == end )
end = contentType.Length();
} else {
end = contentType.FindCharInSet(";\n\r ", start );
if(kNotFound == end )
end = contentType.Length();
}
nsAutoString theCharset;
contentType.Mid(theCharset, start, end - start);
nsICharsetAlias* calias = nsnull;
rv = nsServiceManager::GetService(
kCharsetAliasCID,
NS_GET_IID(nsICharsetAlias),
(nsISupports**) &calias);
if(NS_SUCCEEDED(rv) && (nsnull != calias) )
{
nsAutoString preferred;
rv = calias->GetPreferred(theCharset, preferred);
if(NS_SUCCEEDED(rv))
{
charset = preferred;
charsetSource = kCharsetFromHTTPHeader;
}
nsServiceManager::ReleaseService(kCharsetAliasCID, calias);
}
}
}
}
static NS_DEFINE_IID(kCParserCID, NS_PARSER_IID);
rv = nsComponentManager::CreateInstance(kCParserCID, nsnull,
NS_GET_IID(nsIParser),
(void **)&mParser);
if (NS_OK == rv) {
nsIXMLContentSink* sink;
nsCOMPtr<nsIDocShell> docShell;
if(aContainer)
docShell = do_QueryInterface(aContainer, &rv);
else rv = NS_NewXMLContentSink(&sink, this, aUrl, nsnull);
if(NS_SUCCEEDED(rv) && (docShell)) {
nsCOMPtr<nsISupportsParserBundle> parserBundle;
nsresult result;
parserBundle = do_QueryInterface(mParser, &result);
if(NS_SUCCEEDED(result)) {
// We do this to help consumers who don't have access to the webshell.
nsAutoString theID;
theID.AssignWithConversion("docshell");
parserBundle->SetDataIntoBundle(theID,docShell);
}
nsCOMPtr<nsIContentViewer> cv;
docShell->GetContentViewer(getter_AddRefs(cv));
if (cv) {
nsCOMPtr<nsIMarkupDocumentViewer> muCV = do_QueryInterface(cv);
if (muCV) {
if(bIsHTML &&(0 == nsCRT::strcmp("view-source", aCommand))) { // only do this for view-source
//view source is now an XML document and we need to make provisions
//for the usual charset use when displaying those documents, this
//code mirrors nsHTMLDocument.cpp
PRUnichar* requestCharset = nsnull;
nsIParserFilter *cdetflt = nsnull;
nsCharsetSource requestCharsetSource = kCharsetUninitialized;
//need to be able to override doc charset default on user request
if( kCharsetFromDocTypeDefault == charsetSource ) // it is not from HTTP header
charsetSource = kCharsetFromWeakDocTypeDefault;
//check hint Charset (is this needed here?)
PRUnichar* hintCharset = nsnull;
nsCharsetSource hintSource = kCharsetUninitialized;
rv = muCV->GetHintCharacterSet(&hintCharset);
if(NS_SUCCEEDED(rv)) {
rv = muCV->GetHintCharacterSetSource((PRInt32 *)(&hintSource));
if(NS_SUCCEEDED(rv)) {
if(hintSource > charsetSource) {
charset = hintCharset;
Recycle(hintCharset);
charsetSource = hintSource;
}
if(kCharsetUninitialized != hintSource) {
muCV->SetHintCharacterSetSource((PRInt32)(kCharsetUninitialized));
}
}//hint Charset
// get user default charset
if(kCharsetFromUserDefault > charsetSource)
{
PRUnichar* defaultCharsetFromDocShell = NULL;
if (muCV) {
rv = muCV->GetDefaultCharacterSet(&defaultCharsetFromDocShell);
if(NS_SUCCEEDED(rv)) {
#ifdef DEBUG_charset
nsAutoString d(defaultCharsetFromDocShell);
char* cCharset = d.ToNewCString();
printf("From default charset, charset = %s\n", cCharset);
Recycle(cCharset);
#endif
charset = defaultCharsetFromDocShell;
Recycle(defaultCharsetFromDocShell);
charsetSource = kCharsetFromUserDefault;
}
}//user default
//user requested charset
if(NS_SUCCEEDED(rv))
{
if(requestCharsetSource > charsetSource)
{
#ifdef DEBUG_charset
nsAutoString d(requestCharset);
char* cCharset = d.ToNewCString();
printf("From request charset, charset = %s req=%d->%d\n",
cCharset, charsetSource, requestCharsetSource);
Recycle(cCharset);
#endif
charsetSource = requestCharsetSource;
charset = requestCharset;
Recycle(requestCharset);
}
}
//charset from previous loading
if(kCharsetFromPreviousLoading > charsetSource)
{
PRUnichar* forceCharsetFromDocShell = NULL;
if (muCV) {
rv = muCV->GetForceCharacterSet(&forceCharsetFromDocShell);
}
if(NS_SUCCEEDED(rv) && (nsnull != forceCharsetFromDocShell))
{
#ifdef DEBUG_charset
nsAutoString d(forceCharsetFromDocShell);
char* cCharset = d.ToNewCString();
printf("From force, charset = %s \n", cCharset);
Recycle(cCharset);
#endif
charset = forceCharsetFromDocShell;
Recycle(forceCharsetFromDocShell);
//TODO: we should define appropriate constant for force charset
charsetSource = kCharsetFromPreviousLoading;
}
} //previous loading
//auto-detector charset (needed here?)
nsresult rv_detect = NS_OK;
if(! gInitDetector)
{
nsCOMPtr<nsIPref> pref(do_GetService(NS_PREF_CONTRACTID));
if(pref)
{
PRUnichar* detector_name = nsnull;
if(NS_SUCCEEDED(
rv_detect = pref->GetLocalizedUnicharPref("intl.charset.detector",
&detector_name)))
{
PL_strncpy(g_detector_contractid, NS_CHARSET_DETECTOR_CONTRACTID_BASE,DETECTOR_CONTRACTID_MAX);
PL_strncat(g_detector_contractid, NS_ConvertUCS2toUTF8(detector_name),DETECTOR_CONTRACTID_MAX);
gPlugDetector = PR_TRUE;
PR_FREEIF(detector_name);
}
pref->RegisterCallback("intl.charset.detector", MyPrefChangedCallback, nsnull);
}
gInitDetector = PR_TRUE;
}
//auto-detector charset (needed here?)
if((kCharsetFromAutoDetection > charsetSource ) && gPlugDetector)
{
// we could do charset detection
nsICharsetDetector *cdet = nsnull;
nsCOMPtr<nsIWebShellServices> wss;
nsICharsetDetectionAdaptor *adp = nsnull;
if(NS_SUCCEEDED( rv_detect =
nsComponentManager::CreateInstance(g_detector_contractid, nsnull,
NS_GET_IID(nsICharsetDetector), (void**)&cdet)))
{
if(NS_SUCCEEDED( rv_detect =
nsComponentManager::CreateInstance(
NS_CHARSET_DETECTION_ADAPTOR_CONTRACTID, nsnull,
NS_GET_IID(nsIParserFilter), (void**)&cdetflt)))
{
if(cdetflt &&
NS_SUCCEEDED( rv_detect=
cdetflt->QueryInterface(
NS_GET_IID(nsICharsetDetectionAdaptor),(void**) &adp)))
{
wss = do_QueryInterface(docShell,
&rv_detect);
if( NS_SUCCEEDED(rv_detect))
{
rv_detect = adp->Init(wss, cdet, (nsIDocument*)this,
mParser, charset.GetUnicode(),aCommand);
nsIParserFilter *oldFilter = nsnull;
if(cdetflt)
oldFilter = mParser->SetParserFilter(cdetflt);
NS_IF_RELEASE(oldFilter);
NS_IF_RELEASE(cdetflt);
}
}
}
}
else
{
// IF we cannot create the detector, don't bother to
// create one next time.
gPlugDetector = PR_FALSE;
}
NS_IF_RELEASE(cdet);
NS_IF_RELEASE(adp);
// NO NS_IF_RELEASE(cdetflt); here, do it after mParser->SetParserFilter
}
}
} //charset selection for view source only
}
}
}
if(NS_SUCCEEDED(rv))
{
nsCOMPtr<nsIWebShell> webShell(do_QueryInterface(docShell));
rv = NS_NewXMLContentSink(&sink, this, aUrl, webShell);
}
}
if (NS_OK == rv) {
// Set the parser as the stream listener for the document loader...
rv = mParser->QueryInterface(NS_GET_IID(nsIStreamListener), (void**)aDocListener);
if (NS_OK == rv) {
mParser->SetDocumentCharset(charset, charsetSource);
mParser->SetCommand(aCommand);
mParser->SetContentSink(sink);
mParser->Parse(aUrl, nsnull, PR_FALSE, (void *)this);
}
NS_RELEASE(sink);
}
}
return rv;
}
NS_IMETHODIMP
nsXMLDocument::EndLoad()
{
nsAutoString cmd;
if (mParser) mParser->GetCommand(cmd);
NS_IF_RELEASE(mParser);
if (cmd.EqualsWithConversion(kLoadAsData)) {
// Generate a document load event for the case when an XML document was loaded
// as pure data without any presentation attached to it.
nsCOMPtr<nsIScriptGlobalObject> scriptGlobal;
nsEventStatus status = nsEventStatus_eIgnore;
nsMouseEvent event;
event.eventStructType = NS_EVENT;
event.message = NS_PAGE_LOAD;
HandleDOMEvent(nsnull, &event, nsnull, NS_EVENT_FLAG_INIT, &status);
}
return nsDocument::EndLoad();
}
NS_IMETHODIMP
nsXMLDocument::GetAttributeStyleSheet(nsIHTMLStyleSheet** aResult)
{
NS_PRECONDITION(nsnull != aResult, "null ptr");
if (nsnull == aResult) {
return NS_ERROR_NULL_POINTER;
}
*aResult = mAttrStyleSheet;
if (nsnull == mAttrStyleSheet) {
return NS_ERROR_NOT_AVAILABLE; // probably not the right error...
}
else {
NS_ADDREF(mAttrStyleSheet);
}
return NS_OK;
}
NS_IMETHODIMP
nsXMLDocument::GetInlineStyleSheet(nsIHTMLCSSStyleSheet** aResult)
{
NS_PRECONDITION(nsnull != aResult, "null ptr");
if (nsnull == aResult) {
return NS_ERROR_NULL_POINTER;
}
*aResult = mInlineStyleSheet;
if (nsnull == mInlineStyleSheet) {
return NS_ERROR_NOT_AVAILABLE; // probably not the right error...
}
else {
NS_ADDREF(mInlineStyleSheet);
}
return NS_OK;
}
void nsXMLDocument::InternalAddStyleSheet(nsIStyleSheet* aSheet) // subclass hook for sheet ordering
{
if (aSheet == mAttrStyleSheet) { // always first
mStyleSheets.InsertElementAt(aSheet, 0);
}
else if (aSheet == mInlineStyleSheet) { // always last
mStyleSheets.AppendElement(aSheet);
}
else {
if (mInlineStyleSheet == mStyleSheets.ElementAt(mStyleSheets.Count() - 1)) {
// keep attr sheet last
mStyleSheets.InsertElementAt(aSheet, mStyleSheets.Count() - 1);
}
else {
mStyleSheets.AppendElement(aSheet);
}
}
}
void
nsXMLDocument::InternalInsertStyleSheetAt(nsIStyleSheet* aSheet, PRInt32 aIndex)
{
mStyleSheets.InsertElementAt(aSheet, aIndex + 1); // offset one for the attr style sheet
}
// nsIDOMDocument interface
NS_IMETHODIMP
nsXMLDocument::GetDoctype(nsIDOMDocumentType** aDocumentType)
{
return nsDocument::GetDoctype(aDocumentType);
}
NS_IMETHODIMP
nsXMLDocument::CreateCDATASection(const nsAReadableString& aData, nsIDOMCDATASection** aReturn)
{
NS_ENSURE_ARG_POINTER(aReturn);
*aReturn = nsnull;
nsReadingIterator<PRUnichar> begin;
nsReadingIterator<PRUnichar> end;
aData.BeginReading(begin);
aData.EndReading(end);
if (FindInReadable(NS_LITERAL_STRING("]]>"),begin,end))
return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
nsIContent* content;
nsresult rv = NS_NewXMLCDATASection(&content);
if (NS_OK == rv) {
rv = content->QueryInterface(NS_GET_IID(nsIDOMCDATASection), (void**)aReturn);
(*aReturn)->AppendData(aData);
NS_RELEASE(content);
}
return rv;
}
NS_IMETHODIMP
nsXMLDocument::CreateEntityReference(const nsAReadableString& aName, nsIDOMEntityReference** aReturn)
{
NS_ENSURE_ARG_POINTER(aReturn);
*aReturn = nsnull;
return NS_OK;
}
NS_IMETHODIMP
nsXMLDocument::CreateProcessingInstruction(const nsAReadableString& aTarget,
const nsAReadableString& aData,
nsIDOMProcessingInstruction** aReturn)
{
NS_ENSURE_ARG_POINTER(aReturn);
*aReturn = nsnull;
nsIContent* content;
nsresult rv = NS_NewXMLProcessingInstruction(&content, aTarget, aData);
if (NS_OK != rv) {
return rv;
}
rv = content->QueryInterface(NS_GET_IID(nsIDOMProcessingInstruction), (void**)aReturn);
NS_RELEASE(content);
return rv;
}
NS_IMETHODIMP
nsXMLDocument::CreateElement(const nsAReadableString& aTagName,
nsIDOMElement** aReturn)
{
NS_ENSURE_ARG_POINTER(aReturn);
*aReturn = nsnull;
NS_ENSURE_TRUE(aTagName.Length(), NS_ERROR_DOM_INVALID_CHARACTER_ERR);
nsIXMLContent* content;
nsCOMPtr<nsINodeInfo> nodeInfo;
nsresult rv;
rv = mNodeInfoManager->GetNodeInfo(aTagName, nsnull, kNameSpaceID_None,
*getter_AddRefs(nodeInfo));
NS_ENSURE_SUCCESS(rv, rv);
rv = NS_NewXMLElement(&content, nodeInfo);
NS_ENSURE_SUCCESS(rv, rv);
rv = content->QueryInterface(NS_GET_IID(nsIDOMElement), (void**)aReturn);
NS_RELEASE(content);
return rv;
}
NS_IMETHODIMP
nsXMLDocument::CloneNode(PRBool aDeep, nsIDOMNode** aReturn)
{
NS_ENSURE_ARG_POINTER(aReturn);
*aReturn = nsnull;
nsresult rv;
nsCOMPtr<nsIDOMDocumentType> docType, newDocType;
nsCOMPtr<nsIDOMDocument> newDoc;
// Get the doctype prior to new document construction. There's no big
// advantage now to dealing with the doctype separately, but maybe one
// day we'll do something significant with the doctype on document creation.
GetDoctype(getter_AddRefs(docType));
if (docType) {
nsCOMPtr<nsIDOMNode> newDocTypeNode;
rv = docType->CloneNode(PR_TRUE, getter_AddRefs(newDocTypeNode));
if (NS_FAILED(rv)) return rv;
newDocType = do_QueryInterface(newDocTypeNode);
}
// Create an empty document
nsAutoString emptyStr;
emptyStr.Truncate();
rv = NS_NewDOMDocument(getter_AddRefs(newDoc), emptyStr, emptyStr,
newDocType, mDocumentURL);
if (NS_FAILED(rv)) return rv;
if (aDeep) {
// If there was a doctype, a new one has already been inserted into the
// new document. We might have to add nodes before it.
PRBool beforeDocType = (docType.get() != nsnull);
nsCOMPtr<nsIDOMNodeList> childNodes;
GetChildNodes(getter_AddRefs(childNodes));
if (childNodes) {
PRUint32 index, count;
childNodes->GetLength(&count);
for (index=0; index < count; index++) {
nsCOMPtr<nsIDOMNode> child;
childNodes->Item(index, getter_AddRefs(child));
if (child && (child != docType)) {
nsCOMPtr<nsIDOMNode> newChild;
rv = child->CloneNode(aDeep, getter_AddRefs(newChild));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIDOMNode> dummyNode;
if (beforeDocType) {
rv = newDoc->InsertBefore(newChild,
docType,
getter_AddRefs(dummyNode));
if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
}
else {
rv = newDoc->AppendChild(newChild,
getter_AddRefs(dummyNode));
if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
}
}
else {
beforeDocType = PR_FALSE;
}
}
}
}
return newDoc->QueryInterface(NS_GET_IID(nsIDOMNode), (void**)aReturn);
}
NS_IMETHODIMP
nsXMLDocument::ImportNode(nsIDOMNode* aImportedNode,
PRBool aDeep,
nsIDOMNode** aReturn)
{
return nsDocument::ImportNode(aImportedNode, aDeep, aReturn);
}
NS_IMETHODIMP
nsXMLDocument::CreateAttributeNS(const nsAReadableString& aNamespaceURI,
const nsAReadableString& aQualifiedName,
nsIDOMAttr** aReturn)
{
NS_NOTYETIMPLEMENTED("write me");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsXMLDocument::CreateElementNS(const nsAReadableString& aNamespaceURI,
const nsAReadableString& aQualifiedName,
nsIDOMElement** aReturn)
{
NS_ENSURE_ARG_POINTER(aReturn);
*aReturn = nsnull;
nsresult rv = NS_OK;
nsCOMPtr<nsINodeInfo> nodeInfo;
rv = mNodeInfoManager->GetNodeInfo(aQualifiedName, aNamespaceURI,
*getter_AddRefs(nodeInfo));
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 namespaceID;
nodeInfo->GetNamespaceID(namespaceID);
nsCOMPtr<nsIContent> content;
if (namespaceID == kNameSpaceID_HTML) {
nsCOMPtr<nsIHTMLContent> htmlContent;
rv = NS_CreateHTMLElement(getter_AddRefs(htmlContent), nodeInfo);
content = do_QueryInterface(htmlContent);
}
else {
nsCOMPtr<nsIXMLContent> xmlContent;
rv = NS_NewXMLElement(getter_AddRefs(xmlContent), nodeInfo);
content = do_QueryInterface(xmlContent);
}
NS_ENSURE_SUCCESS(rv, rv);
content->SetContentID(mNextContentID++);
return content->QueryInterface(NS_GET_IID(nsIDOMElement), (void**)aReturn);
}
static nsIContent *
MatchName(nsIContent *aContent, const nsAReadableString& aName)
{
nsAutoString value;
nsIContent *result = nsnull;
PRInt32 ns;
aContent->GetNameSpaceID(ns);
if (kNameSpaceID_HTML == ns) {
if ((NS_CONTENT_ATTR_HAS_VALUE == aContent->GetAttribute(kNameSpaceID_None, nsHTMLAtoms::id, value)) &&
aName.Equals(value)) {
return aContent;
}
else if ((NS_CONTENT_ATTR_HAS_VALUE == aContent->GetAttribute(kNameSpaceID_None, nsHTMLAtoms::name, value)) &&
aName.Equals(value)) {
return aContent;
}
}
else {
nsCOMPtr<nsIXMLContent> xmlContent = do_QueryInterface(aContent);
nsCOMPtr<nsIAtom> IDValue;
if (xmlContent && NS_SUCCEEDED(xmlContent->GetID(*getter_AddRefs(IDValue))) && IDValue) {
const PRUnichar* IDValStr = nsnull;
IDValue->GetUnicode(&IDValStr);
if (aName.Equals(IDValStr)) {
return aContent;
}
}
}
PRInt32 i, count;
aContent->ChildCount(count);
for (i = 0; i < count && result == nsnull; i++) {
nsIContent *child;
aContent->ChildAt(i, child);
result = MatchName(child, aName);
NS_RELEASE(child);
}
return result;
}
NS_IMETHODIMP
nsXMLDocument::GetElementById(const nsAReadableString& aElementId,
nsIDOMElement** aReturn)
{
NS_ENSURE_ARG_POINTER(aReturn);
*aReturn = nsnull;
NS_WARN_IF_FALSE(!aElementId.IsEmpty(),"getElementById(\"\"), fix caller?");
if (aElementId.IsEmpty())
return NS_OK;
// XXX For now, we do a brute force search of the content tree.
// We should come up with a more efficient solution.
nsCOMPtr<nsIContent> content = do_QueryInterface(MatchName(mRootContent,aElementId));
nsresult rv = NS_OK;
if (content) {
rv = content->QueryInterface(NS_GET_IID(nsIDOMElement),(void**)aReturn);
}
return rv;
}
// nsIXMLDocument
NS_IMETHODIMP
nsXMLDocument::SetDefaultStylesheets(nsIURI* aUrl)
{
nsresult result = NS_OK;
if (aUrl) {
//result = NS_NewHTMLStyleSheet(&mAttrStyleSheet, aUrl, this);
result = nsComponentManager::CreateInstance(kHTMLStyleSheetCID,nsnull,NS_GET_IID(nsIHTMLStyleSheet),(void**)&mAttrStyleSheet);
if (NS_SUCCEEDED(result)) {
result = mAttrStyleSheet->Init(aUrl,this);
if (NS_FAILED(result)) {
NS_RELEASE(mAttrStyleSheet);
}
}
if (NS_OK == result) {
AddStyleSheet(mAttrStyleSheet); // tell the world about our new style sheet
result = NS_NewHTMLCSSStyleSheet(&mInlineStyleSheet, aUrl, this);
if (NS_OK == result) {
AddStyleSheet(mInlineStyleSheet); // tell the world about our new style sheet
}
}
}
return result;
}
NS_IMETHODIMP
nsXMLDocument::SetTitle(const PRUnichar *aTitle)
{
// Pass on to any interested containers
PRInt32 i, n = mPresShells.Count();
for (i = 0; i < n; i++) {
nsIPresShell* shell = (nsIPresShell*) mPresShells.ElementAt(i);
nsCOMPtr<nsIPresContext> cx;
shell->GetPresContext(getter_AddRefs(cx));
nsCOMPtr<nsISupports> container;
if (NS_OK == cx->GetContainer(getter_AddRefs(container))) {
if (container) {
nsCOMPtr<nsIBaseWindow> docShell(do_QueryInterface(container));
if(docShell) {
docShell->SetTitle(aTitle);
}
}
}
}
return NS_OK;
}
NS_IMETHODIMP
nsXMLDocument::GetCSSLoader(nsICSSLoader*& aLoader)
{
nsresult result = NS_OK;
if (! mCSSLoader) {
result = NS_NewCSSLoader(this, &mCSSLoader);
if (mCSSLoader) {
mCSSLoader->SetCaseSensitive(PR_TRUE);
mCSSLoader->SetQuirkMode(PR_FALSE); // No quirks in XML
}
}
aLoader = mCSSLoader;
NS_IF_ADDREF(aLoader);
return result;
}