DOMJSProxyHandler.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  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 file,
  4. * You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "mozilla/dom/DOMJSProxyHandler.h"
  6. #include "xpcpublic.h"
  7. #include "xpcprivate.h"
  8. #include "XPCWrapper.h"
  9. #include "WrapperFactory.h"
  10. #include "nsDOMClassInfo.h"
  11. #include "nsWrapperCacheInlines.h"
  12. #include "mozilla/dom/BindingUtils.h"
  13. #include "jsapi.h"
  14. using namespace JS;
  15. namespace mozilla {
  16. namespace dom {
  17. jsid s_length_id = JSID_VOID;
  18. bool
  19. DefineStaticJSVals(JSContext* cx)
  20. {
  21. return AtomizeAndPinJSString(cx, s_length_id, "length");
  22. }
  23. const char DOMProxyHandler::family = 0;
  24. js::DOMProxyShadowsResult
  25. DOMProxyShadows(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id)
  26. {
  27. JS::Rooted<JSObject*> expando(cx, DOMProxyHandler::GetExpandoObject(proxy));
  28. JS::Value v = js::GetProxyExtra(proxy, JSPROXYSLOT_EXPANDO);
  29. bool isOverrideBuiltins = !v.isObject() && !v.isUndefined();
  30. if (expando) {
  31. bool hasOwn;
  32. if (!JS_AlreadyHasOwnPropertyById(cx, expando, id, &hasOwn))
  33. return js::ShadowCheckFailed;
  34. if (hasOwn) {
  35. return isOverrideBuiltins ?
  36. js::ShadowsViaIndirectExpando : js::ShadowsViaDirectExpando;
  37. }
  38. }
  39. if (!isOverrideBuiltins) {
  40. // Our expando, if any, didn't shadow, so we're not shadowing at all.
  41. return js::DoesntShadow;
  42. }
  43. bool hasOwn;
  44. if (!GetProxyHandler(proxy)->hasOwn(cx, proxy, id, &hasOwn))
  45. return js::ShadowCheckFailed;
  46. return hasOwn ? js::Shadows : js::DoesntShadowUnique;
  47. }
  48. // Store the information for the specialized ICs.
  49. struct SetDOMProxyInformation
  50. {
  51. SetDOMProxyInformation() {
  52. js::SetDOMProxyInformation((const void*) &DOMProxyHandler::family,
  53. JSPROXYSLOT_EXPANDO, DOMProxyShadows);
  54. }
  55. };
  56. SetDOMProxyInformation gSetDOMProxyInformation;
  57. // static
  58. void
  59. DOMProxyHandler::ClearExternalRefsForWrapperRelease(JSObject* obj)
  60. {
  61. MOZ_ASSERT(IsDOMProxy(obj), "expected a DOM proxy object");
  62. JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
  63. if (v.isUndefined()) {
  64. // No expando.
  65. return;
  66. }
  67. // See EnsureExpandoObject for the work we're trying to undo here.
  68. if (v.isObject()) {
  69. // Drop us from the DOM expando hashtable. Don't worry about clearing our
  70. // slot reference to the expando; we're about to die anyway.
  71. xpc::ObjectScope(obj)->RemoveDOMExpandoObject(obj);
  72. return;
  73. }
  74. // Prevent having a dangling pointer to our expando from the
  75. // ExpandoAndGeneration.
  76. js::ExpandoAndGeneration* expandoAndGeneration =
  77. static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
  78. expandoAndGeneration->expando = UndefinedValue();
  79. }
  80. // static
  81. JSObject*
  82. DOMProxyHandler::GetAndClearExpandoObject(JSObject* obj)
  83. {
  84. MOZ_ASSERT(IsDOMProxy(obj), "expected a DOM proxy object");
  85. JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
  86. if (v.isUndefined()) {
  87. return nullptr;
  88. }
  89. if (v.isObject()) {
  90. js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, UndefinedValue());
  91. xpc::ObjectScope(obj)->RemoveDOMExpandoObject(obj);
  92. } else {
  93. js::ExpandoAndGeneration* expandoAndGeneration =
  94. static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
  95. v = expandoAndGeneration->expando;
  96. if (v.isUndefined()) {
  97. return nullptr;
  98. }
  99. // We have to expose v to active JS here. The reason for that is that we
  100. // might be in the middle of a GC right now. If our proxy hasn't been
  101. // traced yet, when it _does_ get traced it won't trace the expando, since
  102. // we're breaking that link. But the Rooted we're presumably being placed
  103. // into is also not going to trace us, because Rooted marking is done at
  104. // the very beginning of the GC. In that situation, we need to manually
  105. // mark the expando as live here. JS::ExposeValueToActiveJS will do just
  106. // that for us.
  107. //
  108. // We don't need to do this in the non-expandoAndGeneration case, because
  109. // in that case our value is stored in a slot and slots will already mark
  110. // the old thing live when the value in the slot changes.
  111. JS::ExposeValueToActiveJS(v);
  112. expandoAndGeneration->expando = UndefinedValue();
  113. }
  114. return &v.toObject();
  115. }
  116. // static
  117. JSObject*
  118. DOMProxyHandler::EnsureExpandoObject(JSContext* cx, JS::Handle<JSObject*> obj)
  119. {
  120. NS_ASSERTION(IsDOMProxy(obj), "expected a DOM proxy object");
  121. JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
  122. if (v.isObject()) {
  123. return &v.toObject();
  124. }
  125. js::ExpandoAndGeneration* expandoAndGeneration;
  126. if (!v.isUndefined()) {
  127. expandoAndGeneration = static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
  128. if (expandoAndGeneration->expando.isObject()) {
  129. return &expandoAndGeneration->expando.toObject();
  130. }
  131. } else {
  132. expandoAndGeneration = nullptr;
  133. }
  134. JS::Rooted<JSObject*> expando(cx,
  135. JS_NewObjectWithGivenProto(cx, nullptr, nullptr));
  136. if (!expando) {
  137. return nullptr;
  138. }
  139. nsISupports* native = UnwrapDOMObject<nsISupports>(obj);
  140. nsWrapperCache* cache;
  141. CallQueryInterface(native, &cache);
  142. if (!cache) {
  143. return expando;
  144. }
  145. if (expandoAndGeneration) {
  146. cache->PreserveWrapper(native);
  147. expandoAndGeneration->expando.setObject(*expando);
  148. return expando;
  149. }
  150. if (!xpc::ObjectScope(obj)->RegisterDOMExpandoObject(obj)) {
  151. return nullptr;
  152. }
  153. cache->SetPreservingWrapper(true);
  154. js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, ObjectValue(*expando));
  155. return expando;
  156. }
  157. bool
  158. DOMProxyHandler::preventExtensions(JSContext* cx, JS::Handle<JSObject*> proxy,
  159. JS::ObjectOpResult& result) const
  160. {
  161. // always extensible per WebIDL
  162. return result.failCantPreventExtensions();
  163. }
  164. bool
  165. DOMProxyHandler::isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible) const
  166. {
  167. *extensible = true;
  168. return true;
  169. }
  170. bool
  171. BaseDOMProxyHandler::getOwnPropertyDescriptor(JSContext* cx,
  172. JS::Handle<JSObject*> proxy,
  173. JS::Handle<jsid> id,
  174. MutableHandle<PropertyDescriptor> desc) const
  175. {
  176. return getOwnPropDescriptor(cx, proxy, id, /* ignoreNamedProps = */ false,
  177. desc);
  178. }
  179. bool
  180. DOMProxyHandler::defineProperty(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
  181. Handle<PropertyDescriptor> desc,
  182. JS::ObjectOpResult &result, bool *defined) const
  183. {
  184. if (desc.hasGetterObject() && desc.setter() == JS_StrictPropertyStub) {
  185. return result.failGetterOnly();
  186. }
  187. if (xpc::WrapperFactory::IsXrayWrapper(proxy)) {
  188. return result.succeed();
  189. }
  190. JS::Rooted<JSObject*> expando(cx, EnsureExpandoObject(cx, proxy));
  191. if (!expando) {
  192. return false;
  193. }
  194. if (!JS_DefinePropertyById(cx, expando, id, desc, result)) {
  195. return false;
  196. }
  197. *defined = true;
  198. return true;
  199. }
  200. bool
  201. DOMProxyHandler::set(JSContext *cx, Handle<JSObject*> proxy, Handle<jsid> id,
  202. Handle<JS::Value> v, Handle<JS::Value> receiver,
  203. ObjectOpResult &result) const
  204. {
  205. MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
  206. "Should not have a XrayWrapper here");
  207. bool done;
  208. if (!setCustom(cx, proxy, id, v, &done)) {
  209. return false;
  210. }
  211. if (done) {
  212. return result.succeed();
  213. }
  214. // Make sure to ignore our named properties when checking for own
  215. // property descriptors for a set.
  216. JS::Rooted<PropertyDescriptor> ownDesc(cx);
  217. if (!getOwnPropDescriptor(cx, proxy, id, /* ignoreNamedProps = */ true,
  218. &ownDesc)) {
  219. return false;
  220. }
  221. return js::SetPropertyIgnoringNamedGetter(cx, proxy, id, v, receiver, ownDesc, result);
  222. }
  223. bool
  224. DOMProxyHandler::delete_(JSContext* cx, JS::Handle<JSObject*> proxy,
  225. JS::Handle<jsid> id, JS::ObjectOpResult &result) const
  226. {
  227. JS::Rooted<JSObject*> expando(cx);
  228. if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = GetExpandoObject(proxy))) {
  229. return JS_DeletePropertyById(cx, expando, id, result);
  230. }
  231. return result.succeed();
  232. }
  233. bool
  234. BaseDOMProxyHandler::ownPropertyKeys(JSContext* cx,
  235. JS::Handle<JSObject*> proxy,
  236. JS::AutoIdVector& props) const
  237. {
  238. return ownPropNames(cx, proxy, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props);
  239. }
  240. bool
  241. BaseDOMProxyHandler::getPrototypeIfOrdinary(JSContext* cx, JS::Handle<JSObject*> proxy,
  242. bool* isOrdinary,
  243. JS::MutableHandle<JSObject*> proto) const
  244. {
  245. *isOrdinary = true;
  246. proto.set(GetStaticPrototype(proxy));
  247. return true;
  248. }
  249. bool
  250. BaseDOMProxyHandler::getOwnEnumerablePropertyKeys(JSContext* cx,
  251. JS::Handle<JSObject*> proxy,
  252. JS::AutoIdVector& props) const
  253. {
  254. return ownPropNames(cx, proxy, JSITER_OWNONLY, props);
  255. }
  256. bool
  257. DOMProxyHandler::setCustom(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
  258. JS::Handle<JS::Value> v, bool *done) const
  259. {
  260. *done = false;
  261. return true;
  262. }
  263. //static
  264. JSObject *
  265. DOMProxyHandler::GetExpandoObject(JSObject *obj)
  266. {
  267. MOZ_ASSERT(IsDOMProxy(obj), "expected a DOM proxy object");
  268. JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
  269. if (v.isObject()) {
  270. return &v.toObject();
  271. }
  272. if (v.isUndefined()) {
  273. return nullptr;
  274. }
  275. js::ExpandoAndGeneration* expandoAndGeneration =
  276. static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
  277. v = expandoAndGeneration->expando;
  278. return v.isUndefined() ? nullptr : &v.toObject();
  279. }
  280. void
  281. ShadowingDOMProxyHandler::trace(JSTracer* trc, JSObject* proxy) const
  282. {
  283. DOMProxyHandler::trace(trc, proxy);
  284. MOZ_ASSERT(IsDOMProxy(proxy), "expected a DOM proxy object");
  285. JS::Value v = js::GetProxyExtra(proxy, JSPROXYSLOT_EXPANDO);
  286. MOZ_ASSERT(!v.isObject(), "Should not have expando object directly!");
  287. if (v.isUndefined()) {
  288. // This can happen if we GC while creating our object, before we get a
  289. // chance to set up its JSPROXYSLOT_EXPANDO slot.
  290. return;
  291. }
  292. js::ExpandoAndGeneration* expandoAndGeneration =
  293. static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
  294. JS::TraceEdge(trc, &expandoAndGeneration->expando,
  295. "Shadowing DOM proxy expando");
  296. }
  297. } // namespace dom
  298. } // namespace mozilla