diff options
Diffstat (limited to 'dom/xul/nsXULContentSink.cpp')
-rw-r--r-- | dom/xul/nsXULContentSink.cpp | 1051 |
1 files changed, 1051 insertions, 0 deletions
diff --git a/dom/xul/nsXULContentSink.cpp b/dom/xul/nsXULContentSink.cpp new file mode 100644 index 000000000..7103be758 --- /dev/null +++ b/dom/xul/nsXULContentSink.cpp @@ -0,0 +1,1051 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=4 et sw=4 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 "nsForwardReference.h" +#include "nsHTMLStyleSheet.h" +#include "nsIContentSink.h" +#include "nsIDocument.h" +#include "nsIDOMEventListener.h" +#include "nsIDOMHTMLFormElement.h" +#include "nsIDOMXULDocument.h" +#include "nsIFormControl.h" +#include "mozilla/dom/NodeInfo.h" +#include "nsIScriptContext.h" +#include "nsIScriptGlobalObject.h" +#include "nsIServiceManager.h" +#include "nsIURL.h" +#include "nsNameSpaceManager.h" +#include "nsParserBase.h" +#include "nsViewManager.h" +#include "nsIXULDocument.h" +#include "nsIScriptSecurityManager.h" +#include "nsLayoutCID.h" +#include "nsNetUtil.h" +#include "nsRDFCID.h" +#include "nsXPIDLString.h" +#include "nsReadableUtils.h" +#include "nsXULElement.h" +#include "mozilla/Logging.h" +#include "prmem.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 "nsIConsoleService.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; + } +} + +nsresult +XULContentSinkImpl::ContextStack::Push(nsXULPrototypeNode* aNode, State aState) +{ + Entry* entry = new Entry; + entry->mNode = aNode; + entry->mState = aState; + entry->mNext = mTop; + + mTop = entry; + + ++mDepth; + return NS_OK; +} + +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<nsIDocument> doc = do_QueryReferent(mDocument); + if (doc) { + doc->EndLoad(); + 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; +} + +NS_IMETHODIMP +XULContentSinkImpl::SetDocumentCharset(nsACString& aCharset) +{ + nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument); + if (doc) { + doc->SetDocumentCharacterSet(aCharset); + } + + return NS_OK; +} + +nsISupports * +XULContentSinkImpl::GetTarget() +{ + nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument); + return doc; +} + +//---------------------------------------------------------------------- + +nsresult +XULContentSinkImpl::Init(nsIDocument* aDocument, + nsXULPrototypeDocument* aPrototype) +{ + NS_PRECONDITION(aDocument != nullptr, "null ptr"); + if (! aDocument) + return NS_ERROR_NULL_POINTER; + + nsresult rv; + + mDocument = do_GetWeakReference(aDocument); + mPrototype = aPrototype; + + mDocumentURL = mPrototype->GetURI(); + + // XXX this presumes HTTP header info is already set in document + // XXX if it isn't we need to set it here... + // XXXbz not like GetHeaderData on the proto doc _does_ anything.... + nsAutoString preferredStyle; + rv = mPrototype->GetHeaderData(nsGkAtoms::headerDefaultStyle, + preferredStyle); + if (NS_FAILED(rv)) return rv; + + if (!preferredStyle.IsEmpty()) { + aDocument->SetHeaderData(nsGkAtoms::headerDefaultStyle, + preferredStyle); + } + + // Set the right preferred style on the document's CSSLoader. + aDocument->CSSLoader()->SetPreferredSheet(preferredStyle); + + 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; + + 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; + + // transfer ownership of 'text' to the children array + children->AppendElement(text); + } while (0); + + // Reset our text buffer + mTextLength = 0; + return NS_OK; +} + +//---------------------------------------------------------------------- + +nsresult +XULContentSinkImpl::NormalizeAttributeString(const char16_t *aExpatName, + nsAttrName &aName) +{ + int32_t nameSpaceID; + nsCOMPtr<nsIAtom> 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, + nsIDOMNode::ATTRIBUTE_NODE); + aName.SetTo(ni); + + return NS_OK; +} + +nsresult +XULContentSinkImpl::CreateElement(mozilla::dom::NodeInfo *aNodeInfo, + nsXULPrototypeElement** aResult) +{ + nsXULPrototypeElement* element = new nsXULPrototypeElement(); + element->mNodeInfo = aNodeInfo; + + *aResult = element; + return NS_OK; +} + +/**** BEGIN NEW APIs ****/ + + +NS_IMETHODIMP +XULContentSinkImpl::HandleStartElement(const char16_t *aName, + const char16_t **aAtts, + uint32_t aAttsCount, + uint32_t aLineNumber) +{ + // XXX Hopefully the parser will flag this before we get here. If + // we're in the epilog, there should be no new elements + NS_PRECONDITION(mState != eInEpilog, "tag in XUL doc epilog"); + NS_PRECONDITION(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; + nsCOMPtr<nsIAtom> prefix, localName; + nsContentUtils::SplitExpatName(aName, getter_AddRefs(prefix), + getter_AddRefs(localName), &nameSpaceID); + + RefPtr<mozilla::dom::NodeInfo> nodeInfo; + nodeInfo = mNodeInfoManager->GetNodeInfo(localName, prefix, nameSpaceID, + nsIDOMNode::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<nsIDocument> doc = do_QueryReferent(mDocument); + + script->mOutOfLine = false; + if (doc) + script->Compile(mText, mTextLength, 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; + } + + if (!children->AppendElement(pi)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + 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) +{ + NS_PRECONDITION(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<nsIDocument> idoc = do_QueryReferent(mDocument); + if (idoc && idoc->SuppressParserErrorElement()) { + return NS_OK; + }; + + nsCOMPtr<nsIXULDocument> doc = do_QueryReferent(mDocument); + if (doc && !doc->OnDocumentParserError()) { + // The overlay was broken. Don't add a messy element to the master doc. + return NS_OK; + } + + const char16_t* noAtts[] = { 0, 0 }; + + NS_NAMED_LITERAL_STRING(errorNs, + "http://www.mozilla.org/newlayout/xml/parsererror.xml"); + + nsAutoString parsererror(errorNs); + parsererror.Append((char16_t)0xFFFF); + parsererror.AppendLiteral("parsererror"); + + rv = HandleStartElement(parsererror.get(), noAtts, 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); + 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; + + nsresult rv; + + 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 + nsXULPrototypeElement* element; + rv = CreateElement(aNodeInfo, &element); + + if (NS_FAILED(rv)) { + if (MOZ_LOG_TEST(gContentSinkLog, LogLevel::Error)) { + nsAutoString anodeC; + aNodeInfo->GetName(anodeC); + MOZ_LOG(gContentSinkLog, LogLevel::Error, + ("xul: unable to create element '%s' at line %d", + NS_ConvertUTF16toUTF8(anodeC).get(), + -1)); // XXX pass in line number + } + + return rv; + } + + // Push the element onto the context stack, so that child + // containers will hook up to us as their parent. + rv = mContextStack.Push(element, mState); + if (NS_FAILED(rv)) { + element->Release(); + return rv; + } + + // Add the attributes + rv = AddAttributes(aAttributes, aAttrLen, element); + if (NS_FAILED(rv)) return rv; + + mState = eInDocumentElement; + return NS_OK; +} + +nsresult +XULContentSinkImpl::OpenTag(const char16_t** aAttributes, + const uint32_t aAttrLen, + const uint32_t aLineNumber, + mozilla::dom::NodeInfo *aNodeInfo) +{ + nsresult rv; + + // Create the element + nsXULPrototypeElement* element; + rv = CreateElement(aNodeInfo, &element); + + if (NS_FAILED(rv)) { + if (MOZ_LOG_TEST(gContentSinkLog, LogLevel::Error)) { + nsAutoString anodeC; + aNodeInfo->GetName(anodeC); + MOZ_LOG(gContentSinkLog, LogLevel::Error, + ("xul: unable to create element '%s' at line %d", + NS_ConvertUTF16toUTF8(anodeC).get(), + aLineNumber)); + } + + return rv; + } + + // Link this element to its parent. + nsPrototypeArray* children = nullptr; + rv = mContextStack.GetTopChildren(&children); + if (NS_FAILED(rv)) { + delete element; + 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. + rv = mContextStack.Push(element, mState); + if (NS_FAILED(rv)) return rv; + + mState = eInDocumentElement; + return NS_OK; +} + +nsresult +XULContentSinkImpl::OpenScript(const char16_t** aAttributes, + const uint32_t aLineNumber) +{ + bool isJavaScript = true; + uint32_t version = JSVERSION_LATEST; + 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; + version = JSVERSION_LATEST; + + // Get the version string, and ensure that JavaScript supports it. + nsAutoString versionName; + rv = parser.GetParameter("version", versionName); + + if (NS_SUCCEEDED(rv)) { + version = nsContentUtils::ParseJavascriptVersion(versionName); + } else if (rv != NS_ERROR_INVALID_ARG) { + return rv; + } + } else { + isJavaScript = false; + } + } else if (key.EqualsLiteral("language")) { + // Language is deprecated, and the impl in nsScriptLoader 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; + version = JSVERSION_DEFAULT; + } + } + aAttributes += 2; + } + + // Don't process scripts that aren't JavaScript. + if (!isJavaScript) { + return NS_OK; + } + + nsCOMPtr<nsIDocument> doc(do_QueryReferent(mDocument)); + nsCOMPtr<nsIScriptGlobalObject> globalObject; + if (doc) + globalObject = do_QueryInterface(doc->GetWindow()); + RefPtr<nsXULPrototypeScript> script = + new nsXULPrototypeScript(aLineNumber, version); + + // 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<nsIDocument> doc = do_QueryReferent(mDocument, &rv); + + if (NS_SUCCEEDED(rv)) { + rv = mSecMan-> + CheckLoadURIWithPrincipal(doc->NodePrincipal(), + script->mSrcURI, + nsIScriptSecurityManager::ALLOW_CHROME); + } + } + } + + 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 = new nsXULPrototypeAttribute[aAttrLen]; + } + + aElement->mAttributes = attrs; + aElement->mNumAttributes = 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; +} |