diff options
Diffstat (limited to '')
-rw-r--r-- | dom/xul/nsXULContentSink.cpp | 869 |
1 files changed, 869 insertions, 0 deletions
diff --git a/dom/xul/nsXULContentSink.cpp b/dom/xul/nsXULContentSink.cpp new file mode 100644 index 0000000000..a4c15c63cd --- /dev/null +++ b/dom/xul/nsXULContentSink.cpp @@ -0,0 +1,869 @@ +/* -*- 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"); + +using namespace mozilla; +using namespace mozilla::dom; +//---------------------------------------------------------------------- + +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::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; +} + +void XULContentSinkImpl::WillResume() { + // XXX Notify the docshell, if necessary +} + +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->HasStencil()) { + 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); + } + + // NOTE(emilio): Module scripts don't pass this test, aren't cached yet. + // If they become cached, then we need to tweak + // PrototypeDocumentContentSink and remove the special cases there. + 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, u""_ns, 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; +} |