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