michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 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: michael@0: #include "nsExpatDriver.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsParserCIID.h" michael@0: #include "CParserContext.h" michael@0: #include "nsIExpatSink.h" michael@0: #include "nsIExtendedExpatSink.h" michael@0: #include "nsIContentSink.h" michael@0: #include "nsParserMsgUtils.h" michael@0: #include "nsIURL.h" michael@0: #include "nsIUnicharInputStream.h" michael@0: #include "nsISimpleUnicharStreamFactory.h" michael@0: #include "nsNetUtil.h" michael@0: #include "prprf.h" michael@0: #include "prmem.h" michael@0: #include "nsTextFormatter.h" michael@0: #include "nsDirectoryServiceDefs.h" michael@0: #include "nsCRT.h" michael@0: #include "nsIConsoleService.h" michael@0: #include "nsIScriptError.h" michael@0: #include "nsIContentPolicy.h" michael@0: #include "nsContentPolicyUtils.h" michael@0: #include "nsError.h" michael@0: #include "nsXPCOMCIDInternal.h" michael@0: #include "nsUnicharInputStream.h" michael@0: michael@0: #define kExpatSeparatorChar 0xFFFF michael@0: michael@0: static const char16_t kUTF16[] = { 'U', 'T', 'F', '-', '1', '6', '\0' }; michael@0: michael@0: #ifdef PR_LOGGING michael@0: static PRLogModuleInfo * michael@0: GetExpatDriverLog() michael@0: { michael@0: static PRLogModuleInfo *sLog; michael@0: if (!sLog) michael@0: sLog = PR_NewLogModule("expatdriver"); michael@0: return sLog; michael@0: } michael@0: #endif michael@0: michael@0: /***************************** EXPAT CALL BACKS ******************************/ michael@0: // The callback handlers that get called from the expat parser. michael@0: michael@0: static void michael@0: Driver_HandleXMLDeclaration(void *aUserData, michael@0: const XML_Char *aVersion, michael@0: const XML_Char *aEncoding, michael@0: int aStandalone) michael@0: { michael@0: NS_ASSERTION(aUserData, "expat driver should exist"); michael@0: if (aUserData) { michael@0: nsExpatDriver* driver = static_cast(aUserData); michael@0: driver->HandleXMLDeclaration(aVersion, aEncoding, aStandalone); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: Driver_HandleStartElement(void *aUserData, michael@0: const XML_Char *aName, michael@0: const XML_Char **aAtts) michael@0: { michael@0: NS_ASSERTION(aUserData, "expat driver should exist"); michael@0: if (aUserData) { michael@0: static_cast(aUserData)->HandleStartElement(aName, michael@0: aAtts); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: Driver_HandleEndElement(void *aUserData, michael@0: const XML_Char *aName) michael@0: { michael@0: NS_ASSERTION(aUserData, "expat driver should exist"); michael@0: if (aUserData) { michael@0: static_cast(aUserData)->HandleEndElement(aName); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: Driver_HandleCharacterData(void *aUserData, michael@0: const XML_Char *aData, michael@0: int aLength) michael@0: { michael@0: NS_ASSERTION(aUserData, "expat driver should exist"); michael@0: if (aUserData) { michael@0: nsExpatDriver* driver = static_cast(aUserData); michael@0: driver->HandleCharacterData(aData, uint32_t(aLength)); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: Driver_HandleComment(void *aUserData, michael@0: const XML_Char *aName) michael@0: { michael@0: NS_ASSERTION(aUserData, "expat driver should exist"); michael@0: if(aUserData) { michael@0: static_cast(aUserData)->HandleComment(aName); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: Driver_HandleProcessingInstruction(void *aUserData, michael@0: const XML_Char *aTarget, michael@0: const XML_Char *aData) michael@0: { michael@0: NS_ASSERTION(aUserData, "expat driver should exist"); michael@0: if (aUserData) { michael@0: nsExpatDriver* driver = static_cast(aUserData); michael@0: driver->HandleProcessingInstruction(aTarget, aData); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: Driver_HandleDefault(void *aUserData, michael@0: const XML_Char *aData, michael@0: int aLength) michael@0: { michael@0: NS_ASSERTION(aUserData, "expat driver should exist"); michael@0: if (aUserData) { michael@0: nsExpatDriver* driver = static_cast(aUserData); michael@0: driver->HandleDefault(aData, uint32_t(aLength)); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: Driver_HandleStartCdataSection(void *aUserData) michael@0: { michael@0: NS_ASSERTION(aUserData, "expat driver should exist"); michael@0: if (aUserData) { michael@0: static_cast(aUserData)->HandleStartCdataSection(); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: Driver_HandleEndCdataSection(void *aUserData) michael@0: { michael@0: NS_ASSERTION(aUserData, "expat driver should exist"); michael@0: if (aUserData) { michael@0: static_cast(aUserData)->HandleEndCdataSection(); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: Driver_HandleStartDoctypeDecl(void *aUserData, michael@0: const XML_Char *aDoctypeName, michael@0: const XML_Char *aSysid, michael@0: const XML_Char *aPubid, michael@0: int aHasInternalSubset) michael@0: { michael@0: NS_ASSERTION(aUserData, "expat driver should exist"); michael@0: if (aUserData) { michael@0: static_cast(aUserData)-> michael@0: HandleStartDoctypeDecl(aDoctypeName, aSysid, aPubid, !!aHasInternalSubset); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: Driver_HandleEndDoctypeDecl(void *aUserData) michael@0: { michael@0: NS_ASSERTION(aUserData, "expat driver should exist"); michael@0: if (aUserData) { michael@0: static_cast(aUserData)->HandleEndDoctypeDecl(); michael@0: } michael@0: } michael@0: michael@0: static int michael@0: Driver_HandleExternalEntityRef(void *aExternalEntityRefHandler, michael@0: const XML_Char *aOpenEntityNames, michael@0: const XML_Char *aBase, michael@0: const XML_Char *aSystemId, michael@0: const XML_Char *aPublicId) michael@0: { michael@0: NS_ASSERTION(aExternalEntityRefHandler, "expat driver should exist"); michael@0: if (!aExternalEntityRefHandler) { michael@0: return 1; michael@0: } michael@0: michael@0: nsExpatDriver* driver = static_cast michael@0: (aExternalEntityRefHandler); michael@0: michael@0: return driver->HandleExternalEntityRef(aOpenEntityNames, aBase, aSystemId, michael@0: aPublicId); michael@0: } michael@0: michael@0: static void michael@0: Driver_HandleStartNamespaceDecl(void *aUserData, michael@0: const XML_Char *aPrefix, michael@0: const XML_Char *aUri) michael@0: { michael@0: NS_ASSERTION(aUserData, "expat driver should exist"); michael@0: if (aUserData) { michael@0: static_cast(aUserData)-> michael@0: HandleStartNamespaceDecl(aPrefix, aUri); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: Driver_HandleEndNamespaceDecl(void *aUserData, michael@0: const XML_Char *aPrefix) michael@0: { michael@0: NS_ASSERTION(aUserData, "expat driver should exist"); michael@0: if (aUserData) { michael@0: static_cast(aUserData)-> michael@0: HandleEndNamespaceDecl(aPrefix); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: Driver_HandleNotationDecl(void *aUserData, michael@0: const XML_Char *aNotationName, michael@0: const XML_Char *aBase, michael@0: const XML_Char *aSysid, michael@0: const XML_Char *aPubid) michael@0: { michael@0: NS_ASSERTION(aUserData, "expat driver should exist"); michael@0: if (aUserData) { michael@0: static_cast(aUserData)-> michael@0: HandleNotationDecl(aNotationName, aBase, aSysid, aPubid); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: Driver_HandleUnparsedEntityDecl(void *aUserData, michael@0: const XML_Char *aEntityName, michael@0: const XML_Char *aBase, michael@0: const XML_Char *aSysid, michael@0: const XML_Char *aPubid, michael@0: const XML_Char *aNotationName) michael@0: { michael@0: NS_ASSERTION(aUserData, "expat driver should exist"); michael@0: if (aUserData) { michael@0: static_cast(aUserData)-> michael@0: HandleUnparsedEntityDecl(aEntityName, aBase, aSysid, aPubid, michael@0: aNotationName); michael@0: } michael@0: } michael@0: michael@0: michael@0: /***************************** END CALL BACKS ********************************/ michael@0: michael@0: /***************************** CATALOG UTILS *********************************/ michael@0: michael@0: // Initially added for bug 113400 to switch from the remote "XHTML 1.0 plus michael@0: // MathML 2.0" DTD to the the lightweight customized version that Mozilla uses. michael@0: // Since Mozilla is not validating, no need to fetch a *huge* file at each michael@0: // click. michael@0: // XXX The cleanest solution here would be to fix Bug 98413: Implement XML michael@0: // Catalogs. michael@0: struct nsCatalogData { michael@0: const char* mPublicID; michael@0: const char* mLocalDTD; michael@0: const char* mAgentSheet; michael@0: }; michael@0: michael@0: // The order of this table is guestimated to be in the optimum order michael@0: static const nsCatalogData kCatalogTable[] = { michael@0: { "-//W3C//DTD XHTML 1.0 Transitional//EN", "htmlmathml-f.ent", nullptr }, michael@0: { "-//W3C//DTD XHTML 1.1//EN", "htmlmathml-f.ent", nullptr }, michael@0: { "-//W3C//DTD XHTML 1.0 Strict//EN", "htmlmathml-f.ent", nullptr }, michael@0: { "-//W3C//DTD XHTML 1.0 Frameset//EN", "htmlmathml-f.ent", nullptr }, michael@0: { "-//W3C//DTD XHTML Basic 1.0//EN", "htmlmathml-f.ent", nullptr }, michael@0: { "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN", "htmlmathml-f.ent", "resource://gre-resources/mathml.css" }, michael@0: { "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN", "htmlmathml-f.ent", "resource://gre-resources/mathml.css" }, michael@0: { "-//W3C//DTD MathML 2.0//EN", "htmlmathml-f.ent", "resource://gre-resources/mathml.css" }, michael@0: { "-//WAPFORUM//DTD XHTML Mobile 1.0//EN", "htmlmathml-f.ent", nullptr }, michael@0: { nullptr, nullptr, nullptr } michael@0: }; michael@0: michael@0: static const nsCatalogData* michael@0: LookupCatalogData(const char16_t* aPublicID) michael@0: { michael@0: nsDependentString publicID(aPublicID); michael@0: michael@0: // linear search for now since the number of entries is going to michael@0: // be negligible, and the fix for bug 98413 would get rid of this michael@0: // code anyway michael@0: const nsCatalogData* data = kCatalogTable; michael@0: while (data->mPublicID) { michael@0: if (publicID.EqualsASCII(data->mPublicID)) { michael@0: return data; michael@0: } michael@0: ++data; michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: // This function provides a resource URI to a local DTD michael@0: // in resource://gre/res/dtd/ which may or may not exist. michael@0: // If aCatalogData is provided, it is used to remap the michael@0: // DTD instead of taking the filename from the URI. michael@0: static void michael@0: GetLocalDTDURI(const nsCatalogData* aCatalogData, nsIURI* aDTD, michael@0: nsIURI** aResult) michael@0: { michael@0: NS_ASSERTION(aDTD, "Null parameter."); michael@0: michael@0: nsAutoCString fileName; michael@0: if (aCatalogData) { michael@0: // remap the DTD to a known local DTD michael@0: fileName.Assign(aCatalogData->mLocalDTD); michael@0: } michael@0: michael@0: if (fileName.IsEmpty()) { michael@0: // Try to see if the user has installed the DTD file -- we extract the michael@0: // filename.ext of the DTD here. Hence, for any DTD for which we have michael@0: // no predefined mapping, users just have to copy the DTD file to our michael@0: // special DTD directory and it will be picked. michael@0: nsCOMPtr dtdURL = do_QueryInterface(aDTD); michael@0: if (!dtdURL) { michael@0: return; michael@0: } michael@0: michael@0: dtdURL->GetFileName(fileName); michael@0: if (fileName.IsEmpty()) { michael@0: return; michael@0: } michael@0: } michael@0: michael@0: nsAutoCString respath("resource://gre/res/dtd/"); michael@0: respath += fileName; michael@0: NS_NewURI(aResult, respath); michael@0: } michael@0: michael@0: /***************************** END CATALOG UTILS *****************************/ michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsExpatDriver) michael@0: NS_INTERFACE_MAP_ENTRY(nsITokenizer) michael@0: NS_INTERFACE_MAP_ENTRY(nsIDTD) michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDTD) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(nsExpatDriver) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(nsExpatDriver) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION(nsExpatDriver, mSink, mExtendedSink) michael@0: michael@0: nsExpatDriver::nsExpatDriver() michael@0: : mExpatParser(nullptr), michael@0: mInCData(false), michael@0: mInInternalSubset(false), michael@0: mInExternalDTD(false), michael@0: mMadeFinalCallToExpat(false), michael@0: mIsFinalChunk(false), michael@0: mInternalState(NS_OK), michael@0: mExpatBuffered(0), michael@0: mCatalogData(nullptr), michael@0: mInnerWindowID(0) michael@0: { michael@0: } michael@0: michael@0: nsExpatDriver::~nsExpatDriver() michael@0: { michael@0: if (mExpatParser) { michael@0: XML_ParserFree(mExpatParser); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsExpatDriver::HandleStartElement(const char16_t *aValue, michael@0: const char16_t **aAtts) michael@0: { michael@0: NS_ASSERTION(mSink, "content sink not found!"); michael@0: michael@0: // Calculate the total number of elements in aAtts. michael@0: // XML_GetSpecifiedAttributeCount will only give us the number of specified michael@0: // attrs (twice that number, actually), so we have to check for default attrs michael@0: // ourselves. michael@0: uint32_t attrArrayLength; michael@0: for (attrArrayLength = XML_GetSpecifiedAttributeCount(mExpatParser); michael@0: aAtts[attrArrayLength]; michael@0: attrArrayLength += 2) { michael@0: // Just looping till we find out what the length is michael@0: } michael@0: michael@0: if (mSink) { michael@0: nsresult rv = mSink-> michael@0: HandleStartElement(aValue, aAtts, attrArrayLength, michael@0: XML_GetIdAttributeIndex(mExpatParser), michael@0: XML_GetCurrentLineNumber(mExpatParser)); michael@0: MaybeStopParser(rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsExpatDriver::HandleEndElement(const char16_t *aValue) michael@0: { michael@0: NS_ASSERTION(mSink, "content sink not found!"); michael@0: NS_ASSERTION(mInternalState != NS_ERROR_HTMLPARSER_BLOCK, michael@0: "Shouldn't block from HandleStartElement."); michael@0: michael@0: if (mSink && mInternalState != NS_ERROR_HTMLPARSER_STOPPARSING) { michael@0: nsresult rv = mSink->HandleEndElement(aValue); michael@0: MaybeStopParser(rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsExpatDriver::HandleCharacterData(const char16_t *aValue, michael@0: const uint32_t aLength) michael@0: { michael@0: NS_ASSERTION(mSink, "content sink not found!"); michael@0: michael@0: if (mInCData) { michael@0: mCDataText.Append(aValue, aLength); michael@0: } michael@0: else if (mSink) { michael@0: nsresult rv = mSink->HandleCharacterData(aValue, aLength); michael@0: MaybeStopParser(rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsExpatDriver::HandleComment(const char16_t *aValue) michael@0: { michael@0: NS_ASSERTION(mSink, "content sink not found!"); michael@0: michael@0: if (mInExternalDTD) { michael@0: // Ignore comments from external DTDs michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (mInInternalSubset) { michael@0: mInternalSubset.AppendLiteral(""); michael@0: } michael@0: else if (mSink) { michael@0: nsresult rv = mSink->HandleComment(aValue); michael@0: MaybeStopParser(rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsExpatDriver::HandleProcessingInstruction(const char16_t *aTarget, michael@0: const char16_t *aData) michael@0: { michael@0: NS_ASSERTION(mSink, "content sink not found!"); michael@0: michael@0: if (mInExternalDTD) { michael@0: // Ignore PIs in external DTDs for now. Eventually we want to michael@0: // pass them to the sink in a way that doesn't put them in the DOM michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (mInInternalSubset) { michael@0: mInternalSubset.AppendLiteral(""); michael@0: } michael@0: else if (mSink) { michael@0: nsresult rv = mSink->HandleProcessingInstruction(aTarget, aData); michael@0: MaybeStopParser(rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsExpatDriver::HandleXMLDeclaration(const char16_t *aVersion, michael@0: const char16_t *aEncoding, michael@0: int32_t aStandalone) michael@0: { michael@0: if (mSink) { michael@0: nsresult rv = mSink->HandleXMLDeclaration(aVersion, aEncoding, aStandalone); michael@0: MaybeStopParser(rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsExpatDriver::HandleDefault(const char16_t *aValue, michael@0: const uint32_t aLength) michael@0: { michael@0: NS_ASSERTION(mSink, "content sink not found!"); michael@0: michael@0: if (mInExternalDTD) { michael@0: // Ignore newlines in external DTDs michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (mInInternalSubset) { michael@0: mInternalSubset.Append(aValue, aLength); michael@0: } michael@0: else if (mSink) { michael@0: uint32_t i; michael@0: nsresult rv = mInternalState; michael@0: for (i = 0; i < aLength && NS_SUCCEEDED(rv); ++i) { michael@0: if (aValue[i] == '\n' || aValue[i] == '\r') { michael@0: rv = mSink->HandleCharacterData(&aValue[i], 1); michael@0: } michael@0: } michael@0: MaybeStopParser(rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsExpatDriver::HandleStartCdataSection() michael@0: { michael@0: mInCData = true; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsExpatDriver::HandleEndCdataSection() michael@0: { michael@0: NS_ASSERTION(mSink, "content sink not found!"); michael@0: michael@0: mInCData = false; michael@0: if (mSink) { michael@0: nsresult rv = mSink->HandleCDataSection(mCDataText.get(), michael@0: mCDataText.Length()); michael@0: MaybeStopParser(rv); michael@0: } michael@0: mCDataText.Truncate(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsExpatDriver::HandleStartNamespaceDecl(const char16_t* aPrefix, michael@0: const char16_t* aUri) michael@0: { michael@0: if (mExtendedSink) { michael@0: nsresult rv = mExtendedSink->HandleStartNamespaceDecl(aPrefix, aUri); michael@0: MaybeStopParser(rv); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsExpatDriver::HandleEndNamespaceDecl(const char16_t* aPrefix) michael@0: { michael@0: if (mExtendedSink && mInternalState != NS_ERROR_HTMLPARSER_STOPPARSING) { michael@0: nsresult rv = mExtendedSink->HandleEndNamespaceDecl(aPrefix); michael@0: MaybeStopParser(rv); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsExpatDriver::HandleNotationDecl(const char16_t* aNotationName, michael@0: const char16_t* aBase, michael@0: const char16_t* aSysid, michael@0: const char16_t* aPubid) michael@0: { michael@0: if (mExtendedSink) { michael@0: nsresult rv = mExtendedSink->HandleNotationDecl(aNotationName, aSysid, michael@0: aPubid); michael@0: MaybeStopParser(rv); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsExpatDriver::HandleUnparsedEntityDecl(const char16_t* aEntityName, michael@0: const char16_t* aBase, michael@0: const char16_t* aSysid, michael@0: const char16_t* aPubid, michael@0: const char16_t* aNotationName) michael@0: { michael@0: if (mExtendedSink) { michael@0: nsresult rv = mExtendedSink->HandleUnparsedEntityDecl(aEntityName, michael@0: aSysid, michael@0: aPubid, michael@0: aNotationName); michael@0: MaybeStopParser(rv); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsExpatDriver::HandleStartDoctypeDecl(const char16_t* aDoctypeName, michael@0: const char16_t* aSysid, michael@0: const char16_t* aPubid, michael@0: bool aHasInternalSubset) michael@0: { michael@0: mDoctypeName = aDoctypeName; michael@0: mSystemID = aSysid; michael@0: mPublicID = aPubid; michael@0: michael@0: if (mExtendedSink) { michael@0: nsresult rv = mExtendedSink->HandleStartDTD(aDoctypeName, aSysid, aPubid); michael@0: MaybeStopParser(rv); michael@0: } michael@0: michael@0: if (aHasInternalSubset) { michael@0: // Consuming a huge internal subset translates to numerous michael@0: // allocations. In an effort to avoid too many allocations michael@0: // setting mInternalSubset's capacity to be 1K ( just a guesstimate! ). michael@0: mInInternalSubset = true; michael@0: mInternalSubset.SetCapacity(1024); michael@0: } else { michael@0: // Distinguish missing internal subset from an empty one michael@0: mInternalSubset.SetIsVoid(true); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsExpatDriver::HandleEndDoctypeDecl() michael@0: { michael@0: NS_ASSERTION(mSink, "content sink not found!"); michael@0: michael@0: mInInternalSubset = false; michael@0: michael@0: if (mSink) { michael@0: // let the sink know any additional knowledge that we have about the michael@0: // document (currently, from bug 124570, we only expect to pass additional michael@0: // agent sheets needed to layout the XML vocabulary of the document) michael@0: nsCOMPtr data; michael@0: if (mCatalogData && mCatalogData->mAgentSheet) { michael@0: NS_NewURI(getter_AddRefs(data), mCatalogData->mAgentSheet); michael@0: } michael@0: michael@0: // Note: mInternalSubset already doesn't include the [] around it. michael@0: nsresult rv = mSink->HandleDoctypeDecl(mInternalSubset, mDoctypeName, michael@0: mSystemID, mPublicID, data); michael@0: MaybeStopParser(rv); michael@0: } michael@0: michael@0: mInternalSubset.SetCapacity(0); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: static NS_METHOD michael@0: ExternalDTDStreamReaderFunc(nsIUnicharInputStream* aIn, michael@0: void* aClosure, michael@0: const char16_t* aFromSegment, michael@0: uint32_t aToOffset, michael@0: uint32_t aCount, michael@0: uint32_t *aWriteCount) michael@0: { michael@0: // Pass the buffer to expat for parsing. michael@0: if (XML_Parse((XML_Parser)aClosure, (const char *)aFromSegment, michael@0: aCount * sizeof(char16_t), 0) == XML_STATUS_OK) { michael@0: *aWriteCount = aCount; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: *aWriteCount = 0; michael@0: michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: int michael@0: nsExpatDriver::HandleExternalEntityRef(const char16_t *openEntityNames, michael@0: const char16_t *base, michael@0: const char16_t *systemId, michael@0: const char16_t *publicId) michael@0: { michael@0: if (mInInternalSubset && !mInExternalDTD && openEntityNames) { michael@0: mInternalSubset.Append(char16_t('%')); michael@0: mInternalSubset.Append(nsDependentString(openEntityNames)); michael@0: mInternalSubset.Append(char16_t(';')); michael@0: } michael@0: michael@0: // Load the external entity into a buffer. michael@0: nsCOMPtr in; michael@0: nsAutoString absURL; michael@0: nsresult rv = OpenInputStreamFromExternalDTD(publicId, systemId, base, michael@0: getter_AddRefs(in), absURL); michael@0: if (NS_FAILED(rv)) { michael@0: #ifdef DEBUG michael@0: nsCString message("Failed to open external DTD: publicId \""); michael@0: AppendUTF16toUTF8(publicId, message); michael@0: message += "\" systemId \""; michael@0: AppendUTF16toUTF8(systemId, message); michael@0: message += "\" base \""; michael@0: AppendUTF16toUTF8(base, message); michael@0: message += "\" URL \""; michael@0: AppendUTF16toUTF8(absURL, message); michael@0: message += "\""; michael@0: NS_WARNING(message.get()); michael@0: #endif michael@0: return 1; michael@0: } michael@0: michael@0: nsCOMPtr uniIn; michael@0: rv = nsSimpleUnicharStreamFactory::GetInstance()-> michael@0: CreateInstanceFromUTF8Stream(in, getter_AddRefs(uniIn)); michael@0: NS_ENSURE_SUCCESS(rv, 1); michael@0: michael@0: int result = 1; michael@0: if (uniIn) { michael@0: XML_Parser entParser = XML_ExternalEntityParserCreate(mExpatParser, 0, michael@0: kUTF16); michael@0: if (entParser) { michael@0: XML_SetBase(entParser, absURL.get()); michael@0: michael@0: mInExternalDTD = true; michael@0: michael@0: uint32_t totalRead; michael@0: do { michael@0: rv = uniIn->ReadSegments(ExternalDTDStreamReaderFunc, entParser, michael@0: uint32_t(-1), &totalRead); michael@0: } while (NS_SUCCEEDED(rv) && totalRead > 0); michael@0: michael@0: result = XML_Parse(entParser, nullptr, 0, 1); michael@0: michael@0: mInExternalDTD = false; michael@0: michael@0: XML_ParserFree(entParser); michael@0: } michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: nsresult michael@0: nsExpatDriver::OpenInputStreamFromExternalDTD(const char16_t* aFPIStr, michael@0: const char16_t* aURLStr, michael@0: const char16_t* aBaseURL, michael@0: nsIInputStream** aStream, michael@0: nsAString& aAbsURL) michael@0: { michael@0: nsCOMPtr baseURI; michael@0: nsresult rv = NS_NewURI(getter_AddRefs(baseURI), michael@0: NS_ConvertUTF16toUTF8(aBaseURL)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr uri; michael@0: rv = NS_NewURI(getter_AddRefs(uri), NS_ConvertUTF16toUTF8(aURLStr), nullptr, michael@0: baseURI); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // check if it is alright to load this uri michael@0: bool isChrome = false; michael@0: uri->SchemeIs("chrome", &isChrome); michael@0: if (!isChrome) { michael@0: // since the url is not a chrome url, check to see if we can map the DTD michael@0: // to a known local DTD, or if a DTD file of the same name exists in the michael@0: // special DTD directory michael@0: if (aFPIStr) { michael@0: // see if the Formal Public Identifier (FPI) maps to a catalog entry michael@0: mCatalogData = LookupCatalogData(aFPIStr); michael@0: } michael@0: michael@0: nsCOMPtr localURI; michael@0: GetLocalDTDURI(mCatalogData, uri, getter_AddRefs(localURI)); michael@0: if (!localURI) { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: localURI.swap(uri); michael@0: } michael@0: michael@0: nsCOMPtr doc; michael@0: NS_ASSERTION(mSink == nsCOMPtr(do_QueryInterface(mOriginalSink)), michael@0: "In nsExpatDriver::OpenInputStreamFromExternalDTD: " michael@0: "mOriginalSink not the same object as mSink?"); michael@0: if (mOriginalSink) michael@0: doc = do_QueryInterface(mOriginalSink->GetTarget()); michael@0: int16_t shouldLoad = nsIContentPolicy::ACCEPT; michael@0: rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_DTD, michael@0: uri, michael@0: (doc ? doc->NodePrincipal() : nullptr), michael@0: doc, michael@0: EmptyCString(), //mime guess michael@0: nullptr, //extra michael@0: &shouldLoad); michael@0: if (NS_FAILED(rv)) return rv; michael@0: if (NS_CP_REJECTED(shouldLoad)) { michael@0: // Disallowed by content policy michael@0: return NS_ERROR_CONTENT_BLOCKED; michael@0: } michael@0: michael@0: nsAutoCString absURL; michael@0: uri->GetSpec(absURL); michael@0: michael@0: CopyUTF8toUTF16(absURL, aAbsURL); michael@0: michael@0: nsCOMPtr channel; michael@0: rv = NS_NewChannel(getter_AddRefs(channel), uri); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: channel->SetContentType(NS_LITERAL_CSTRING("application/xml")); michael@0: return channel->Open(aStream); michael@0: } michael@0: michael@0: static nsresult michael@0: CreateErrorText(const char16_t* aDescription, michael@0: const char16_t* aSourceURL, michael@0: const uint32_t aLineNumber, michael@0: const uint32_t aColNumber, michael@0: nsString& aErrorString) michael@0: { michael@0: aErrorString.Truncate(); michael@0: michael@0: nsAutoString msg; michael@0: nsresult rv = michael@0: nsParserMsgUtils::GetLocalizedStringByName(XMLPARSER_PROPERTIES, michael@0: "XMLParsingError", msg); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // XML Parsing Error: %1$S\nLocation: %2$S\nLine Number %3$u, Column %4$u: michael@0: char16_t *message = nsTextFormatter::smprintf(msg.get(), aDescription, michael@0: aSourceURL, aLineNumber, michael@0: aColNumber); michael@0: if (!message) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: aErrorString.Assign(message); michael@0: nsTextFormatter::smprintf_free(message); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: static nsresult michael@0: AppendErrorPointer(const int32_t aColNumber, michael@0: const char16_t *aSourceLine, michael@0: nsString& aSourceString) michael@0: { michael@0: aSourceString.Append(char16_t('\n')); michael@0: michael@0: // Last character will be '^'. michael@0: int32_t last = aColNumber - 1; michael@0: int32_t i; michael@0: uint32_t minuses = 0; michael@0: for (i = 0; i < last; ++i) { michael@0: if (aSourceLine[i] == '\t') { michael@0: // Since this uses |white-space: pre;| a tab stop equals 8 spaces. michael@0: uint32_t add = 8 - (minuses % 8); michael@0: aSourceString.AppendASCII("--------", add); michael@0: minuses += add; michael@0: } michael@0: else { michael@0: aSourceString.Append(char16_t('-')); michael@0: ++minuses; michael@0: } michael@0: } michael@0: aSourceString.Append(char16_t('^')); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsExpatDriver::HandleError() michael@0: { michael@0: int32_t code = XML_GetErrorCode(mExpatParser); michael@0: NS_ASSERTION(code > XML_ERROR_NONE, "unexpected XML error code"); michael@0: michael@0: // Map Expat error code to an error string michael@0: // XXX Deal with error returns. michael@0: nsAutoString description; michael@0: nsParserMsgUtils::GetLocalizedStringByID(XMLPARSER_PROPERTIES, code, michael@0: description); michael@0: michael@0: if (code == XML_ERROR_TAG_MISMATCH) { michael@0: /** michael@0: * Expat can send the following: michael@0: * localName michael@0: * namespaceURIlocalName michael@0: * namespaceURIlocalNameprefix michael@0: * michael@0: * and we use 0xFFFF for the . michael@0: * michael@0: */ michael@0: const char16_t *mismatch = MOZ_XML_GetMismatchedTag(mExpatParser); michael@0: const char16_t *uriEnd = nullptr; michael@0: const char16_t *nameEnd = nullptr; michael@0: const char16_t *pos; michael@0: for (pos = mismatch; *pos; ++pos) { michael@0: if (*pos == kExpatSeparatorChar) { michael@0: if (uriEnd) { michael@0: nameEnd = pos; michael@0: } michael@0: else { michael@0: uriEnd = pos; michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsAutoString tagName; michael@0: if (uriEnd && nameEnd) { michael@0: // We have a prefix. michael@0: tagName.Append(nameEnd + 1, pos - nameEnd - 1); michael@0: tagName.Append(char16_t(':')); michael@0: } michael@0: const char16_t *nameStart = uriEnd ? uriEnd + 1 : mismatch; michael@0: tagName.Append(nameStart, (nameEnd ? nameEnd : pos) - nameStart); michael@0: michael@0: nsAutoString msg; michael@0: nsParserMsgUtils::GetLocalizedStringByName(XMLPARSER_PROPERTIES, michael@0: "Expected", msg); michael@0: michael@0: // . Expected: . michael@0: char16_t *message = nsTextFormatter::smprintf(msg.get(), tagName.get()); michael@0: if (!message) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: description.Append(message); michael@0: michael@0: nsTextFormatter::smprintf_free(message); michael@0: } michael@0: michael@0: // Adjust the column number so that it is one based rather than zero based. michael@0: uint32_t colNumber = XML_GetCurrentColumnNumber(mExpatParser) + 1; michael@0: uint32_t lineNumber = XML_GetCurrentLineNumber(mExpatParser); michael@0: michael@0: nsAutoString errorText; michael@0: CreateErrorText(description.get(), XML_GetBase(mExpatParser), lineNumber, michael@0: colNumber, errorText); michael@0: michael@0: NS_ASSERTION(mSink, "no sink?"); michael@0: michael@0: nsAutoString sourceText(mLastLine); michael@0: AppendErrorPointer(colNumber, mLastLine.get(), sourceText); michael@0: michael@0: // Try to create and initialize the script error. michael@0: nsCOMPtr serr(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID)); michael@0: nsresult rv = NS_ERROR_FAILURE; michael@0: if (serr) { michael@0: rv = serr->InitWithWindowID(description, michael@0: mURISpec, michael@0: mLastLine, michael@0: lineNumber, colNumber, michael@0: nsIScriptError::errorFlag, "malformed-xml", michael@0: mInnerWindowID); michael@0: } michael@0: michael@0: // If it didn't initialize, we can't do any logging. michael@0: bool shouldReportError = NS_SUCCEEDED(rv); michael@0: michael@0: if (mSink && shouldReportError) { michael@0: rv = mSink->ReportError(errorText.get(), michael@0: sourceText.get(), michael@0: serr, michael@0: &shouldReportError); michael@0: if (NS_FAILED(rv)) { michael@0: shouldReportError = true; michael@0: } michael@0: } michael@0: michael@0: if (shouldReportError) { michael@0: nsCOMPtr cs michael@0: (do_GetService(NS_CONSOLESERVICE_CONTRACTID)); michael@0: if (cs) { michael@0: cs->LogMessage(serr); michael@0: } michael@0: } michael@0: michael@0: return NS_ERROR_HTMLPARSER_STOPPARSING; michael@0: } michael@0: michael@0: void michael@0: nsExpatDriver::ParseBuffer(const char16_t *aBuffer, michael@0: uint32_t aLength, michael@0: bool aIsFinal, michael@0: uint32_t *aConsumed) michael@0: { michael@0: NS_ASSERTION((aBuffer && aLength != 0) || (!aBuffer && aLength == 0), "?"); michael@0: NS_ASSERTION(mInternalState != NS_OK || aIsFinal || aBuffer, michael@0: "Useless call, we won't call Expat"); michael@0: NS_PRECONDITION(!BlockedOrInterrupted() || !aBuffer, michael@0: "Non-null buffer when resuming"); michael@0: NS_PRECONDITION(XML_GetCurrentByteIndex(mExpatParser) % sizeof(char16_t) == 0, michael@0: "Consumed part of a char16_t?"); michael@0: michael@0: if (mExpatParser && (mInternalState == NS_OK || BlockedOrInterrupted())) { michael@0: int32_t parserBytesBefore = XML_GetCurrentByteIndex(mExpatParser); michael@0: NS_ASSERTION(parserBytesBefore >= 0, "Unexpected value"); michael@0: michael@0: XML_Status status; michael@0: if (BlockedOrInterrupted()) { michael@0: mInternalState = NS_OK; // Resume in case we're blocked. michael@0: status = XML_ResumeParser(mExpatParser); michael@0: } michael@0: else { michael@0: status = XML_Parse(mExpatParser, michael@0: reinterpret_cast(aBuffer), michael@0: aLength * sizeof(char16_t), aIsFinal); michael@0: } michael@0: michael@0: int32_t parserBytesConsumed = XML_GetCurrentByteIndex(mExpatParser); michael@0: michael@0: NS_ASSERTION(parserBytesConsumed >= 0, "Unexpected value"); michael@0: NS_ASSERTION(parserBytesConsumed >= parserBytesBefore, michael@0: "How'd this happen?"); michael@0: NS_ASSERTION(parserBytesConsumed % sizeof(char16_t) == 0, michael@0: "Consumed part of a char16_t?"); michael@0: michael@0: // Consumed something. michael@0: *aConsumed = (parserBytesConsumed - parserBytesBefore) / sizeof(char16_t); michael@0: NS_ASSERTION(*aConsumed <= aLength + mExpatBuffered, michael@0: "Too many bytes consumed?"); michael@0: michael@0: NS_ASSERTION(status != XML_STATUS_SUSPENDED || BlockedOrInterrupted(), michael@0: "Inconsistent expat suspension state."); michael@0: michael@0: if (status == XML_STATUS_ERROR) { michael@0: mInternalState = NS_ERROR_HTMLPARSER_STOPPARSING; michael@0: } michael@0: } michael@0: else { michael@0: *aConsumed = 0; michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsExpatDriver::ConsumeToken(nsScanner& aScanner, bool& aFlushTokens) michael@0: { michael@0: // We keep the scanner pointing to the position where Expat will start michael@0: // parsing. michael@0: nsScannerIterator currentExpatPosition; michael@0: aScanner.CurrentPosition(currentExpatPosition); michael@0: michael@0: // This is the start of the first buffer that we need to pass to Expat. michael@0: nsScannerIterator start = currentExpatPosition; michael@0: start.advance(mExpatBuffered); michael@0: michael@0: // This is the end of the last buffer (at this point, more data could come in michael@0: // later). michael@0: nsScannerIterator end; michael@0: aScanner.EndReading(end); michael@0: michael@0: PR_LOG(GetExpatDriverLog(), PR_LOG_DEBUG, michael@0: ("Remaining in expat's buffer: %i, remaining in scanner: %i.", michael@0: mExpatBuffered, Distance(start, end))); michael@0: michael@0: // We want to call Expat if we have more buffers, or if we know there won't michael@0: // be more buffers (and so we want to flush the remaining data), or if we're michael@0: // currently blocked and there's data in Expat's buffer. michael@0: while (start != end || (mIsFinalChunk && !mMadeFinalCallToExpat) || michael@0: (BlockedOrInterrupted() && mExpatBuffered > 0)) { michael@0: bool noMoreBuffers = start == end && mIsFinalChunk; michael@0: bool blocked = BlockedOrInterrupted(); michael@0: michael@0: const char16_t *buffer; michael@0: uint32_t length; michael@0: if (blocked || noMoreBuffers) { michael@0: // If we're blocked we just resume Expat so we don't need a buffer, if michael@0: // there aren't any more buffers we pass a null buffer to Expat. michael@0: buffer = nullptr; michael@0: length = 0; michael@0: michael@0: #if defined(PR_LOGGING) || defined (DEBUG) michael@0: if (blocked) { michael@0: PR_LOG(GetExpatDriverLog(), PR_LOG_DEBUG, michael@0: ("Resuming Expat, will parse data remaining in Expat's " michael@0: "buffer.\nContent of Expat's buffer:\n-----\n%s\n-----\n", michael@0: NS_ConvertUTF16toUTF8(currentExpatPosition.get(), michael@0: mExpatBuffered).get())); michael@0: } michael@0: else { michael@0: NS_ASSERTION(mExpatBuffered == Distance(currentExpatPosition, end), michael@0: "Didn't pass all the data to Expat?"); michael@0: PR_LOG(GetExpatDriverLog(), PR_LOG_DEBUG, michael@0: ("Last call to Expat, will parse data remaining in Expat's " michael@0: "buffer.\nContent of Expat's buffer:\n-----\n%s\n-----\n", michael@0: NS_ConvertUTF16toUTF8(currentExpatPosition.get(), michael@0: mExpatBuffered).get())); michael@0: } michael@0: #endif michael@0: } michael@0: else { michael@0: buffer = start.get(); michael@0: length = uint32_t(start.size_forward()); michael@0: michael@0: PR_LOG(GetExpatDriverLog(), PR_LOG_DEBUG, michael@0: ("Calling Expat, will parse data remaining in Expat's buffer and " michael@0: "new data.\nContent of Expat's buffer:\n-----\n%s\n-----\nNew " michael@0: "data:\n-----\n%s\n-----\n", michael@0: NS_ConvertUTF16toUTF8(currentExpatPosition.get(), michael@0: mExpatBuffered).get(), michael@0: NS_ConvertUTF16toUTF8(start.get(), length).get())); michael@0: } michael@0: michael@0: uint32_t consumed; michael@0: ParseBuffer(buffer, length, noMoreBuffers, &consumed); michael@0: if (consumed > 0) { michael@0: nsScannerIterator oldExpatPosition = currentExpatPosition; michael@0: currentExpatPosition.advance(consumed); michael@0: michael@0: // We consumed some data, we want to store the last line of data that michael@0: // was consumed in case we run into an error (to show the line in which michael@0: // the error occurred). michael@0: michael@0: // The length of the last line that Expat has parsed. michael@0: XML_Size lastLineLength = XML_GetCurrentColumnNumber(mExpatParser); michael@0: michael@0: if (lastLineLength <= consumed) { michael@0: // The length of the last line was less than what expat consumed, so michael@0: // there was at least one line break in the consumed data. Store the michael@0: // last line until the point where we stopped parsing. michael@0: nsScannerIterator startLastLine = currentExpatPosition; michael@0: startLastLine.advance(-((ptrdiff_t)lastLineLength)); michael@0: CopyUnicodeTo(startLastLine, currentExpatPosition, mLastLine); michael@0: } michael@0: else { michael@0: // There was no line break in the consumed data, append the consumed michael@0: // data. michael@0: AppendUnicodeTo(oldExpatPosition, currentExpatPosition, mLastLine); michael@0: } michael@0: } michael@0: michael@0: mExpatBuffered += length - consumed; michael@0: michael@0: if (BlockedOrInterrupted()) { michael@0: PR_LOG(GetExpatDriverLog(), PR_LOG_DEBUG, michael@0: ("Blocked or interrupted parser (probably for loading linked " michael@0: "stylesheets or scripts).")); michael@0: michael@0: aScanner.SetPosition(currentExpatPosition, true); michael@0: aScanner.Mark(); michael@0: michael@0: return mInternalState; michael@0: } michael@0: michael@0: if (noMoreBuffers && mExpatBuffered == 0) { michael@0: mMadeFinalCallToExpat = true; michael@0: } michael@0: michael@0: if (NS_FAILED(mInternalState)) { michael@0: if (XML_GetErrorCode(mExpatParser) != XML_ERROR_NONE) { michael@0: NS_ASSERTION(mInternalState == NS_ERROR_HTMLPARSER_STOPPARSING, michael@0: "Unexpected error"); michael@0: michael@0: // Look for the next newline after the last one we consumed michael@0: nsScannerIterator lastLine = currentExpatPosition; michael@0: while (lastLine != end) { michael@0: length = uint32_t(lastLine.size_forward()); michael@0: uint32_t endOffset = 0; michael@0: const char16_t *buffer = lastLine.get(); michael@0: while (endOffset < length && buffer[endOffset] != '\n' && michael@0: buffer[endOffset] != '\r') { michael@0: ++endOffset; michael@0: } michael@0: mLastLine.Append(Substring(buffer, buffer + endOffset)); michael@0: if (endOffset < length) { michael@0: // We found a newline. michael@0: break; michael@0: } michael@0: michael@0: lastLine.advance(length); michael@0: } michael@0: michael@0: HandleError(); michael@0: } michael@0: michael@0: return mInternalState; michael@0: } michael@0: michael@0: // Either we have more buffers, or we were blocked (and we'll flush in the michael@0: // next iteration), or we should have emptied Expat's buffer. michael@0: NS_ASSERTION(!noMoreBuffers || blocked || michael@0: (mExpatBuffered == 0 && currentExpatPosition == end), michael@0: "Unreachable data left in Expat's buffer"); michael@0: michael@0: start.advance(length); michael@0: michael@0: // It's possible for start to have passed end if we received more data michael@0: // (e.g. if we spun the event loop in an inline script). Reload end now michael@0: // to compensate. michael@0: aScanner.EndReading(end); michael@0: } michael@0: michael@0: aScanner.SetPosition(currentExpatPosition, true); michael@0: aScanner.Mark(); michael@0: michael@0: PR_LOG(GetExpatDriverLog(), PR_LOG_DEBUG, michael@0: ("Remaining in expat's buffer: %i, remaining in scanner: %i.", michael@0: mExpatBuffered, Distance(currentExpatPosition, end))); michael@0: michael@0: return NS_SUCCEEDED(mInternalState) ? kEOF : NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsExpatDriver::WillBuildModel(const CParserContext& aParserContext, michael@0: nsITokenizer* aTokenizer, michael@0: nsIContentSink* aSink) michael@0: { michael@0: mSink = do_QueryInterface(aSink); michael@0: if (!mSink) { michael@0: NS_ERROR("nsExpatDriver didn't get an nsIExpatSink"); michael@0: // Make sure future calls to us bail out as needed michael@0: mInternalState = NS_ERROR_UNEXPECTED; michael@0: return mInternalState; michael@0: } michael@0: michael@0: mOriginalSink = aSink; michael@0: michael@0: static const XML_Memory_Handling_Suite memsuite = michael@0: { michael@0: (void *(*)(size_t))PR_Malloc, michael@0: (void *(*)(void *, size_t))PR_Realloc, michael@0: PR_Free michael@0: }; michael@0: michael@0: static const char16_t kExpatSeparator[] = { kExpatSeparatorChar, '\0' }; michael@0: michael@0: mExpatParser = XML_ParserCreate_MM(kUTF16, &memsuite, kExpatSeparator); michael@0: NS_ENSURE_TRUE(mExpatParser, NS_ERROR_FAILURE); michael@0: michael@0: XML_SetReturnNSTriplet(mExpatParser, XML_TRUE); michael@0: michael@0: #ifdef XML_DTD michael@0: XML_SetParamEntityParsing(mExpatParser, XML_PARAM_ENTITY_PARSING_ALWAYS); michael@0: #endif michael@0: michael@0: mURISpec = aParserContext.mScanner->GetFilename(); michael@0: michael@0: XML_SetBase(mExpatParser, mURISpec.get()); michael@0: michael@0: nsCOMPtr doc = do_QueryInterface(mOriginalSink->GetTarget()); michael@0: if (doc) { michael@0: nsCOMPtr win = doc->GetWindow(); michael@0: if (!win) { michael@0: bool aHasHadScriptHandlingObject; michael@0: nsIScriptGlobalObject *global = michael@0: doc->GetScriptHandlingObject(aHasHadScriptHandlingObject); michael@0: if (global) { michael@0: win = do_QueryInterface(global); michael@0: } michael@0: } michael@0: if (win && !win->IsInnerWindow()) { michael@0: win = win->GetCurrentInnerWindow(); michael@0: } michael@0: if (win) { michael@0: mInnerWindowID = win->WindowID(); michael@0: } michael@0: } michael@0: michael@0: // Set up the callbacks michael@0: XML_SetXmlDeclHandler(mExpatParser, Driver_HandleXMLDeclaration); michael@0: XML_SetElementHandler(mExpatParser, Driver_HandleStartElement, michael@0: Driver_HandleEndElement); michael@0: XML_SetCharacterDataHandler(mExpatParser, Driver_HandleCharacterData); michael@0: XML_SetProcessingInstructionHandler(mExpatParser, michael@0: Driver_HandleProcessingInstruction); michael@0: XML_SetDefaultHandlerExpand(mExpatParser, Driver_HandleDefault); michael@0: XML_SetExternalEntityRefHandler(mExpatParser, michael@0: (XML_ExternalEntityRefHandler) michael@0: Driver_HandleExternalEntityRef); michael@0: XML_SetExternalEntityRefHandlerArg(mExpatParser, this); michael@0: XML_SetCommentHandler(mExpatParser, Driver_HandleComment); michael@0: XML_SetCdataSectionHandler(mExpatParser, Driver_HandleStartCdataSection, michael@0: Driver_HandleEndCdataSection); michael@0: michael@0: XML_SetParamEntityParsing(mExpatParser, michael@0: XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE); michael@0: XML_SetDoctypeDeclHandler(mExpatParser, Driver_HandleStartDoctypeDecl, michael@0: Driver_HandleEndDoctypeDecl); michael@0: michael@0: // If the sink is an nsIExtendedExpatSink, michael@0: // register some addtional handlers. michael@0: mExtendedSink = do_QueryInterface(mSink); michael@0: if (mExtendedSink) { michael@0: XML_SetNamespaceDeclHandler(mExpatParser, michael@0: Driver_HandleStartNamespaceDecl, michael@0: Driver_HandleEndNamespaceDecl); michael@0: XML_SetUnparsedEntityDeclHandler(mExpatParser, michael@0: Driver_HandleUnparsedEntityDecl); michael@0: XML_SetNotationDeclHandler(mExpatParser, michael@0: Driver_HandleNotationDecl); michael@0: } michael@0: michael@0: // Set up the user data. michael@0: XML_SetUserData(mExpatParser, this); michael@0: michael@0: // XML must detect invalid character convertion michael@0: aParserContext.mScanner->OverrideReplacementCharacter(0xffff); michael@0: michael@0: return mInternalState; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsExpatDriver::BuildModel(nsITokenizer* aTokenizer, nsIContentSink* aSink) michael@0: { michael@0: return mInternalState; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsExpatDriver::DidBuildModel(nsresult anErrorCode) michael@0: { michael@0: mOriginalSink = nullptr; michael@0: mSink = nullptr; michael@0: mExtendedSink = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsExpatDriver::WillTokenize(bool aIsFinalChunk) michael@0: { michael@0: mIsFinalChunk = aIsFinalChunk; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(void) michael@0: nsExpatDriver::Terminate() michael@0: { michael@0: // XXX - not sure what happens to the unparsed data. michael@0: if (mExpatParser) { michael@0: XML_StopParser(mExpatParser, XML_FALSE); michael@0: } michael@0: mInternalState = NS_ERROR_HTMLPARSER_STOPPARSING; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(int32_t) michael@0: nsExpatDriver::GetType() michael@0: { michael@0: return NS_IPARSER_FLAG_XML; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(nsDTDMode) michael@0: nsExpatDriver::GetMode() const michael@0: { michael@0: return eDTDMode_full_standards; michael@0: } michael@0: michael@0: /*************************** Unused methods **********************************/ michael@0: michael@0: NS_IMETHODIMP_(bool) michael@0: nsExpatDriver::IsContainer(int32_t aTag) const michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(bool) michael@0: nsExpatDriver::CanContain(int32_t aParent,int32_t aChild) const michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: nsExpatDriver::MaybeStopParser(nsresult aState) michael@0: { michael@0: if (NS_FAILED(aState)) { michael@0: // If we had a failure we want to override NS_ERROR_HTMLPARSER_INTERRUPTED michael@0: // and we want to override NS_ERROR_HTMLPARSER_BLOCK but not with michael@0: // NS_ERROR_HTMLPARSER_INTERRUPTED. michael@0: if (NS_SUCCEEDED(mInternalState) || michael@0: mInternalState == NS_ERROR_HTMLPARSER_INTERRUPTED || michael@0: (mInternalState == NS_ERROR_HTMLPARSER_BLOCK && michael@0: aState != NS_ERROR_HTMLPARSER_INTERRUPTED)) { michael@0: mInternalState = (aState == NS_ERROR_HTMLPARSER_INTERRUPTED || michael@0: aState == NS_ERROR_HTMLPARSER_BLOCK) ? michael@0: aState : michael@0: NS_ERROR_HTMLPARSER_STOPPARSING; michael@0: } michael@0: michael@0: // If we get an error then we need to stop Expat (by calling XML_StopParser michael@0: // with false as the last argument). If the parser should be blocked or michael@0: // interrupted we need to pause Expat (by calling XML_StopParser with michael@0: // true as the last argument). michael@0: XML_StopParser(mExpatParser, BlockedOrInterrupted()); michael@0: } michael@0: else if (NS_SUCCEEDED(mInternalState)) { michael@0: // Only clobber mInternalState with the success code if we didn't block or michael@0: // interrupt before. michael@0: mInternalState = aState; michael@0: } michael@0: }