/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* 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/. */ #include "mozilla/Move.h" #include "XPathExpression.h" #include "txExpr.h" #include "txExprResult.h" #include "txIXPathContext.h" #include "nsError.h" #include "nsIDOMCharacterData.h" #include "nsDOMClassInfoID.h" #include "nsIDOMDocument.h" #include "XPathResult.h" #include "txURIUtils.h" #include "txXPathTreeWalker.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/XPathResultBinding.h" using mozilla::Move; namespace mozilla { namespace dom { class EvalContextImpl : public txIEvalContext { public: EvalContextImpl(const txXPathNode& aContextNode, uint32_t aContextPosition, uint32_t aContextSize, txResultRecycler* aRecycler) : mContextNode(aContextNode), mContextPosition(aContextPosition), mContextSize(aContextSize), mLastError(NS_OK), mRecycler(aRecycler) { } nsresult getError() { return mLastError; } TX_DECL_EVAL_CONTEXT; private: const txXPathNode& mContextNode; uint32_t mContextPosition; uint32_t mContextSize; nsresult mLastError; RefPtr mRecycler; }; XPathExpression::XPathExpression(nsAutoPtr&& aExpression, txResultRecycler* aRecycler, nsIDocument *aDocument) : mExpression(Move(aExpression)), mRecycler(aRecycler), mDocument(do_GetWeakReference(aDocument)), mCheckDocument(aDocument != nullptr) { } XPathExpression::~XPathExpression() { } already_AddRefed XPathExpression::EvaluateWithContext(JSContext* aCx, nsINode& aContextNode, uint32_t aContextPosition, uint32_t aContextSize, uint16_t aType, JS::Handle aInResult, ErrorResult& aRv) { RefPtr inResult; if (aInResult) { nsresult rv = UNWRAP_OBJECT(XPathResult, aInResult, inResult); if (NS_FAILED(rv) && rv != NS_ERROR_XPC_BAD_CONVERT_JS) { aRv.Throw(rv); return nullptr; } } return EvaluateWithContext(aContextNode, aContextPosition, aContextSize, aType, inResult, aRv); } already_AddRefed XPathExpression::EvaluateWithContext(nsINode& aContextNode, uint32_t aContextPosition, uint32_t aContextSize, uint16_t aType, XPathResult* aInResult, ErrorResult& aRv) { if (aContextPosition > aContextSize) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; } if (!nsContentUtils::LegacyIsCallerNativeCode() && !nsContentUtils::CanCallerAccess(&aContextNode)) { aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); return nullptr; } if (mCheckDocument) { nsCOMPtr doc = do_QueryReferent(mDocument); if (doc != aContextNode.OwnerDoc()) { aRv.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR); return nullptr; } } uint16_t nodeType = aContextNode.NodeType(); if (nodeType == nsIDOMNode::TEXT_NODE || nodeType == nsIDOMNode::CDATA_SECTION_NODE) { nsCOMPtr textNode = do_QueryInterface(&aContextNode); if (!textNode) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; } uint32_t textLength; textNode->GetLength(&textLength); if (textLength == 0) { aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); return nullptr; } // XXX Need to get logical XPath text node for CDATASection // and Text nodes. } else if (nodeType != nsIDOMNode::DOCUMENT_NODE && nodeType != nsIDOMNode::ELEMENT_NODE && nodeType != nsIDOMNode::ATTRIBUTE_NODE && nodeType != nsIDOMNode::COMMENT_NODE && nodeType != nsIDOMNode::PROCESSING_INSTRUCTION_NODE) { aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); return nullptr; } nsAutoPtr contextNode(txXPathNativeNode::createXPathNode(&aContextNode)); if (!contextNode) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; } EvalContextImpl eContext(*contextNode, aContextPosition, aContextSize, mRecycler); RefPtr exprResult; aRv = mExpression->evaluate(&eContext, getter_AddRefs(exprResult)); if (aRv.Failed()) { return nullptr; } uint16_t resultType = aType; if (aType == XPathResult::ANY_TYPE) { short exprResultType = exprResult->getResultType(); switch (exprResultType) { case txAExprResult::NUMBER: resultType = XPathResult::NUMBER_TYPE; break; case txAExprResult::STRING: resultType = XPathResult::STRING_TYPE; break; case txAExprResult::BOOLEAN: resultType = XPathResult::BOOLEAN_TYPE; break; case txAExprResult::NODESET: resultType = XPathResult::UNORDERED_NODE_ITERATOR_TYPE; break; case txAExprResult::RESULT_TREE_FRAGMENT: aRv.Throw(NS_ERROR_FAILURE); return nullptr; } } RefPtr xpathResult = aInResult; if (!xpathResult) { xpathResult = new XPathResult(&aContextNode); } aRv = xpathResult->SetExprResult(exprResult, resultType, &aContextNode); return xpathResult.forget(); } /* * Implementation of the txIEvalContext private to XPathExpression * EvalContextImpl bases on only one context node and no variables */ nsresult EvalContextImpl::getVariable(int32_t aNamespace, nsIAtom* aLName, txAExprResult*& aResult) { aResult = 0; return NS_ERROR_INVALID_ARG; } bool EvalContextImpl::isStripSpaceAllowed(const txXPathNode& aNode) { return false; } void* EvalContextImpl::getPrivateContext() { // we don't have a private context here. return nullptr; } txResultRecycler* EvalContextImpl::recycler() { return mRecycler; } void EvalContextImpl::receiveError(const nsAString& aMsg, nsresult aRes) { mLastError = aRes; // forward aMsg to console service? } const txXPathNode& EvalContextImpl::getContextNode() { return mContextNode; } uint32_t EvalContextImpl::size() { return mContextSize; } uint32_t EvalContextImpl::position() { return mContextPosition; } } // namespace dom } // namespace mozilla