michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* vim: set ts=8 sts=4 et sw=4 tw=80: */ 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: /* michael@0: * An implementation for a Gecko-style content sink that knows how michael@0: * to build a content model (the "prototype" document) from XUL. michael@0: * michael@0: * For more information on XUL, michael@0: * see http://developer.mozilla.org/en/docs/XUL michael@0: */ michael@0: michael@0: #include "nsXULContentSink.h" michael@0: michael@0: #include "jsfriendapi.h" michael@0: michael@0: #include "nsCOMPtr.h" michael@0: #include "nsForwardReference.h" michael@0: #include "nsHTMLStyleSheet.h" michael@0: #include "nsIContentSink.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIDOMEventListener.h" michael@0: #include "nsIDOMHTMLFormElement.h" michael@0: #include "nsIDOMXULDocument.h" michael@0: #include "nsIFormControl.h" michael@0: #include "nsINodeInfo.h" michael@0: #include "nsIScriptContext.h" michael@0: #include "nsIScriptGlobalObject.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsIURL.h" michael@0: #include "nsNameSpaceManager.h" michael@0: #include "nsParserBase.h" michael@0: #include "nsViewManager.h" michael@0: #include "nsIXULDocument.h" michael@0: #include "nsIScriptSecurityManager.h" michael@0: #include "nsLayoutCID.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsRDFCID.h" michael@0: #include "nsXPIDLString.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsXULElement.h" michael@0: #include "prlog.h" michael@0: #include "prmem.h" michael@0: #include "nsCRT.h" michael@0: michael@0: #include "nsXULPrototypeDocument.h" // XXXbe temporary michael@0: #include "mozilla/css/Loader.h" michael@0: michael@0: #include "nsUnicharUtils.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsAttrName.h" michael@0: #include "nsXMLContentSink.h" michael@0: #include "nsIConsoleService.h" michael@0: #include "nsIScriptError.h" michael@0: #include "nsContentTypeParser.h" michael@0: michael@0: #ifdef PR_LOGGING michael@0: static PRLogModuleInfo* gContentSinkLog; michael@0: #endif michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: XULContentSinkImpl::ContextStack::ContextStack() michael@0: : mTop(nullptr), mDepth(0) michael@0: { michael@0: } michael@0: michael@0: XULContentSinkImpl::ContextStack::~ContextStack() michael@0: { michael@0: while (mTop) { michael@0: Entry* doomed = mTop; michael@0: mTop = mTop->mNext; michael@0: delete doomed; michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: XULContentSinkImpl::ContextStack::Push(nsXULPrototypeNode* aNode, State aState) michael@0: { michael@0: Entry* entry = new Entry; michael@0: if (! entry) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: entry->mNode = aNode; michael@0: entry->mState = aState; michael@0: entry->mNext = mTop; michael@0: mTop = entry; michael@0: michael@0: ++mDepth; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: XULContentSinkImpl::ContextStack::Pop(State* aState) michael@0: { michael@0: if (mDepth == 0) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: Entry* entry = mTop; michael@0: mTop = mTop->mNext; michael@0: --mDepth; michael@0: michael@0: *aState = entry->mState; michael@0: delete entry; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: XULContentSinkImpl::ContextStack::GetTopNode(nsRefPtr& aNode) michael@0: { michael@0: if (mDepth == 0) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: aNode = mTop->mNode; michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: XULContentSinkImpl::ContextStack::GetTopChildren(nsPrototypeArray** aChildren) michael@0: { michael@0: if (mDepth == 0) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: *aChildren = &(mTop->mChildren); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: XULContentSinkImpl::ContextStack::Clear() michael@0: { michael@0: Entry *cur = mTop; michael@0: while (cur) { michael@0: // Release the root element (and its descendants). michael@0: Entry *next = cur->mNext; michael@0: delete cur; michael@0: cur = next; michael@0: } michael@0: michael@0: mTop = nullptr; michael@0: mDepth = 0; michael@0: } michael@0: michael@0: void michael@0: XULContentSinkImpl::ContextStack::Traverse(nsCycleCollectionTraversalCallback& aCb) michael@0: { michael@0: nsCycleCollectionTraversalCallback& cb = aCb; michael@0: for (ContextStack::Entry* tmp = mTop; tmp; tmp = tmp->mNext) { michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNode) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildren) michael@0: } michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: michael@0: XULContentSinkImpl::XULContentSinkImpl() michael@0: : mText(nullptr), michael@0: mTextLength(0), michael@0: mTextSize(0), michael@0: mConstrainSize(true), michael@0: mState(eInProlog), michael@0: mParser(nullptr) michael@0: { michael@0: michael@0: #ifdef PR_LOGGING michael@0: if (! gContentSinkLog) michael@0: gContentSinkLog = PR_NewLogModule("nsXULContentSink"); michael@0: #endif michael@0: } michael@0: michael@0: michael@0: XULContentSinkImpl::~XULContentSinkImpl() michael@0: { michael@0: NS_IF_RELEASE(mParser); // XXX should've been released by now, unless error. michael@0: michael@0: // The context stack _should_ be empty, unless something has gone wrong. michael@0: NS_ASSERTION(mContextStack.Depth() == 0, "Context stack not empty?"); michael@0: mContextStack.Clear(); michael@0: michael@0: moz_free(mText); michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // nsISupports interface michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(XULContentSinkImpl) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(XULContentSinkImpl) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mNodeInfoManager) michael@0: tmp->mContextStack.Clear(); michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrototype) michael@0: NS_IF_RELEASE(tmp->mParser); michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(XULContentSinkImpl) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfoManager) michael@0: tmp->mContextStack.Traverse(cb); michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrototype) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mParser) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XULContentSinkImpl) michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXMLContentSink) michael@0: NS_INTERFACE_MAP_ENTRY(nsIXMLContentSink) michael@0: NS_INTERFACE_MAP_ENTRY(nsIExpatSink) michael@0: NS_INTERFACE_MAP_ENTRY(nsIContentSink) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(XULContentSinkImpl) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(XULContentSinkImpl) michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // nsIContentSink interface michael@0: michael@0: NS_IMETHODIMP michael@0: XULContentSinkImpl::WillBuildModel(nsDTDMode aDTDMode) michael@0: { michael@0: #if FIXME michael@0: if (! mParentContentSink) { michael@0: // If we're _not_ an overlay, then notify the document that michael@0: // the load is beginning. michael@0: mDocument->BeginLoad(); michael@0: } michael@0: #endif michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULContentSinkImpl::DidBuildModel(bool aTerminated) michael@0: { michael@0: nsCOMPtr doc = do_QueryReferent(mDocument); michael@0: if (doc) { michael@0: doc->EndLoad(); michael@0: mDocument = nullptr; michael@0: } michael@0: michael@0: // Drop our reference to the parser to get rid of a circular michael@0: // reference. michael@0: NS_IF_RELEASE(mParser); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULContentSinkImpl::WillInterrupt(void) michael@0: { michael@0: // XXX Notify the docshell, if necessary michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULContentSinkImpl::WillResume(void) michael@0: { michael@0: // XXX Notify the docshell, if necessary michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULContentSinkImpl::SetParser(nsParserBase* aParser) michael@0: { michael@0: NS_IF_RELEASE(mParser); michael@0: mParser = aParser; michael@0: NS_IF_ADDREF(mParser); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULContentSinkImpl::SetDocumentCharset(nsACString& aCharset) michael@0: { michael@0: nsCOMPtr doc = do_QueryReferent(mDocument); michael@0: if (doc) { michael@0: doc->SetDocumentCharacterSet(aCharset); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsISupports * michael@0: XULContentSinkImpl::GetTarget() michael@0: { michael@0: nsCOMPtr doc = do_QueryReferent(mDocument); michael@0: return doc; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: nsresult michael@0: XULContentSinkImpl::Init(nsIDocument* aDocument, michael@0: nsXULPrototypeDocument* aPrototype) michael@0: { michael@0: NS_PRECONDITION(aDocument != nullptr, "null ptr"); michael@0: if (! aDocument) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: nsresult rv; michael@0: michael@0: mDocument = do_GetWeakReference(aDocument); michael@0: mPrototype = aPrototype; michael@0: michael@0: mDocumentURL = mPrototype->GetURI(); michael@0: michael@0: // XXX this presumes HTTP header info is already set in document michael@0: // XXX if it isn't we need to set it here... michael@0: // XXXbz not like GetHeaderData on the proto doc _does_ anything.... michael@0: nsAutoString preferredStyle; michael@0: rv = mPrototype->GetHeaderData(nsGkAtoms::headerDefaultStyle, michael@0: preferredStyle); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (!preferredStyle.IsEmpty()) { michael@0: aDocument->SetHeaderData(nsGkAtoms::headerDefaultStyle, michael@0: preferredStyle); michael@0: } michael@0: michael@0: // Set the right preferred style on the document's CSSLoader. michael@0: aDocument->CSSLoader()->SetPreferredSheet(preferredStyle); michael@0: michael@0: mNodeInfoManager = aPrototype->GetNodeInfoManager(); michael@0: if (! mNodeInfoManager) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: mState = eInProlog; michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // michael@0: // Text buffering michael@0: // michael@0: michael@0: bool michael@0: XULContentSinkImpl::IsDataInBuffer(char16_t* buffer, int32_t length) michael@0: { michael@0: for (int32_t i = 0; i < length; ++i) { michael@0: if (buffer[i] == ' ' || michael@0: buffer[i] == '\t' || michael@0: buffer[i] == '\n' || michael@0: buffer[i] == '\r') michael@0: continue; michael@0: michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: XULContentSinkImpl::FlushText(bool aCreateTextNode) michael@0: { michael@0: nsresult rv; michael@0: michael@0: do { michael@0: // Don't do anything if there's no text to create a node from, or michael@0: // if they've told us not to create a text node michael@0: if (! mTextLength) michael@0: break; michael@0: michael@0: if (! aCreateTextNode) michael@0: break; michael@0: michael@0: nsRefPtr node; michael@0: rv = mContextStack.GetTopNode(node); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: bool stripWhitespace = false; michael@0: if (node->mType == nsXULPrototypeNode::eType_Element) { michael@0: nsINodeInfo *nodeInfo = michael@0: static_cast(node.get())->mNodeInfo; michael@0: michael@0: if (nodeInfo->NamespaceEquals(kNameSpaceID_XUL)) michael@0: stripWhitespace = !nodeInfo->Equals(nsGkAtoms::label) && michael@0: !nodeInfo->Equals(nsGkAtoms::description); michael@0: } michael@0: michael@0: // Don't bother if there's nothing but whitespace. michael@0: if (stripWhitespace && ! IsDataInBuffer(mText, mTextLength)) michael@0: break; michael@0: michael@0: // Don't bother if we're not in XUL document body michael@0: if (mState != eInDocumentElement || mContextStack.Depth() == 0) michael@0: break; michael@0: michael@0: nsXULPrototypeText* text = new nsXULPrototypeText(); michael@0: if (! text) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: text->mValue.Assign(mText, mTextLength); michael@0: if (stripWhitespace) michael@0: text->mValue.Trim(" \t\n\r"); michael@0: michael@0: // hook it up michael@0: nsPrototypeArray* children = nullptr; michael@0: rv = mContextStack.GetTopChildren(&children); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // transfer ownership of 'text' to the children array michael@0: children->AppendElement(text); michael@0: } while (0); michael@0: michael@0: // Reset our text buffer michael@0: mTextLength = 0; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: nsresult michael@0: XULContentSinkImpl::NormalizeAttributeString(const char16_t *aExpatName, michael@0: nsAttrName &aName) michael@0: { michael@0: int32_t nameSpaceID; michael@0: nsCOMPtr prefix, localName; michael@0: nsContentUtils::SplitExpatName(aExpatName, getter_AddRefs(prefix), michael@0: getter_AddRefs(localName), &nameSpaceID); michael@0: michael@0: if (nameSpaceID == kNameSpaceID_None) { michael@0: aName.SetTo(localName); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCOMPtr ni; michael@0: ni = mNodeInfoManager->GetNodeInfo(localName, prefix, michael@0: nameSpaceID, michael@0: nsIDOMNode::ATTRIBUTE_NODE); michael@0: aName.SetTo(ni); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: XULContentSinkImpl::CreateElement(nsINodeInfo *aNodeInfo, michael@0: nsXULPrototypeElement** aResult) michael@0: { michael@0: nsXULPrototypeElement* element = new nsXULPrototypeElement(); michael@0: if (! element) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: element->mNodeInfo = aNodeInfo; michael@0: michael@0: *aResult = element; michael@0: return NS_OK; michael@0: } michael@0: michael@0: /**** BEGIN NEW APIs ****/ michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: XULContentSinkImpl::HandleStartElement(const char16_t *aName, michael@0: const char16_t **aAtts, michael@0: uint32_t aAttsCount, michael@0: int32_t aIndex, michael@0: uint32_t aLineNumber) michael@0: { michael@0: // XXX Hopefully the parser will flag this before we get here. If michael@0: // we're in the epilog, there should be no new elements michael@0: NS_PRECONDITION(mState != eInEpilog, "tag in XUL doc epilog"); michael@0: NS_PRECONDITION(aIndex >= -1, "Bogus aIndex"); michael@0: NS_PRECONDITION(aAttsCount % 2 == 0, "incorrect aAttsCount"); michael@0: // Adjust aAttsCount so it's the actual number of attributes michael@0: aAttsCount /= 2; michael@0: michael@0: if (mState == eInEpilog) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: if (mState != eInScript) { michael@0: FlushText(); michael@0: } michael@0: michael@0: int32_t nameSpaceID; michael@0: nsCOMPtr prefix, localName; michael@0: nsContentUtils::SplitExpatName(aName, getter_AddRefs(prefix), michael@0: getter_AddRefs(localName), &nameSpaceID); michael@0: michael@0: nsCOMPtr nodeInfo; michael@0: nodeInfo = mNodeInfoManager->GetNodeInfo(localName, prefix, nameSpaceID, michael@0: nsIDOMNode::ELEMENT_NODE); michael@0: michael@0: nsresult rv = NS_OK; michael@0: switch (mState) { michael@0: case eInProlog: michael@0: // We're the root document element michael@0: rv = OpenRoot(aAtts, aAttsCount, nodeInfo); michael@0: break; michael@0: michael@0: case eInDocumentElement: michael@0: rv = OpenTag(aAtts, aAttsCount, aLineNumber, nodeInfo); michael@0: break; michael@0: michael@0: case eInEpilog: michael@0: case eInScript: michael@0: PR_LOG(gContentSinkLog, PR_LOG_WARNING, michael@0: ("xul: warning: unexpected tags in epilog at line %d", michael@0: aLineNumber)); michael@0: rv = NS_ERROR_UNEXPECTED; // XXX michael@0: break; michael@0: } michael@0: michael@0: // Set the ID attribute atom on the node info object for this node michael@0: if (aIndex != -1 && NS_SUCCEEDED(rv)) { michael@0: nsCOMPtr IDAttr = do_GetAtom(aAtts[aIndex]); michael@0: michael@0: if (IDAttr) { michael@0: nodeInfo->SetIDAttributeAtom(IDAttr); michael@0: } michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULContentSinkImpl::HandleEndElement(const char16_t *aName) michael@0: { michael@0: // Never EVER return anything but NS_OK or michael@0: // NS_ERROR_HTMLPARSER_BLOCK from this method. Doing so will blow michael@0: // the parser's little mind all over the planet. michael@0: nsresult rv; michael@0: michael@0: nsRefPtr node; michael@0: rv = mContextStack.GetTopNode(node); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: switch (node->mType) { michael@0: case nsXULPrototypeNode::eType_Element: { michael@0: // Flush any text _now_, so that we'll get text nodes created michael@0: // before popping the stack. michael@0: FlushText(); michael@0: michael@0: // Pop the context stack and do prototype hookup. michael@0: nsPrototypeArray* children = nullptr; michael@0: rv = mContextStack.GetTopChildren(&children); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsXULPrototypeElement* element = michael@0: static_cast(node.get()); michael@0: michael@0: int32_t count = children->Length(); michael@0: if (count) { michael@0: element->mChildren.SetCapacity(count); michael@0: michael@0: for (int32_t i = 0; i < count; ++i) michael@0: element->mChildren.AppendElement(children->ElementAt(i)); michael@0: michael@0: } michael@0: } michael@0: break; michael@0: michael@0: case nsXULPrototypeNode::eType_Script: { michael@0: nsXULPrototypeScript* script = michael@0: static_cast(node.get()); michael@0: michael@0: // If given a src= attribute, we must ignore script tag content. michael@0: if (!script->mSrcURI && !script->GetScriptObject()) { michael@0: nsCOMPtr doc = do_QueryReferent(mDocument); michael@0: michael@0: script->mOutOfLine = false; michael@0: if (doc) michael@0: script->Compile(mText, mTextLength, mDocumentURL, michael@0: script->mLineNo, doc, mPrototype); michael@0: } michael@0: michael@0: FlushText(false); michael@0: } michael@0: break; michael@0: michael@0: default: michael@0: NS_ERROR("didn't expect that"); michael@0: break; michael@0: } michael@0: michael@0: rv = mContextStack.Pop(&mState); michael@0: NS_ASSERTION(NS_SUCCEEDED(rv), "context stack corrupted"); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (mContextStack.Depth() == 0) { michael@0: // The root element should -always- be an element, because michael@0: // it'll have been created via XULContentSinkImpl::OpenRoot(). michael@0: NS_ASSERTION(node->mType == nsXULPrototypeNode::eType_Element, "root is not an element"); michael@0: if (node->mType != nsXULPrototypeNode::eType_Element) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: // Now that we're done parsing, set the prototype document's michael@0: // root element. This transfers ownership of the prototype michael@0: // element tree to the prototype document. michael@0: nsXULPrototypeElement* element = michael@0: static_cast(node.get()); michael@0: michael@0: mPrototype->SetRootElement(element); michael@0: mState = eInEpilog; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULContentSinkImpl::HandleComment(const char16_t *aName) michael@0: { michael@0: FlushText(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULContentSinkImpl::HandleCDataSection(const char16_t *aData, uint32_t aLength) michael@0: { michael@0: FlushText(); michael@0: return AddText(aData, aLength); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULContentSinkImpl::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: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULContentSinkImpl::HandleCharacterData(const char16_t *aData, michael@0: uint32_t aLength) michael@0: { michael@0: if (aData && mState != eInProlog && mState != eInEpilog) { michael@0: return AddText(aData, aLength); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULContentSinkImpl::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: // Note: the created nsXULPrototypePI has mRefCnt == 1 michael@0: nsRefPtr pi = new nsXULPrototypePI(); michael@0: if (!pi) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: pi->mTarget = target; michael@0: pi->mData = data; michael@0: michael@0: if (mState == eInProlog) { michael@0: // Note: passing in already addrefed pi michael@0: return mPrototype->AddProcessingInstruction(pi); michael@0: } michael@0: michael@0: nsresult rv; michael@0: nsPrototypeArray* children = nullptr; michael@0: rv = mContextStack.GetTopChildren(&children); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: if (!children->AppendElement(pi)) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: XULContentSinkImpl::HandleXMLDeclaration(const char16_t *aVersion, michael@0: const char16_t *aEncoding, michael@0: int32_t aStandalone) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: XULContentSinkImpl::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: nsresult rv = NS_OK; michael@0: michael@0: // make sure to empty the context stack so that michael@0: // could become the root element. michael@0: mContextStack.Clear(); michael@0: michael@0: mState = eInProlog; 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: nsCOMPtr doc = do_QueryReferent(mDocument); michael@0: if (doc && !doc->OnDocumentParserError()) { michael@0: // The overlay was broken. Don't add a messy element to the master doc. michael@0: return NS_OK; michael@0: } michael@0: michael@0: const char16_t* noAtts[] = { 0, 0 }; michael@0: michael@0: NS_NAMED_LITERAL_STRING(errorNs, michael@0: "http://www.mozilla.org/newlayout/xml/parsererror.xml"); michael@0: michael@0: nsAutoString parsererror(errorNs); michael@0: parsererror.Append((char16_t)0xFFFF); michael@0: parsererror.AppendLiteral("parsererror"); michael@0: michael@0: rv = HandleStartElement(parsererror.get(), noAtts, 0, -1, 0); michael@0: NS_ENSURE_SUCCESS(rv,rv); michael@0: michael@0: rv = HandleCharacterData(aErrorText, NS_strlen(aErrorText)); michael@0: NS_ENSURE_SUCCESS(rv,rv); michael@0: michael@0: nsAutoString sourcetext(errorNs); michael@0: sourcetext.Append((char16_t)0xFFFF); michael@0: sourcetext.AppendLiteral("sourcetext"); michael@0: michael@0: rv = HandleStartElement(sourcetext.get(), noAtts, 0, -1, 0); michael@0: NS_ENSURE_SUCCESS(rv,rv); michael@0: michael@0: rv = HandleCharacterData(aSourceText, NS_strlen(aSourceText)); michael@0: NS_ENSURE_SUCCESS(rv,rv); michael@0: michael@0: rv = HandleEndElement(sourcetext.get()); michael@0: NS_ENSURE_SUCCESS(rv,rv); michael@0: michael@0: rv = HandleEndElement(parsererror.get()); michael@0: NS_ENSURE_SUCCESS(rv,rv); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: XULContentSinkImpl::OpenRoot(const char16_t** aAttributes, michael@0: const uint32_t aAttrLen, michael@0: nsINodeInfo *aNodeInfo) michael@0: { michael@0: NS_ASSERTION(mState == eInProlog, "how'd we get here?"); michael@0: if (mState != eInProlog) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: nsresult rv; michael@0: michael@0: if (aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XHTML) || michael@0: aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XUL)) { michael@0: PR_LOG(gContentSinkLog, PR_LOG_ERROR, michael@0: ("xul: script tag not allowed as root content element")); michael@0: michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: // Create the element michael@0: nsXULPrototypeElement* element; michael@0: rv = CreateElement(aNodeInfo, &element); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: #ifdef PR_LOGGING michael@0: if (PR_LOG_TEST(gContentSinkLog, PR_LOG_ERROR)) { michael@0: nsAutoString anodeC; michael@0: aNodeInfo->GetName(anodeC); michael@0: PR_LOG(gContentSinkLog, PR_LOG_ERROR, michael@0: ("xul: unable to create element '%s' at line %d", michael@0: NS_ConvertUTF16toUTF8(anodeC).get(), michael@0: -1)); // XXX pass in line number michael@0: } michael@0: #endif michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: // Push the element onto the context stack, so that child michael@0: // containers will hook up to us as their parent. michael@0: rv = mContextStack.Push(element, mState); michael@0: if (NS_FAILED(rv)) { michael@0: element->Release(); michael@0: return rv; michael@0: } michael@0: michael@0: // Add the attributes michael@0: rv = AddAttributes(aAttributes, aAttrLen, element); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: mState = eInDocumentElement; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: XULContentSinkImpl::OpenTag(const char16_t** aAttributes, michael@0: const uint32_t aAttrLen, michael@0: const uint32_t aLineNumber, michael@0: nsINodeInfo *aNodeInfo) michael@0: { michael@0: nsresult rv; michael@0: michael@0: // Create the element michael@0: nsXULPrototypeElement* element; michael@0: rv = CreateElement(aNodeInfo, &element); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: #ifdef PR_LOGGING michael@0: if (PR_LOG_TEST(gContentSinkLog, PR_LOG_ERROR)) { michael@0: nsAutoString anodeC; michael@0: aNodeInfo->GetName(anodeC); michael@0: PR_LOG(gContentSinkLog, PR_LOG_ERROR, michael@0: ("xul: unable to create element '%s' at line %d", michael@0: NS_ConvertUTF16toUTF8(anodeC).get(), michael@0: aLineNumber)); michael@0: } michael@0: #endif michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: // Link this element to its parent. michael@0: nsPrototypeArray* children = nullptr; michael@0: rv = mContextStack.GetTopChildren(&children); michael@0: if (NS_FAILED(rv)) { michael@0: delete element; michael@0: return rv; michael@0: } michael@0: michael@0: // Add the attributes michael@0: rv = AddAttributes(aAttributes, aAttrLen, element); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: children->AppendElement(element); michael@0: michael@0: if (aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XHTML) || michael@0: aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XUL)) { michael@0: // Do scripty things now michael@0: rv = OpenScript(aAttributes, aLineNumber); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: NS_ASSERTION(mState == eInScript || mState == eInDocumentElement, michael@0: "Unexpected state"); michael@0: if (mState == eInScript) { michael@0: // OpenScript has pushed the nsPrototypeScriptElement onto the michael@0: // stack, so we're done. michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: // Push the element onto the context stack, so that child michael@0: // containers will hook up to us as their parent. michael@0: rv = mContextStack.Push(element, mState); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: mState = eInDocumentElement; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: XULContentSinkImpl::OpenScript(const char16_t** aAttributes, michael@0: const uint32_t aLineNumber) michael@0: { michael@0: uint32_t langID = nsIProgrammingLanguage::JAVASCRIPT; michael@0: uint32_t version = JSVERSION_LATEST; michael@0: nsresult rv; michael@0: michael@0: // Look for SRC attribute and look for a LANGUAGE attribute michael@0: nsAutoString src; michael@0: while (*aAttributes) { michael@0: const nsDependentString key(aAttributes[0]); michael@0: if (key.EqualsLiteral("src")) { michael@0: src.Assign(aAttributes[1]); michael@0: } michael@0: else if (key.EqualsLiteral("type")) { michael@0: nsDependentString str(aAttributes[1]); michael@0: nsContentTypeParser parser(str); michael@0: nsAutoString mimeType; michael@0: rv = parser.GetType(mimeType); michael@0: if (NS_FAILED(rv)) { michael@0: if (rv == NS_ERROR_INVALID_ARG) { michael@0: // Might as well bail out now instead of setting langID to michael@0: // nsIProgrammingLanguage::UNKNOWN and bailing out later. michael@0: return NS_OK; michael@0: } michael@0: // We do want the warning here michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: if (nsContentUtils::IsJavascriptMIMEType(mimeType)) { michael@0: langID = nsIProgrammingLanguage::JAVASCRIPT; michael@0: version = JSVERSION_LATEST; michael@0: } else { michael@0: langID = nsIProgrammingLanguage::UNKNOWN; michael@0: } michael@0: michael@0: if (langID != nsIProgrammingLanguage::UNKNOWN) { michael@0: // Get the version string, and ensure the language supports it. michael@0: nsAutoString versionName; michael@0: rv = parser.GetParameter("version", versionName); michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: version = nsContentUtils::ParseJavascriptVersion(versionName); michael@0: } else if (rv != NS_ERROR_INVALID_ARG) { michael@0: return rv; michael@0: } michael@0: } michael@0: } michael@0: else if (key.EqualsLiteral("language")) { michael@0: // Language is deprecated, and the impl in nsScriptLoader ignores the michael@0: // various version strings anyway. So we make no attempt to support michael@0: // languages other than JS for language= michael@0: nsAutoString lang(aAttributes[1]); michael@0: if (nsContentUtils::IsJavaScriptLanguage(lang)) { michael@0: version = JSVERSION_DEFAULT; michael@0: langID = nsIProgrammingLanguage::JAVASCRIPT; michael@0: } michael@0: } michael@0: aAttributes += 2; michael@0: } michael@0: michael@0: // Not all script languages have a "sandbox" concept. At time of michael@0: // writing, Python is the only other language, and it does not. michael@0: // For such languages, neither any inline script nor remote script are michael@0: // safe to execute from untrusted sources. michael@0: // So for such languages, we only allow script when the document michael@0: // itself is from chrome. We then don't bother to check the michael@0: // "src=" tag - we trust chrome to do the right thing. michael@0: // (See also similar code in nsScriptLoader.cpp) michael@0: nsCOMPtr doc(do_QueryReferent(mDocument)); michael@0: if (langID != nsIProgrammingLanguage::UNKNOWN && michael@0: langID != nsIProgrammingLanguage::JAVASCRIPT && michael@0: doc && !nsContentUtils::IsChromeDoc(doc)) { michael@0: langID = nsIProgrammingLanguage::UNKNOWN; michael@0: NS_WARNING("Non JS language called from non chrome - ignored"); michael@0: } michael@0: michael@0: // Don't process scripts that aren't known michael@0: if (langID != nsIProgrammingLanguage::UNKNOWN) { michael@0: nsCOMPtr globalObject; michael@0: if (doc) michael@0: globalObject = do_QueryInterface(doc->GetWindow()); michael@0: nsRefPtr script = michael@0: new nsXULPrototypeScript(aLineNumber, version); michael@0: if (! script) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: // If there is a SRC attribute... michael@0: if (! src.IsEmpty()) { michael@0: // Use the SRC attribute value to load the URL michael@0: rv = NS_NewURI(getter_AddRefs(script->mSrcURI), src, nullptr, mDocumentURL); michael@0: michael@0: // Check if this document is allowed to load a script from this source michael@0: // NOTE: if we ever allow scripts added via the DOM to run, we need to michael@0: // add a CheckLoadURI call for that as well. michael@0: if (NS_SUCCEEDED(rv)) { michael@0: if (!mSecMan) michael@0: mSecMan = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: nsCOMPtr doc = do_QueryReferent(mDocument, &rv); michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: rv = mSecMan-> michael@0: CheckLoadURIWithPrincipal(doc->NodePrincipal(), michael@0: script->mSrcURI, michael@0: nsIScriptSecurityManager::ALLOW_CHROME); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: // Attempt to deserialize an out-of-line script from the FastLoad michael@0: // file right away. Otherwise we'll end up reloading the script and michael@0: // corrupting the FastLoad file trying to serialize it, in the case michael@0: // where it's already there. michael@0: script->DeserializeOutOfLine(nullptr, mPrototype); michael@0: } michael@0: michael@0: nsPrototypeArray* children = nullptr; michael@0: rv = mContextStack.GetTopChildren(&children); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: children->AppendElement(script); michael@0: michael@0: mConstrainSize = false; michael@0: michael@0: mContextStack.Push(script, mState); michael@0: mState = eInScript; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: XULContentSinkImpl::AddAttributes(const char16_t** aAttributes, michael@0: const uint32_t aAttrLen, michael@0: nsXULPrototypeElement* aElement) michael@0: { michael@0: // Add tag attributes to the element michael@0: nsresult rv; michael@0: michael@0: // Create storage for the attributes michael@0: nsXULPrototypeAttribute* attrs = nullptr; michael@0: if (aAttrLen > 0) { michael@0: attrs = new nsXULPrototypeAttribute[aAttrLen]; michael@0: if (! attrs) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: aElement->mAttributes = attrs; michael@0: aElement->mNumAttributes = aAttrLen; michael@0: michael@0: // Copy the attributes into the prototype michael@0: uint32_t i; michael@0: for (i = 0; i < aAttrLen; ++i) { michael@0: rv = NormalizeAttributeString(aAttributes[i * 2], attrs[i].mName); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = aElement->SetAttrAt(i, nsDependentString(aAttributes[i * 2 + 1]), michael@0: mDocumentURL); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: #ifdef PR_LOGGING michael@0: if (PR_LOG_TEST(gContentSinkLog, PR_LOG_DEBUG)) { michael@0: nsAutoString extraWhiteSpace; michael@0: int32_t cnt = mContextStack.Depth(); michael@0: while (--cnt >= 0) michael@0: extraWhiteSpace.AppendLiteral(" "); michael@0: nsAutoString qnameC,valueC; michael@0: qnameC.Assign(aAttributes[0]); michael@0: valueC.Assign(aAttributes[1]); michael@0: PR_LOG(gContentSinkLog, PR_LOG_DEBUG, michael@0: ("xul: %.5d. %s %s=%s", michael@0: -1, // XXX pass in line number michael@0: NS_ConvertUTF16toUTF8(extraWhiteSpace).get(), michael@0: NS_ConvertUTF16toUTF8(qnameC).get(), michael@0: NS_ConvertUTF16toUTF8(valueC).get())); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: XULContentSinkImpl::AddText(const char16_t* aText, michael@0: int32_t aLength) michael@0: { michael@0: // Create buffer when we first need it michael@0: if (0 == mTextSize) { michael@0: mText = (char16_t *) moz_malloc(sizeof(char16_t) * 4096); michael@0: if (nullptr == mText) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: mTextSize = 4096; michael@0: } michael@0: michael@0: // Copy data from string into our buffer; flush buffer when it fills up michael@0: int32_t offset = 0; michael@0: while (0 != aLength) { michael@0: int32_t amount = mTextSize - mTextLength; michael@0: if (amount > aLength) { michael@0: amount = aLength; michael@0: } michael@0: if (0 == amount) { michael@0: if (mConstrainSize) { michael@0: nsresult rv = FlushText(); michael@0: if (NS_OK != rv) { michael@0: return rv; michael@0: } michael@0: } michael@0: else { michael@0: mTextSize += aLength; michael@0: mText = (char16_t *) moz_realloc(mText, sizeof(char16_t) * mTextSize); michael@0: if (nullptr == mText) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: } michael@0: } michael@0: memcpy(&mText[mTextLength],aText + offset, sizeof(char16_t) * amount); michael@0: michael@0: mTextLength += amount; michael@0: offset += amount; michael@0: aLength -= amount; michael@0: } michael@0: michael@0: return NS_OK; michael@0: }