Line data Source code
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 "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"
16 :
17 : const int32_t txExecutionState::kMaxRecursionDepth = 20000;
18 :
19 : nsresult
20 0 : txLoadedDocumentsHash::init(txXPathNode* aSourceDocument)
21 : {
22 0 : mSourceDocument = aSourceDocument;
23 :
24 0 : nsAutoString baseURI;
25 0 : nsresult rv = txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI);
26 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
27 0 : return rv;
28 : }
29 :
30 0 : PutEntry(baseURI)->mDocument = mSourceDocument;
31 0 : return NS_OK;
32 : }
33 :
34 0 : txLoadedDocumentsHash::~txLoadedDocumentsHash()
35 : {
36 0 : if (mSourceDocument) {
37 0 : nsAutoString baseURI;
38 0 : nsresult rv = txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI);
39 0 : if (NS_SUCCEEDED(rv)) {
40 0 : txLoadedDocumentEntry* entry = GetEntry(baseURI);
41 0 : if (entry) {
42 0 : delete entry->mDocument.forget();
43 : }
44 : }
45 : }
46 0 : }
47 :
48 0 : txExecutionState::txExecutionState(txStylesheet* aStylesheet,
49 0 : bool aDisableLoads)
50 : : mOutputHandler(nullptr),
51 : mResultHandler(nullptr),
52 : mStylesheet(aStylesheet),
53 : mNextInstruction(nullptr),
54 : mLocalVariables(nullptr),
55 : mRecursionDepth(0),
56 : mEvalContext(nullptr),
57 : mInitialEvalContext(nullptr),
58 : mGlobalParams(nullptr),
59 : mKeyHash(aStylesheet->getKeyMap()),
60 0 : mDisableLoads(aDisableLoads)
61 : {
62 0 : MOZ_COUNT_CTOR(txExecutionState);
63 0 : }
64 :
65 0 : txExecutionState::~txExecutionState()
66 : {
67 0 : MOZ_COUNT_DTOR(txExecutionState);
68 :
69 0 : delete mResultHandler;
70 0 : delete mLocalVariables;
71 0 : if (mEvalContext != mInitialEvalContext) {
72 0 : delete mEvalContext;
73 : }
74 :
75 0 : txStackIterator varsIter(&mLocalVarsStack);
76 0 : while (varsIter.hasNext()) {
77 0 : delete (txVariableMap*)varsIter.next();
78 : }
79 :
80 0 : txStackIterator contextIter(&mEvalContextStack);
81 0 : while (contextIter.hasNext()) {
82 0 : txIEvalContext* context = (txIEvalContext*)contextIter.next();
83 0 : if (context != mInitialEvalContext) {
84 0 : delete context;
85 : }
86 : }
87 :
88 0 : txStackIterator handlerIter(&mResultHandlerStack);
89 0 : while (handlerIter.hasNext()) {
90 0 : delete (txAXMLEventHandler*)handlerIter.next();
91 : }
92 :
93 0 : txStackIterator paramIter(&mParamStack);
94 0 : while (paramIter.hasNext()) {
95 0 : delete (txVariableMap*)paramIter.next();
96 : }
97 :
98 0 : delete mInitialEvalContext;
99 0 : }
100 :
101 : nsresult
102 0 : txExecutionState::init(const txXPathNode& aNode,
103 : txOwningExpandedNameMap<txIGlobalParameter>* aGlobalParams)
104 : {
105 0 : nsresult rv = NS_OK;
106 :
107 0 : mGlobalParams = aGlobalParams;
108 :
109 : // Set up initial context
110 0 : mEvalContext = new txSingleNodeContext(aNode, this);
111 0 : mInitialEvalContext = mEvalContext;
112 :
113 : // Set up output and result-handler
114 : txAXMLEventHandler* handler;
115 0 : rv = mOutputHandlerFactory->
116 0 : createHandlerWith(mStylesheet->getOutputFormat(), &handler);
117 0 : NS_ENSURE_SUCCESS(rv, rv);
118 :
119 0 : mOutputHandler = handler;
120 0 : mResultHandler = handler;
121 0 : mOutputHandler->startDocument();
122 :
123 : // Set up loaded-documents-hash
124 0 : rv = mLoadedDocuments.init(txXPathNodeUtils::getOwnerDocument(aNode));
125 0 : NS_ENSURE_SUCCESS(rv, rv);
126 :
127 : // Init members
128 0 : rv = mKeyHash.init();
129 0 : NS_ENSURE_SUCCESS(rv, rv);
130 :
131 0 : mRecycler = new txResultRecycler;
132 :
133 : // The actual value here doesn't really matter since noone should use this
134 : // value. But lets put something errorlike in just in case
135 0 : mGlobalVarPlaceholderValue = new StringResult(NS_LITERAL_STRING("Error"), nullptr);
136 :
137 : // Initiate first instruction. This has to be done last since findTemplate
138 : // might use us.
139 0 : txStylesheet::ImportFrame* frame = 0;
140 0 : txExpandedName nullName;
141 : txInstruction* templ;
142 0 : rv = mStylesheet->findTemplate(aNode, nullName, this, nullptr, &templ,
143 0 : &frame);
144 0 : NS_ENSURE_SUCCESS(rv, rv);
145 :
146 0 : pushTemplateRule(frame, nullName, nullptr);
147 :
148 0 : return runTemplate(templ);
149 : }
150 :
151 : nsresult
152 0 : txExecutionState::end(nsresult aResult)
153 : {
154 0 : NS_ASSERTION(NS_FAILED(aResult) || mTemplateRules.Length() == 1,
155 : "Didn't clean up template rules properly");
156 0 : if (NS_SUCCEEDED(aResult)) {
157 0 : popTemplateRule();
158 : }
159 0 : else if (!mOutputHandler) {
160 0 : return NS_OK;
161 : }
162 0 : return mOutputHandler->endDocument(aResult);
163 : }
164 :
165 : void
166 0 : txExecutionState::popAndDeleteEvalContextUntil(txIEvalContext* aContext)
167 : {
168 0 : auto ctx = popEvalContext();
169 0 : while (ctx && ctx != aContext) {
170 0 : MOZ_RELEASE_ASSERT(ctx != mInitialEvalContext);
171 0 : delete ctx;
172 0 : ctx = popEvalContext();
173 : }
174 0 : }
175 :
176 : nsresult
177 0 : txExecutionState::getVariable(int32_t aNamespace, nsIAtom* aLName,
178 : txAExprResult*& aResult)
179 : {
180 0 : nsresult rv = NS_OK;
181 0 : txExpandedName name(aNamespace, aLName);
182 :
183 : // look for a local variable
184 0 : if (mLocalVariables) {
185 0 : mLocalVariables->getVariable(name, &aResult);
186 0 : if (aResult) {
187 0 : return NS_OK;
188 : }
189 : }
190 :
191 : // look for an evaluated global variable
192 0 : mGlobalVariableValues.getVariable(name, &aResult);
193 0 : if (aResult) {
194 0 : if (aResult == mGlobalVarPlaceholderValue) {
195 : // XXX ErrorReport: cyclic variable-value
196 0 : NS_RELEASE(aResult);
197 0 : return NS_ERROR_XSLT_BAD_RECURSION;
198 : }
199 0 : return NS_OK;
200 : }
201 :
202 : // Is there perchance a global variable not evaluated yet?
203 0 : txStylesheet::GlobalVariable* var = mStylesheet->getGlobalVariable(name);
204 0 : if (!var) {
205 : // XXX ErrorReport: variable doesn't exist in this scope
206 0 : return NS_ERROR_FAILURE;
207 : }
208 :
209 0 : NS_ASSERTION((var->mExpr && !var->mFirstInstruction) ||
210 : (!var->mExpr && var->mFirstInstruction),
211 : "global variable should have either instruction or expression");
212 :
213 : // Is this a stylesheet parameter that has a value?
214 0 : if (var->mIsParam && mGlobalParams) {
215 0 : txIGlobalParameter* param = mGlobalParams->get(name);
216 0 : if (param) {
217 0 : rv = param->getValue(&aResult);
218 0 : NS_ENSURE_SUCCESS(rv, rv);
219 :
220 0 : rv = mGlobalVariableValues.bindVariable(name, aResult);
221 0 : if (NS_FAILED(rv)) {
222 0 : NS_RELEASE(aResult);
223 0 : return rv;
224 : }
225 :
226 0 : return NS_OK;
227 : }
228 : }
229 :
230 : // Insert a placeholdervalue to protect against recursion
231 0 : rv = mGlobalVariableValues.bindVariable(name, mGlobalVarPlaceholderValue);
232 0 : NS_ENSURE_SUCCESS(rv, rv);
233 :
234 : // evaluate the global variable
235 0 : pushEvalContext(mInitialEvalContext);
236 0 : if (var->mExpr) {
237 0 : txVariableMap* oldVars = mLocalVariables;
238 0 : mLocalVariables = nullptr;
239 0 : rv = var->mExpr->evaluate(getEvalContext(), &aResult);
240 0 : mLocalVariables = oldVars;
241 :
242 0 : if (NS_FAILED(rv)) {
243 0 : popAndDeleteEvalContextUntil(mInitialEvalContext);
244 0 : return rv;
245 : }
246 : }
247 : else {
248 0 : nsAutoPtr<txRtfHandler> rtfHandler(new txRtfHandler);
249 :
250 0 : rv = pushResultHandler(rtfHandler);
251 0 : if (NS_FAILED(rv)) {
252 0 : popAndDeleteEvalContextUntil(mInitialEvalContext);
253 0 : return rv;
254 : }
255 :
256 0 : rtfHandler.forget();
257 :
258 0 : txInstruction* prevInstr = mNextInstruction;
259 : // set return to nullptr to stop execution
260 0 : mNextInstruction = nullptr;
261 0 : rv = runTemplate(var->mFirstInstruction);
262 0 : if (NS_FAILED(rv)) {
263 0 : popAndDeleteEvalContextUntil(mInitialEvalContext);
264 0 : return rv;
265 : }
266 :
267 0 : pushTemplateRule(nullptr, txExpandedName(), nullptr);
268 0 : rv = txXSLTProcessor::execute(*this);
269 0 : if (NS_FAILED(rv)) {
270 0 : popAndDeleteEvalContextUntil(mInitialEvalContext);
271 0 : return rv;
272 : }
273 :
274 0 : popTemplateRule();
275 :
276 0 : mNextInstruction = prevInstr;
277 0 : rtfHandler = (txRtfHandler*)popResultHandler();
278 0 : rv = rtfHandler->getAsRTF(&aResult);
279 0 : if (NS_FAILED(rv)) {
280 0 : popAndDeleteEvalContextUntil(mInitialEvalContext);
281 0 : return rv;
282 : }
283 : }
284 0 : popEvalContext();
285 :
286 : // Remove the placeholder and insert the calculated value
287 0 : mGlobalVariableValues.removeVariable(name);
288 0 : rv = mGlobalVariableValues.bindVariable(name, aResult);
289 0 : if (NS_FAILED(rv)) {
290 0 : NS_RELEASE(aResult);
291 :
292 0 : return rv;
293 : }
294 :
295 0 : return NS_OK;
296 : }
297 :
298 : nsresult
299 0 : txExecutionState::isStripSpaceAllowed(const txXPathNode& aNode, bool& aAllowed)
300 : {
301 0 : return mStylesheet->isStripSpaceAllowed(aNode, this, aAllowed);
302 : }
303 :
304 : void*
305 0 : txExecutionState::getPrivateContext()
306 : {
307 0 : return this;
308 : }
309 :
310 : txResultRecycler*
311 0 : txExecutionState::recycler()
312 : {
313 0 : return mRecycler;
314 : }
315 :
316 : void
317 0 : txExecutionState::receiveError(const nsAString& aMsg, nsresult aRes)
318 : {
319 : // XXX implement me
320 0 : }
321 :
322 : nsresult
323 0 : txExecutionState::pushEvalContext(txIEvalContext* aContext)
324 : {
325 0 : nsresult rv = mEvalContextStack.push(mEvalContext);
326 0 : NS_ENSURE_SUCCESS(rv, rv);
327 :
328 0 : mEvalContext = aContext;
329 :
330 0 : return NS_OK;
331 : }
332 :
333 : txIEvalContext*
334 0 : txExecutionState::popEvalContext()
335 : {
336 0 : txIEvalContext* prev = mEvalContext;
337 0 : mEvalContext = (txIEvalContext*)mEvalContextStack.pop();
338 :
339 0 : return prev;
340 : }
341 :
342 : nsresult
343 0 : txExecutionState::pushBool(bool aBool)
344 : {
345 0 : return mBoolStack.AppendElement(aBool) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
346 : }
347 :
348 : bool
349 0 : txExecutionState::popBool()
350 : {
351 0 : NS_ASSERTION(mBoolStack.Length(), "popping from empty stack");
352 0 : uint32_t last = mBoolStack.Length() - 1;
353 0 : NS_ENSURE_TRUE(last != (uint32_t)-1, false);
354 :
355 0 : bool res = mBoolStack.ElementAt(last);
356 0 : mBoolStack.RemoveElementAt(last);
357 :
358 0 : return res;
359 : }
360 :
361 : nsresult
362 0 : txExecutionState::pushResultHandler(txAXMLEventHandler* aHandler)
363 : {
364 0 : nsresult rv = mResultHandlerStack.push(mResultHandler);
365 0 : NS_ENSURE_SUCCESS(rv, rv);
366 :
367 0 : mResultHandler = aHandler;
368 :
369 0 : return NS_OK;
370 : }
371 :
372 : txAXMLEventHandler*
373 0 : txExecutionState::popResultHandler()
374 : {
375 0 : txAXMLEventHandler* oldHandler = mResultHandler;
376 0 : mResultHandler = (txAXMLEventHandler*)mResultHandlerStack.pop();
377 :
378 0 : return oldHandler;
379 : }
380 :
381 : void
382 0 : txExecutionState::pushTemplateRule(txStylesheet::ImportFrame* aFrame,
383 : const txExpandedName& aMode,
384 : txVariableMap* aParams)
385 : {
386 0 : TemplateRule* rule = mTemplateRules.AppendElement();
387 0 : rule->mFrame = aFrame;
388 0 : rule->mModeNsId = aMode.mNamespaceID;
389 0 : rule->mModeLocalName = aMode.mLocalName;
390 0 : rule->mParams = aParams;
391 0 : }
392 :
393 : void
394 0 : txExecutionState::popTemplateRule()
395 : {
396 0 : NS_PRECONDITION(!mTemplateRules.IsEmpty(), "No rules to pop");
397 0 : mTemplateRules.RemoveElementAt(mTemplateRules.Length() - 1);
398 0 : }
399 :
400 : txIEvalContext*
401 0 : txExecutionState::getEvalContext()
402 : {
403 0 : return mEvalContext;
404 : }
405 :
406 : const txXPathNode*
407 0 : txExecutionState::retrieveDocument(const nsAString& aUri)
408 : {
409 0 : NS_ASSERTION(!aUri.Contains(char16_t('#')),
410 : "Remove the fragment.");
411 :
412 0 : if (mDisableLoads) {
413 0 : return nullptr;
414 : }
415 :
416 0 : MOZ_LOG(txLog::xslt, LogLevel::Debug,
417 : ("Retrieve Document %s", NS_LossyConvertUTF16toASCII(aUri).get()));
418 :
419 : // try to get already loaded document
420 0 : txLoadedDocumentEntry *entry = mLoadedDocuments.PutEntry(aUri);
421 0 : if (!entry) {
422 0 : return nullptr;
423 : }
424 :
425 0 : if (!entry->mDocument && !entry->LoadingFailed()) {
426 : // open URI
427 0 : nsAutoString errMsg;
428 : // XXX we should get the loader from the actual node
429 : // triggering the load, but this will do for the time being
430 0 : entry->mLoadResult =
431 0 : txParseDocumentFromURI(aUri, *mLoadedDocuments.mSourceDocument,
432 0 : errMsg, getter_Transfers(entry->mDocument));
433 :
434 0 : if (entry->LoadingFailed()) {
435 0 : receiveError(NS_LITERAL_STRING("Couldn't load document '") +
436 0 : aUri + NS_LITERAL_STRING("': ") + errMsg,
437 0 : entry->mLoadResult);
438 : }
439 : }
440 :
441 0 : return entry->mDocument;
442 : }
443 :
444 : nsresult
445 0 : txExecutionState::getKeyNodes(const txExpandedName& aKeyName,
446 : const txXPathNode& aRoot,
447 : const nsAString& aKeyValue,
448 : bool aIndexIfNotFound,
449 : txNodeSet** aResult)
450 : {
451 0 : return mKeyHash.getKeyNodes(aKeyName, aRoot, aKeyValue,
452 0 : aIndexIfNotFound, *this, aResult);
453 : }
454 :
455 : txExecutionState::TemplateRule*
456 0 : txExecutionState::getCurrentTemplateRule()
457 : {
458 0 : NS_PRECONDITION(!mTemplateRules.IsEmpty(), "No current rule!");
459 0 : return &mTemplateRules[mTemplateRules.Length() - 1];
460 : }
461 :
462 : txInstruction*
463 0 : txExecutionState::getNextInstruction()
464 : {
465 0 : txInstruction* instr = mNextInstruction;
466 0 : if (instr) {
467 0 : mNextInstruction = instr->mNext;
468 : }
469 :
470 0 : return instr;
471 : }
472 :
473 : nsresult
474 0 : txExecutionState::runTemplate(txInstruction* aTemplate)
475 : {
476 0 : NS_ENSURE_TRUE(++mRecursionDepth < kMaxRecursionDepth,
477 : NS_ERROR_XSLT_BAD_RECURSION);
478 :
479 0 : nsresult rv = mLocalVarsStack.push(mLocalVariables);
480 0 : NS_ENSURE_SUCCESS(rv, rv);
481 :
482 0 : rv = mReturnStack.push(mNextInstruction);
483 0 : NS_ENSURE_SUCCESS(rv, rv);
484 :
485 0 : mLocalVariables = nullptr;
486 0 : mNextInstruction = aTemplate;
487 :
488 0 : return NS_OK;
489 : }
490 :
491 : void
492 0 : txExecutionState::gotoInstruction(txInstruction* aNext)
493 : {
494 0 : mNextInstruction = aNext;
495 0 : }
496 :
497 : void
498 0 : txExecutionState::returnFromTemplate()
499 : {
500 0 : --mRecursionDepth;
501 0 : NS_ASSERTION(!mReturnStack.isEmpty() && !mLocalVarsStack.isEmpty(),
502 : "return or variable stack is empty");
503 0 : delete mLocalVariables;
504 0 : mNextInstruction = (txInstruction*)mReturnStack.pop();
505 0 : mLocalVariables = (txVariableMap*)mLocalVarsStack.pop();
506 0 : }
507 :
508 : nsresult
509 0 : txExecutionState::bindVariable(const txExpandedName& aName,
510 : txAExprResult* aValue)
511 : {
512 0 : if (!mLocalVariables) {
513 0 : mLocalVariables = new txVariableMap;
514 : }
515 0 : return mLocalVariables->bindVariable(aName, aValue);
516 : }
517 :
518 : void
519 0 : txExecutionState::removeVariable(const txExpandedName& aName)
520 : {
521 0 : mLocalVariables->removeVariable(aName);
522 0 : }
523 :
524 : nsresult
525 0 : txExecutionState::pushParamMap(txVariableMap* aParams)
526 : {
527 0 : nsresult rv = mParamStack.push(mTemplateParams);
528 0 : NS_ENSURE_SUCCESS(rv, rv);
529 :
530 0 : mTemplateParams.forget();
531 0 : mTemplateParams = aParams;
532 :
533 0 : return NS_OK;
534 : }
535 :
536 : txVariableMap*
537 0 : txExecutionState::popParamMap()
538 : {
539 0 : txVariableMap* oldParams = mTemplateParams.forget();
540 0 : mTemplateParams = (txVariableMap*)mParamStack.pop();
541 :
542 0 : return oldParams;
543 : }
|