Bug 1874684 - Part 4: Prefer const references instead of copying Instant values....
[gecko.git] / dom / xslt / xslt / txExecutionState.cpp
bloba3cfddac1c62cb7dc732e48c174701958ce404f8
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/. */
6 #include "txExecutionState.h"
7 #include "txSingleNodeContext.h"
8 #include "txInstructions.h"
9 #include "txStylesheet.h"
10 #include "txVariableMap.h"
11 #include "txRtfHandler.h"
12 #include "txXSLTProcessor.h"
13 #include "txLog.h"
14 #include "txURIUtils.h"
15 #include "txXMLParser.h"
17 using mozilla::UniquePtr;
18 using mozilla::Unused;
19 using mozilla::WrapUnique;
21 const int32_t txExecutionState::kMaxRecursionDepth = 20000;
23 nsresult txLoadedDocumentsHash::init(const txXPathNode& aSource) {
24 mSourceDocument = WrapUnique(txXPathNodeUtils::getOwnerDocument(aSource));
26 nsAutoString baseURI;
27 nsresult rv = txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI);
28 if (NS_WARN_IF(NS_FAILED(rv))) {
29 return rv;
32 // Technically the hash holds documents, but we allow any node that we're
33 // transforming from. In particular, the document() function uses this hash
34 // and it can return the source document, but if we're transforming from a
35 // document fragment (through
36 // txMozillaXSLTProcessor::SetSourceContentModel/txMozillaXSLTProcessor::DoTransform)
37 // or from another type of node (through
38 // txMozillaXSLTProcessor::TransformToDocument or
39 // txMozillaXSLTProcessor::TransformToFragment) it makes more sense to return
40 // the real root of the source tree, which is the node where the transform
41 // started.
42 PutEntry(baseURI)->mDocument = WrapUnique(
43 txXPathNativeNode::createXPathNode(txXPathNativeNode::getNode(aSource)));
44 return NS_OK;
47 txLoadedDocumentsHash::~txLoadedDocumentsHash() {
48 if (mSourceDocument) {
49 nsAutoString baseURI;
50 nsresult rv = txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI);
51 if (NS_SUCCEEDED(rv)) {
52 txLoadedDocumentEntry* entry = GetEntry(baseURI);
53 if (entry) {
54 delete entry->mDocument.release();
60 txExecutionState::txExecutionState(txStylesheet* aStylesheet,
61 bool aDisableLoads)
62 : mOutputHandler(nullptr),
63 mResultHandler(nullptr),
64 mOutputHandlerFactory(nullptr),
65 mStylesheet(aStylesheet),
66 mNextInstruction(nullptr),
67 mLocalVariables(nullptr),
68 mRecursionDepth(0),
69 mEvalContext(nullptr),
70 mInitialEvalContext(nullptr),
71 mGlobalParams(nullptr),
72 mKeyHash(aStylesheet->getKeyMap()),
73 mDisableLoads(aDisableLoads) {
74 MOZ_COUNT_CTOR(txExecutionState);
77 txExecutionState::~txExecutionState() {
78 MOZ_COUNT_DTOR(txExecutionState);
80 delete mResultHandler;
81 delete mLocalVariables;
82 if (mEvalContext != mInitialEvalContext) {
83 delete mEvalContext;
86 txStackIterator varsIter(&mLocalVarsStack);
87 while (varsIter.hasNext()) {
88 delete (txVariableMap*)varsIter.next();
91 txStackIterator contextIter(&mEvalContextStack);
92 while (contextIter.hasNext()) {
93 txIEvalContext* context = (txIEvalContext*)contextIter.next();
94 if (context != mInitialEvalContext) {
95 delete context;
99 txStackIterator handlerIter(&mResultHandlerStack);
100 while (handlerIter.hasNext()) {
101 delete (txAXMLEventHandler*)handlerIter.next();
104 delete mInitialEvalContext;
107 nsresult txExecutionState::init(
108 const txXPathNode& aNode,
109 txOwningExpandedNameMap<txIGlobalParameter>* aGlobalParams) {
110 nsresult rv = NS_OK;
112 mGlobalParams = aGlobalParams;
114 // Set up initial context
115 mEvalContext = new txSingleNodeContext(aNode, this);
116 mInitialEvalContext = mEvalContext;
118 // Set up output and result-handler
119 txAXMLEventHandler* handler;
120 rv = mOutputHandlerFactory->createHandlerWith(mStylesheet->getOutputFormat(),
121 &handler);
122 NS_ENSURE_SUCCESS(rv, rv);
124 mOutputHandler = handler;
125 mResultHandler = handler;
126 mOutputHandler->startDocument();
128 // Set up loaded-documents-hash
129 rv = mLoadedDocuments.init(aNode);
130 NS_ENSURE_SUCCESS(rv, rv);
132 // Init members
133 rv = mKeyHash.init();
134 NS_ENSURE_SUCCESS(rv, rv);
136 mRecycler = new txResultRecycler;
138 // The actual value here doesn't really matter since noone should use this
139 // value. But lets put something errorlike in just in case
140 mGlobalVarPlaceholderValue = new StringResult(u"Error"_ns, nullptr);
142 // Initiate first instruction. This has to be done last since findTemplate
143 // might use us.
144 txStylesheet::ImportFrame* frame = 0;
145 txExpandedName nullName;
146 txInstruction* templ;
147 rv =
148 mStylesheet->findTemplate(aNode, nullName, this, nullptr, &templ, &frame);
149 NS_ENSURE_SUCCESS(rv, rv);
151 pushTemplateRule(frame, nullName, nullptr);
153 return runTemplate(templ);
156 nsresult txExecutionState::end(nsresult aResult) {
157 NS_ASSERTION(NS_FAILED(aResult) || mTemplateRules.Length() == 1,
158 "Didn't clean up template rules properly");
159 if (NS_SUCCEEDED(aResult)) {
160 popTemplateRule();
161 } else if (!mOutputHandler) {
162 return NS_OK;
164 return mOutputHandler->endDocument(aResult);
167 void txExecutionState::popAndDeleteEvalContextUntil(txIEvalContext* aContext) {
168 auto ctx = popEvalContext();
169 while (ctx && ctx != aContext) {
170 MOZ_RELEASE_ASSERT(ctx != mInitialEvalContext);
171 delete ctx;
172 ctx = popEvalContext();
176 nsresult txExecutionState::getVariable(int32_t aNamespace, nsAtom* aLName,
177 txAExprResult*& aResult) {
178 nsresult rv = NS_OK;
179 txExpandedName name(aNamespace, aLName);
181 // look for a local variable
182 if (mLocalVariables) {
183 mLocalVariables->getVariable(name, &aResult);
184 if (aResult) {
185 return NS_OK;
189 // look for an evaluated global variable
190 mGlobalVariableValues.getVariable(name, &aResult);
191 if (aResult) {
192 if (aResult == mGlobalVarPlaceholderValue) {
193 // XXX ErrorReport: cyclic variable-value
194 NS_RELEASE(aResult);
195 return NS_ERROR_XSLT_BAD_RECURSION;
197 return NS_OK;
200 // Is there perchance a global variable not evaluated yet?
201 txStylesheet::GlobalVariable* var = mStylesheet->getGlobalVariable(name);
202 if (!var) {
203 // XXX ErrorReport: variable doesn't exist in this scope
204 return NS_ERROR_FAILURE;
207 NS_ASSERTION((var->mExpr && !var->mFirstInstruction) ||
208 (!var->mExpr && var->mFirstInstruction),
209 "global variable should have either instruction or expression");
211 // Is this a stylesheet parameter that has a value?
212 if (var->mIsParam && mGlobalParams) {
213 txIGlobalParameter* param = mGlobalParams->get(name);
214 if (param) {
215 rv = param->getValue(&aResult);
216 NS_ENSURE_SUCCESS(rv, rv);
218 rv = mGlobalVariableValues.bindVariable(name, aResult);
219 if (NS_FAILED(rv)) {
220 NS_RELEASE(aResult);
221 return rv;
224 return NS_OK;
228 // Insert a placeholdervalue to protect against recursion
229 rv = mGlobalVariableValues.bindVariable(name, mGlobalVarPlaceholderValue);
230 NS_ENSURE_SUCCESS(rv, rv);
232 // evaluate the global variable
233 pushEvalContext(mInitialEvalContext);
234 if (var->mExpr) {
235 txVariableMap* oldVars = mLocalVariables;
236 mLocalVariables = nullptr;
237 rv = var->mExpr->evaluate(getEvalContext(), &aResult);
238 mLocalVariables = oldVars;
240 if (NS_FAILED(rv)) {
241 popAndDeleteEvalContextUntil(mInitialEvalContext);
242 return rv;
244 } else {
245 pushResultHandler(new txRtfHandler);
247 txInstruction* prevInstr = mNextInstruction;
248 // set return to nullptr to stop execution
249 mNextInstruction = nullptr;
250 rv = runTemplate(var->mFirstInstruction.get());
251 if (NS_FAILED(rv)) {
252 popAndDeleteEvalContextUntil(mInitialEvalContext);
253 return rv;
256 pushTemplateRule(nullptr, txExpandedName(), nullptr);
257 rv = txXSLTProcessor::execute(*this);
258 if (NS_FAILED(rv)) {
259 popAndDeleteEvalContextUntil(mInitialEvalContext);
260 return rv;
263 popTemplateRule();
265 mNextInstruction = prevInstr;
266 UniquePtr<txRtfHandler> rtfHandler(
267 static_cast<txRtfHandler*>(popResultHandler()));
268 rv = rtfHandler->getAsRTF(&aResult);
269 if (NS_FAILED(rv)) {
270 popAndDeleteEvalContextUntil(mInitialEvalContext);
271 return rv;
274 popEvalContext();
276 // Remove the placeholder and insert the calculated value
277 mGlobalVariableValues.removeVariable(name);
278 rv = mGlobalVariableValues.bindVariable(name, aResult);
279 if (NS_FAILED(rv)) {
280 NS_RELEASE(aResult);
282 return rv;
285 return NS_OK;
288 nsresult txExecutionState::isStripSpaceAllowed(const txXPathNode& aNode,
289 bool& aAllowed) {
290 return mStylesheet->isStripSpaceAllowed(aNode, this, aAllowed);
293 void* txExecutionState::getPrivateContext() { return this; }
295 txResultRecycler* txExecutionState::recycler() { return mRecycler; }
297 void txExecutionState::receiveError(const nsAString& aMsg, nsresult aRes) {
298 // XXX implement me
301 void txExecutionState::pushEvalContext(txIEvalContext* aContext) {
302 mEvalContextStack.push(mEvalContext);
303 mEvalContext = aContext;
306 txIEvalContext* txExecutionState::popEvalContext() {
307 txIEvalContext* prev = mEvalContext;
308 mEvalContext = (txIEvalContext*)mEvalContextStack.pop();
310 return prev;
313 void txExecutionState::pushBool(bool aBool) { mBoolStack.AppendElement(aBool); }
315 bool txExecutionState::popBool() {
316 NS_ASSERTION(mBoolStack.Length(), "popping from empty stack");
318 return mBoolStack.IsEmpty() ? false : mBoolStack.PopLastElement();
321 void txExecutionState::pushResultHandler(txAXMLEventHandler* aHandler) {
322 mResultHandlerStack.push(mResultHandler);
323 mResultHandler = aHandler;
326 txAXMLEventHandler* txExecutionState::popResultHandler() {
327 txAXMLEventHandler* oldHandler = mResultHandler;
328 mResultHandler = (txAXMLEventHandler*)mResultHandlerStack.pop();
330 return oldHandler;
333 void txExecutionState::pushTemplateRule(txStylesheet::ImportFrame* aFrame,
334 const txExpandedName& aMode,
335 txParameterMap* aParams) {
336 TemplateRule* rule = mTemplateRules.AppendElement();
337 rule->mFrame = aFrame;
338 rule->mModeNsId = aMode.mNamespaceID;
339 rule->mModeLocalName = aMode.mLocalName;
340 rule->mParams = aParams;
343 void txExecutionState::popTemplateRule() {
344 MOZ_ASSERT(!mTemplateRules.IsEmpty(), "No rules to pop");
345 mTemplateRules.RemoveLastElement();
348 txIEvalContext* txExecutionState::getEvalContext() { return mEvalContext; }
350 const txXPathNode* txExecutionState::retrieveDocument(const nsAString& aUri) {
351 NS_ASSERTION(!aUri.Contains(char16_t('#')), "Remove the fragment.");
353 if (mDisableLoads) {
354 return nullptr;
357 MOZ_LOG(txLog::xslt, mozilla::LogLevel::Debug,
358 ("Retrieve Document %s", NS_LossyConvertUTF16toASCII(aUri).get()));
360 // try to get already loaded document
361 txLoadedDocumentEntry* entry = mLoadedDocuments.PutEntry(aUri);
362 if (!entry) {
363 return nullptr;
366 if (!entry->mDocument && !entry->LoadingFailed()) {
367 // open URI
368 nsAutoString errMsg;
369 // XXX we should get the loader from the actual node
370 // triggering the load, but this will do for the time being
371 entry->mLoadResult =
372 txParseDocumentFromURI(aUri, *mLoadedDocuments.mSourceDocument, errMsg,
373 getter_Transfers(entry->mDocument));
375 if (entry->LoadingFailed()) {
376 receiveError(u"Couldn't load document '"_ns + aUri + u"': "_ns + errMsg,
377 entry->mLoadResult);
381 return entry->mDocument.get();
384 nsresult txExecutionState::getKeyNodes(const txExpandedName& aKeyName,
385 const txXPathNode& aRoot,
386 const nsAString& aKeyValue,
387 bool aIndexIfNotFound,
388 txNodeSet** aResult) {
389 return mKeyHash.getKeyNodes(aKeyName, aRoot, aKeyValue, aIndexIfNotFound,
390 *this, aResult);
393 txExecutionState::TemplateRule* txExecutionState::getCurrentTemplateRule() {
394 MOZ_ASSERT(!mTemplateRules.IsEmpty(), "No current rule!");
395 return &mTemplateRules[mTemplateRules.Length() - 1];
398 mozilla::Result<txInstruction*, nsresult>
399 txExecutionState::getNextInstruction() {
400 if (mStopProcessing) {
401 return mozilla::Err(NS_ERROR_FAILURE);
404 txInstruction* instr = mNextInstruction;
405 if (instr) {
406 mNextInstruction = instr->mNext.get();
409 return instr;
412 nsresult txExecutionState::runTemplate(txInstruction* aTemplate) {
413 NS_ENSURE_TRUE(++mRecursionDepth < kMaxRecursionDepth,
414 NS_ERROR_XSLT_BAD_RECURSION);
416 mLocalVarsStack.push(mLocalVariables);
417 mReturnStack.push(mNextInstruction);
419 mLocalVariables = nullptr;
420 mNextInstruction = aTemplate;
422 return NS_OK;
425 void txExecutionState::gotoInstruction(txInstruction* aNext) {
426 mNextInstruction = aNext;
429 void txExecutionState::returnFromTemplate() {
430 --mRecursionDepth;
431 NS_ASSERTION(!mReturnStack.isEmpty() && !mLocalVarsStack.isEmpty(),
432 "return or variable stack is empty");
433 delete mLocalVariables;
434 mNextInstruction = (txInstruction*)mReturnStack.pop();
435 mLocalVariables = (txVariableMap*)mLocalVarsStack.pop();
438 nsresult txExecutionState::bindVariable(const txExpandedName& aName,
439 txAExprResult* aValue) {
440 if (!mLocalVariables) {
441 mLocalVariables = new txVariableMap;
443 return mLocalVariables->bindVariable(aName, aValue);
446 void txExecutionState::removeVariable(const txExpandedName& aName) {
447 mLocalVariables->removeVariable(aName);
450 void txExecutionState::pushParamMap(txParameterMap* aParams) {
451 mParamStack.AppendElement(mTemplateParams.forget());
452 mTemplateParams = aParams;
455 already_AddRefed<txParameterMap> txExecutionState::popParamMap() {
456 RefPtr<txParameterMap> oldParams = std::move(mTemplateParams);
457 mTemplateParams = mParamStack.PopLastElement();
459 return oldParams.forget();