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 "nsAutoPtr.h"
7 : #include "nsComponentManagerUtils.h"
8 : #include "nsDependentString.h"
9 : #include "nsIAtom.h"
10 : #include "nsIInterfaceInfoManager.h"
11 : #include "nsServiceManagerUtils.h"
12 : #include "txExpr.h"
13 : #include "txIFunctionEvaluationContext.h"
14 : #include "txIXPathContext.h"
15 : #include "txNodeSetAdaptor.h"
16 : #include "txXPathTreeWalker.h"
17 : #include "xptcall.h"
18 : #include "txXPathObjectAdaptor.h"
19 : #include "mozilla/Attributes.h"
20 : #include "mozilla/UniquePtr.h"
21 : #include "mozilla/dom/ScriptSettings.h"
22 : #include "nsIClassInfo.h"
23 : #include "nsIInterfaceInfo.h"
24 : #include "js/RootingAPI.h"
25 :
26 0 : NS_IMPL_ISUPPORTS(txXPathObjectAdaptor, txIXPathObject)
27 :
28 : class txFunctionEvaluationContext final : public txIFunctionEvaluationContext
29 : {
30 : public:
31 : txFunctionEvaluationContext(txIEvalContext *aContext, nsISupports *aState);
32 :
33 : NS_DECL_ISUPPORTS
34 : NS_DECL_TXIFUNCTIONEVALUATIONCONTEXT
35 :
36 0 : void ClearContext()
37 : {
38 0 : mContext = nullptr;
39 0 : }
40 :
41 : private:
42 0 : ~txFunctionEvaluationContext() {}
43 :
44 : txIEvalContext *mContext;
45 : nsCOMPtr<nsISupports> mState;
46 : };
47 :
48 0 : txFunctionEvaluationContext::txFunctionEvaluationContext(txIEvalContext *aContext,
49 0 : nsISupports *aState)
50 : : mContext(aContext),
51 0 : mState(aState)
52 : {
53 0 : }
54 :
55 0 : NS_IMPL_ISUPPORTS(txFunctionEvaluationContext, txIFunctionEvaluationContext)
56 :
57 : NS_IMETHODIMP
58 0 : txFunctionEvaluationContext::GetPosition(uint32_t *aPosition)
59 : {
60 0 : NS_ENSURE_TRUE(mContext, NS_ERROR_FAILURE);
61 :
62 0 : *aPosition = mContext->position();
63 :
64 0 : return NS_OK;
65 : }
66 :
67 : NS_IMETHODIMP
68 0 : txFunctionEvaluationContext::GetSize(uint32_t *aSize)
69 : {
70 0 : NS_ENSURE_TRUE(mContext, NS_ERROR_FAILURE);
71 :
72 0 : *aSize = mContext->size();
73 :
74 0 : return NS_OK;
75 : }
76 :
77 : NS_IMETHODIMP
78 0 : txFunctionEvaluationContext::GetContextNode(nsIDOMNode **aNode)
79 : {
80 0 : NS_ENSURE_TRUE(mContext, NS_ERROR_FAILURE);
81 :
82 0 : return txXPathNativeNode::getNode(mContext->getContextNode(), aNode);
83 : }
84 :
85 : NS_IMETHODIMP
86 0 : txFunctionEvaluationContext::GetState(nsISupports **aState)
87 : {
88 0 : NS_IF_ADDREF(*aState = mState);
89 :
90 0 : return NS_OK;
91 : }
92 :
93 : enum txArgumentType {
94 : eBOOLEAN = nsXPTType::T_BOOL,
95 : eNUMBER = nsXPTType::T_DOUBLE,
96 : eSTRING = nsXPTType::T_DOMSTRING,
97 : eNODESET,
98 : eCONTEXT,
99 : eOBJECT,
100 : eUNKNOWN
101 : };
102 :
103 0 : class txXPCOMExtensionFunctionCall : public FunctionCall
104 : {
105 : public:
106 : txXPCOMExtensionFunctionCall(nsISupports *aHelper, const nsIID &aIID,
107 : uint16_t aMethodIndex,
108 : #ifdef TX_TO_STRING
109 : nsIAtom *aName,
110 : #endif
111 : nsISupports *aState);
112 :
113 : TX_DECL_FUNCTION
114 :
115 : private:
116 : txArgumentType GetParamType(const nsXPTParamInfo &aParam,
117 : nsIInterfaceInfo *aInfo);
118 :
119 : nsCOMPtr<nsISupports> mHelper;
120 : nsIID mIID;
121 : uint16_t mMethodIndex;
122 : #ifdef TX_TO_STRING
123 : nsCOMPtr<nsIAtom> mName;
124 : #endif
125 : nsCOMPtr<nsISupports> mState;
126 : };
127 :
128 0 : txXPCOMExtensionFunctionCall::txXPCOMExtensionFunctionCall(nsISupports *aHelper,
129 : const nsIID &aIID,
130 : uint16_t aMethodIndex,
131 : #ifdef TX_TO_STRING
132 : nsIAtom *aName,
133 : #endif
134 0 : nsISupports *aState)
135 : : mHelper(aHelper),
136 : mIID(aIID),
137 : mMethodIndex(aMethodIndex),
138 : #ifdef TX_TO_STRING
139 : mName(aName),
140 : #endif
141 0 : mState(aState)
142 : {
143 0 : }
144 :
145 : class txInterfacesArrayHolder
146 : {
147 : public:
148 0 : txInterfacesArrayHolder(nsIID **aArray, uint32_t aCount) : mArray(aArray),
149 0 : mCount(aCount)
150 : {
151 0 : }
152 0 : ~txInterfacesArrayHolder()
153 0 : {
154 0 : NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(mCount, mArray);
155 0 : }
156 :
157 : private:
158 : nsIID **mArray;
159 : uint32_t mCount;
160 : };
161 :
162 : static nsresult
163 0 : LookupFunction(const char *aContractID, nsIAtom* aName, nsIID &aIID,
164 : uint16_t &aMethodIndex, nsISupports **aHelper)
165 : {
166 : nsresult rv;
167 0 : nsCOMPtr<nsISupports> helper = do_GetService(aContractID, &rv);
168 0 : NS_ENSURE_SUCCESS(rv, rv);
169 :
170 0 : nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface(helper, &rv);
171 0 : NS_ENSURE_SUCCESS(rv, rv);
172 :
173 : nsCOMPtr<nsIInterfaceInfoManager> iim =
174 0 : do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID);
175 0 : NS_ENSURE_TRUE(iim, NS_ERROR_FAILURE);
176 :
177 0 : nsIID** iidArray = nullptr;
178 0 : uint32_t iidCount = 0;
179 0 : rv = classInfo->GetInterfaces(&iidCount, &iidArray);
180 0 : NS_ENSURE_SUCCESS(rv, rv);
181 :
182 0 : txInterfacesArrayHolder holder(iidArray, iidCount);
183 :
184 : // Remove any minus signs and uppercase the following letter (so
185 : // foo-bar becomes fooBar). Note that if there are any names that already
186 : // have uppercase letters they might cause false matches (both fooBar and
187 : // foo-bar matching fooBar).
188 0 : const char16_t *name = aName->GetUTF16String();
189 0 : nsAutoCString methodName;
190 : char16_t letter;
191 0 : bool upperNext = false;
192 0 : while ((letter = *name)) {
193 0 : if (letter == '-') {
194 0 : upperNext = true;
195 : }
196 : else {
197 0 : MOZ_ASSERT(nsCRT::IsAscii(letter),
198 : "invalid static_cast coming up");
199 0 : methodName.Append(upperNext ?
200 0 : nsCRT::ToUpper(static_cast<char>(letter)) :
201 0 : letter);
202 0 : upperNext = false;
203 : }
204 0 : ++name;
205 : }
206 :
207 : uint32_t i;
208 0 : for (i = 0; i < iidCount; ++i) {
209 0 : nsIID *iid = iidArray[i];
210 :
211 0 : nsCOMPtr<nsIInterfaceInfo> info;
212 0 : rv = iim->GetInfoForIID(iid, getter_AddRefs(info));
213 0 : NS_ENSURE_SUCCESS(rv, rv);
214 :
215 : uint16_t methodIndex;
216 : const nsXPTMethodInfo *methodInfo;
217 0 : rv = info->GetMethodInfoForName(methodName.get(), &methodIndex,
218 0 : &methodInfo);
219 0 : if (NS_SUCCEEDED(rv)) {
220 : // Exclude notxpcom and hidden. Also check that we have at least a
221 : // return value (the xpidl compiler ensures that that return value
222 : // is the last argument).
223 0 : uint8_t paramCount = methodInfo->GetParamCount();
224 0 : if (methodInfo->IsNotXPCOM() || methodInfo->IsHidden() ||
225 0 : paramCount == 0 ||
226 0 : !methodInfo->GetParam(paramCount - 1).IsRetval()) {
227 0 : return NS_ERROR_FAILURE;
228 : }
229 :
230 0 : aIID = *iid;
231 0 : aMethodIndex = methodIndex;
232 0 : return helper->QueryInterface(aIID, (void**)aHelper);
233 : }
234 : }
235 :
236 0 : return NS_ERROR_XPATH_UNKNOWN_FUNCTION;
237 : }
238 :
239 : /* static */
240 : nsresult
241 0 : TX_ResolveFunctionCallXPCOM(const nsCString &aContractID, int32_t aNamespaceID,
242 : nsIAtom* aName, nsISupports *aState,
243 : FunctionCall **aFunction)
244 : {
245 : nsIID iid;
246 0 : uint16_t methodIndex = 0;
247 0 : nsCOMPtr<nsISupports> helper;
248 :
249 0 : nsresult rv = LookupFunction(aContractID.get(), aName, iid, methodIndex,
250 0 : getter_AddRefs(helper));
251 0 : NS_ENSURE_SUCCESS(rv, rv);
252 :
253 0 : if (!aFunction) {
254 0 : return NS_OK;
255 : }
256 :
257 0 : *aFunction = new txXPCOMExtensionFunctionCall(helper, iid, methodIndex,
258 : #ifdef TX_TO_STRING
259 : aName,
260 : #endif
261 0 : aState);
262 0 : return NS_OK;
263 : }
264 :
265 : txArgumentType
266 0 : txXPCOMExtensionFunctionCall::GetParamType(const nsXPTParamInfo &aParam,
267 : nsIInterfaceInfo *aInfo)
268 : {
269 0 : uint8_t tag = aParam.GetType().TagPart();
270 0 : switch (tag) {
271 : case nsXPTType::T_BOOL:
272 : case nsXPTType::T_DOUBLE:
273 : case nsXPTType::T_DOMSTRING:
274 : {
275 0 : return txArgumentType(tag);
276 : }
277 : case nsXPTType::T_INTERFACE:
278 : case nsXPTType::T_INTERFACE_IS:
279 : {
280 : nsIID iid;
281 0 : aInfo->GetIIDForParamNoAlloc(mMethodIndex, &aParam, &iid);
282 0 : if (iid.Equals(NS_GET_IID(txINodeSet))) {
283 0 : return eNODESET;
284 : }
285 0 : if (iid.Equals(NS_GET_IID(txIFunctionEvaluationContext))) {
286 0 : return eCONTEXT;
287 : }
288 0 : if (iid.Equals(NS_GET_IID(txIXPathObject))) {
289 0 : return eOBJECT;
290 : }
291 0 : return eUNKNOWN;
292 : }
293 : default:
294 : {
295 : // XXX Error!
296 0 : return eUNKNOWN;
297 : }
298 : }
299 : }
300 :
301 : class txParamArrayHolder
302 : {
303 : public:
304 0 : txParamArrayHolder()
305 0 : : mCount(0)
306 : {
307 0 : }
308 0 : txParamArrayHolder(txParamArrayHolder&& rhs)
309 0 : : mArray(mozilla::Move(rhs.mArray))
310 0 : , mCount(rhs.mCount)
311 : {
312 0 : rhs.mCount = 0;
313 0 : }
314 : ~txParamArrayHolder();
315 :
316 : bool Init(uint8_t aCount);
317 0 : operator nsXPTCVariant*() const
318 : {
319 0 : return mArray.get();
320 : }
321 :
322 0 : void trace(JSTracer* trc) {
323 0 : for (uint8_t i = 0; i < mCount; ++i) {
324 0 : if (mArray[i].type == nsXPTType::T_JSVAL) {
325 0 : JS::UnsafeTraceRoot(trc, &mArray[i].val.j, "txParam value");
326 : }
327 : }
328 0 : }
329 :
330 : private:
331 : mozilla::UniquePtr<nsXPTCVariant[]> mArray;
332 : uint8_t mCount;
333 : };
334 :
335 0 : txParamArrayHolder::~txParamArrayHolder()
336 : {
337 : uint8_t i;
338 0 : for (i = 0; i < mCount; ++i) {
339 0 : nsXPTCVariant &variant = mArray[i];
340 0 : if (variant.DoesValNeedCleanup()) {
341 0 : if (variant.type.TagPart() == nsXPTType::T_DOMSTRING)
342 0 : delete (nsAString*)variant.val.p;
343 : else {
344 0 : MOZ_ASSERT(variant.type.TagPart() == nsXPTType::T_INTERFACE ||
345 : variant.type.TagPart() == nsXPTType::T_INTERFACE_IS,
346 : "We only support cleanup of strings and interfaces "
347 : "here, and this looks like neither!");
348 0 : if (variant.val.p != nullptr) {
349 0 : static_cast<nsISupports*>(variant.val.p)->Release();
350 : }
351 : }
352 : }
353 : }
354 0 : }
355 :
356 : bool
357 0 : txParamArrayHolder::Init(uint8_t aCount)
358 : {
359 0 : mCount = aCount;
360 0 : mArray = mozilla::MakeUnique<nsXPTCVariant[]>(mCount);
361 0 : if (!mArray) {
362 0 : return false;
363 : }
364 :
365 0 : memset(mArray.get(), 0, mCount * sizeof(nsXPTCVariant));
366 :
367 0 : return true;
368 : }
369 :
370 : nsresult
371 0 : txXPCOMExtensionFunctionCall::evaluate(txIEvalContext* aContext,
372 : txAExprResult** aResult)
373 : {
374 : nsCOMPtr<nsIInterfaceInfoManager> iim =
375 0 : do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID);
376 0 : NS_ENSURE_TRUE(iim, NS_ERROR_FAILURE);
377 :
378 0 : nsCOMPtr<nsIInterfaceInfo> info;
379 0 : nsresult rv = iim->GetInfoForIID(&mIID, getter_AddRefs(info));
380 0 : NS_ENSURE_SUCCESS(rv, rv);
381 :
382 : const nsXPTMethodInfo *methodInfo;
383 0 : rv = info->GetMethodInfo(mMethodIndex, &methodInfo);
384 0 : NS_ENSURE_SUCCESS(rv, rv);
385 :
386 0 : uint8_t paramCount = methodInfo->GetParamCount();
387 0 : uint8_t inArgs = paramCount - 1;
388 :
389 0 : JS::Rooted<txParamArrayHolder> invokeParams(mozilla::dom::RootingCx());
390 0 : if (!invokeParams.get().Init(paramCount)) {
391 0 : return NS_ERROR_OUT_OF_MEMORY;
392 : }
393 :
394 0 : const nsXPTParamInfo ¶mInfo = methodInfo->GetParam(0);
395 0 : txArgumentType type = GetParamType(paramInfo, info);
396 0 : if (type == eUNKNOWN) {
397 0 : return NS_ERROR_FAILURE;
398 : }
399 :
400 : txFunctionEvaluationContext *context;
401 0 : uint32_t paramStart = 0;
402 0 : if (type == eCONTEXT) {
403 0 : if (paramInfo.IsOut()) {
404 : // We don't support out values.
405 0 : return NS_ERROR_FAILURE;
406 : }
407 :
408 : // Create context wrapper.
409 0 : context = new txFunctionEvaluationContext(aContext, mState);
410 :
411 0 : nsXPTCVariant &invokeParam = invokeParams.get()[0];
412 0 : invokeParam.type = paramInfo.GetType();
413 0 : invokeParam.SetValNeedsCleanup();
414 0 : NS_ADDREF((txIFunctionEvaluationContext*&)invokeParam.val.p = context);
415 :
416 : // Skip first argument, since it's the context.
417 0 : paramStart = 1;
418 : }
419 : else {
420 0 : context = nullptr;
421 : }
422 :
423 : // XXX varargs
424 0 : if (!requireParams(inArgs - paramStart, inArgs - paramStart, aContext)) {
425 0 : return NS_ERROR_FAILURE;
426 : }
427 :
428 : uint32_t i;
429 0 : for (i = paramStart; i < inArgs; ++i) {
430 0 : Expr* expr = mParams[i - paramStart];
431 :
432 0 : const nsXPTParamInfo ¶mInfo = methodInfo->GetParam(i);
433 0 : txArgumentType type = GetParamType(paramInfo, info);
434 0 : if (type == eUNKNOWN) {
435 0 : return NS_ERROR_FAILURE;
436 : }
437 :
438 0 : nsXPTCVariant &invokeParam = invokeParams.get()[i];
439 0 : if (paramInfo.IsOut()) {
440 : // We don't support out values.
441 0 : return NS_ERROR_FAILURE;
442 : }
443 :
444 0 : invokeParam.type = paramInfo.GetType();
445 0 : switch (type) {
446 : case eNODESET:
447 : {
448 0 : RefPtr<txNodeSet> nodes;
449 0 : rv = evaluateToNodeSet(expr, aContext, getter_AddRefs(nodes));
450 0 : NS_ENSURE_SUCCESS(rv, rv);
451 :
452 0 : txNodeSetAdaptor *adaptor = new txNodeSetAdaptor(nodes);
453 0 : if (!adaptor) {
454 0 : return NS_ERROR_OUT_OF_MEMORY;
455 : }
456 :
457 0 : nsCOMPtr<txINodeSet> nodeSet = adaptor;
458 0 : rv = adaptor->Init();
459 0 : NS_ENSURE_SUCCESS(rv, rv);
460 :
461 0 : invokeParam.SetValNeedsCleanup();
462 0 : nodeSet.swap((txINodeSet*&)invokeParam.val.p);
463 0 : break;
464 : }
465 : case eBOOLEAN:
466 : {
467 0 : rv = expr->evaluateToBool(aContext, invokeParam.val.b);
468 0 : NS_ENSURE_SUCCESS(rv, rv);
469 :
470 0 : break;
471 : }
472 : case eNUMBER:
473 : {
474 : double dbl;
475 0 : rv = evaluateToNumber(mParams[0], aContext, &dbl);
476 0 : NS_ENSURE_SUCCESS(rv, rv);
477 :
478 0 : invokeParam.val.d = dbl;
479 0 : break;
480 : }
481 : case eSTRING:
482 : {
483 0 : nsString *value = new nsString();
484 0 : if (!value) {
485 0 : return NS_ERROR_OUT_OF_MEMORY;
486 : }
487 :
488 0 : rv = expr->evaluateToString(aContext, *value);
489 0 : NS_ENSURE_SUCCESS(rv, rv);
490 :
491 0 : invokeParam.SetValNeedsCleanup();
492 0 : invokeParam.val.p = value;
493 0 : break;
494 : }
495 : case eOBJECT:
496 : {
497 0 : RefPtr<txAExprResult> exprRes;
498 0 : rv = expr->evaluate(aContext, getter_AddRefs(exprRes));
499 0 : NS_ENSURE_SUCCESS(rv, rv);
500 :
501 : nsCOMPtr<txIXPathObject> adaptor =
502 0 : new txXPathObjectAdaptor(exprRes);
503 0 : if (!adaptor) {
504 0 : return NS_ERROR_OUT_OF_MEMORY;
505 : }
506 :
507 0 : invokeParam.SetValNeedsCleanup();
508 0 : adaptor.swap((txIXPathObject*&)invokeParam.val.p);
509 0 : break;
510 : }
511 : case eCONTEXT:
512 : case eUNKNOWN:
513 : {
514 : // We only support passing the context as the *first* argument.
515 0 : return NS_ERROR_FAILURE;
516 : }
517 : }
518 : }
519 :
520 0 : const nsXPTParamInfo &returnInfo = methodInfo->GetParam(inArgs);
521 0 : txArgumentType returnType = GetParamType(returnInfo, info);
522 0 : if (returnType == eUNKNOWN) {
523 0 : return NS_ERROR_FAILURE;
524 : }
525 :
526 0 : nsXPTCVariant &returnParam = invokeParams.get()[inArgs];
527 0 : returnParam.type = returnInfo.GetType();
528 0 : if (returnType == eSTRING) {
529 0 : nsString *value = new nsString();
530 0 : returnParam.SetValNeedsCleanup();
531 0 : returnParam.val.p = value;
532 : }
533 : else {
534 0 : returnParam.SetIndirect();
535 0 : if (returnType == eNODESET || returnType == eOBJECT) {
536 0 : returnParam.SetValNeedsCleanup();
537 : }
538 : }
539 :
540 0 : rv = NS_InvokeByIndex(mHelper, mMethodIndex, paramCount, invokeParams.get());
541 :
542 : // In case someone is holding on to the txFunctionEvaluationContext which
543 : // could thus stay alive longer than this function.
544 0 : if (context) {
545 0 : context->ClearContext();
546 : }
547 :
548 0 : NS_ENSURE_SUCCESS(rv, rv);
549 :
550 0 : switch (returnType) {
551 : case eNODESET:
552 : {
553 0 : txINodeSet* nodeSet = static_cast<txINodeSet*>(returnParam.val.p);
554 0 : nsCOMPtr<txIXPathObject> object = do_QueryInterface(nodeSet, &rv);
555 0 : NS_ENSURE_SUCCESS(rv, rv);
556 :
557 0 : NS_ADDREF(*aResult = object->GetResult());
558 :
559 0 : return NS_OK;
560 : }
561 : case eBOOLEAN:
562 : {
563 0 : aContext->recycler()->getBoolResult(returnParam.val.b, aResult);
564 :
565 0 : return NS_OK;
566 : }
567 : case eNUMBER:
568 : {
569 0 : return aContext->recycler()->getNumberResult(returnParam.val.d,
570 0 : aResult);
571 : }
572 : case eSTRING:
573 : {
574 : nsString *returned = static_cast<nsString*>
575 0 : (returnParam.val.p);
576 0 : return aContext->recycler()->getStringResult(*returned, aResult);
577 : }
578 : case eOBJECT:
579 : {
580 : txIXPathObject *object =
581 0 : static_cast<txIXPathObject*>(returnParam.val.p);
582 :
583 0 : NS_ADDREF(*aResult = object->GetResult());
584 :
585 0 : return NS_OK;
586 : }
587 : default:
588 : {
589 : // Huh?
590 0 : return NS_ERROR_FAILURE;
591 : }
592 : }
593 : }
594 :
595 : Expr::ResultType
596 0 : txXPCOMExtensionFunctionCall::getReturnType()
597 : {
598 : // It doesn't really matter what we return here, but it might
599 : // be a good idea to try to keep this as unoptimizable as possible
600 0 : return ANY_RESULT;
601 : }
602 :
603 : bool
604 0 : txXPCOMExtensionFunctionCall::isSensitiveTo(ContextSensitivity aContext)
605 : {
606 : // It doesn't really matter what we return here, but it might
607 : // be a good idea to try to keep this as unoptimizable as possible
608 0 : return true;
609 : }
610 :
611 : #ifdef TX_TO_STRING
612 : nsresult
613 0 : txXPCOMExtensionFunctionCall::getNameAtom(nsIAtom** aAtom)
614 : {
615 0 : NS_ADDREF(*aAtom = mName);
616 :
617 0 : return NS_OK;
618 : }
619 : #endif
|