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