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"
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
));
27 nsresult rv
= txXPathNodeUtils::getBaseURI(*mSourceDocument
, baseURI
);
28 if (NS_WARN_IF(NS_FAILED(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
42 PutEntry(baseURI
)->mDocument
= WrapUnique(
43 txXPathNativeNode::createXPathNode(txXPathNativeNode::getNode(aSource
)));
47 txLoadedDocumentsHash::~txLoadedDocumentsHash() {
48 if (mSourceDocument
) {
50 nsresult rv
= txXPathNodeUtils::getBaseURI(*mSourceDocument
, baseURI
);
51 if (NS_SUCCEEDED(rv
)) {
52 txLoadedDocumentEntry
* entry
= GetEntry(baseURI
);
54 delete entry
->mDocument
.release();
60 txExecutionState::txExecutionState(txStylesheet
* aStylesheet
,
62 : mOutputHandler(nullptr),
63 mResultHandler(nullptr),
64 mOutputHandlerFactory(nullptr),
65 mStylesheet(aStylesheet
),
66 mNextInstruction(nullptr),
67 mLocalVariables(nullptr),
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
) {
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
) {
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
) {
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(),
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
);
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
144 txStylesheet::ImportFrame
* frame
= 0;
145 txExpandedName nullName
;
146 txInstruction
* templ
;
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
)) {
161 } else if (!mOutputHandler
) {
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
);
172 ctx
= popEvalContext();
176 nsresult
txExecutionState::getVariable(int32_t aNamespace
, nsAtom
* aLName
,
177 txAExprResult
*& aResult
) {
179 txExpandedName
name(aNamespace
, aLName
);
181 // look for a local variable
182 if (mLocalVariables
) {
183 mLocalVariables
->getVariable(name
, &aResult
);
189 // look for an evaluated global variable
190 mGlobalVariableValues
.getVariable(name
, &aResult
);
192 if (aResult
== mGlobalVarPlaceholderValue
) {
193 // XXX ErrorReport: cyclic variable-value
195 return NS_ERROR_XSLT_BAD_RECURSION
;
200 // Is there perchance a global variable not evaluated yet?
201 txStylesheet::GlobalVariable
* var
= mStylesheet
->getGlobalVariable(name
);
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
);
215 rv
= param
->getValue(&aResult
);
216 NS_ENSURE_SUCCESS(rv
, rv
);
218 rv
= mGlobalVariableValues
.bindVariable(name
, aResult
);
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
);
235 txVariableMap
* oldVars
= mLocalVariables
;
236 mLocalVariables
= nullptr;
237 rv
= var
->mExpr
->evaluate(getEvalContext(), &aResult
);
238 mLocalVariables
= oldVars
;
241 popAndDeleteEvalContextUntil(mInitialEvalContext
);
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());
252 popAndDeleteEvalContextUntil(mInitialEvalContext
);
256 pushTemplateRule(nullptr, txExpandedName(), nullptr);
257 rv
= txXSLTProcessor::execute(*this);
259 popAndDeleteEvalContextUntil(mInitialEvalContext
);
265 mNextInstruction
= prevInstr
;
266 UniquePtr
<txRtfHandler
> rtfHandler(
267 static_cast<txRtfHandler
*>(popResultHandler()));
268 rv
= rtfHandler
->getAsRTF(&aResult
);
270 popAndDeleteEvalContextUntil(mInitialEvalContext
);
276 // Remove the placeholder and insert the calculated value
277 mGlobalVariableValues
.removeVariable(name
);
278 rv
= mGlobalVariableValues
.bindVariable(name
, aResult
);
288 nsresult
txExecutionState::isStripSpaceAllowed(const txXPathNode
& aNode
,
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
) {
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();
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();
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.");
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
);
366 if (!entry
->mDocument
&& !entry
->LoadingFailed()) {
369 // XXX we should get the loader from the actual node
370 // triggering the load, but this will do for the time being
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
,
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
,
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
;
406 mNextInstruction
= instr
->mNext
.get();
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
;
425 void txExecutionState::gotoInstruction(txInstruction
* aNext
) {
426 mNextInstruction
= aNext
;
429 void txExecutionState::returnFromTemplate() {
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();