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