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: #include "nsCOMPtr.h" michael@0: #include "nsXMLContentSink.h" michael@0: #include "nsIFragmentContentSink.h" michael@0: #include "nsIXMLContentSink.h" michael@0: #include "nsContentSink.h" michael@0: #include "nsIExpatSink.h" michael@0: #include "nsIDTD.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIDOMDocumentFragment.h" michael@0: #include "nsIContent.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsINodeInfo.h" michael@0: #include "nsContentCreatorFunctions.h" michael@0: #include "nsError.h" michael@0: #include "nsIConsoleService.h" michael@0: #include "nsIScriptError.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsTHashtable.h" michael@0: #include "nsHashKeys.h" michael@0: #include "nsTArray.h" michael@0: #include "nsCycleCollectionParticipant.h" michael@0: #include "nsIDocShell.h" michael@0: #include "nsScriptLoader.h" michael@0: #include "mozilla/css/Loader.h" michael@0: #include "mozilla/dom/DocumentFragment.h" michael@0: #include "mozilla/dom/ProcessingInstruction.h" michael@0: michael@0: using namespace mozilla::dom; michael@0: michael@0: class nsXMLFragmentContentSink : public nsXMLContentSink, michael@0: public nsIFragmentContentSink michael@0: { michael@0: public: michael@0: nsXMLFragmentContentSink(); michael@0: virtual ~nsXMLFragmentContentSink(); michael@0: michael@0: NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW michael@0: michael@0: // nsISupports michael@0: NS_DECL_ISUPPORTS_INHERITED michael@0: NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_NO_UNLINK(nsXMLFragmentContentSink, michael@0: nsXMLContentSink) michael@0: michael@0: // nsIExpatSink michael@0: NS_IMETHOD HandleDoctypeDecl(const nsAString & aSubset, michael@0: const nsAString & aName, michael@0: const nsAString & aSystemId, michael@0: const nsAString & aPublicId, michael@0: nsISupports* aCatalogData); michael@0: NS_IMETHOD HandleProcessingInstruction(const char16_t *aTarget, michael@0: const char16_t *aData); michael@0: NS_IMETHOD HandleXMLDeclaration(const char16_t *aVersion, michael@0: const char16_t *aEncoding, michael@0: int32_t aStandalone); michael@0: NS_IMETHOD ReportError(const char16_t* aErrorText, michael@0: const char16_t* aSourceText, michael@0: nsIScriptError *aError, michael@0: bool *_retval); michael@0: michael@0: // nsIContentSink michael@0: NS_IMETHOD WillBuildModel(nsDTDMode aDTDMode); michael@0: NS_IMETHOD DidBuildModel(bool aTerminated); michael@0: NS_IMETHOD SetDocumentCharset(nsACString& aCharset); michael@0: virtual nsISupports *GetTarget(); michael@0: NS_IMETHOD DidProcessATokenImpl(); michael@0: michael@0: // nsIXMLContentSink michael@0: michael@0: // nsIFragmentContentSink michael@0: NS_IMETHOD FinishFragmentParsing(nsIDOMDocumentFragment** aFragment); michael@0: NS_IMETHOD SetTargetDocument(nsIDocument* aDocument); michael@0: NS_IMETHOD WillBuildContent(); michael@0: NS_IMETHOD DidBuildContent(); michael@0: NS_IMETHOD IgnoreFirstContainer(); michael@0: NS_IMETHOD SetPreventScriptExecution(bool aPreventScriptExecution); michael@0: michael@0: protected: michael@0: virtual bool SetDocElement(int32_t aNameSpaceID, michael@0: nsIAtom *aTagName, michael@0: nsIContent *aContent); michael@0: virtual nsresult CreateElement(const char16_t** aAtts, uint32_t aAttsCount, michael@0: nsINodeInfo* aNodeInfo, uint32_t aLineNumber, michael@0: nsIContent** aResult, bool* aAppendContent, michael@0: mozilla::dom::FromParser aFromParser); michael@0: virtual nsresult CloseElement(nsIContent* aContent); michael@0: michael@0: virtual void MaybeStartLayout(bool aIgnorePendingSheets); michael@0: michael@0: // nsContentSink overrides michael@0: virtual nsresult ProcessStyleLink(nsIContent* aElement, michael@0: const nsSubstring& aHref, michael@0: bool aAlternate, michael@0: const nsSubstring& aTitle, michael@0: const nsSubstring& aType, michael@0: const nsSubstring& aMedia); michael@0: nsresult LoadXSLStyleSheet(nsIURI* aUrl); michael@0: void StartLayout(); michael@0: michael@0: nsCOMPtr mTargetDocument; michael@0: // the fragment michael@0: nsCOMPtr mRoot; michael@0: bool mParseError; michael@0: }; michael@0: michael@0: static nsresult michael@0: NewXMLFragmentContentSinkHelper(nsIFragmentContentSink** aResult) michael@0: { michael@0: nsXMLFragmentContentSink* it = new nsXMLFragmentContentSink(); michael@0: michael@0: NS_ADDREF(*aResult = it); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: NS_NewXMLFragmentContentSink(nsIFragmentContentSink** aResult) michael@0: { michael@0: return NewXMLFragmentContentSinkHelper(aResult); michael@0: } michael@0: michael@0: nsXMLFragmentContentSink::nsXMLFragmentContentSink() michael@0: : mParseError(false) michael@0: { michael@0: mRunsToCompletion = true; michael@0: } michael@0: michael@0: nsXMLFragmentContentSink::~nsXMLFragmentContentSink() michael@0: { michael@0: } michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsXMLFragmentContentSink) michael@0: NS_INTERFACE_MAP_ENTRY(nsIFragmentContentSink) michael@0: NS_INTERFACE_MAP_END_INHERITING(nsXMLContentSink) michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(nsXMLFragmentContentSink, nsXMLContentSink) michael@0: NS_IMPL_RELEASE_INHERITED(nsXMLFragmentContentSink, nsXMLContentSink) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLFragmentContentSink) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXMLFragmentContentSink, michael@0: nsXMLContentSink) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTargetDocument) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: NS_IMETHODIMP michael@0: nsXMLFragmentContentSink::WillBuildModel(nsDTDMode aDTDMode) michael@0: { michael@0: if (mRoot) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: mState = eXMLContentSinkState_InDocumentElement; michael@0: michael@0: NS_ASSERTION(mTargetDocument, "Need a document!"); michael@0: michael@0: mRoot = new DocumentFragment(mNodeInfoManager); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXMLFragmentContentSink::DidBuildModel(bool aTerminated) michael@0: { michael@0: nsRefPtr kungFuDeathGrip(mParser); michael@0: michael@0: // Drop our reference to the parser to get rid of a circular michael@0: // reference. michael@0: mParser = nullptr; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXMLFragmentContentSink::SetDocumentCharset(nsACString& aCharset) michael@0: { michael@0: NS_NOTREACHED("fragments shouldn't set charset"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsISupports * michael@0: nsXMLFragmentContentSink::GetTarget() michael@0: { michael@0: return mTargetDocument; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////// michael@0: michael@0: bool michael@0: nsXMLFragmentContentSink::SetDocElement(int32_t aNameSpaceID, michael@0: nsIAtom* aTagName, michael@0: nsIContent *aContent) michael@0: { michael@0: // this is a fragment, not a document michael@0: return false; michael@0: } michael@0: michael@0: nsresult michael@0: nsXMLFragmentContentSink::CreateElement(const char16_t** aAtts, uint32_t aAttsCount, michael@0: nsINodeInfo* aNodeInfo, uint32_t aLineNumber, michael@0: nsIContent** aResult, bool* aAppendContent, michael@0: FromParser /*aFromParser*/) michael@0: { michael@0: // Claim to not be coming from parser, since we don't do any of the michael@0: // fancy CloseElement stuff. michael@0: nsresult rv = nsXMLContentSink::CreateElement(aAtts, aAttsCount, michael@0: aNodeInfo, aLineNumber, michael@0: aResult, aAppendContent, michael@0: NOT_FROM_PARSER); michael@0: michael@0: // When we aren't grabbing all of the content we, never open a doc michael@0: // element, we run into trouble on the first element, so we don't append, michael@0: // and simply push this onto the content stack. michael@0: if (mContentStack.Length() == 0) { michael@0: *aAppendContent = false; michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsXMLFragmentContentSink::CloseElement(nsIContent* aContent) michael@0: { michael@0: // don't do fancy stuff in nsXMLContentSink michael@0: if (mPreventScriptExecution && aContent->Tag() == nsGkAtoms::script && michael@0: (aContent->IsHTML() || aContent->IsSVG())) { michael@0: nsCOMPtr sele = do_QueryInterface(aContent); michael@0: NS_ASSERTION(sele, "script did QI correctly!"); michael@0: sele->PreventExecution(); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsXMLFragmentContentSink::MaybeStartLayout(bool aIgnorePendingSheets) michael@0: { michael@0: return; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////// michael@0: michael@0: NS_IMETHODIMP michael@0: nsXMLFragmentContentSink::HandleDoctypeDecl(const nsAString & aSubset, michael@0: const nsAString & aName, michael@0: const nsAString & aSystemId, michael@0: const nsAString & aPublicId, michael@0: nsISupports* aCatalogData) michael@0: { michael@0: NS_NOTREACHED("fragments shouldn't have doctype declarations"); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXMLFragmentContentSink::HandleProcessingInstruction(const char16_t *aTarget, michael@0: const char16_t *aData) michael@0: { michael@0: FlushText(); michael@0: michael@0: const nsDependentString target(aTarget); michael@0: const nsDependentString data(aData); michael@0: michael@0: nsRefPtr node = michael@0: NS_NewXMLProcessingInstruction(mNodeInfoManager, target, data); michael@0: michael@0: // no special processing here. that should happen when the fragment moves into the document michael@0: return AddContentAsLeaf(node); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXMLFragmentContentSink::HandleXMLDeclaration(const char16_t *aVersion, michael@0: const char16_t *aEncoding, michael@0: int32_t aStandalone) michael@0: { michael@0: NS_NOTREACHED("fragments shouldn't have XML declarations"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXMLFragmentContentSink::ReportError(const char16_t* aErrorText, michael@0: const char16_t* aSourceText, michael@0: nsIScriptError *aError, michael@0: bool *_retval) michael@0: { michael@0: NS_PRECONDITION(aError && aSourceText && aErrorText, "Check arguments!!!"); michael@0: michael@0: // The expat driver should report the error. michael@0: *_retval = true; michael@0: michael@0: mParseError = true; michael@0: michael@0: #ifdef DEBUG michael@0: // Report the error to stderr. michael@0: fprintf(stderr, michael@0: "\n%s\n%s\n\n", michael@0: NS_LossyConvertUTF16toASCII(aErrorText).get(), michael@0: NS_LossyConvertUTF16toASCII(aSourceText).get()); michael@0: #endif michael@0: michael@0: // The following code is similar to the cleanup in nsXMLContentSink::ReportError() michael@0: mState = eXMLContentSinkState_InProlog; michael@0: michael@0: // Clear the current content michael@0: nsCOMPtr node(do_QueryInterface(mRoot)); michael@0: if (node) { michael@0: for (;;) { michael@0: nsCOMPtr child, dummy; michael@0: node->GetLastChild(getter_AddRefs(child)); michael@0: if (!child) michael@0: break; michael@0: node->RemoveChild(child, getter_AddRefs(dummy)); michael@0: } michael@0: } michael@0: michael@0: // Clear any buffered-up text we have. It's enough to set the length to 0. michael@0: // The buffer itself is allocated when we're created and deleted in our michael@0: // destructor, so don't mess with it. michael@0: mTextLength = 0; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsXMLFragmentContentSink::ProcessStyleLink(nsIContent* aElement, michael@0: const nsSubstring& aHref, michael@0: bool aAlternate, michael@0: const nsSubstring& aTitle, michael@0: const nsSubstring& aType, michael@0: const nsSubstring& aMedia) michael@0: { michael@0: // don't process until moved to document michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsXMLFragmentContentSink::LoadXSLStyleSheet(nsIURI* aUrl) michael@0: { michael@0: NS_NOTREACHED("fragments shouldn't have XSL style sheets"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: void michael@0: nsXMLFragmentContentSink::StartLayout() michael@0: { michael@0: NS_NOTREACHED("fragments shouldn't layout"); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////// michael@0: michael@0: NS_IMETHODIMP michael@0: nsXMLFragmentContentSink::FinishFragmentParsing(nsIDOMDocumentFragment** aFragment) michael@0: { michael@0: *aFragment = nullptr; michael@0: mTargetDocument = nullptr; michael@0: mNodeInfoManager = nullptr; michael@0: mScriptLoader = nullptr; michael@0: mCSSLoader = nullptr; michael@0: mContentStack.Clear(); michael@0: mDocumentURI = nullptr; michael@0: mDocShell = nullptr; michael@0: mDocElement = nullptr; michael@0: mCurrentHead = nullptr; michael@0: if (mParseError) { michael@0: //XXX PARSE_ERR from DOM3 Load and Save would be more appropriate michael@0: mRoot = nullptr; michael@0: mParseError = false; michael@0: return NS_ERROR_DOM_SYNTAX_ERR; michael@0: } else if (mRoot) { michael@0: nsresult rv = CallQueryInterface(mRoot, aFragment); michael@0: mRoot = nullptr; michael@0: return rv; michael@0: } else { michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXMLFragmentContentSink::SetTargetDocument(nsIDocument* aTargetDocument) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aTargetDocument); michael@0: michael@0: mTargetDocument = aTargetDocument; michael@0: mNodeInfoManager = aTargetDocument->NodeInfoManager(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXMLFragmentContentSink::WillBuildContent() michael@0: { michael@0: PushContent(mRoot); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXMLFragmentContentSink::DidBuildContent() michael@0: { michael@0: // Note: we need to FlushText() here because if we don't, we might not get michael@0: // an end element to do it for us, so make sure. michael@0: if (!mParseError) { michael@0: FlushText(); michael@0: } michael@0: PopContent(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXMLFragmentContentSink::DidProcessATokenImpl() michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXMLFragmentContentSink::IgnoreFirstContainer() michael@0: { michael@0: NS_NOTREACHED("XML isn't as broken as HTML"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXMLFragmentContentSink::SetPreventScriptExecution(bool aPrevent) michael@0: { michael@0: mPreventScriptExecution = aPrevent; michael@0: return NS_OK; michael@0: }