WrapperOwner.cpp 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2. *
  3. * This Source Code Form is subject to the terms of the Mozilla Public
  4. * License, v. 2.0. If a copy of the MPL was not distributed with this
  5. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6. #include "WrapperOwner.h"
  7. #include "JavaScriptLogging.h"
  8. #include "mozilla/Unused.h"
  9. #include "mozilla/dom/BindingUtils.h"
  10. #include "jsfriendapi.h"
  11. #include "js/CharacterEncoding.h"
  12. #include "xpcprivate.h"
  13. #include "CPOWTimer.h"
  14. #include "WrapperFactory.h"
  15. #include "nsIDocShellTreeItem.h"
  16. #include "nsIDOMDocument.h"
  17. using namespace js;
  18. using namespace JS;
  19. using namespace mozilla;
  20. using namespace mozilla::jsipc;
  21. struct AuxCPOWData
  22. {
  23. ObjectId id;
  24. bool isCallable;
  25. bool isConstructor;
  26. bool isDOMObject;
  27. // The object tag is just some auxilliary information that clients can use
  28. // however they see fit.
  29. nsCString objectTag;
  30. // The class name for WrapperOwner::className, below.
  31. nsCString className;
  32. AuxCPOWData(ObjectId id,
  33. bool isCallable,
  34. bool isConstructor,
  35. bool isDOMObject,
  36. const nsACString& objectTag)
  37. : id(id),
  38. isCallable(isCallable),
  39. isConstructor(isConstructor),
  40. isDOMObject(isDOMObject),
  41. objectTag(objectTag)
  42. {}
  43. };
  44. WrapperOwner::WrapperOwner()
  45. : inactive_(false)
  46. {
  47. }
  48. static inline AuxCPOWData*
  49. AuxCPOWDataOf(JSObject* obj)
  50. {
  51. MOZ_ASSERT(IsCPOW(obj));
  52. return static_cast<AuxCPOWData*>(GetProxyExtra(obj, 1).toPrivate());
  53. }
  54. static inline WrapperOwner*
  55. OwnerOf(JSObject* obj)
  56. {
  57. MOZ_ASSERT(IsCPOW(obj));
  58. return reinterpret_cast<WrapperOwner*>(GetProxyExtra(obj, 0).toPrivate());
  59. }
  60. ObjectId
  61. WrapperOwner::idOfUnchecked(JSObject* obj)
  62. {
  63. MOZ_ASSERT(IsCPOW(obj));
  64. AuxCPOWData* aux = AuxCPOWDataOf(obj);
  65. MOZ_ASSERT(!aux->id.isNull());
  66. return aux->id;
  67. }
  68. ObjectId
  69. WrapperOwner::idOf(JSObject* obj)
  70. {
  71. ObjectId objId = idOfUnchecked(obj);
  72. MOZ_ASSERT(hasCPOW(objId, obj));
  73. return objId;
  74. }
  75. class CPOWProxyHandler : public BaseProxyHandler
  76. {
  77. public:
  78. constexpr CPOWProxyHandler()
  79. : BaseProxyHandler(&family) {}
  80. virtual bool finalizeInBackground(const Value& priv) const override {
  81. return false;
  82. }
  83. virtual bool getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
  84. MutableHandle<PropertyDescriptor> desc) const override;
  85. virtual bool defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
  86. Handle<PropertyDescriptor> desc,
  87. ObjectOpResult& result) const override;
  88. virtual bool ownPropertyKeys(JSContext* cx, HandleObject proxy,
  89. AutoIdVector& props) const override;
  90. virtual bool delete_(JSContext* cx, HandleObject proxy, HandleId id,
  91. ObjectOpResult& result) const override;
  92. virtual bool enumerate(JSContext* cx, HandleObject proxy, MutableHandleObject objp) const override;
  93. virtual bool preventExtensions(JSContext* cx, HandleObject proxy,
  94. ObjectOpResult& result) const override;
  95. virtual bool isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) const override;
  96. virtual bool has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const override;
  97. virtual bool get(JSContext* cx, HandleObject proxy, HandleValue receiver,
  98. HandleId id, MutableHandleValue vp) const override;
  99. virtual bool set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleValue v,
  100. JS::HandleValue receiver, JS::ObjectOpResult& result) const override;
  101. virtual bool call(JSContext* cx, HandleObject proxy, const CallArgs& args) const override;
  102. virtual bool construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const override;
  103. virtual bool getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
  104. MutableHandle<PropertyDescriptor> desc) const override;
  105. virtual bool hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const override;
  106. virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy,
  107. AutoIdVector& props) const override;
  108. virtual bool hasInstance(JSContext* cx, HandleObject proxy,
  109. MutableHandleValue v, bool* bp) const override;
  110. virtual bool getBuiltinClass(JSContext* cx, HandleObject obj, js::ESClass* cls) const override;
  111. virtual bool isArray(JSContext* cx, HandleObject obj,
  112. IsArrayAnswer* answer) const override;
  113. virtual const char* className(JSContext* cx, HandleObject proxy) const override;
  114. virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
  115. virtual void finalize(JSFreeOp* fop, JSObject* proxy) const override;
  116. virtual void objectMoved(JSObject* proxy, const JSObject* old) const override;
  117. virtual bool isCallable(JSObject* obj) const override;
  118. virtual bool isConstructor(JSObject* obj) const override;
  119. virtual bool getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject protop) const override;
  120. virtual bool getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary,
  121. MutableHandleObject protop) const override;
  122. static const char family;
  123. static const CPOWProxyHandler singleton;
  124. };
  125. const char CPOWProxyHandler::family = 0;
  126. const CPOWProxyHandler CPOWProxyHandler::singleton;
  127. #define FORWARD(call, args) \
  128. PROFILER_LABEL_FUNC(js::ProfileEntry::Category::JS); \
  129. WrapperOwner* owner = OwnerOf(proxy); \
  130. if (!owner->active()) { \
  131. JS_ReportErrorASCII(cx, "cannot use a CPOW whose process is gone"); \
  132. return false; \
  133. } \
  134. if (!owner->allowMessage(cx)) { \
  135. return false; \
  136. } \
  137. { \
  138. CPOWTimer timer(cx); \
  139. return owner->call args; \
  140. }
  141. bool
  142. CPOWProxyHandler::getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
  143. MutableHandle<PropertyDescriptor> desc) const
  144. {
  145. FORWARD(getPropertyDescriptor, (cx, proxy, id, desc));
  146. }
  147. bool
  148. WrapperOwner::getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
  149. MutableHandle<PropertyDescriptor> desc)
  150. {
  151. ObjectId objId = idOf(proxy);
  152. JSIDVariant idVar;
  153. if (!toJSIDVariant(cx, id, &idVar))
  154. return false;
  155. ReturnStatus status;
  156. PPropertyDescriptor result;
  157. if (!SendGetPropertyDescriptor(objId, idVar, &status, &result))
  158. return ipcfail(cx);
  159. LOG_STACK();
  160. if (!ok(cx, status))
  161. return false;
  162. return toDescriptor(cx, result, desc);
  163. }
  164. bool
  165. CPOWProxyHandler::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
  166. MutableHandle<PropertyDescriptor> desc) const
  167. {
  168. FORWARD(getOwnPropertyDescriptor, (cx, proxy, id, desc));
  169. }
  170. bool
  171. WrapperOwner::getOwnPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
  172. MutableHandle<PropertyDescriptor> desc)
  173. {
  174. ObjectId objId = idOf(proxy);
  175. JSIDVariant idVar;
  176. if (!toJSIDVariant(cx, id, &idVar))
  177. return false;
  178. ReturnStatus status;
  179. PPropertyDescriptor result;
  180. if (!SendGetOwnPropertyDescriptor(objId, idVar, &status, &result))
  181. return ipcfail(cx);
  182. LOG_STACK();
  183. if (!ok(cx, status))
  184. return false;
  185. return toDescriptor(cx, result, desc);
  186. }
  187. bool
  188. CPOWProxyHandler::defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
  189. Handle<PropertyDescriptor> desc,
  190. ObjectOpResult& result) const
  191. {
  192. FORWARD(defineProperty, (cx, proxy, id, desc, result));
  193. }
  194. bool
  195. WrapperOwner::defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
  196. Handle<PropertyDescriptor> desc,
  197. ObjectOpResult& result)
  198. {
  199. ObjectId objId = idOf(proxy);
  200. JSIDVariant idVar;
  201. if (!toJSIDVariant(cx, id, &idVar))
  202. return false;
  203. PPropertyDescriptor descriptor;
  204. if (!fromDescriptor(cx, desc, &descriptor))
  205. return false;
  206. ReturnStatus status;
  207. if (!SendDefineProperty(objId, idVar, descriptor, &status))
  208. return ipcfail(cx);
  209. LOG_STACK();
  210. return ok(cx, status, result);
  211. }
  212. bool
  213. CPOWProxyHandler::ownPropertyKeys(JSContext* cx, HandleObject proxy,
  214. AutoIdVector& props) const
  215. {
  216. FORWARD(ownPropertyKeys, (cx, proxy, props));
  217. }
  218. bool
  219. WrapperOwner::ownPropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props)
  220. {
  221. return getPropertyKeys(cx, proxy, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props);
  222. }
  223. bool
  224. CPOWProxyHandler::delete_(JSContext* cx, HandleObject proxy, HandleId id,
  225. ObjectOpResult& result) const
  226. {
  227. FORWARD(delete_, (cx, proxy, id, result));
  228. }
  229. bool
  230. WrapperOwner::delete_(JSContext* cx, HandleObject proxy, HandleId id, ObjectOpResult& result)
  231. {
  232. ObjectId objId = idOf(proxy);
  233. JSIDVariant idVar;
  234. if (!toJSIDVariant(cx, id, &idVar))
  235. return false;
  236. ReturnStatus status;
  237. if (!SendDelete(objId, idVar, &status))
  238. return ipcfail(cx);
  239. LOG_STACK();
  240. return ok(cx, status, result);
  241. }
  242. bool
  243. CPOWProxyHandler::enumerate(JSContext* cx, HandleObject proxy, MutableHandleObject objp) const
  244. {
  245. // Using a CPOW for the Iterator would slow down for .. in performance, instead
  246. // call the base hook, that will use our implementation of getOwnEnumerablePropertyKeys
  247. // and follow the proto chain.
  248. return BaseProxyHandler::enumerate(cx, proxy, objp);
  249. }
  250. bool
  251. CPOWProxyHandler::has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const
  252. {
  253. FORWARD(has, (cx, proxy, id, bp));
  254. }
  255. bool
  256. WrapperOwner::has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp)
  257. {
  258. ObjectId objId = idOf(proxy);
  259. JSIDVariant idVar;
  260. if (!toJSIDVariant(cx, id, &idVar))
  261. return false;
  262. ReturnStatus status;
  263. if (!SendHas(objId, idVar, &status, bp))
  264. return ipcfail(cx);
  265. LOG_STACK();
  266. return ok(cx, status);
  267. }
  268. bool
  269. CPOWProxyHandler::hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const
  270. {
  271. FORWARD(hasOwn, (cx, proxy, id, bp));
  272. }
  273. bool
  274. WrapperOwner::hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp)
  275. {
  276. ObjectId objId = idOf(proxy);
  277. JSIDVariant idVar;
  278. if (!toJSIDVariant(cx, id, &idVar))
  279. return false;
  280. ReturnStatus status;
  281. if (!SendHasOwn(objId, idVar, &status, bp))
  282. return ipcfail(cx);
  283. LOG_STACK();
  284. return !!ok(cx, status);
  285. }
  286. bool
  287. CPOWProxyHandler::get(JSContext* cx, HandleObject proxy, HandleValue receiver,
  288. HandleId id, MutableHandleValue vp) const
  289. {
  290. FORWARD(get, (cx, proxy, receiver, id, vp));
  291. }
  292. static bool
  293. CPOWDOMQI(JSContext* cx, unsigned argc, Value* vp)
  294. {
  295. CallArgs args = CallArgsFromVp(argc, vp);
  296. if (!args.thisv().isObject() || !IsCPOW(&args.thisv().toObject())) {
  297. JS_ReportErrorASCII(cx, "bad this object passed to special QI");
  298. return false;
  299. }
  300. RootedObject proxy(cx, &args.thisv().toObject());
  301. FORWARD(DOMQI, (cx, proxy, args));
  302. }
  303. static bool
  304. CPOWToString(JSContext* cx, unsigned argc, Value* vp)
  305. {
  306. CallArgs args = CallArgsFromVp(argc, vp);
  307. RootedObject callee(cx, &args.callee());
  308. RootedValue cpowValue(cx);
  309. if (!JS_GetProperty(cx, callee, "__cpow__", &cpowValue))
  310. return false;
  311. if (!cpowValue.isObject() || !IsCPOW(&cpowValue.toObject())) {
  312. JS_ReportErrorASCII(cx, "CPOWToString called on an incompatible object");
  313. return false;
  314. }
  315. RootedObject proxy(cx, &cpowValue.toObject());
  316. FORWARD(toString, (cx, proxy, args));
  317. }
  318. bool
  319. WrapperOwner::toString(JSContext* cx, HandleObject cpow, JS::CallArgs& args)
  320. {
  321. // Ask the other side to call its toString method. Update the callee so that
  322. // it points to the CPOW and not to the synthesized CPOWToString function.
  323. args.setCallee(ObjectValue(*cpow));
  324. if (!callOrConstruct(cx, cpow, args, false))
  325. return false;
  326. if (!args.rval().isString())
  327. return true;
  328. RootedString cpowResult(cx, args.rval().toString());
  329. nsAutoJSString toStringResult;
  330. if (!toStringResult.init(cx, cpowResult))
  331. return false;
  332. // We don't want to wrap toString() results for things like the location
  333. // object, where toString() is supposed to return a URL and nothing else.
  334. nsAutoString result;
  335. if (toStringResult[0] == '[') {
  336. result.AppendLiteral("[object CPOW ");
  337. result += toStringResult;
  338. result.AppendLiteral("]");
  339. } else {
  340. result += toStringResult;
  341. }
  342. JSString* str = JS_NewUCStringCopyN(cx, result.get(), result.Length());
  343. if (!str)
  344. return false;
  345. args.rval().setString(str);
  346. return true;
  347. }
  348. bool
  349. WrapperOwner::DOMQI(JSContext* cx, JS::HandleObject proxy, JS::CallArgs& args)
  350. {
  351. // Someone's calling us, handle nsISupports specially to avoid unnecessary
  352. // CPOW traffic.
  353. HandleValue id = args[0];
  354. if (id.isObject()) {
  355. RootedObject idobj(cx, &id.toObject());
  356. nsCOMPtr<nsIJSID> jsid;
  357. nsresult rv = UnwrapArg<nsIJSID>(idobj, getter_AddRefs(jsid));
  358. if (NS_SUCCEEDED(rv)) {
  359. MOZ_ASSERT(jsid, "bad wrapJS");
  360. const nsID* idptr = jsid->GetID();
  361. if (idptr->Equals(NS_GET_IID(nsISupports))) {
  362. args.rval().set(args.thisv());
  363. return true;
  364. }
  365. // Webidl-implemented DOM objects never have nsIClassInfo.
  366. if (idptr->Equals(NS_GET_IID(nsIClassInfo)))
  367. return Throw(cx, NS_ERROR_NO_INTERFACE);
  368. }
  369. }
  370. // It wasn't nsISupports, call into the other process to do the QI for us
  371. // (since we don't know what other interfaces our object supports). Note
  372. // that we have to use JS_GetPropertyDescriptor here to avoid infinite
  373. // recursion back into CPOWDOMQI via WrapperOwner::get().
  374. // We could stash the actual QI function on our own function object to avoid
  375. // if we're called multiple times, but since we're transient, there's no
  376. // point right now.
  377. JS::Rooted<PropertyDescriptor> propDesc(cx);
  378. if (!JS_GetPropertyDescriptor(cx, proxy, "QueryInterface", &propDesc))
  379. return false;
  380. if (!propDesc.value().isObject()) {
  381. MOZ_ASSERT_UNREACHABLE("We didn't get QueryInterface off a node");
  382. return Throw(cx, NS_ERROR_UNEXPECTED);
  383. }
  384. return JS_CallFunctionValue(cx, proxy, propDesc.value(), args, args.rval());
  385. }
  386. bool
  387. WrapperOwner::get(JSContext* cx, HandleObject proxy, HandleValue receiver,
  388. HandleId id, MutableHandleValue vp)
  389. {
  390. ObjectId objId = idOf(proxy);
  391. JSVariant receiverVar;
  392. if (!toVariant(cx, receiver, &receiverVar))
  393. return false;
  394. JSIDVariant idVar;
  395. if (!toJSIDVariant(cx, id, &idVar))
  396. return false;
  397. AuxCPOWData* data = AuxCPOWDataOf(proxy);
  398. if (data->isDOMObject &&
  399. idVar.type() == JSIDVariant::TnsString &&
  400. idVar.get_nsString().EqualsLiteral("QueryInterface"))
  401. {
  402. // Handle QueryInterface on DOM Objects specially since we can assume
  403. // certain things about their implementation.
  404. RootedFunction qi(cx, JS_NewFunction(cx, CPOWDOMQI, 1, 0,
  405. "QueryInterface"));
  406. if (!qi)
  407. return false;
  408. vp.set(ObjectValue(*JS_GetFunctionObject(qi)));
  409. return true;
  410. }
  411. JSVariant val;
  412. ReturnStatus status;
  413. if (!SendGet(objId, receiverVar, idVar, &status, &val))
  414. return ipcfail(cx);
  415. LOG_STACK();
  416. if (!ok(cx, status))
  417. return false;
  418. if (!fromVariant(cx, val, vp))
  419. return false;
  420. if (idVar.type() == JSIDVariant::TnsString &&
  421. idVar.get_nsString().EqualsLiteral("toString")) {
  422. RootedFunction toString(cx, JS_NewFunction(cx, CPOWToString, 0, 0,
  423. "toString"));
  424. if (!toString)
  425. return false;
  426. RootedObject toStringObj(cx, JS_GetFunctionObject(toString));
  427. if (!JS_DefineProperty(cx, toStringObj, "__cpow__", vp, JSPROP_PERMANENT | JSPROP_READONLY))
  428. return false;
  429. vp.set(ObjectValue(*toStringObj));
  430. }
  431. return true;
  432. }
  433. bool
  434. CPOWProxyHandler::set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleValue v,
  435. JS::HandleValue receiver, JS::ObjectOpResult& result) const
  436. {
  437. FORWARD(set, (cx, proxy, id, v, receiver, result));
  438. }
  439. bool
  440. WrapperOwner::set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleValue v,
  441. JS::HandleValue receiver, JS::ObjectOpResult& result)
  442. {
  443. ObjectId objId = idOf(proxy);
  444. JSIDVariant idVar;
  445. if (!toJSIDVariant(cx, id, &idVar))
  446. return false;
  447. JSVariant val;
  448. if (!toVariant(cx, v, &val))
  449. return false;
  450. JSVariant receiverVar;
  451. if (!toVariant(cx, receiver, &receiverVar))
  452. return false;
  453. ReturnStatus status;
  454. if (!SendSet(objId, idVar, val, receiverVar, &status))
  455. return ipcfail(cx);
  456. LOG_STACK();
  457. return ok(cx, status, result);
  458. }
  459. bool
  460. CPOWProxyHandler::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy,
  461. AutoIdVector& props) const
  462. {
  463. FORWARD(getOwnEnumerablePropertyKeys, (cx, proxy, props));
  464. }
  465. bool
  466. WrapperOwner::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy, AutoIdVector& props)
  467. {
  468. return getPropertyKeys(cx, proxy, JSITER_OWNONLY, props);
  469. }
  470. bool
  471. CPOWProxyHandler::preventExtensions(JSContext* cx, HandleObject proxy, ObjectOpResult& result) const
  472. {
  473. FORWARD(preventExtensions, (cx, proxy, result));
  474. }
  475. bool
  476. WrapperOwner::preventExtensions(JSContext* cx, HandleObject proxy, ObjectOpResult& result)
  477. {
  478. ObjectId objId = idOf(proxy);
  479. ReturnStatus status;
  480. if (!SendPreventExtensions(objId, &status))
  481. return ipcfail(cx);
  482. LOG_STACK();
  483. return ok(cx, status, result);
  484. }
  485. bool
  486. CPOWProxyHandler::isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) const
  487. {
  488. FORWARD(isExtensible, (cx, proxy, extensible));
  489. }
  490. bool
  491. WrapperOwner::isExtensible(JSContext* cx, HandleObject proxy, bool* extensible)
  492. {
  493. ObjectId objId = idOf(proxy);
  494. ReturnStatus status;
  495. if (!SendIsExtensible(objId, &status, extensible))
  496. return ipcfail(cx);
  497. LOG_STACK();
  498. return ok(cx, status);
  499. }
  500. bool
  501. CPOWProxyHandler::call(JSContext* cx, HandleObject proxy, const CallArgs& args) const
  502. {
  503. FORWARD(callOrConstruct, (cx, proxy, args, false));
  504. }
  505. bool
  506. CPOWProxyHandler::construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const
  507. {
  508. FORWARD(callOrConstruct, (cx, proxy, args, true));
  509. }
  510. bool
  511. WrapperOwner::callOrConstruct(JSContext* cx, HandleObject proxy, const CallArgs& args,
  512. bool construct)
  513. {
  514. ObjectId objId = idOf(proxy);
  515. InfallibleTArray<JSParam> vals;
  516. AutoValueVector outobjects(cx);
  517. RootedValue v(cx);
  518. for (size_t i = 0; i < args.length() + 2; i++) {
  519. // The |this| value for constructors is a magic value that we won't be
  520. // able to convert, so skip it.
  521. if (i == 1 && construct)
  522. v = UndefinedValue();
  523. else
  524. v = args.base()[i];
  525. if (v.isObject()) {
  526. RootedObject obj(cx, &v.toObject());
  527. if (xpc::IsOutObject(cx, obj)) {
  528. // Make sure it is not an in-out object.
  529. bool found;
  530. if (!JS_HasProperty(cx, obj, "value", &found))
  531. return false;
  532. if (found) {
  533. JS_ReportErrorASCII(cx, "in-out objects cannot be sent via CPOWs yet");
  534. return false;
  535. }
  536. vals.AppendElement(JSParam(void_t()));
  537. if (!outobjects.append(ObjectValue(*obj)))
  538. return false;
  539. continue;
  540. }
  541. }
  542. JSVariant val;
  543. if (!toVariant(cx, v, &val))
  544. return false;
  545. vals.AppendElement(JSParam(val));
  546. }
  547. JSVariant result;
  548. ReturnStatus status;
  549. InfallibleTArray<JSParam> outparams;
  550. if (!SendCallOrConstruct(objId, vals, construct, &status, &result, &outparams))
  551. return ipcfail(cx);
  552. LOG_STACK();
  553. if (!ok(cx, status))
  554. return false;
  555. if (outparams.Length() != outobjects.length())
  556. return ipcfail(cx);
  557. RootedObject obj(cx);
  558. for (size_t i = 0; i < outparams.Length(); i++) {
  559. // Don't bother doing anything for outparams that weren't set.
  560. if (outparams[i].type() == JSParam::Tvoid_t)
  561. continue;
  562. // Take the value the child process returned, and set it on the XPC
  563. // object.
  564. if (!fromVariant(cx, outparams[i], &v))
  565. return false;
  566. obj = &outobjects[i].toObject();
  567. if (!JS_SetProperty(cx, obj, "value", v))
  568. return false;
  569. }
  570. if (!fromVariant(cx, result, args.rval()))
  571. return false;
  572. return true;
  573. }
  574. bool
  575. CPOWProxyHandler::hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp) const
  576. {
  577. FORWARD(hasInstance, (cx, proxy, v, bp));
  578. }
  579. bool
  580. WrapperOwner::hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp)
  581. {
  582. ObjectId objId = idOf(proxy);
  583. JSVariant vVar;
  584. if (!toVariant(cx, v, &vVar))
  585. return false;
  586. ReturnStatus status;
  587. JSVariant result;
  588. if (!SendHasInstance(objId, vVar, &status, bp))
  589. return ipcfail(cx);
  590. LOG_STACK();
  591. return ok(cx, status);
  592. }
  593. bool
  594. CPOWProxyHandler::getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls) const
  595. {
  596. FORWARD(getBuiltinClass, (cx, proxy, cls));
  597. }
  598. bool
  599. WrapperOwner::getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls)
  600. {
  601. ObjectId objId = idOf(proxy);
  602. uint32_t classValue = uint32_t(ESClass::Other);
  603. ReturnStatus status;
  604. if (!SendGetBuiltinClass(objId, &status, &classValue))
  605. return ipcfail(cx);
  606. *cls = ESClass(classValue);
  607. LOG_STACK();
  608. return ok(cx, status);
  609. }
  610. bool
  611. CPOWProxyHandler::isArray(JSContext* cx, HandleObject proxy,
  612. IsArrayAnswer* answer) const
  613. {
  614. FORWARD(isArray, (cx, proxy, answer));
  615. }
  616. bool
  617. WrapperOwner::isArray(JSContext* cx, HandleObject proxy, IsArrayAnswer* answer)
  618. {
  619. ObjectId objId = idOf(proxy);
  620. uint32_t ans;
  621. ReturnStatus status;
  622. if (!SendIsArray(objId, &status, &ans))
  623. return ipcfail(cx);
  624. LOG_STACK();
  625. *answer = IsArrayAnswer(ans);
  626. MOZ_ASSERT(*answer == IsArrayAnswer::Array ||
  627. *answer == IsArrayAnswer::NotArray ||
  628. *answer == IsArrayAnswer::RevokedProxy);
  629. return ok(cx, status);
  630. }
  631. const char*
  632. CPOWProxyHandler::className(JSContext* cx, HandleObject proxy) const
  633. {
  634. WrapperOwner* parent = OwnerOf(proxy);
  635. if (!parent->active())
  636. return "<dead CPOW>";
  637. return parent->className(cx, proxy);
  638. }
  639. const char*
  640. WrapperOwner::className(JSContext* cx, HandleObject proxy)
  641. {
  642. AuxCPOWData* data = AuxCPOWDataOf(proxy);
  643. if (data->className.IsEmpty()) {
  644. ObjectId objId = idOf(proxy);
  645. if (!SendClassName(objId, &data->className))
  646. return "<error>";
  647. LOG_STACK();
  648. }
  649. return data->className.get();
  650. }
  651. bool
  652. CPOWProxyHandler::getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject objp) const
  653. {
  654. FORWARD(getPrototype, (cx, proxy, objp));
  655. }
  656. bool
  657. WrapperOwner::getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject objp)
  658. {
  659. ObjectId objId = idOf(proxy);
  660. ObjectOrNullVariant val;
  661. ReturnStatus status;
  662. if (!SendGetPrototype(objId, &status, &val))
  663. return ipcfail(cx);
  664. LOG_STACK();
  665. if (!ok(cx, status))
  666. return false;
  667. objp.set(fromObjectOrNullVariant(cx, val));
  668. return true;
  669. }
  670. bool
  671. CPOWProxyHandler::getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary,
  672. MutableHandleObject objp) const
  673. {
  674. FORWARD(getPrototypeIfOrdinary, (cx, proxy, isOrdinary, objp));
  675. }
  676. bool
  677. WrapperOwner::getPrototypeIfOrdinary(JSContext* cx, HandleObject proxy, bool* isOrdinary,
  678. MutableHandleObject objp)
  679. {
  680. ObjectId objId = idOf(proxy);
  681. ObjectOrNullVariant val;
  682. ReturnStatus status;
  683. if (!SendGetPrototypeIfOrdinary(objId, &status, isOrdinary, &val))
  684. return ipcfail(cx);
  685. LOG_STACK();
  686. if (!ok(cx, status))
  687. return false;
  688. objp.set(fromObjectOrNullVariant(cx, val));
  689. return true;
  690. }
  691. bool
  692. CPOWProxyHandler::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const
  693. {
  694. FORWARD(regexp_toShared, (cx, proxy, g));
  695. }
  696. bool
  697. WrapperOwner::regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g)
  698. {
  699. ObjectId objId = idOf(proxy);
  700. ReturnStatus status;
  701. nsString source;
  702. unsigned flags = 0;
  703. if (!SendRegExpToShared(objId, &status, &source, &flags))
  704. return ipcfail(cx);
  705. LOG_STACK();
  706. if (!ok(cx, status))
  707. return false;
  708. RootedObject regexp(cx);
  709. regexp = JS_NewUCRegExpObject(cx, source.get(), source.Length(), flags);
  710. if (!regexp)
  711. return false;
  712. return js::RegExpToSharedNonInline(cx, regexp, g);
  713. }
  714. void
  715. CPOWProxyHandler::finalize(JSFreeOp* fop, JSObject* proxy) const
  716. {
  717. AuxCPOWData* aux = AuxCPOWDataOf(proxy);
  718. OwnerOf(proxy)->drop(proxy);
  719. if (aux)
  720. delete aux;
  721. }
  722. void
  723. CPOWProxyHandler::objectMoved(JSObject* proxy, const JSObject* old) const
  724. {
  725. OwnerOf(proxy)->updatePointer(proxy, old);
  726. }
  727. bool
  728. CPOWProxyHandler::isCallable(JSObject* proxy) const
  729. {
  730. AuxCPOWData* aux = AuxCPOWDataOf(proxy);
  731. return aux->isCallable;
  732. }
  733. bool
  734. CPOWProxyHandler::isConstructor(JSObject* proxy) const
  735. {
  736. AuxCPOWData* aux = AuxCPOWDataOf(proxy);
  737. return aux->isConstructor;
  738. }
  739. void
  740. WrapperOwner::drop(JSObject* obj)
  741. {
  742. ObjectId objId = idOf(obj);
  743. cpows_.remove(objId);
  744. if (active())
  745. Unused << SendDropObject(objId);
  746. decref();
  747. }
  748. void
  749. WrapperOwner::updatePointer(JSObject* obj, const JSObject* old)
  750. {
  751. ObjectId objId = idOfUnchecked(obj);
  752. MOZ_ASSERT(hasCPOW(objId, old));
  753. cpows_.add(objId, obj);
  754. }
  755. bool
  756. WrapperOwner::init()
  757. {
  758. if (!JavaScriptShared::init())
  759. return false;
  760. return true;
  761. }
  762. bool
  763. WrapperOwner::getPropertyKeys(JSContext* cx, HandleObject proxy, uint32_t flags, AutoIdVector& props)
  764. {
  765. ObjectId objId = idOf(proxy);
  766. ReturnStatus status;
  767. InfallibleTArray<JSIDVariant> ids;
  768. if (!SendGetPropertyKeys(objId, flags, &status, &ids))
  769. return ipcfail(cx);
  770. LOG_STACK();
  771. if (!ok(cx, status))
  772. return false;
  773. for (size_t i = 0; i < ids.Length(); i++) {
  774. RootedId id(cx);
  775. if (!fromJSIDVariant(cx, ids[i], &id))
  776. return false;
  777. if (!props.append(id))
  778. return false;
  779. }
  780. return true;
  781. }
  782. namespace mozilla {
  783. namespace jsipc {
  784. bool
  785. IsCPOW(JSObject* obj)
  786. {
  787. return IsProxy(obj) && GetProxyHandler(obj) == &CPOWProxyHandler::singleton;
  788. }
  789. bool
  790. IsWrappedCPOW(JSObject* obj)
  791. {
  792. JSObject* unwrapped = js::UncheckedUnwrap(obj, true);
  793. if (!unwrapped)
  794. return false;
  795. return IsCPOW(unwrapped);
  796. }
  797. void
  798. GetWrappedCPOWTag(JSObject* obj, nsACString& out)
  799. {
  800. JSObject* unwrapped = js::UncheckedUnwrap(obj, true);
  801. MOZ_ASSERT(IsCPOW(unwrapped));
  802. AuxCPOWData* aux = AuxCPOWDataOf(unwrapped);
  803. if (aux)
  804. out = aux->objectTag;
  805. }
  806. nsresult
  807. InstanceOf(JSObject* proxy, const nsID* id, bool* bp)
  808. {
  809. WrapperOwner* parent = OwnerOf(proxy);
  810. if (!parent->active())
  811. return NS_ERROR_UNEXPECTED;
  812. return parent->instanceOf(proxy, id, bp);
  813. }
  814. bool
  815. DOMInstanceOf(JSContext* cx, JSObject* proxyArg, int prototypeID, int depth, bool* bp)
  816. {
  817. RootedObject proxy(cx, proxyArg);
  818. FORWARD(domInstanceOf, (cx, proxy, prototypeID, depth, bp));
  819. }
  820. } /* namespace jsipc */
  821. } /* namespace mozilla */
  822. nsresult
  823. WrapperOwner::instanceOf(JSObject* obj, const nsID* id, bool* bp)
  824. {
  825. ObjectId objId = idOf(obj);
  826. JSIID iid;
  827. ConvertID(*id, &iid);
  828. ReturnStatus status;
  829. if (!SendInstanceOf(objId, iid, &status, bp))
  830. return NS_ERROR_UNEXPECTED;
  831. if (status.type() != ReturnStatus::TReturnSuccess)
  832. return NS_ERROR_UNEXPECTED;
  833. return NS_OK;
  834. }
  835. bool
  836. WrapperOwner::domInstanceOf(JSContext* cx, JSObject* obj, int prototypeID, int depth, bool* bp)
  837. {
  838. ObjectId objId = idOf(obj);
  839. ReturnStatus status;
  840. if (!SendDOMInstanceOf(objId, prototypeID, depth, &status, bp))
  841. return ipcfail(cx);
  842. LOG_STACK();
  843. return ok(cx, status);
  844. }
  845. void
  846. WrapperOwner::ActorDestroy(ActorDestroyReason why)
  847. {
  848. inactive_ = true;
  849. objects_.clear();
  850. unwaivedObjectIds_.clear();
  851. waivedObjectIds_.clear();
  852. }
  853. bool
  854. WrapperOwner::ipcfail(JSContext* cx)
  855. {
  856. JS_ReportErrorASCII(cx, "cross-process JS call failed");
  857. return false;
  858. }
  859. bool
  860. WrapperOwner::ok(JSContext* cx, const ReturnStatus& status)
  861. {
  862. if (status.type() == ReturnStatus::TReturnSuccess)
  863. return true;
  864. if (status.type() == ReturnStatus::TReturnStopIteration)
  865. return JS_ThrowStopIteration(cx);
  866. if (status.type() == ReturnStatus::TReturnDeadCPOW) {
  867. JS_ReportErrorASCII(cx, "operation not possible on dead CPOW");
  868. return false;
  869. }
  870. RootedValue exn(cx);
  871. if (!fromVariant(cx, status.get_ReturnException().exn(), &exn))
  872. return false;
  873. JS_SetPendingException(cx, exn);
  874. return false;
  875. }
  876. bool
  877. WrapperOwner::ok(JSContext* cx, const ReturnStatus& status, ObjectOpResult& result)
  878. {
  879. if (status.type() == ReturnStatus::TReturnObjectOpResult)
  880. return result.fail(status.get_ReturnObjectOpResult().code());
  881. if (!ok(cx, status))
  882. return false;
  883. return result.succeed();
  884. }
  885. // CPOWs can have a tag string attached to them, originating in the local
  886. // process from this function. It's sent with the CPOW to the remote process,
  887. // where it can be fetched with Components.utils.getCrossProcessWrapperTag.
  888. static nsCString
  889. GetRemoteObjectTag(JS::Handle<JSObject*> obj)
  890. {
  891. if (nsCOMPtr<nsISupports> supports = xpc::UnwrapReflectorToISupports(obj)) {
  892. nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(supports));
  893. if (treeItem)
  894. return NS_LITERAL_CSTRING("ContentDocShellTreeItem");
  895. nsCOMPtr<nsIDOMDocument> doc(do_QueryInterface(supports));
  896. if (doc)
  897. return NS_LITERAL_CSTRING("ContentDocument");
  898. }
  899. return NS_LITERAL_CSTRING("generic");
  900. }
  901. static RemoteObject
  902. MakeRemoteObject(JSContext* cx, ObjectId id, HandleObject obj)
  903. {
  904. return RemoteObject(id.serialize(),
  905. JS::IsCallable(obj),
  906. JS::IsConstructor(obj),
  907. dom::IsDOMObject(obj),
  908. GetRemoteObjectTag(obj));
  909. }
  910. bool
  911. WrapperOwner::toObjectVariant(JSContext* cx, JSObject* objArg, ObjectVariant* objVarp)
  912. {
  913. RootedObject obj(cx, objArg);
  914. MOZ_ASSERT(obj);
  915. // We always save objects unwrapped in the CPOW table. If we stored
  916. // wrappers, then the wrapper might be GCed while the target remained alive.
  917. // Whenever operating on an object that comes from the table, we wrap it
  918. // in findObjectById.
  919. unsigned wrapperFlags = 0;
  920. obj = js::UncheckedUnwrap(obj, true, &wrapperFlags);
  921. if (obj && IsCPOW(obj) && OwnerOf(obj) == this) {
  922. *objVarp = LocalObject(idOf(obj).serialize());
  923. return true;
  924. }
  925. bool waiveXray = wrapperFlags & xpc::WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG;
  926. ObjectId id = objectIdMap(waiveXray).find(obj);
  927. if (!id.isNull()) {
  928. MOZ_ASSERT(id.hasXrayWaiver() == waiveXray);
  929. *objVarp = MakeRemoteObject(cx, id, obj);
  930. return true;
  931. }
  932. // Need to call PreserveWrapper on |obj| in case it's a reflector.
  933. // FIXME: What if it's an XPCWrappedNative?
  934. if (mozilla::dom::IsDOMObject(obj))
  935. mozilla::dom::TryPreserveWrapper(obj);
  936. id = ObjectId(nextSerialNumber_++, waiveXray);
  937. if (!objects_.add(id, obj))
  938. return false;
  939. if (!objectIdMap(waiveXray).add(cx, obj, id))
  940. return false;
  941. *objVarp = MakeRemoteObject(cx, id, obj);
  942. return true;
  943. }
  944. JSObject*
  945. WrapperOwner::fromObjectVariant(JSContext* cx, const ObjectVariant& objVar)
  946. {
  947. if (objVar.type() == ObjectVariant::TRemoteObject) {
  948. return fromRemoteObjectVariant(cx, objVar.get_RemoteObject());
  949. } else {
  950. return fromLocalObjectVariant(cx, objVar.get_LocalObject());
  951. }
  952. }
  953. JSObject*
  954. WrapperOwner::fromRemoteObjectVariant(JSContext* cx, const RemoteObject& objVar)
  955. {
  956. ObjectId objId = ObjectId::deserialize(objVar.serializedId());
  957. RootedObject obj(cx, findCPOWById(objId));
  958. if (!obj) {
  959. // All CPOWs live in the privileged junk scope.
  960. RootedObject junkScope(cx, xpc::PrivilegedJunkScope());
  961. JSAutoCompartment ac(cx, junkScope);
  962. RootedValue v(cx, UndefinedValue());
  963. // We need to setLazyProto for the getPrototype/getPrototypeIfOrdinary
  964. // hooks.
  965. ProxyOptions options;
  966. options.setLazyProto(true);
  967. obj = NewProxyObject(cx,
  968. &CPOWProxyHandler::singleton,
  969. v,
  970. nullptr,
  971. options);
  972. if (!obj)
  973. return nullptr;
  974. if (!cpows_.add(objId, obj))
  975. return nullptr;
  976. nextCPOWNumber_ = objId.serialNumber() + 1;
  977. // Incref once we know the decref will be called.
  978. incref();
  979. AuxCPOWData* aux = new AuxCPOWData(objId,
  980. objVar.isCallable(),
  981. objVar.isConstructor(),
  982. objVar.isDOMObject(),
  983. objVar.objectTag());
  984. SetProxyExtra(obj, 0, PrivateValue(this));
  985. SetProxyExtra(obj, 1, PrivateValue(aux));
  986. }
  987. if (!JS_WrapObject(cx, &obj))
  988. return nullptr;
  989. return obj;
  990. }
  991. JSObject*
  992. WrapperOwner::fromLocalObjectVariant(JSContext* cx, const LocalObject& objVar)
  993. {
  994. ObjectId id = ObjectId::deserialize(objVar.serializedId());
  995. Rooted<JSObject*> obj(cx, findObjectById(cx, id));
  996. if (!obj)
  997. return nullptr;
  998. if (!JS_WrapObject(cx, &obj))
  999. return nullptr;
  1000. return obj;
  1001. }