forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			879 lines
		
	
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			879 lines
		
	
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| /*
 | |
|  * An implementation for a Gecko-style content sink that knows how
 | |
|  * to build a content model (the "prototype" document) from XUL.
 | |
|  *
 | |
|  * For more information on XUL,
 | |
|  * see http://developer.mozilla.org/en/docs/XUL
 | |
|  */
 | |
| 
 | |
| #include "nsXULContentSink.h"
 | |
| 
 | |
| #include "jsfriendapi.h"
 | |
| 
 | |
| #include "nsCOMPtr.h"
 | |
| #include "nsHTMLStyleSheet.h"
 | |
| #include "nsIContentSink.h"
 | |
| #include "mozilla/dom/Document.h"
 | |
| #include "nsIFormControl.h"
 | |
| #include "mozilla/dom/NodeInfo.h"
 | |
| #include "nsIScriptContext.h"
 | |
| #include "nsIScriptGlobalObject.h"
 | |
| #include "nsNameSpaceManager.h"
 | |
| #include "nsParserBase.h"
 | |
| #include "nsViewManager.h"
 | |
| #include "nsIScriptSecurityManager.h"
 | |
| #include "nsLayoutCID.h"
 | |
| #include "nsNetUtil.h"
 | |
| #include "nsString.h"
 | |
| #include "nsReadableUtils.h"
 | |
| #include "nsXULElement.h"
 | |
| #include "mozilla/Logging.h"
 | |
| #include "nsCRT.h"
 | |
| 
 | |
| #include "nsXULPrototypeDocument.h"  // XXXbe temporary
 | |
| #include "mozilla/css/Loader.h"
 | |
| 
 | |
| #include "nsUnicharUtils.h"
 | |
| #include "nsGkAtoms.h"
 | |
| #include "nsContentUtils.h"
 | |
| #include "nsAttrName.h"
 | |
| #include "nsXMLContentSink.h"
 | |
| #include "nsIScriptError.h"
 | |
| #include "nsContentTypeParser.h"
 | |
| 
 | |
| static mozilla::LazyLogModule gContentSinkLog("nsXULContentSink");
 | |
| 
 | |
| //----------------------------------------------------------------------
 | |
| 
 | |
| XULContentSinkImpl::ContextStack::ContextStack() : mTop(nullptr), mDepth(0) {}
 | |
| 
 | |
| XULContentSinkImpl::ContextStack::~ContextStack() {
 | |
|   while (mTop) {
 | |
|     Entry* doomed = mTop;
 | |
|     mTop = mTop->mNext;
 | |
|     delete doomed;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void XULContentSinkImpl::ContextStack::Push(RefPtr<nsXULPrototypeNode>&& aNode,
 | |
|                                             State aState) {
 | |
|   mTop = new Entry(std::move(aNode), aState, mTop);
 | |
|   ++mDepth;
 | |
| }
 | |
| 
 | |
| nsresult XULContentSinkImpl::ContextStack::Pop(State* aState) {
 | |
|   if (mDepth == 0) return NS_ERROR_UNEXPECTED;
 | |
| 
 | |
|   Entry* entry = mTop;
 | |
|   mTop = mTop->mNext;
 | |
|   --mDepth;
 | |
| 
 | |
|   *aState = entry->mState;
 | |
|   delete entry;
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult XULContentSinkImpl::ContextStack::GetTopNode(
 | |
|     RefPtr<nsXULPrototypeNode>& aNode) {
 | |
|   if (mDepth == 0) return NS_ERROR_UNEXPECTED;
 | |
| 
 | |
|   aNode = mTop->mNode;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult XULContentSinkImpl::ContextStack::GetTopChildren(
 | |
|     nsPrototypeArray** aChildren) {
 | |
|   if (mDepth == 0) return NS_ERROR_UNEXPECTED;
 | |
| 
 | |
|   *aChildren = &(mTop->mChildren);
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| void XULContentSinkImpl::ContextStack::Clear() {
 | |
|   Entry* cur = mTop;
 | |
|   while (cur) {
 | |
|     // Release the root element (and its descendants).
 | |
|     Entry* next = cur->mNext;
 | |
|     delete cur;
 | |
|     cur = next;
 | |
|   }
 | |
| 
 | |
|   mTop = nullptr;
 | |
|   mDepth = 0;
 | |
| }
 | |
| 
 | |
| void XULContentSinkImpl::ContextStack::Traverse(
 | |
|     nsCycleCollectionTraversalCallback& aCb) {
 | |
|   nsCycleCollectionTraversalCallback& cb = aCb;
 | |
|   for (ContextStack::Entry* tmp = mTop; tmp; tmp = tmp->mNext) {
 | |
|     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNode)
 | |
|     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildren)
 | |
|   }
 | |
| }
 | |
| 
 | |
| //----------------------------------------------------------------------
 | |
| 
 | |
| XULContentSinkImpl::XULContentSinkImpl()
 | |
|     : mText(nullptr),
 | |
|       mTextLength(0),
 | |
|       mTextSize(0),
 | |
|       mConstrainSize(true),
 | |
|       mState(eInProlog) {}
 | |
| 
 | |
| XULContentSinkImpl::~XULContentSinkImpl() {
 | |
|   // The context stack _should_ be empty, unless something has gone wrong.
 | |
|   NS_ASSERTION(mContextStack.Depth() == 0, "Context stack not empty?");
 | |
|   mContextStack.Clear();
 | |
| 
 | |
|   free(mText);
 | |
| }
 | |
| 
 | |
| //----------------------------------------------------------------------
 | |
| // nsISupports interface
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_CLASS(XULContentSinkImpl)
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(XULContentSinkImpl)
 | |
|   NS_IMPL_CYCLE_COLLECTION_UNLINK(mNodeInfoManager)
 | |
|   tmp->mContextStack.Clear();
 | |
|   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrototype)
 | |
|   NS_IMPL_CYCLE_COLLECTION_UNLINK(mParser)
 | |
| NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(XULContentSinkImpl)
 | |
|   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfoManager)
 | |
|   tmp->mContextStack.Traverse(cb);
 | |
|   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrototype)
 | |
|   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser)
 | |
| NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 | |
| 
 | |
| NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XULContentSinkImpl)
 | |
|   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXMLContentSink)
 | |
|   NS_INTERFACE_MAP_ENTRY(nsIXMLContentSink)
 | |
|   NS_INTERFACE_MAP_ENTRY(nsIExpatSink)
 | |
|   NS_INTERFACE_MAP_ENTRY(nsIContentSink)
 | |
| NS_INTERFACE_MAP_END
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTING_ADDREF(XULContentSinkImpl)
 | |
| NS_IMPL_CYCLE_COLLECTING_RELEASE(XULContentSinkImpl)
 | |
| 
 | |
| //----------------------------------------------------------------------
 | |
| // nsIContentSink interface
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| XULContentSinkImpl::WillBuildModel(nsDTDMode aDTDMode) {
 | |
| #if FIXME
 | |
|   if (!mParentContentSink) {
 | |
|     // If we're _not_ an overlay, then notify the document that
 | |
|     // the load is beginning.
 | |
|     mDocument->BeginLoad();
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| XULContentSinkImpl::DidBuildModel(bool aTerminated) {
 | |
|   nsCOMPtr<Document> doc = do_QueryReferent(mDocument);
 | |
|   if (doc) {
 | |
|     mPrototype->NotifyLoadDone();
 | |
|     mDocument = nullptr;
 | |
|   }
 | |
| 
 | |
|   // Drop our reference to the parser to get rid of a circular
 | |
|   // reference.
 | |
|   mParser = nullptr;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| XULContentSinkImpl::WillInterrupt(void) {
 | |
|   // XXX Notify the docshell, if necessary
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| XULContentSinkImpl::WillResume(void) {
 | |
|   // XXX Notify the docshell, if necessary
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| XULContentSinkImpl::SetParser(nsParserBase* aParser) {
 | |
|   mParser = aParser;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| void XULContentSinkImpl::SetDocumentCharset(
 | |
|     NotNull<const Encoding*> aEncoding) {
 | |
|   nsCOMPtr<Document> doc = do_QueryReferent(mDocument);
 | |
|   if (doc) {
 | |
|     doc->SetDocumentCharacterSet(aEncoding);
 | |
|   }
 | |
| }
 | |
| 
 | |
| nsISupports* XULContentSinkImpl::GetTarget() {
 | |
|   nsCOMPtr<Document> doc = do_QueryReferent(mDocument);
 | |
|   return ToSupports(doc);
 | |
| }
 | |
| 
 | |
| //----------------------------------------------------------------------
 | |
| 
 | |
| nsresult XULContentSinkImpl::Init(Document* aDocument,
 | |
|                                   nsXULPrototypeDocument* aPrototype) {
 | |
|   MOZ_ASSERT(aDocument != nullptr, "null ptr");
 | |
|   if (!aDocument) return NS_ERROR_NULL_POINTER;
 | |
| 
 | |
|   mDocument = do_GetWeakReference(aDocument);
 | |
|   mPrototype = aPrototype;
 | |
| 
 | |
|   mDocumentURL = mPrototype->GetURI();
 | |
|   mNodeInfoManager = aPrototype->GetNodeInfoManager();
 | |
|   if (!mNodeInfoManager) return NS_ERROR_UNEXPECTED;
 | |
| 
 | |
|   mState = eInProlog;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| //----------------------------------------------------------------------
 | |
| //
 | |
| // Text buffering
 | |
| //
 | |
| 
 | |
| bool XULContentSinkImpl::IsDataInBuffer(char16_t* buffer, int32_t length) {
 | |
|   for (int32_t i = 0; i < length; ++i) {
 | |
|     if (buffer[i] == ' ' || buffer[i] == '\t' || buffer[i] == '\n' ||
 | |
|         buffer[i] == '\r')
 | |
|       continue;
 | |
| 
 | |
|     return true;
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| nsresult XULContentSinkImpl::FlushText(bool aCreateTextNode) {
 | |
|   nsresult rv;
 | |
| 
 | |
|   do {
 | |
|     // Don't do anything if there's no text to create a node from, or
 | |
|     // if they've told us not to create a text node
 | |
|     if (!mTextLength) break;
 | |
| 
 | |
|     if (!aCreateTextNode) break;
 | |
| 
 | |
|     RefPtr<nsXULPrototypeNode> node;
 | |
|     rv = mContextStack.GetTopNode(node);
 | |
|     if (NS_FAILED(rv)) return rv;
 | |
| 
 | |
|     bool stripWhitespace = false;
 | |
|     if (node->mType == nsXULPrototypeNode::eType_Element) {
 | |
|       mozilla::dom::NodeInfo* nodeInfo =
 | |
|           static_cast<nsXULPrototypeElement*>(node.get())->mNodeInfo;
 | |
| 
 | |
|       if (nodeInfo->NamespaceEquals(kNameSpaceID_XUL))
 | |
|         stripWhitespace = !nodeInfo->Equals(nsGkAtoms::label) &&
 | |
|                           !nodeInfo->Equals(nsGkAtoms::description);
 | |
|     }
 | |
| 
 | |
|     // Don't bother if there's nothing but whitespace.
 | |
|     if (stripWhitespace && !IsDataInBuffer(mText, mTextLength)) break;
 | |
| 
 | |
|     // Don't bother if we're not in XUL document body
 | |
|     if (mState != eInDocumentElement || mContextStack.Depth() == 0) break;
 | |
| 
 | |
|     RefPtr<nsXULPrototypeText> text = new nsXULPrototypeText();
 | |
|     text->mValue.Assign(mText, mTextLength);
 | |
|     if (stripWhitespace) text->mValue.Trim(" \t\n\r");
 | |
| 
 | |
|     // hook it up
 | |
|     nsPrototypeArray* children = nullptr;
 | |
|     rv = mContextStack.GetTopChildren(&children);
 | |
|     if (NS_FAILED(rv)) return rv;
 | |
| 
 | |
|     children->AppendElement(text.forget());
 | |
|   } while (0);
 | |
| 
 | |
|   // Reset our text buffer
 | |
|   mTextLength = 0;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| //----------------------------------------------------------------------
 | |
| 
 | |
| nsresult XULContentSinkImpl::NormalizeAttributeString(
 | |
|     const char16_t* aExpatName, nsAttrName& aName) {
 | |
|   int32_t nameSpaceID;
 | |
|   RefPtr<nsAtom> prefix, localName;
 | |
|   nsContentUtils::SplitExpatName(aExpatName, getter_AddRefs(prefix),
 | |
|                                  getter_AddRefs(localName), &nameSpaceID);
 | |
| 
 | |
|   if (nameSpaceID == kNameSpaceID_None) {
 | |
|     aName.SetTo(localName);
 | |
| 
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   RefPtr<mozilla::dom::NodeInfo> ni;
 | |
|   ni = mNodeInfoManager->GetNodeInfo(localName, prefix, nameSpaceID,
 | |
|                                      nsINode::ATTRIBUTE_NODE);
 | |
|   aName.SetTo(ni);
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| /**** BEGIN NEW APIs ****/
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| XULContentSinkImpl::HandleStartElement(const char16_t* aName,
 | |
|                                        const char16_t** aAtts,
 | |
|                                        uint32_t aAttsCount,
 | |
|                                        uint32_t aLineNumber,
 | |
|                                        uint32_t aColumnNumber) {
 | |
|   // XXX Hopefully the parser will flag this before we get here. If
 | |
|   // we're in the epilog, there should be no new elements
 | |
|   MOZ_ASSERT(mState != eInEpilog, "tag in XUL doc epilog");
 | |
|   MOZ_ASSERT(aAttsCount % 2 == 0, "incorrect aAttsCount");
 | |
| 
 | |
|   // Adjust aAttsCount so it's the actual number of attributes
 | |
|   aAttsCount /= 2;
 | |
| 
 | |
|   if (mState == eInEpilog) return NS_ERROR_UNEXPECTED;
 | |
| 
 | |
|   if (mState != eInScript) {
 | |
|     FlushText();
 | |
|   }
 | |
| 
 | |
|   int32_t nameSpaceID;
 | |
|   RefPtr<nsAtom> prefix, localName;
 | |
|   nsContentUtils::SplitExpatName(aName, getter_AddRefs(prefix),
 | |
|                                  getter_AddRefs(localName), &nameSpaceID);
 | |
| 
 | |
|   RefPtr<mozilla::dom::NodeInfo> nodeInfo;
 | |
|   nodeInfo = mNodeInfoManager->GetNodeInfo(localName, prefix, nameSpaceID,
 | |
|                                            nsINode::ELEMENT_NODE);
 | |
| 
 | |
|   nsresult rv = NS_OK;
 | |
|   switch (mState) {
 | |
|     case eInProlog:
 | |
|       // We're the root document element
 | |
|       rv = OpenRoot(aAtts, aAttsCount, nodeInfo);
 | |
|       break;
 | |
| 
 | |
|     case eInDocumentElement:
 | |
|       rv = OpenTag(aAtts, aAttsCount, aLineNumber, nodeInfo);
 | |
|       break;
 | |
| 
 | |
|     case eInEpilog:
 | |
|     case eInScript:
 | |
|       MOZ_LOG(
 | |
|           gContentSinkLog, LogLevel::Warning,
 | |
|           ("xul: warning: unexpected tags in epilog at line %d", aLineNumber));
 | |
|       rv = NS_ERROR_UNEXPECTED;  // XXX
 | |
|       break;
 | |
|   }
 | |
| 
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| XULContentSinkImpl::HandleEndElement(const char16_t* aName) {
 | |
|   // Never EVER return anything but NS_OK or
 | |
|   // NS_ERROR_HTMLPARSER_BLOCK from this method. Doing so will blow
 | |
|   // the parser's little mind all over the planet.
 | |
|   nsresult rv;
 | |
| 
 | |
|   RefPtr<nsXULPrototypeNode> node;
 | |
|   rv = mContextStack.GetTopNode(node);
 | |
| 
 | |
|   if (NS_FAILED(rv)) {
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   switch (node->mType) {
 | |
|     case nsXULPrototypeNode::eType_Element: {
 | |
|       // Flush any text _now_, so that we'll get text nodes created
 | |
|       // before popping the stack.
 | |
|       FlushText();
 | |
| 
 | |
|       // Pop the context stack and do prototype hookup.
 | |
|       nsPrototypeArray* children = nullptr;
 | |
|       rv = mContextStack.GetTopChildren(&children);
 | |
|       if (NS_FAILED(rv)) return rv;
 | |
| 
 | |
|       nsXULPrototypeElement* element =
 | |
|           static_cast<nsXULPrototypeElement*>(node.get());
 | |
| 
 | |
|       int32_t count = children->Length();
 | |
|       if (count) {
 | |
|         element->mChildren.SetCapacity(count);
 | |
| 
 | |
|         for (int32_t i = 0; i < count; ++i)
 | |
|           element->mChildren.AppendElement(children->ElementAt(i));
 | |
|       }
 | |
|     } break;
 | |
| 
 | |
|     case nsXULPrototypeNode::eType_Script: {
 | |
|       nsXULPrototypeScript* script =
 | |
|           static_cast<nsXULPrototypeScript*>(node.get());
 | |
| 
 | |
|       // If given a src= attribute, we must ignore script tag content.
 | |
|       if (!script->mSrcURI && !script->HasScriptObject()) {
 | |
|         nsCOMPtr<Document> doc = do_QueryReferent(mDocument);
 | |
| 
 | |
|         script->mOutOfLine = false;
 | |
|         if (doc) {
 | |
|           script->Compile(mText, mTextLength, JS::SourceOwnership::Borrowed,
 | |
|                           mDocumentURL, script->mLineNo, doc);
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       FlushText(false);
 | |
|     } break;
 | |
| 
 | |
|     default:
 | |
|       NS_ERROR("didn't expect that");
 | |
|       break;
 | |
|   }
 | |
| 
 | |
|   rv = mContextStack.Pop(&mState);
 | |
|   NS_ASSERTION(NS_SUCCEEDED(rv), "context stack corrupted");
 | |
|   if (NS_FAILED(rv)) return rv;
 | |
| 
 | |
|   if (mContextStack.Depth() == 0) {
 | |
|     // The root element should -always- be an element, because
 | |
|     // it'll have been created via XULContentSinkImpl::OpenRoot().
 | |
|     NS_ASSERTION(node->mType == nsXULPrototypeNode::eType_Element,
 | |
|                  "root is not an element");
 | |
|     if (node->mType != nsXULPrototypeNode::eType_Element)
 | |
|       return NS_ERROR_UNEXPECTED;
 | |
| 
 | |
|     // Now that we're done parsing, set the prototype document's
 | |
|     // root element. This transfers ownership of the prototype
 | |
|     // element tree to the prototype document.
 | |
|     nsXULPrototypeElement* element =
 | |
|         static_cast<nsXULPrototypeElement*>(node.get());
 | |
| 
 | |
|     mPrototype->SetRootElement(element);
 | |
|     mState = eInEpilog;
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| XULContentSinkImpl::HandleComment(const char16_t* aName) {
 | |
|   FlushText();
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| XULContentSinkImpl::HandleCDataSection(const char16_t* aData,
 | |
|                                        uint32_t aLength) {
 | |
|   FlushText();
 | |
|   return AddText(aData, aLength);
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| XULContentSinkImpl::HandleDoctypeDecl(const nsAString& aSubset,
 | |
|                                       const nsAString& aName,
 | |
|                                       const nsAString& aSystemId,
 | |
|                                       const nsAString& aPublicId,
 | |
|                                       nsISupports* aCatalogData) {
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| XULContentSinkImpl::HandleCharacterData(const char16_t* aData,
 | |
|                                         uint32_t aLength) {
 | |
|   if (aData && mState != eInProlog && mState != eInEpilog) {
 | |
|     return AddText(aData, aLength);
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| XULContentSinkImpl::HandleProcessingInstruction(const char16_t* aTarget,
 | |
|                                                 const char16_t* aData) {
 | |
|   FlushText();
 | |
| 
 | |
|   const nsDependentString target(aTarget);
 | |
|   const nsDependentString data(aData);
 | |
| 
 | |
|   // Note: the created nsXULPrototypePI has mRefCnt == 1
 | |
|   RefPtr<nsXULPrototypePI> pi = new nsXULPrototypePI();
 | |
|   pi->mTarget = target;
 | |
|   pi->mData = data;
 | |
| 
 | |
|   if (mState == eInProlog) {
 | |
|     // Note: passing in already addrefed pi
 | |
|     return mPrototype->AddProcessingInstruction(pi);
 | |
|   }
 | |
| 
 | |
|   nsresult rv;
 | |
|   nsPrototypeArray* children = nullptr;
 | |
|   rv = mContextStack.GetTopChildren(&children);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     return rv;
 | |
|   }
 | |
| 
 | |
|   // XXX(Bug 1631371) Check if this should use a fallible operation as it
 | |
|   // pretended earlier.
 | |
|   children->AppendElement(pi);
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| XULContentSinkImpl::HandleXMLDeclaration(const char16_t* aVersion,
 | |
|                                          const char16_t* aEncoding,
 | |
|                                          int32_t aStandalone) {
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| XULContentSinkImpl::ReportError(const char16_t* aErrorText,
 | |
|                                 const char16_t* aSourceText,
 | |
|                                 nsIScriptError* aError, bool* _retval) {
 | |
|   MOZ_ASSERT(aError && aSourceText && aErrorText, "Check arguments!!!");
 | |
| 
 | |
|   // The expat driver should report the error.
 | |
|   *_retval = true;
 | |
| 
 | |
|   nsresult rv = NS_OK;
 | |
| 
 | |
|   // make sure to empty the context stack so that
 | |
|   // <parsererror> could become the root element.
 | |
|   mContextStack.Clear();
 | |
| 
 | |
|   mState = eInProlog;
 | |
| 
 | |
|   // Clear any buffered-up text we have.  It's enough to set the length to 0.
 | |
|   // The buffer itself is allocated when we're created and deleted in our
 | |
|   // destructor, so don't mess with it.
 | |
|   mTextLength = 0;
 | |
| 
 | |
|   // return leaving the document empty if we're asked to not add a <parsererror>
 | |
|   // root node
 | |
|   nsCOMPtr<Document> idoc = do_QueryReferent(mDocument);
 | |
|   if (idoc && idoc->SuppressParserErrorElement()) {
 | |
|     return NS_OK;
 | |
|   };
 | |
| 
 | |
|   const char16_t* noAtts[] = {0, 0};
 | |
| 
 | |
|   constexpr auto errorNs =
 | |
|       u"http://www.mozilla.org/newlayout/xml/parsererror.xml"_ns;
 | |
| 
 | |
|   nsAutoString parsererror(errorNs);
 | |
|   parsererror.Append((char16_t)0xFFFF);
 | |
|   parsererror.AppendLiteral("parsererror");
 | |
| 
 | |
|   rv = HandleStartElement(parsererror.get(), noAtts, 0, 0, 0);
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   rv = HandleCharacterData(aErrorText, NS_strlen(aErrorText));
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   nsAutoString sourcetext(errorNs);
 | |
|   sourcetext.Append((char16_t)0xFFFF);
 | |
|   sourcetext.AppendLiteral("sourcetext");
 | |
| 
 | |
|   rv = HandleStartElement(sourcetext.get(), noAtts, 0, 0, 0);
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   rv = HandleCharacterData(aSourceText, NS_strlen(aSourceText));
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   rv = HandleEndElement(sourcetext.get());
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   rv = HandleEndElement(parsererror.get());
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| nsresult XULContentSinkImpl::OpenRoot(const char16_t** aAttributes,
 | |
|                                       const uint32_t aAttrLen,
 | |
|                                       mozilla::dom::NodeInfo* aNodeInfo) {
 | |
|   NS_ASSERTION(mState == eInProlog, "how'd we get here?");
 | |
|   if (mState != eInProlog) return NS_ERROR_UNEXPECTED;
 | |
| 
 | |
|   if (aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XHTML) ||
 | |
|       aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XUL)) {
 | |
|     MOZ_LOG(gContentSinkLog, LogLevel::Error,
 | |
|             ("xul: script tag not allowed as root content element"));
 | |
| 
 | |
|     return NS_ERROR_UNEXPECTED;
 | |
|   }
 | |
| 
 | |
|   // Create the element
 | |
|   RefPtr<nsXULPrototypeElement> element = new nsXULPrototypeElement(aNodeInfo);
 | |
| 
 | |
|   // Add the attributes
 | |
|   nsresult rv = AddAttributes(aAttributes, aAttrLen, element);
 | |
|   if (NS_FAILED(rv)) return rv;
 | |
| 
 | |
|   // Push the element onto the context stack, so that child
 | |
|   // containers will hook up to us as their parent.
 | |
|   mContextStack.Push(std::move(element), mState);
 | |
| 
 | |
|   mState = eInDocumentElement;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult XULContentSinkImpl::OpenTag(const char16_t** aAttributes,
 | |
|                                      const uint32_t aAttrLen,
 | |
|                                      const uint32_t aLineNumber,
 | |
|                                      mozilla::dom::NodeInfo* aNodeInfo) {
 | |
|   // Create the element
 | |
|   RefPtr<nsXULPrototypeElement> element = new nsXULPrototypeElement(aNodeInfo);
 | |
| 
 | |
|   // Link this element to its parent.
 | |
|   nsPrototypeArray* children = nullptr;
 | |
|   nsresult rv = mContextStack.GetTopChildren(&children);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     return rv;
 | |
|   }
 | |
| 
 | |
|   // Add the attributes
 | |
|   rv = AddAttributes(aAttributes, aAttrLen, element);
 | |
|   if (NS_FAILED(rv)) return rv;
 | |
| 
 | |
|   children->AppendElement(element);
 | |
| 
 | |
|   if (aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XHTML) ||
 | |
|       aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XUL)) {
 | |
|     // Do scripty things now
 | |
|     rv = OpenScript(aAttributes, aLineNumber);
 | |
|     NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|     NS_ASSERTION(mState == eInScript || mState == eInDocumentElement,
 | |
|                  "Unexpected state");
 | |
|     if (mState == eInScript) {
 | |
|       // OpenScript has pushed the nsPrototypeScriptElement onto the
 | |
|       // stack, so we're done.
 | |
|       return NS_OK;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Push the element onto the context stack, so that child
 | |
|   // containers will hook up to us as their parent.
 | |
|   mContextStack.Push(std::move(element), mState);
 | |
| 
 | |
|   mState = eInDocumentElement;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult XULContentSinkImpl::OpenScript(const char16_t** aAttributes,
 | |
|                                         const uint32_t aLineNumber) {
 | |
|   bool isJavaScript = true;
 | |
|   nsresult rv;
 | |
| 
 | |
|   // Look for SRC attribute and look for a LANGUAGE attribute
 | |
|   nsAutoString src;
 | |
|   while (*aAttributes) {
 | |
|     const nsDependentString key(aAttributes[0]);
 | |
|     if (key.EqualsLiteral("src")) {
 | |
|       src.Assign(aAttributes[1]);
 | |
|     } else if (key.EqualsLiteral("type")) {
 | |
|       nsDependentString str(aAttributes[1]);
 | |
|       nsContentTypeParser parser(str);
 | |
|       nsAutoString mimeType;
 | |
|       rv = parser.GetType(mimeType);
 | |
|       if (NS_FAILED(rv)) {
 | |
|         if (rv == NS_ERROR_INVALID_ARG) {
 | |
|           // Fail immediately rather than checking if later things
 | |
|           // are okay.
 | |
|           return NS_OK;
 | |
|         }
 | |
|         // We do want the warning here
 | |
|         NS_ENSURE_SUCCESS(rv, rv);
 | |
|       }
 | |
| 
 | |
|       if (nsContentUtils::IsJavascriptMIMEType(mimeType)) {
 | |
|         isJavaScript = true;
 | |
| 
 | |
|         // Get the version string, and ensure that JavaScript supports it.
 | |
|         nsAutoString versionName;
 | |
|         rv = parser.GetParameter("version", versionName);
 | |
| 
 | |
|         if (NS_SUCCEEDED(rv)) {
 | |
|           nsContentUtils::ReportToConsoleNonLocalized(
 | |
|               u"Versioned JavaScripts are no longer supported. "
 | |
|               "Please remove the version parameter."_ns,
 | |
|               nsIScriptError::errorFlag, "XUL Document"_ns, nullptr,
 | |
|               mDocumentURL, EmptyString(), aLineNumber);
 | |
|           isJavaScript = false;
 | |
|         } else if (rv != NS_ERROR_INVALID_ARG) {
 | |
|           return rv;
 | |
|         }
 | |
|       } else {
 | |
|         isJavaScript = false;
 | |
|       }
 | |
|     } else if (key.EqualsLiteral("language")) {
 | |
|       // Language is deprecated, and the impl in ScriptLoader ignores the
 | |
|       // various version strings anyway.  So we make no attempt to support
 | |
|       // languages other than JS for language=
 | |
|       nsAutoString lang(aAttributes[1]);
 | |
|       if (nsContentUtils::IsJavaScriptLanguage(lang)) {
 | |
|         isJavaScript = true;
 | |
|       }
 | |
|     }
 | |
|     aAttributes += 2;
 | |
|   }
 | |
| 
 | |
|   // Don't process scripts that aren't JavaScript.
 | |
|   if (!isJavaScript) {
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<Document> doc(do_QueryReferent(mDocument));
 | |
|   nsCOMPtr<nsIScriptGlobalObject> globalObject;
 | |
|   if (doc) globalObject = do_QueryInterface(doc->GetWindow());
 | |
|   RefPtr<nsXULPrototypeScript> script = new nsXULPrototypeScript(aLineNumber);
 | |
| 
 | |
|   // If there is a SRC attribute...
 | |
|   if (!src.IsEmpty()) {
 | |
|     // Use the SRC attribute value to load the URL
 | |
|     rv = NS_NewURI(getter_AddRefs(script->mSrcURI), src, nullptr, mDocumentURL);
 | |
| 
 | |
|     // Check if this document is allowed to load a script from this source
 | |
|     // NOTE: if we ever allow scripts added via the DOM to run, we need to
 | |
|     // add a CheckLoadURI call for that as well.
 | |
|     if (NS_SUCCEEDED(rv)) {
 | |
|       if (!mSecMan)
 | |
|         mSecMan = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
 | |
|       if (NS_SUCCEEDED(rv)) {
 | |
|         nsCOMPtr<Document> doc = do_QueryReferent(mDocument, &rv);
 | |
| 
 | |
|         if (NS_SUCCEEDED(rv)) {
 | |
|           rv = mSecMan->CheckLoadURIWithPrincipal(
 | |
|               doc->NodePrincipal(), script->mSrcURI,
 | |
|               nsIScriptSecurityManager::ALLOW_CHROME, doc->InnerWindowID());
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (NS_FAILED(rv)) {
 | |
|       return rv;
 | |
|     }
 | |
| 
 | |
|     // Attempt to deserialize an out-of-line script from the FastLoad
 | |
|     // file right away.  Otherwise we'll end up reloading the script and
 | |
|     // corrupting the FastLoad file trying to serialize it, in the case
 | |
|     // where it's already there.
 | |
|     script->DeserializeOutOfLine(nullptr, mPrototype);
 | |
|   }
 | |
| 
 | |
|   nsPrototypeArray* children = nullptr;
 | |
|   rv = mContextStack.GetTopChildren(&children);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     return rv;
 | |
|   }
 | |
| 
 | |
|   children->AppendElement(script);
 | |
| 
 | |
|   mConstrainSize = false;
 | |
| 
 | |
|   mContextStack.Push(script, mState);
 | |
|   mState = eInScript;
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult XULContentSinkImpl::AddAttributes(const char16_t** aAttributes,
 | |
|                                            const uint32_t aAttrLen,
 | |
|                                            nsXULPrototypeElement* aElement) {
 | |
|   // Add tag attributes to the element
 | |
|   nsresult rv;
 | |
| 
 | |
|   // Create storage for the attributes
 | |
|   nsXULPrototypeAttribute* attrs = nullptr;
 | |
|   if (aAttrLen > 0) {
 | |
|     attrs = aElement->mAttributes.AppendElements(aAttrLen);
 | |
|   }
 | |
| 
 | |
|   // Copy the attributes into the prototype
 | |
|   uint32_t i;
 | |
|   for (i = 0; i < aAttrLen; ++i) {
 | |
|     rv = NormalizeAttributeString(aAttributes[i * 2], attrs[i].mName);
 | |
|     NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|     rv = aElement->SetAttrAt(i, nsDependentString(aAttributes[i * 2 + 1]),
 | |
|                              mDocumentURL);
 | |
|     NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|     if (MOZ_LOG_TEST(gContentSinkLog, LogLevel::Debug)) {
 | |
|       nsAutoString extraWhiteSpace;
 | |
|       int32_t cnt = mContextStack.Depth();
 | |
|       while (--cnt >= 0) extraWhiteSpace.AppendLiteral("  ");
 | |
|       nsAutoString qnameC, valueC;
 | |
|       qnameC.Assign(aAttributes[0]);
 | |
|       valueC.Assign(aAttributes[1]);
 | |
|       MOZ_LOG(gContentSinkLog, LogLevel::Debug,
 | |
|               ("xul: %.5d. %s    %s=%s",
 | |
|                -1,  // XXX pass in line number
 | |
|                NS_ConvertUTF16toUTF8(extraWhiteSpace).get(),
 | |
|                NS_ConvertUTF16toUTF8(qnameC).get(),
 | |
|                NS_ConvertUTF16toUTF8(valueC).get()));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult XULContentSinkImpl::AddText(const char16_t* aText, int32_t aLength) {
 | |
|   // Create buffer when we first need it
 | |
|   if (0 == mTextSize) {
 | |
|     mText = (char16_t*)malloc(sizeof(char16_t) * 4096);
 | |
|     if (nullptr == mText) {
 | |
|       return NS_ERROR_OUT_OF_MEMORY;
 | |
|     }
 | |
|     mTextSize = 4096;
 | |
|   }
 | |
| 
 | |
|   // Copy data from string into our buffer; flush buffer when it fills up
 | |
|   int32_t offset = 0;
 | |
|   while (0 != aLength) {
 | |
|     int32_t amount = mTextSize - mTextLength;
 | |
|     if (amount > aLength) {
 | |
|       amount = aLength;
 | |
|     }
 | |
|     if (0 == amount) {
 | |
|       if (mConstrainSize) {
 | |
|         nsresult rv = FlushText();
 | |
|         if (NS_OK != rv) {
 | |
|           return rv;
 | |
|         }
 | |
|       } else {
 | |
|         CheckedInt32 size = mTextSize;
 | |
|         size += aLength;
 | |
|         if (!size.isValid()) {
 | |
|           return NS_ERROR_OUT_OF_MEMORY;
 | |
|         }
 | |
|         mTextSize = size.value();
 | |
| 
 | |
|         mText = (char16_t*)realloc(mText, sizeof(char16_t) * mTextSize);
 | |
|         if (nullptr == mText) {
 | |
|           return NS_ERROR_OUT_OF_MEMORY;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     memcpy(&mText[mTextLength], aText + offset, sizeof(char16_t) * amount);
 | |
| 
 | |
|     mTextLength += amount;
 | |
|     offset += amount;
 | |
|     aLength -= amount;
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | 
