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