1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "XPathExpression.h"
7 
8 #include <utility>
9 
10 #include "XPathResult.h"
11 #include "mozilla/dom/BindingUtils.h"
12 #include "mozilla/dom/Text.h"
13 #include "mozilla/dom/XPathResultBinding.h"
14 #include "nsError.h"
15 #include "nsINode.h"
16 #include "txExpr.h"
17 #include "txExprResult.h"
18 #include "txIXPathContext.h"
19 #include "txURIUtils.h"
20 #include "txXPathTreeWalker.h"
21 
22 namespace mozilla {
23 namespace dom {
24 
25 class EvalContextImpl : public txIEvalContext {
26  public:
EvalContextImpl(const txXPathNode & aContextNode,uint32_t aContextPosition,uint32_t aContextSize,txResultRecycler * aRecycler)27   EvalContextImpl(const txXPathNode& aContextNode, uint32_t aContextPosition,
28                   uint32_t aContextSize, txResultRecycler* aRecycler)
29       : mContextNode(aContextNode),
30         mContextPosition(aContextPosition),
31         mContextSize(aContextSize),
32         mLastError(NS_OK),
33         mRecycler(aRecycler) {}
34 
getError()35   nsresult getError() { return mLastError; }
36 
37   TX_DECL_EVAL_CONTEXT;
38 
39  private:
40   const txXPathNode& mContextNode;
41   uint32_t mContextPosition;
42   uint32_t mContextSize;
43   nsresult mLastError;
44   RefPtr<txResultRecycler> mRecycler;
45 };
46 
XPathExpression(UniquePtr<Expr> && aExpression,txResultRecycler * aRecycler,Document * aDocument)47 XPathExpression::XPathExpression(UniquePtr<Expr>&& aExpression,
48                                  txResultRecycler* aRecycler,
49                                  Document* aDocument)
50     : mExpression(std::move(aExpression)),
51       mRecycler(aRecycler),
52       mDocument(do_GetWeakReference(aDocument)),
53       mCheckDocument(aDocument != nullptr) {}
54 
55 XPathExpression::~XPathExpression() = default;
56 
EvaluateWithContext(JSContext * aCx,nsINode & aContextNode,uint32_t aContextPosition,uint32_t aContextSize,uint16_t aType,JS::Handle<JSObject * > aInResult,ErrorResult & aRv)57 already_AddRefed<XPathResult> XPathExpression::EvaluateWithContext(
58     JSContext* aCx, nsINode& aContextNode, uint32_t aContextPosition,
59     uint32_t aContextSize, uint16_t aType, JS::Handle<JSObject*> aInResult,
60     ErrorResult& aRv) {
61   RefPtr<XPathResult> inResult;
62   if (aInResult) {
63     nsresult rv = UNWRAP_OBJECT(XPathResult, aInResult, inResult);
64     if (NS_FAILED(rv) && rv != NS_ERROR_XPC_BAD_CONVERT_JS) {
65       aRv.Throw(rv);
66       return nullptr;
67     }
68   }
69 
70   return EvaluateWithContext(aContextNode, aContextPosition, aContextSize,
71                              aType, inResult, aRv);
72 }
73 
EvaluateWithContext(nsINode & aContextNode,uint32_t aContextPosition,uint32_t aContextSize,uint16_t aType,XPathResult * aInResult,ErrorResult & aRv)74 already_AddRefed<XPathResult> XPathExpression::EvaluateWithContext(
75     nsINode& aContextNode, uint32_t aContextPosition, uint32_t aContextSize,
76     uint16_t aType, XPathResult* aInResult, ErrorResult& aRv) {
77   if (aContextPosition > aContextSize) {
78     aRv.Throw(NS_ERROR_FAILURE);
79     return nullptr;
80   }
81 
82   if (aType > XPathResult_Binding::FIRST_ORDERED_NODE_TYPE) {
83     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
84     return nullptr;
85   }
86 
87   if (!nsContentUtils::LegacyIsCallerNativeCode() &&
88       !nsContentUtils::CanCallerAccess(&aContextNode)) {
89     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
90     return nullptr;
91   }
92 
93   if (mCheckDocument) {
94     nsCOMPtr<Document> doc = do_QueryReferent(mDocument);
95     if (doc != aContextNode.OwnerDoc()) {
96       aRv.Throw(NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
97       return nullptr;
98     }
99   }
100 
101   uint16_t nodeType = aContextNode.NodeType();
102 
103   if (nodeType == nsINode::TEXT_NODE ||
104       nodeType == nsINode::CDATA_SECTION_NODE) {
105     Text* textNode = aContextNode.GetAsText();
106     MOZ_ASSERT(textNode);
107 
108     uint32_t textLength = textNode->Length();
109     if (textLength == 0) {
110       aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
111       return nullptr;
112     }
113 
114     // XXX Need to get logical XPath text node for CDATASection
115     //     and Text nodes.
116   } else if (nodeType != nsINode::DOCUMENT_NODE &&
117              nodeType != nsINode::ELEMENT_NODE &&
118              nodeType != nsINode::ATTRIBUTE_NODE &&
119              nodeType != nsINode::COMMENT_NODE &&
120              nodeType != nsINode::PROCESSING_INSTRUCTION_NODE) {
121     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
122     return nullptr;
123   }
124 
125   UniquePtr<txXPathNode> contextNode(
126       txXPathNativeNode::createXPathNode(&aContextNode));
127   if (!contextNode) {
128     aRv.Throw(NS_ERROR_FAILURE);
129     return nullptr;
130   }
131 
132   EvalContextImpl eContext(*contextNode, aContextPosition, aContextSize,
133                            mRecycler);
134   RefPtr<txAExprResult> exprResult;
135   aRv = mExpression->evaluate(&eContext, getter_AddRefs(exprResult));
136   if (aRv.Failed()) {
137     return nullptr;
138   }
139 
140   uint16_t resultType = aType;
141   if (aType == XPathResult::ANY_TYPE) {
142     short exprResultType = exprResult->getResultType();
143     switch (exprResultType) {
144       case txAExprResult::NUMBER:
145         resultType = XPathResult::NUMBER_TYPE;
146         break;
147       case txAExprResult::STRING:
148         resultType = XPathResult::STRING_TYPE;
149         break;
150       case txAExprResult::BOOLEAN:
151         resultType = XPathResult::BOOLEAN_TYPE;
152         break;
153       case txAExprResult::NODESET:
154         resultType = XPathResult::UNORDERED_NODE_ITERATOR_TYPE;
155         break;
156       case txAExprResult::RESULT_TREE_FRAGMENT:
157         aRv.Throw(NS_ERROR_FAILURE);
158         return nullptr;
159     }
160   }
161 
162   RefPtr<XPathResult> xpathResult = aInResult;
163   if (!xpathResult) {
164     xpathResult = new XPathResult(&aContextNode);
165   }
166 
167   xpathResult->SetExprResult(exprResult, resultType, &aContextNode, aRv);
168   if (aRv.Failed()) {
169     return nullptr;
170   }
171 
172   return xpathResult.forget();
173 }
174 
175 /*
176  * Implementation of the txIEvalContext private to XPathExpression
177  * EvalContextImpl bases on only one context node and no variables
178  */
179 
getVariable(int32_t aNamespace,nsAtom * aLName,txAExprResult * & aResult)180 nsresult EvalContextImpl::getVariable(int32_t aNamespace, nsAtom* aLName,
181                                       txAExprResult*& aResult) {
182   aResult = 0;
183   return NS_ERROR_INVALID_ARG;
184 }
185 
isStripSpaceAllowed(const txXPathNode & aNode,bool & aAllowed)186 nsresult EvalContextImpl::isStripSpaceAllowed(const txXPathNode& aNode,
187                                               bool& aAllowed) {
188   aAllowed = false;
189 
190   return NS_OK;
191 }
192 
getPrivateContext()193 void* EvalContextImpl::getPrivateContext() {
194   // we don't have a private context here.
195   return nullptr;
196 }
197 
recycler()198 txResultRecycler* EvalContextImpl::recycler() { return mRecycler; }
199 
receiveError(const nsAString & aMsg,nsresult aRes)200 void EvalContextImpl::receiveError(const nsAString& aMsg, nsresult aRes) {
201   mLastError = aRes;
202   // forward aMsg to console service?
203 }
204 
getContextNode()205 const txXPathNode& EvalContextImpl::getContextNode() { return mContextNode; }
206 
size()207 uint32_t EvalContextImpl::size() { return mContextSize; }
208 
position()209 uint32_t EvalContextImpl::position() { return mContextPosition; }
210 
211 }  // namespace dom
212 }  // namespace mozilla
213