txXPCOMExtensionFunction.cpp 17 KB

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