|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
|
2 /* vim: set ts=8 sts=4 et sw=4 tw=99: */ |
|
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 |
|
7 #include "XrayWrapper.h" |
|
8 #include "AccessCheck.h" |
|
9 #include "WrapperFactory.h" |
|
10 |
|
11 #include "nsIContent.h" |
|
12 #include "nsIControllers.h" |
|
13 #include "nsContentUtils.h" |
|
14 |
|
15 #include "XPCWrapper.h" |
|
16 #include "xpcprivate.h" |
|
17 |
|
18 #include "jsapi.h" |
|
19 #include "jsprf.h" |
|
20 #include "nsJSUtils.h" |
|
21 |
|
22 #include "mozilla/dom/BindingUtils.h" |
|
23 #include "mozilla/dom/WindowBinding.h" |
|
24 #include "nsGlobalWindow.h" |
|
25 |
|
26 using namespace mozilla::dom; |
|
27 using namespace JS; |
|
28 using namespace mozilla; |
|
29 |
|
30 using js::Wrapper; |
|
31 using js::BaseProxyHandler; |
|
32 using js::IsCrossCompartmentWrapper; |
|
33 using js::UncheckedUnwrap; |
|
34 using js::CheckedUnwrap; |
|
35 |
|
36 namespace xpc { |
|
37 |
|
38 using namespace XrayUtils; |
|
39 |
|
40 // Whitelist for the standard ES classes we can Xray to. |
|
41 static bool |
|
42 IsJSXraySupported(JSProtoKey key) |
|
43 { |
|
44 switch (key) { |
|
45 case JSProto_Date: |
|
46 return true; |
|
47 default: |
|
48 return false; |
|
49 } |
|
50 } |
|
51 |
|
52 XrayType |
|
53 GetXrayType(JSObject *obj) |
|
54 { |
|
55 obj = js::UncheckedUnwrap(obj, /* stopAtOuter = */ false); |
|
56 if (mozilla::dom::UseDOMXray(obj)) |
|
57 return XrayForDOMObject; |
|
58 |
|
59 const js::Class* clasp = js::GetObjectClass(obj); |
|
60 if (IS_WN_CLASS(clasp) || clasp->ext.innerObject) |
|
61 return XrayForWrappedNative; |
|
62 |
|
63 JSProtoKey standardProto = IdentifyStandardInstanceOrPrototype(obj); |
|
64 if (IsJSXraySupported(standardProto)) |
|
65 return XrayForJSObject; |
|
66 |
|
67 return NotXray; |
|
68 } |
|
69 |
|
70 JSObject * |
|
71 XrayAwareCalleeGlobal(JSObject *fun) |
|
72 { |
|
73 MOZ_ASSERT(js::IsFunctionObject(fun)); |
|
74 JSObject *scope = js::GetObjectParent(fun); |
|
75 if (IsXrayWrapper(scope)) |
|
76 scope = js::UncheckedUnwrap(scope); |
|
77 return js::GetGlobalForObjectCrossCompartment(scope); |
|
78 } |
|
79 |
|
80 const uint32_t JSSLOT_RESOLVING = 0; |
|
81 ResolvingId::ResolvingId(JSContext *cx, HandleObject wrapper, HandleId id) |
|
82 : mId(id), |
|
83 mHolder(cx, getHolderObject(wrapper)), |
|
84 mPrev(getResolvingId(mHolder)), |
|
85 mXrayShadowing(false) |
|
86 { |
|
87 js::SetReservedSlot(mHolder, JSSLOT_RESOLVING, js::PrivateValue(this)); |
|
88 } |
|
89 |
|
90 ResolvingId::~ResolvingId() |
|
91 { |
|
92 MOZ_ASSERT(getResolvingId(mHolder) == this, "unbalanced ResolvingIds"); |
|
93 js::SetReservedSlot(mHolder, JSSLOT_RESOLVING, js::PrivateValue(mPrev)); |
|
94 } |
|
95 |
|
96 bool |
|
97 ResolvingId::isXrayShadowing(jsid id) |
|
98 { |
|
99 if (!mXrayShadowing) |
|
100 return false; |
|
101 |
|
102 return mId == id; |
|
103 } |
|
104 |
|
105 bool |
|
106 ResolvingId::isResolving(jsid id) |
|
107 { |
|
108 for (ResolvingId *cur = this; cur; cur = cur->mPrev) { |
|
109 if (cur->mId == id) |
|
110 return true; |
|
111 } |
|
112 |
|
113 return false; |
|
114 } |
|
115 |
|
116 ResolvingId * |
|
117 ResolvingId::getResolvingId(JSObject *holder) |
|
118 { |
|
119 MOZ_ASSERT(strcmp(JS_GetClass(holder)->name, "NativePropertyHolder") == 0); |
|
120 return (ResolvingId *)js::GetReservedSlot(holder, JSSLOT_RESOLVING).toPrivate(); |
|
121 } |
|
122 |
|
123 JSObject * |
|
124 ResolvingId::getHolderObject(JSObject *wrapper) |
|
125 { |
|
126 return &js::GetProxyExtra(wrapper, 0).toObject(); |
|
127 } |
|
128 |
|
129 ResolvingId * |
|
130 ResolvingId::getResolvingIdFromWrapper(JSObject *wrapper) |
|
131 { |
|
132 return getResolvingId(getHolderObject(wrapper)); |
|
133 } |
|
134 |
|
135 class MOZ_STACK_CLASS ResolvingIdDummy |
|
136 { |
|
137 public: |
|
138 ResolvingIdDummy(JSContext *cx, HandleObject wrapper, HandleId id) |
|
139 { |
|
140 } |
|
141 }; |
|
142 |
|
143 class XrayTraits |
|
144 { |
|
145 public: |
|
146 static JSObject* getTargetObject(JSObject *wrapper) { |
|
147 return js::UncheckedUnwrap(wrapper, /* stopAtOuter = */ false); |
|
148 } |
|
149 |
|
150 virtual bool resolveNativeProperty(JSContext *cx, HandleObject wrapper, |
|
151 HandleObject holder, HandleId id, |
|
152 MutableHandle<JSPropertyDescriptor> desc) = 0; |
|
153 // NB: resolveOwnProperty may decide whether or not to cache what it finds |
|
154 // on the holder. If the result is not cached, the lookup will happen afresh |
|
155 // for each access, which is the right thing for things like dynamic NodeList |
|
156 // properties. |
|
157 virtual bool resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper, |
|
158 HandleObject wrapper, HandleObject holder, |
|
159 HandleId id, MutableHandle<JSPropertyDescriptor> desc); |
|
160 |
|
161 virtual void preserveWrapper(JSObject *target) = 0; |
|
162 |
|
163 static bool set(JSContext *cx, HandleObject wrapper, HandleObject receiver, HandleId id, |
|
164 bool strict, MutableHandleValue vp); |
|
165 |
|
166 JSObject* getExpandoObject(JSContext *cx, HandleObject target, |
|
167 HandleObject consumer); |
|
168 JSObject* ensureExpandoObject(JSContext *cx, HandleObject wrapper, |
|
169 HandleObject target); |
|
170 |
|
171 JSObject* getHolder(JSObject *wrapper); |
|
172 JSObject* ensureHolder(JSContext *cx, HandleObject wrapper); |
|
173 virtual JSObject* createHolder(JSContext *cx, JSObject *wrapper) = 0; |
|
174 |
|
175 JSObject* getExpandoChain(HandleObject obj) { |
|
176 return GetObjectScope(obj)->GetExpandoChain(obj); |
|
177 } |
|
178 |
|
179 bool setExpandoChain(JSContext *cx, HandleObject obj, HandleObject chain) { |
|
180 return GetObjectScope(obj)->SetExpandoChain(cx, obj, chain); |
|
181 } |
|
182 bool cloneExpandoChain(JSContext *cx, HandleObject dst, HandleObject src); |
|
183 |
|
184 private: |
|
185 bool expandoObjectMatchesConsumer(JSContext *cx, HandleObject expandoObject, |
|
186 nsIPrincipal *consumerOrigin, |
|
187 HandleObject exclusiveGlobal); |
|
188 JSObject* getExpandoObjectInternal(JSContext *cx, HandleObject target, |
|
189 nsIPrincipal *origin, |
|
190 JSObject *exclusiveGlobal); |
|
191 JSObject* attachExpandoObject(JSContext *cx, HandleObject target, |
|
192 nsIPrincipal *origin, |
|
193 HandleObject exclusiveGlobal); |
|
194 }; |
|
195 |
|
196 class XPCWrappedNativeXrayTraits : public XrayTraits |
|
197 { |
|
198 public: |
|
199 enum { |
|
200 HasPrototype = 0 |
|
201 }; |
|
202 |
|
203 static const XrayType Type = XrayForWrappedNative; |
|
204 |
|
205 virtual bool resolveNativeProperty(JSContext *cx, HandleObject wrapper, |
|
206 HandleObject holder, HandleId id, |
|
207 MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE; |
|
208 virtual bool resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper, HandleObject wrapper, |
|
209 HandleObject holder, HandleId id, |
|
210 MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE; |
|
211 static bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, |
|
212 MutableHandle<JSPropertyDescriptor> desc, |
|
213 Handle<JSPropertyDescriptor> existingDesc, bool *defined); |
|
214 virtual bool enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags, |
|
215 AutoIdVector &props); |
|
216 static bool call(JSContext *cx, HandleObject wrapper, |
|
217 const JS::CallArgs &args, js::Wrapper& baseInstance); |
|
218 static bool construct(JSContext *cx, HandleObject wrapper, |
|
219 const JS::CallArgs &args, js::Wrapper& baseInstance); |
|
220 |
|
221 static bool isResolving(JSContext *cx, JSObject *holder, jsid id); |
|
222 |
|
223 static bool resolveDOMCollectionProperty(JSContext *cx, HandleObject wrapper, |
|
224 HandleObject holder, HandleId id, |
|
225 MutableHandle<JSPropertyDescriptor> desc); |
|
226 |
|
227 static XPCWrappedNative* getWN(JSObject *wrapper) { |
|
228 return XPCWrappedNative::Get(getTargetObject(wrapper)); |
|
229 } |
|
230 |
|
231 virtual void preserveWrapper(JSObject *target) MOZ_OVERRIDE; |
|
232 |
|
233 typedef ResolvingId ResolvingIdImpl; |
|
234 |
|
235 virtual JSObject* createHolder(JSContext *cx, JSObject *wrapper) MOZ_OVERRIDE; |
|
236 |
|
237 static const JSClass HolderClass; |
|
238 static XPCWrappedNativeXrayTraits singleton; |
|
239 }; |
|
240 |
|
241 const JSClass XPCWrappedNativeXrayTraits::HolderClass = { |
|
242 "NativePropertyHolder", JSCLASS_HAS_RESERVED_SLOTS(2), |
|
243 JS_PropertyStub, JS_DeletePropertyStub, holder_get, holder_set, |
|
244 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub |
|
245 }; |
|
246 |
|
247 class DOMXrayTraits : public XrayTraits |
|
248 { |
|
249 public: |
|
250 enum { |
|
251 HasPrototype = 0 |
|
252 }; |
|
253 |
|
254 static const XrayType Type = XrayForDOMObject; |
|
255 |
|
256 virtual bool resolveNativeProperty(JSContext *cx, HandleObject wrapper, |
|
257 HandleObject holder, HandleId id, |
|
258 MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE; |
|
259 virtual bool resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper, HandleObject wrapper, |
|
260 HandleObject holder, HandleId id, |
|
261 MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE; |
|
262 static bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, |
|
263 MutableHandle<JSPropertyDescriptor> desc, |
|
264 Handle<JSPropertyDescriptor> existingDesc, bool *defined); |
|
265 static bool set(JSContext *cx, HandleObject wrapper, HandleObject receiver, HandleId id, |
|
266 bool strict, MutableHandleValue vp); |
|
267 virtual bool enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags, |
|
268 AutoIdVector &props); |
|
269 static bool call(JSContext *cx, HandleObject wrapper, |
|
270 const JS::CallArgs &args, js::Wrapper& baseInstance); |
|
271 static bool construct(JSContext *cx, HandleObject wrapper, |
|
272 const JS::CallArgs &args, js::Wrapper& baseInstance); |
|
273 |
|
274 static bool isResolving(JSContext *cx, JSObject *holder, jsid id) |
|
275 { |
|
276 return false; |
|
277 } |
|
278 |
|
279 typedef ResolvingIdDummy ResolvingIdImpl; |
|
280 |
|
281 virtual void preserveWrapper(JSObject *target) MOZ_OVERRIDE; |
|
282 |
|
283 virtual JSObject* createHolder(JSContext *cx, JSObject *wrapper) MOZ_OVERRIDE; |
|
284 |
|
285 static DOMXrayTraits singleton; |
|
286 }; |
|
287 |
|
288 class JSXrayTraits : public XrayTraits |
|
289 { |
|
290 public: |
|
291 enum { |
|
292 HasPrototype = 1 |
|
293 }; |
|
294 static const XrayType Type = XrayForJSObject; |
|
295 |
|
296 virtual bool resolveNativeProperty(JSContext *cx, HandleObject wrapper, |
|
297 HandleObject holder, HandleId id, |
|
298 MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE |
|
299 { |
|
300 MOZ_ASSUME_UNREACHABLE("resolveNativeProperty hook should never be called with HasPrototype = 1"); |
|
301 } |
|
302 |
|
303 virtual bool resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper, HandleObject wrapper, |
|
304 HandleObject holder, HandleId id, |
|
305 MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE; |
|
306 |
|
307 static bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, |
|
308 MutableHandle<JSPropertyDescriptor> desc, |
|
309 Handle<JSPropertyDescriptor> existingDesc, bool *defined) |
|
310 { |
|
311 // There's no useful per-trait work to do here. Punt back up to the common code. |
|
312 *defined = false; |
|
313 return true; |
|
314 } |
|
315 |
|
316 virtual bool enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags, |
|
317 AutoIdVector &props); |
|
318 |
|
319 static bool call(JSContext *cx, HandleObject wrapper, |
|
320 const JS::CallArgs &args, js::Wrapper& baseInstance) |
|
321 { |
|
322 // We'll handle this when we start supporting Functions. |
|
323 RootedValue v(cx, ObjectValue(*wrapper)); |
|
324 js_ReportIsNotFunction(cx, v); |
|
325 return false; |
|
326 } |
|
327 |
|
328 static bool construct(JSContext *cx, HandleObject wrapper, |
|
329 const JS::CallArgs &args, js::Wrapper& baseInstance) |
|
330 { |
|
331 // We'll handle this when we start supporting Functions. |
|
332 RootedValue v(cx, ObjectValue(*wrapper)); |
|
333 js_ReportIsNotFunction(cx, v); |
|
334 return false; |
|
335 } |
|
336 |
|
337 static bool isResolving(JSContext *cx, JSObject *holder, jsid id) |
|
338 { |
|
339 return false; |
|
340 } |
|
341 |
|
342 typedef ResolvingIdDummy ResolvingIdImpl; |
|
343 |
|
344 bool getPrototypeOf(JSContext *cx, JS::HandleObject wrapper, |
|
345 JS::HandleObject target, |
|
346 JS::MutableHandleObject protop) |
|
347 { |
|
348 RootedObject holder(cx, ensureHolder(cx, wrapper)); |
|
349 JSProtoKey key = isPrototype(holder) ? JSProto_Object |
|
350 : getProtoKey(holder); |
|
351 { |
|
352 JSAutoCompartment ac(cx, target); |
|
353 if (!JS_GetClassPrototype(cx, key, protop)) |
|
354 return nullptr; |
|
355 } |
|
356 return JS_WrapObject(cx, protop); |
|
357 } |
|
358 |
|
359 virtual void preserveWrapper(JSObject *target) MOZ_OVERRIDE { |
|
360 // In the case of pure JS objects, there is no underlying object, and |
|
361 // the target is the canonical representation of state. If it gets |
|
362 // collected, then expandos and such should be collected too. So there's |
|
363 // nothing to do here. |
|
364 } |
|
365 |
|
366 enum { |
|
367 SLOT_PROTOKEY = 0, |
|
368 SLOT_ISPROTOTYPE, |
|
369 SLOT_COUNT |
|
370 }; |
|
371 virtual JSObject* createHolder(JSContext *cx, JSObject *wrapper) MOZ_OVERRIDE; |
|
372 |
|
373 static JSProtoKey getProtoKey(JSObject *holder) { |
|
374 int32_t key = js::GetReservedSlot(holder, SLOT_PROTOKEY).toInt32(); |
|
375 return static_cast<JSProtoKey>(key); |
|
376 } |
|
377 |
|
378 static bool isPrototype(JSObject *holder) { |
|
379 return js::GetReservedSlot(holder, SLOT_ISPROTOTYPE).toBoolean(); |
|
380 } |
|
381 |
|
382 static const JSClass HolderClass; |
|
383 static JSXrayTraits singleton; |
|
384 }; |
|
385 |
|
386 const JSClass JSXrayTraits::HolderClass = { |
|
387 "JSXrayHolder", JSCLASS_HAS_RESERVED_SLOTS(SLOT_COUNT), |
|
388 JS_PropertyStub, JS_DeletePropertyStub, |
|
389 JS_PropertyStub, JS_StrictPropertyStub, |
|
390 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub |
|
391 }; |
|
392 |
|
393 bool |
|
394 JSXrayTraits::resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper, |
|
395 HandleObject wrapper, HandleObject holder, |
|
396 HandleId id, |
|
397 MutableHandle<JSPropertyDescriptor> desc) |
|
398 { |
|
399 // Call the common code. |
|
400 bool ok = XrayTraits::resolveOwnProperty(cx, jsWrapper, wrapper, holder, |
|
401 id, desc); |
|
402 if (!ok || desc.object()) |
|
403 return ok; |
|
404 |
|
405 // Non-prototypes don't have anything on them yet. |
|
406 if (!isPrototype(holder)) |
|
407 return true; |
|
408 |
|
409 // The non-HasPrototypes semantics implemented by traditional Xrays are kind |
|
410 // of broken with respect to |own|-ness and the holder. The common code |
|
411 // muddles through by only checking the holder for non-|own| lookups, but |
|
412 // that doesn't work for us. So we do an explicit holder check here, and hope |
|
413 // that this mess gets fixed up soon. |
|
414 if (!JS_GetPropertyDescriptorById(cx, holder, id, desc)) |
|
415 return false; |
|
416 if (desc.object()) { |
|
417 desc.object().set(wrapper); |
|
418 return true; |
|
419 } |
|
420 |
|
421 // Grab the JSClass. We require all Xrayable classes to have a ClassSpec. |
|
422 RootedObject target(cx, getTargetObject(wrapper)); |
|
423 const js::Class *clasp = js::GetObjectClass(target); |
|
424 JSProtoKey protoKey = JSCLASS_CACHED_PROTO_KEY(clasp); |
|
425 MOZ_ASSERT(protoKey == getProtoKey(holder)); |
|
426 MOZ_ASSERT(clasp->spec.defined()); |
|
427 |
|
428 // Handle the 'constructor' property. |
|
429 if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_CONSTRUCTOR)) { |
|
430 RootedObject constructor(cx); |
|
431 { |
|
432 JSAutoCompartment ac(cx, target); |
|
433 if (!JS_GetClassObject(cx, protoKey, &constructor)) |
|
434 return false; |
|
435 } |
|
436 if (!JS_WrapObject(cx, &constructor)) |
|
437 return false; |
|
438 desc.object().set(wrapper); |
|
439 desc.setAttributes(0); |
|
440 desc.setGetter(nullptr); |
|
441 desc.setSetter(nullptr); |
|
442 desc.value().setObject(*constructor); |
|
443 return true; |
|
444 } |
|
445 |
|
446 // Find the properties available, if any. |
|
447 const JSFunctionSpec *fs = clasp->spec.prototypeFunctions; |
|
448 if (!fs) |
|
449 return true; |
|
450 |
|
451 // Compute the property name we're looking for. We'll handle indexed |
|
452 // properties when we start supporting arrays. |
|
453 if (!JSID_IS_STRING(id)) |
|
454 return true; |
|
455 Rooted<JSFlatString*> str(cx, JSID_TO_FLAT_STRING(id)); |
|
456 |
|
457 // Scan through the properties. If we don't find anything, we're done. |
|
458 for (; fs->name; ++fs) { |
|
459 // We don't support self-hosted functions yet. See bug 972987. |
|
460 if (fs->selfHostedName) |
|
461 continue; |
|
462 if (JS_FlatStringEqualsAscii(str, fs->name)) |
|
463 break; |
|
464 } |
|
465 if (!fs->name) |
|
466 return true; |
|
467 |
|
468 // Generate an Xrayed version of the method. |
|
469 Rooted<JSFunction*> fun(cx, JS_NewFunctionById(cx, fs->call.op, fs->nargs, |
|
470 0, wrapper, id)); |
|
471 if (!fun) |
|
472 return false; |
|
473 |
|
474 // The generic Xray machinery only defines non-own properties on the holder. |
|
475 // This is broken, and will be fixed at some point, but for now we need to |
|
476 // cache the value explicitly. See the corresponding call to |
|
477 // JS_GetPropertyById at the top of this function. |
|
478 return JS_DefinePropertyById(cx, holder, id, |
|
479 ObjectValue(*JS_GetFunctionObject(fun)), |
|
480 nullptr, nullptr, 0) && |
|
481 JS_GetPropertyDescriptorById(cx, holder, id, desc); |
|
482 } |
|
483 |
|
484 bool |
|
485 JSXrayTraits::enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags, |
|
486 AutoIdVector &props) |
|
487 { |
|
488 RootedObject holder(cx, ensureHolder(cx, wrapper)); |
|
489 if (!holder) |
|
490 return false; |
|
491 |
|
492 // Non-prototypes don't have anything on them yet. |
|
493 if (!isPrototype(holder)) |
|
494 return true; |
|
495 |
|
496 // Grab the JSClass. We require all Xrayable classes to have a ClassSpec. |
|
497 RootedObject target(cx, getTargetObject(wrapper)); |
|
498 const js::Class *clasp = js::GetObjectClass(target); |
|
499 MOZ_ASSERT(JSCLASS_CACHED_PROTO_KEY(clasp) == getProtoKey(holder)); |
|
500 MOZ_ASSERT(clasp->spec.defined()); |
|
501 |
|
502 // Find the properties available, if any. |
|
503 const JSFunctionSpec *fs = clasp->spec.prototypeFunctions; |
|
504 if (!fs) |
|
505 return true; |
|
506 |
|
507 // Intern all the strings, and pass theme to the caller. |
|
508 for (; fs->name; ++fs) { |
|
509 // We don't support self-hosted functions yet. See bug 972987. |
|
510 if (fs->selfHostedName) |
|
511 continue; |
|
512 RootedString str(cx, JS_InternString(cx, fs->name)); |
|
513 if (!str) |
|
514 return false; |
|
515 if (!props.append(INTERNED_STRING_TO_JSID(cx, str))) |
|
516 return false; |
|
517 } |
|
518 |
|
519 // Add the 'constructor' property. |
|
520 return props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_CONSTRUCTOR)); |
|
521 } |
|
522 |
|
523 JSObject* |
|
524 JSXrayTraits::createHolder(JSContext *cx, JSObject *wrapper) |
|
525 { |
|
526 RootedObject global(cx, JS_GetGlobalForObject(cx, wrapper)); |
|
527 RootedObject target(cx, getTargetObject(wrapper)); |
|
528 RootedObject holder(cx, JS_NewObjectWithGivenProto(cx, &HolderClass, |
|
529 JS::NullPtr(), global)); |
|
530 if (!holder) |
|
531 return nullptr; |
|
532 |
|
533 // Compute information about the target. |
|
534 bool isPrototype = false; |
|
535 JSProtoKey key = IdentifyStandardInstance(target); |
|
536 if (key == JSProto_Null) { |
|
537 isPrototype = true; |
|
538 key = IdentifyStandardPrototype(target); |
|
539 } |
|
540 MOZ_ASSERT(key != JSProto_Null); |
|
541 |
|
542 // Store it on the holder. |
|
543 RootedValue v(cx); |
|
544 v.setNumber(static_cast<uint32_t>(key)); |
|
545 js::SetReservedSlot(holder, SLOT_PROTOKEY, v); |
|
546 v.setBoolean(isPrototype); |
|
547 js::SetReservedSlot(holder, SLOT_ISPROTOTYPE, v); |
|
548 |
|
549 return holder; |
|
550 } |
|
551 |
|
552 XPCWrappedNativeXrayTraits XPCWrappedNativeXrayTraits::singleton; |
|
553 DOMXrayTraits DOMXrayTraits::singleton; |
|
554 JSXrayTraits JSXrayTraits::singleton; |
|
555 |
|
556 XrayTraits* |
|
557 GetXrayTraits(JSObject *obj) |
|
558 { |
|
559 switch (GetXrayType(obj)) { |
|
560 case XrayForDOMObject: |
|
561 return &DOMXrayTraits::singleton; |
|
562 case XrayForWrappedNative: |
|
563 return &XPCWrappedNativeXrayTraits::singleton; |
|
564 case XrayForJSObject: |
|
565 return &JSXrayTraits::singleton; |
|
566 default: |
|
567 return nullptr; |
|
568 } |
|
569 } |
|
570 |
|
571 /* |
|
572 * Xray expando handling. |
|
573 * |
|
574 * We hang expandos for Xray wrappers off a reserved slot on the target object |
|
575 * so that same-origin compartments can share expandos for a given object. We |
|
576 * have a linked list of expando objects, one per origin. The properties on these |
|
577 * objects are generally wrappers pointing back to the compartment that applied |
|
578 * them. |
|
579 * |
|
580 * The expando objects should _never_ be exposed to script. The fact that they |
|
581 * live in the target compartment is a detail of the implementation, and does |
|
582 * not imply that code in the target compartment should be allowed to inspect |
|
583 * them. They are private to the origin that placed them. |
|
584 */ |
|
585 |
|
586 enum ExpandoSlots { |
|
587 JSSLOT_EXPANDO_NEXT = 0, |
|
588 JSSLOT_EXPANDO_ORIGIN, |
|
589 JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL, |
|
590 JSSLOT_EXPANDO_PROTOTYPE, |
|
591 JSSLOT_EXPANDO_COUNT |
|
592 }; |
|
593 |
|
594 static nsIPrincipal* |
|
595 ObjectPrincipal(JSObject *obj) |
|
596 { |
|
597 return GetCompartmentPrincipal(js::GetObjectCompartment(obj)); |
|
598 } |
|
599 |
|
600 static nsIPrincipal* |
|
601 GetExpandoObjectPrincipal(JSObject *expandoObject) |
|
602 { |
|
603 Value v = JS_GetReservedSlot(expandoObject, JSSLOT_EXPANDO_ORIGIN); |
|
604 return static_cast<nsIPrincipal*>(v.toPrivate()); |
|
605 } |
|
606 |
|
607 static void |
|
608 ExpandoObjectFinalize(JSFreeOp *fop, JSObject *obj) |
|
609 { |
|
610 // Release the principal. |
|
611 nsIPrincipal *principal = GetExpandoObjectPrincipal(obj); |
|
612 NS_RELEASE(principal); |
|
613 } |
|
614 |
|
615 const JSClass ExpandoObjectClass = { |
|
616 "XrayExpandoObject", |
|
617 JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_EXPANDO_COUNT), |
|
618 JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, |
|
619 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, ExpandoObjectFinalize |
|
620 }; |
|
621 |
|
622 bool |
|
623 XrayTraits::expandoObjectMatchesConsumer(JSContext *cx, |
|
624 HandleObject expandoObject, |
|
625 nsIPrincipal *consumerOrigin, |
|
626 HandleObject exclusiveGlobal) |
|
627 { |
|
628 MOZ_ASSERT(js::IsObjectInContextCompartment(expandoObject, cx)); |
|
629 |
|
630 // First, compare the principals. |
|
631 nsIPrincipal *o = GetExpandoObjectPrincipal(expandoObject); |
|
632 // Note that it's very important here to ignore document.domain. We |
|
633 // pull the principal for the expando object off of the first consumer |
|
634 // for a given origin, and freely share the expandos amongst multiple |
|
635 // same-origin consumers afterwards. However, this means that we have |
|
636 // no way to know whether _all_ consumers have opted in to collaboration |
|
637 // by explicitly setting document.domain. So we just mandate that expando |
|
638 // sharing is unaffected by it. |
|
639 if (!consumerOrigin->Equals(o)) |
|
640 return false; |
|
641 |
|
642 // Sandboxes want exclusive expando objects. |
|
643 JSObject *owner = JS_GetReservedSlot(expandoObject, |
|
644 JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL) |
|
645 .toObjectOrNull(); |
|
646 if (!owner && !exclusiveGlobal) |
|
647 return true; |
|
648 |
|
649 // The exclusive global should always be wrapped in the target's compartment. |
|
650 MOZ_ASSERT(!exclusiveGlobal || js::IsObjectInContextCompartment(exclusiveGlobal, cx)); |
|
651 MOZ_ASSERT(!owner || js::IsObjectInContextCompartment(owner, cx)); |
|
652 return owner == exclusiveGlobal; |
|
653 } |
|
654 |
|
655 JSObject * |
|
656 XrayTraits::getExpandoObjectInternal(JSContext *cx, HandleObject target, |
|
657 nsIPrincipal *origin, |
|
658 JSObject *exclusiveGlobalArg) |
|
659 { |
|
660 // The expando object lives in the compartment of the target, so all our |
|
661 // work needs to happen there. |
|
662 RootedObject exclusiveGlobal(cx, exclusiveGlobalArg); |
|
663 JSAutoCompartment ac(cx, target); |
|
664 if (!JS_WrapObject(cx, &exclusiveGlobal)) |
|
665 return nullptr; |
|
666 |
|
667 // Iterate through the chain, looking for a same-origin object. |
|
668 RootedObject head(cx, getExpandoChain(target)); |
|
669 while (head) { |
|
670 if (expandoObjectMatchesConsumer(cx, head, origin, exclusiveGlobal)) |
|
671 return head; |
|
672 head = JS_GetReservedSlot(head, JSSLOT_EXPANDO_NEXT).toObjectOrNull(); |
|
673 } |
|
674 |
|
675 // Not found. |
|
676 return nullptr; |
|
677 } |
|
678 |
|
679 JSObject * |
|
680 XrayTraits::getExpandoObject(JSContext *cx, HandleObject target, HandleObject consumer) |
|
681 { |
|
682 JSObject *consumerGlobal = js::GetGlobalForObjectCrossCompartment(consumer); |
|
683 bool isSandbox = !strcmp(js::GetObjectJSClass(consumerGlobal)->name, "Sandbox"); |
|
684 return getExpandoObjectInternal(cx, target, ObjectPrincipal(consumer), |
|
685 isSandbox ? consumerGlobal : nullptr); |
|
686 } |
|
687 |
|
688 JSObject * |
|
689 XrayTraits::attachExpandoObject(JSContext *cx, HandleObject target, |
|
690 nsIPrincipal *origin, HandleObject exclusiveGlobal) |
|
691 { |
|
692 // Make sure the compartments are sane. |
|
693 MOZ_ASSERT(js::IsObjectInContextCompartment(target, cx)); |
|
694 MOZ_ASSERT(!exclusiveGlobal || js::IsObjectInContextCompartment(exclusiveGlobal, cx)); |
|
695 |
|
696 // No duplicates allowed. |
|
697 MOZ_ASSERT(!getExpandoObjectInternal(cx, target, origin, exclusiveGlobal)); |
|
698 |
|
699 // Create the expando object. We parent it directly to the target object. |
|
700 RootedObject expandoObject(cx, JS_NewObjectWithGivenProto(cx, &ExpandoObjectClass, |
|
701 JS::NullPtr(), target)); |
|
702 if (!expandoObject) |
|
703 return nullptr; |
|
704 |
|
705 // AddRef and store the principal. |
|
706 NS_ADDREF(origin); |
|
707 JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_ORIGIN, PRIVATE_TO_JSVAL(origin)); |
|
708 |
|
709 // Note the exclusive global, if any. |
|
710 JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL, |
|
711 OBJECT_TO_JSVAL(exclusiveGlobal)); |
|
712 |
|
713 // If this is our first expando object, take the opportunity to preserve |
|
714 // the wrapper. This keeps our expandos alive even if the Xray wrapper gets |
|
715 // collected. |
|
716 RootedObject chain(cx, getExpandoChain(target)); |
|
717 if (!chain) |
|
718 preserveWrapper(target); |
|
719 |
|
720 // Insert it at the front of the chain. |
|
721 JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_NEXT, OBJECT_TO_JSVAL(chain)); |
|
722 setExpandoChain(cx, target, expandoObject); |
|
723 |
|
724 return expandoObject; |
|
725 } |
|
726 |
|
727 JSObject * |
|
728 XrayTraits::ensureExpandoObject(JSContext *cx, HandleObject wrapper, |
|
729 HandleObject target) |
|
730 { |
|
731 // Expando objects live in the target compartment. |
|
732 JSAutoCompartment ac(cx, target); |
|
733 JSObject *expandoObject = getExpandoObject(cx, target, wrapper); |
|
734 if (!expandoObject) { |
|
735 // If the object is a sandbox, we don't want it to share expandos with |
|
736 // anyone else, so we tag it with the sandbox global. |
|
737 // |
|
738 // NB: We first need to check the class, _then_ wrap for the target's |
|
739 // compartment. |
|
740 RootedObject consumerGlobal(cx, js::GetGlobalForObjectCrossCompartment(wrapper)); |
|
741 bool isSandbox = !strcmp(js::GetObjectJSClass(consumerGlobal)->name, "Sandbox"); |
|
742 if (!JS_WrapObject(cx, &consumerGlobal)) |
|
743 return nullptr; |
|
744 expandoObject = attachExpandoObject(cx, target, ObjectPrincipal(wrapper), |
|
745 isSandbox ? (HandleObject)consumerGlobal : NullPtr()); |
|
746 } |
|
747 return expandoObject; |
|
748 } |
|
749 |
|
750 bool |
|
751 XrayTraits::cloneExpandoChain(JSContext *cx, HandleObject dst, HandleObject src) |
|
752 { |
|
753 MOZ_ASSERT(js::IsObjectInContextCompartment(dst, cx)); |
|
754 MOZ_ASSERT(getExpandoChain(dst) == nullptr); |
|
755 |
|
756 RootedObject oldHead(cx, getExpandoChain(src)); |
|
757 while (oldHead) { |
|
758 RootedObject exclusive(cx, JS_GetReservedSlot(oldHead, |
|
759 JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL) |
|
760 .toObjectOrNull()); |
|
761 if (!JS_WrapObject(cx, &exclusive)) |
|
762 return false; |
|
763 RootedObject newHead(cx, attachExpandoObject(cx, dst, GetExpandoObjectPrincipal(oldHead), |
|
764 exclusive)); |
|
765 if (!JS_CopyPropertiesFrom(cx, newHead, oldHead)) |
|
766 return false; |
|
767 oldHead = JS_GetReservedSlot(oldHead, JSSLOT_EXPANDO_NEXT).toObjectOrNull(); |
|
768 } |
|
769 return true; |
|
770 } |
|
771 |
|
772 namespace XrayUtils { |
|
773 bool CloneExpandoChain(JSContext *cx, JSObject *dstArg, JSObject *srcArg) |
|
774 { |
|
775 RootedObject dst(cx, dstArg); |
|
776 RootedObject src(cx, srcArg); |
|
777 return GetXrayTraits(src)->cloneExpandoChain(cx, dst, src); |
|
778 } |
|
779 } |
|
780 |
|
781 static JSObject * |
|
782 GetHolder(JSObject *obj) |
|
783 { |
|
784 return &js::GetProxyExtra(obj, 0).toObject(); |
|
785 } |
|
786 |
|
787 JSObject* |
|
788 XrayTraits::getHolder(JSObject *wrapper) |
|
789 { |
|
790 MOZ_ASSERT(WrapperFactory::IsXrayWrapper(wrapper)); |
|
791 js::Value v = js::GetProxyExtra(wrapper, 0); |
|
792 return v.isObject() ? &v.toObject() : nullptr; |
|
793 } |
|
794 |
|
795 JSObject* |
|
796 XrayTraits::ensureHolder(JSContext *cx, HandleObject wrapper) |
|
797 { |
|
798 RootedObject holder(cx, getHolder(wrapper)); |
|
799 if (holder) |
|
800 return holder; |
|
801 holder = createHolder(cx, wrapper); // virtual trap. |
|
802 if (holder) |
|
803 js::SetProxyExtra(wrapper, 0, ObjectValue(*holder)); |
|
804 return holder; |
|
805 } |
|
806 |
|
807 bool |
|
808 XPCWrappedNativeXrayTraits::isResolving(JSContext *cx, JSObject *holder, |
|
809 jsid id) |
|
810 { |
|
811 ResolvingId *cur = ResolvingId::getResolvingId(holder); |
|
812 if (!cur) |
|
813 return false; |
|
814 return cur->isResolving(id); |
|
815 } |
|
816 |
|
817 namespace XrayUtils { |
|
818 |
|
819 bool |
|
820 IsXPCWNHolderClass(const JSClass *clasp) |
|
821 { |
|
822 return clasp == &XPCWrappedNativeXrayTraits::HolderClass; |
|
823 } |
|
824 |
|
825 } |
|
826 |
|
827 |
|
828 // Some DOM objects have shared properties that don't have an explicit |
|
829 // getter/setter and rely on the class getter/setter. We install a |
|
830 // class getter/setter on the holder object to trigger them. |
|
831 bool |
|
832 holder_get(JSContext *cx, HandleObject wrapper, HandleId id, MutableHandleValue vp) |
|
833 { |
|
834 // JSClass::getProperty is wacky enough that it's hard to be sure someone |
|
835 // can't inherit this getter by prototyping a random object to an |
|
836 // XrayWrapper. Be safe. |
|
837 NS_ENSURE_TRUE(WrapperFactory::IsXrayWrapper(wrapper), true); |
|
838 JSObject *holder = GetHolder(wrapper); |
|
839 |
|
840 XPCWrappedNative *wn = XPCWrappedNativeXrayTraits::getWN(wrapper); |
|
841 if (NATIVE_HAS_FLAG(wn, WantGetProperty)) { |
|
842 JSAutoCompartment ac(cx, holder); |
|
843 bool retval = true; |
|
844 nsresult rv = wn->GetScriptableCallback()->GetProperty(wn, cx, wrapper, |
|
845 id, vp.address(), &retval); |
|
846 if (NS_FAILED(rv) || !retval) { |
|
847 if (retval) |
|
848 XPCThrower::Throw(rv, cx); |
|
849 return false; |
|
850 } |
|
851 } |
|
852 return true; |
|
853 } |
|
854 |
|
855 bool |
|
856 holder_set(JSContext *cx, HandleObject wrapper, HandleId id, bool strict, MutableHandleValue vp) |
|
857 { |
|
858 // JSClass::setProperty is wacky enough that it's hard to be sure someone |
|
859 // can't inherit this getter by prototyping a random object to an |
|
860 // XrayWrapper. Be safe. |
|
861 NS_ENSURE_TRUE(WrapperFactory::IsXrayWrapper(wrapper), true); |
|
862 JSObject *holder = GetHolder(wrapper); |
|
863 if (XPCWrappedNativeXrayTraits::isResolving(cx, holder, id)) { |
|
864 return true; |
|
865 } |
|
866 |
|
867 XPCWrappedNative *wn = XPCWrappedNativeXrayTraits::getWN(wrapper); |
|
868 if (NATIVE_HAS_FLAG(wn, WantSetProperty)) { |
|
869 JSAutoCompartment ac(cx, holder); |
|
870 bool retval = true; |
|
871 nsresult rv = wn->GetScriptableCallback()->SetProperty(wn, cx, wrapper, |
|
872 id, vp.address(), &retval); |
|
873 if (NS_FAILED(rv) || !retval) { |
|
874 if (retval) |
|
875 XPCThrower::Throw(rv, cx); |
|
876 return false; |
|
877 } |
|
878 } |
|
879 return true; |
|
880 } |
|
881 |
|
882 class AutoSetWrapperNotShadowing |
|
883 { |
|
884 public: |
|
885 AutoSetWrapperNotShadowing(ResolvingId *resolvingId MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
|
886 { |
|
887 MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
|
888 MOZ_ASSERT(resolvingId); |
|
889 mResolvingId = resolvingId; |
|
890 mResolvingId->mXrayShadowing = true; |
|
891 } |
|
892 |
|
893 ~AutoSetWrapperNotShadowing() |
|
894 { |
|
895 mResolvingId->mXrayShadowing = false; |
|
896 } |
|
897 |
|
898 private: |
|
899 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
|
900 ResolvingId *mResolvingId; |
|
901 }; |
|
902 |
|
903 // This is called after the resolveNativeProperty could not find any property |
|
904 // with the given id. At this point we can check for DOM specific collections |
|
905 // like document["formName"] because we already know that it is not shadowing |
|
906 // any native property. |
|
907 bool |
|
908 XPCWrappedNativeXrayTraits::resolveDOMCollectionProperty(JSContext *cx, HandleObject wrapper, |
|
909 HandleObject holder, HandleId id, |
|
910 MutableHandle<JSPropertyDescriptor> desc) |
|
911 { |
|
912 // If we are not currently resolving this id and resolveNative is called |
|
913 // we don't do anything. (see defineProperty in case of shadowing is forbidden). |
|
914 ResolvingId *rid = ResolvingId::getResolvingId(holder); |
|
915 if (!rid || rid->mId != id) |
|
916 return true; |
|
917 |
|
918 XPCWrappedNative *wn = getWN(wrapper); |
|
919 if (!wn) { |
|
920 // This should NEVER happen, but let's be extra careful here |
|
921 // because of the reported crashes (Bug 832091). |
|
922 XPCThrower::Throw(NS_ERROR_UNEXPECTED, cx); |
|
923 return false; |
|
924 } |
|
925 if (!NATIVE_HAS_FLAG(wn, WantNewResolve)) |
|
926 return true; |
|
927 |
|
928 ResolvingId *resolvingId = ResolvingId::getResolvingIdFromWrapper(wrapper); |
|
929 if (!resolvingId) { |
|
930 // This should NEVER happen, but let's be extra careful here |
|
931 // becaue of the reported crashes (Bug 832091). |
|
932 XPCThrower::Throw(NS_ERROR_UNEXPECTED, cx); |
|
933 return false; |
|
934 } |
|
935 |
|
936 // Setting the current ResolvingId in non-shadowing mode. So for this id |
|
937 // Xray won't ignore DOM specific collection properties temporarily. |
|
938 AutoSetWrapperNotShadowing asw(resolvingId); |
|
939 |
|
940 bool retval = true; |
|
941 RootedObject pobj(cx); |
|
942 nsresult rv = wn->GetScriptableInfo()->GetCallback()->NewResolve(wn, cx, wrapper, id, |
|
943 pobj.address(), &retval); |
|
944 if (NS_FAILED(rv)) { |
|
945 if (retval) |
|
946 XPCThrower::Throw(rv, cx); |
|
947 return false; |
|
948 } |
|
949 |
|
950 if (pobj && !JS_GetPropertyDescriptorById(cx, holder, id, desc)) |
|
951 return false; |
|
952 |
|
953 return true; |
|
954 } |
|
955 |
|
956 static nsGlobalWindow* |
|
957 AsWindow(JSContext *cx, JSObject *wrapper) |
|
958 { |
|
959 nsGlobalWindow* win; |
|
960 // We want to use our target object here, since we don't want to be |
|
961 // doing a security check while unwrapping. |
|
962 JSObject* target = XrayTraits::getTargetObject(wrapper); |
|
963 nsresult rv = UNWRAP_OBJECT(Window, target, win); |
|
964 if (NS_SUCCEEDED(rv)) |
|
965 return win; |
|
966 |
|
967 nsCOMPtr<nsPIDOMWindow> piWin = do_QueryInterface( |
|
968 nsContentUtils::XPConnect()->GetNativeOfWrapper(cx, target)); |
|
969 return static_cast<nsGlobalWindow*>(piWin.get()); |
|
970 } |
|
971 |
|
972 static bool |
|
973 IsWindow(JSContext *cx, JSObject *wrapper) |
|
974 { |
|
975 return !!AsWindow(cx, wrapper); |
|
976 } |
|
977 |
|
978 static nsQueryInterface |
|
979 do_QueryInterfaceNative(JSContext* cx, HandleObject wrapper); |
|
980 |
|
981 void |
|
982 XPCWrappedNativeXrayTraits::preserveWrapper(JSObject *target) |
|
983 { |
|
984 XPCWrappedNative *wn = XPCWrappedNative::Get(target); |
|
985 nsRefPtr<nsXPCClassInfo> ci; |
|
986 CallQueryInterface(wn->Native(), getter_AddRefs(ci)); |
|
987 if (ci) |
|
988 ci->PreserveWrapper(wn->Native()); |
|
989 } |
|
990 |
|
991 bool |
|
992 XPCWrappedNativeXrayTraits::resolveNativeProperty(JSContext *cx, HandleObject wrapper, |
|
993 HandleObject holder, HandleId id, |
|
994 MutableHandle<JSPropertyDescriptor> desc) |
|
995 { |
|
996 MOZ_ASSERT(js::GetObjectJSClass(holder) == &HolderClass); |
|
997 |
|
998 desc.object().set(nullptr); |
|
999 |
|
1000 // This will do verification and the method lookup for us. |
|
1001 RootedObject target(cx, getTargetObject(wrapper)); |
|
1002 XPCCallContext ccx(JS_CALLER, cx, target, NullPtr(), id); |
|
1003 |
|
1004 // There are no native numeric properties, so we can shortcut here. We will |
|
1005 // not find the property. However we want to support non shadowing dom |
|
1006 // specific collection properties like window.frames, so we still have to |
|
1007 // check for those. |
|
1008 if (!JSID_IS_STRING(id)) { |
|
1009 /* Not found */ |
|
1010 return resolveDOMCollectionProperty(cx, wrapper, holder, id, desc); |
|
1011 } |
|
1012 |
|
1013 |
|
1014 // The |controllers| property is accessible as a [ChromeOnly] property on |
|
1015 // Window.WebIDL, and [noscript] in XPIDL. Chrome needs to see this over |
|
1016 // Xray, so we need to special-case it until we move |Window| to WebIDL. |
|
1017 nsGlobalWindow *win = nullptr; |
|
1018 if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_CONTROLLERS) && |
|
1019 AccessCheck::isChrome(wrapper) && |
|
1020 (win = AsWindow(cx, wrapper))) |
|
1021 { |
|
1022 nsCOMPtr<nsIControllers> c; |
|
1023 nsresult rv = win->GetControllers(getter_AddRefs(c)); |
|
1024 if (NS_SUCCEEDED(rv) && c) { |
|
1025 rv = nsXPConnect::XPConnect()->WrapNativeToJSVal(cx, CurrentGlobalOrNull(cx), |
|
1026 c, nullptr, nullptr, true, |
|
1027 desc.value()); |
|
1028 } |
|
1029 |
|
1030 if (NS_FAILED(rv) || !c) { |
|
1031 JS_ReportError(cx, "Failed to invoke GetControllers via Xrays"); |
|
1032 return false; |
|
1033 } |
|
1034 |
|
1035 desc.object().set(wrapper); |
|
1036 return true; |
|
1037 } |
|
1038 |
|
1039 XPCNativeInterface *iface; |
|
1040 XPCNativeMember *member; |
|
1041 XPCWrappedNative *wn = getWN(wrapper); |
|
1042 |
|
1043 if (ccx.GetWrapper() != wn || !wn->IsValid()) { |
|
1044 // Something is wrong. If the wrapper is not even valid let's not risk |
|
1045 // calling resolveDOMCollectionProperty. |
|
1046 return true; |
|
1047 } else if (!(iface = ccx.GetInterface()) || |
|
1048 !(member = ccx.GetMember())) { |
|
1049 /* Not found */ |
|
1050 return resolveDOMCollectionProperty(cx, wrapper, holder, id, desc); |
|
1051 } |
|
1052 |
|
1053 desc.object().set(holder); |
|
1054 desc.setAttributes(JSPROP_ENUMERATE); |
|
1055 desc.setGetter(nullptr); |
|
1056 desc.setSetter(nullptr); |
|
1057 desc.value().set(JSVAL_VOID); |
|
1058 |
|
1059 RootedValue fval(cx, JSVAL_VOID); |
|
1060 if (member->IsConstant()) { |
|
1061 if (!member->GetConstantValue(ccx, iface, desc.value().address())) { |
|
1062 JS_ReportError(cx, "Failed to convert constant native property to JS value"); |
|
1063 return false; |
|
1064 } |
|
1065 } else if (member->IsAttribute()) { |
|
1066 // This is a getter/setter. Clone a function for it. |
|
1067 if (!member->NewFunctionObject(ccx, iface, wrapper, fval.address())) { |
|
1068 JS_ReportError(cx, "Failed to clone function object for native getter/setter"); |
|
1069 return false; |
|
1070 } |
|
1071 |
|
1072 unsigned attrs = desc.attributes(); |
|
1073 attrs |= JSPROP_GETTER; |
|
1074 if (member->IsWritableAttribute()) |
|
1075 attrs |= JSPROP_SETTER; |
|
1076 |
|
1077 // Make the property shared on the holder so no slot is allocated |
|
1078 // for it. This avoids keeping garbage alive through that slot. |
|
1079 attrs |= JSPROP_SHARED; |
|
1080 desc.setAttributes(attrs); |
|
1081 } else { |
|
1082 // This is a method. Clone a function for it. |
|
1083 if (!member->NewFunctionObject(ccx, iface, wrapper, desc.value().address())) { |
|
1084 JS_ReportError(cx, "Failed to clone function object for native function"); |
|
1085 return false; |
|
1086 } |
|
1087 |
|
1088 // Without a wrapper the function would live on the prototype. Since we |
|
1089 // don't have one, we have to avoid calling the scriptable helper's |
|
1090 // GetProperty method for this property, so stub out the getter and |
|
1091 // setter here explicitly. |
|
1092 desc.setGetter(JS_PropertyStub); |
|
1093 desc.setSetter(JS_StrictPropertyStub); |
|
1094 } |
|
1095 |
|
1096 if (!JS_WrapValue(cx, desc.value()) || !JS_WrapValue(cx, &fval)) |
|
1097 return false; |
|
1098 |
|
1099 if (desc.hasGetterObject()) |
|
1100 desc.setGetterObject(&fval.toObject()); |
|
1101 if (desc.hasSetterObject()) |
|
1102 desc.setSetterObject(&fval.toObject()); |
|
1103 |
|
1104 // Define the property. |
|
1105 return JS_DefinePropertyById(cx, holder, id, desc.value(), |
|
1106 desc.getter(), desc.setter(), desc.attributes()); |
|
1107 } |
|
1108 |
|
1109 static bool |
|
1110 wrappedJSObject_getter(JSContext *cx, HandleObject wrapper, HandleId id, MutableHandleValue vp) |
|
1111 { |
|
1112 if (!IsWrapper(wrapper) || !WrapperFactory::IsXrayWrapper(wrapper)) { |
|
1113 JS_ReportError(cx, "Unexpected object"); |
|
1114 return false; |
|
1115 } |
|
1116 |
|
1117 vp.set(OBJECT_TO_JSVAL(wrapper)); |
|
1118 |
|
1119 return WrapperFactory::WaiveXrayAndWrap(cx, vp); |
|
1120 } |
|
1121 |
|
1122 bool |
|
1123 XrayTraits::resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper, |
|
1124 HandleObject wrapper, HandleObject holder, HandleId id, |
|
1125 MutableHandle<JSPropertyDescriptor> desc) |
|
1126 { |
|
1127 desc.object().set(nullptr); |
|
1128 RootedObject target(cx, getTargetObject(wrapper)); |
|
1129 RootedObject expando(cx, getExpandoObject(cx, target, wrapper)); |
|
1130 |
|
1131 // Check for expando properties first. Note that the expando object lives |
|
1132 // in the target compartment. |
|
1133 bool found = false; |
|
1134 if (expando) { |
|
1135 JSAutoCompartment ac(cx, expando); |
|
1136 if (!JS_GetPropertyDescriptorById(cx, expando, id, desc)) |
|
1137 return false; |
|
1138 found = !!desc.object(); |
|
1139 } |
|
1140 |
|
1141 // Next, check for ES builtins. |
|
1142 if (!found && JS_IsGlobalObject(target)) { |
|
1143 JSProtoKey key = JS_IdToProtoKey(cx, id); |
|
1144 JSAutoCompartment ac(cx, target); |
|
1145 if (key != JSProto_Null) { |
|
1146 MOZ_ASSERT(key < JSProto_LIMIT); |
|
1147 RootedObject constructor(cx); |
|
1148 if (!JS_GetClassObject(cx, key, &constructor)) |
|
1149 return false; |
|
1150 MOZ_ASSERT(constructor); |
|
1151 desc.value().set(ObjectValue(*constructor)); |
|
1152 found = true; |
|
1153 } else if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_EVAL)) { |
|
1154 RootedObject eval(cx); |
|
1155 if (!js::GetOriginalEval(cx, target, &eval)) |
|
1156 return false; |
|
1157 desc.value().set(ObjectValue(*eval)); |
|
1158 found = true; |
|
1159 } |
|
1160 } |
|
1161 |
|
1162 if (found) { |
|
1163 if (!JS_WrapPropertyDescriptor(cx, desc)) |
|
1164 return false; |
|
1165 // Pretend the property lives on the wrapper. |
|
1166 desc.object().set(wrapper); |
|
1167 return true; |
|
1168 } |
|
1169 |
|
1170 // Handle .wrappedJSObject for subsuming callers. This should move once we |
|
1171 // sort out own-ness for the holder. |
|
1172 if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_WRAPPED_JSOBJECT) && |
|
1173 AccessCheck::wrapperSubsumes(wrapper)) |
|
1174 { |
|
1175 if (!JS_AlreadyHasOwnPropertyById(cx, holder, id, &found)) |
|
1176 return false; |
|
1177 if (!found && !JS_DefinePropertyById(cx, holder, id, UndefinedValue(), |
|
1178 wrappedJSObject_getter, nullptr, |
|
1179 JSPROP_ENUMERATE | JSPROP_SHARED)) { |
|
1180 return false; |
|
1181 } |
|
1182 if (!JS_GetPropertyDescriptorById(cx, holder, id, desc)) |
|
1183 return false; |
|
1184 desc.object().set(wrapper); |
|
1185 return true; |
|
1186 } |
|
1187 |
|
1188 return true; |
|
1189 } |
|
1190 |
|
1191 bool |
|
1192 XrayTraits::set(JSContext *cx, HandleObject wrapper, HandleObject receiver, HandleId id, |
|
1193 bool strict, MutableHandleValue vp) |
|
1194 { |
|
1195 // Skip our Base if it isn't already BaseProxyHandler. |
|
1196 js::BaseProxyHandler *handler = js::GetProxyHandler(wrapper); |
|
1197 return handler->js::BaseProxyHandler::set(cx, wrapper, receiver, id, strict, vp); |
|
1198 } |
|
1199 |
|
1200 bool |
|
1201 XPCWrappedNativeXrayTraits::resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper, |
|
1202 HandleObject wrapper, HandleObject holder, |
|
1203 HandleId id, |
|
1204 MutableHandle<JSPropertyDescriptor> desc) |
|
1205 { |
|
1206 // Call the common code. |
|
1207 bool ok = XrayTraits::resolveOwnProperty(cx, jsWrapper, wrapper, holder, |
|
1208 id, desc); |
|
1209 if (!ok || desc.object()) |
|
1210 return ok; |
|
1211 |
|
1212 // Check for indexed access on a window. |
|
1213 int32_t index = GetArrayIndexFromId(cx, id); |
|
1214 if (IsArrayIndex(index)) { |
|
1215 nsGlobalWindow* win = AsWindow(cx, wrapper); |
|
1216 // Note: As() unwraps outer windows to get to the inner window. |
|
1217 if (win) { |
|
1218 bool unused; |
|
1219 nsCOMPtr<nsIDOMWindow> subframe = win->IndexedGetter(index, unused); |
|
1220 if (subframe) { |
|
1221 nsGlobalWindow* global = static_cast<nsGlobalWindow*>(subframe.get()); |
|
1222 global->EnsureInnerWindow(); |
|
1223 JSObject* obj = global->FastGetGlobalJSObject(); |
|
1224 if (MOZ_UNLIKELY(!obj)) { |
|
1225 // It's gone? |
|
1226 return xpc::Throw(cx, NS_ERROR_FAILURE); |
|
1227 } |
|
1228 desc.value().setObject(*obj); |
|
1229 FillPropertyDescriptor(desc, wrapper, true); |
|
1230 return JS_WrapPropertyDescriptor(cx, desc); |
|
1231 } |
|
1232 } |
|
1233 } |
|
1234 |
|
1235 // Xray wrappers don't use the regular wrapper hierarchy, so we should be |
|
1236 // in the wrapper's compartment here, not the wrappee. |
|
1237 MOZ_ASSERT(js::IsObjectInContextCompartment(wrapper, cx)); |
|
1238 |
|
1239 bool hasProp; |
|
1240 if (!JS_HasPropertyById(cx, holder, id, &hasProp)) { |
|
1241 return false; |
|
1242 } |
|
1243 if (!hasProp) { |
|
1244 XPCWrappedNative *wn = getWN(wrapper); |
|
1245 |
|
1246 // Run the resolve hook of the wrapped native. |
|
1247 if (!NATIVE_HAS_FLAG(wn, WantNewResolve)) { |
|
1248 return true; |
|
1249 } |
|
1250 |
|
1251 bool retval = true; |
|
1252 RootedObject pobj(cx); |
|
1253 nsIXPCScriptable *callback = wn->GetScriptableInfo()->GetCallback(); |
|
1254 nsresult rv = callback->NewResolve(wn, cx, wrapper, id, pobj.address(), |
|
1255 &retval); |
|
1256 if (NS_FAILED(rv)) { |
|
1257 if (retval) |
|
1258 XPCThrower::Throw(rv, cx); |
|
1259 return false; |
|
1260 } |
|
1261 |
|
1262 MOZ_ASSERT(!pobj || (JS_HasPropertyById(cx, holder, id, &hasProp) && |
|
1263 hasProp), "id got defined somewhere else?"); |
|
1264 } |
|
1265 |
|
1266 // resolveOwnProperty must return a non-empty |desc| if and only if an |own| |
|
1267 // property was found on the object. However, given how the NewResolve setup |
|
1268 // works, we can't run the resolve hook if the holder already has a property |
|
1269 // of the same name. So if there was a pre-existing property on the holder, |
|
1270 // we have to use it. But we have no way of knowing if it corresponded to an |
|
1271 // |own| or non-|own| property, since both get cached on the holder and the |
|
1272 // |own|-ness information is lost. |
|
1273 // |
|
1274 // So we just over-zealously call things |own| here. This can cause us to |
|
1275 // return non-|own| properties from Object.getOwnPropertyDescriptor if |
|
1276 // lookups are performed in a certain order, but we can probably live with |
|
1277 // that until XPCWN Xrays go away with the new DOM bindings. |
|
1278 return JS_GetPropertyDescriptorById(cx, holder, id, desc); |
|
1279 } |
|
1280 |
|
1281 bool |
|
1282 XPCWrappedNativeXrayTraits::defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, |
|
1283 MutableHandle<JSPropertyDescriptor> desc, |
|
1284 Handle<JSPropertyDescriptor> existingDesc, bool *defined) |
|
1285 { |
|
1286 *defined = false; |
|
1287 JSObject *holder = singleton.ensureHolder(cx, wrapper); |
|
1288 if (isResolving(cx, holder, id)) { |
|
1289 if (!desc.hasAttributes(JSPROP_GETTER | JSPROP_SETTER)) { |
|
1290 if (!desc.getter()) |
|
1291 desc.setGetter(holder_get); |
|
1292 if (!desc.setter()) |
|
1293 desc.setSetter(holder_set); |
|
1294 } |
|
1295 |
|
1296 *defined = true; |
|
1297 return JS_DefinePropertyById(cx, holder, id, desc.value(), desc.getter(), desc.setter(), |
|
1298 desc.attributes()); |
|
1299 } |
|
1300 |
|
1301 // Check for an indexed property on a Window. If that's happening, do |
|
1302 // nothing but claim we defined it so it won't get added as an expando. |
|
1303 int32_t index = GetArrayIndexFromId(cx, id); |
|
1304 if (IsArrayIndex(index) && IsWindow(cx, wrapper)) { |
|
1305 *defined = true; |
|
1306 return true; |
|
1307 } |
|
1308 |
|
1309 return true; |
|
1310 } |
|
1311 |
|
1312 bool |
|
1313 XPCWrappedNativeXrayTraits::enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags, |
|
1314 AutoIdVector &props) |
|
1315 { |
|
1316 // Force all native properties to be materialized onto the wrapped native. |
|
1317 AutoIdVector wnProps(cx); |
|
1318 { |
|
1319 RootedObject target(cx, singleton.getTargetObject(wrapper)); |
|
1320 JSAutoCompartment ac(cx, target); |
|
1321 if (!js::GetPropertyNames(cx, target, flags, &wnProps)) |
|
1322 return false; |
|
1323 } |
|
1324 if (!JS_WrapAutoIdVector(cx, wnProps)) |
|
1325 return false; |
|
1326 |
|
1327 // Go through the properties we got and enumerate all native ones. |
|
1328 for (size_t n = 0; n < wnProps.length(); ++n) { |
|
1329 RootedId id(cx, wnProps[n]); |
|
1330 bool hasProp; |
|
1331 if (!JS_HasPropertyById(cx, wrapper, id, &hasProp)) |
|
1332 return false; |
|
1333 if (hasProp) |
|
1334 props.append(id); |
|
1335 } |
|
1336 return true; |
|
1337 } |
|
1338 |
|
1339 JSObject * |
|
1340 XPCWrappedNativeXrayTraits::createHolder(JSContext *cx, JSObject *wrapper) |
|
1341 { |
|
1342 RootedObject global(cx, JS_GetGlobalForObject(cx, wrapper)); |
|
1343 JSObject *holder = JS_NewObjectWithGivenProto(cx, &HolderClass, JS::NullPtr(), |
|
1344 global); |
|
1345 if (!holder) |
|
1346 return nullptr; |
|
1347 |
|
1348 js::SetReservedSlot(holder, JSSLOT_RESOLVING, PrivateValue(nullptr)); |
|
1349 return holder; |
|
1350 } |
|
1351 |
|
1352 bool |
|
1353 XPCWrappedNativeXrayTraits::call(JSContext *cx, HandleObject wrapper, |
|
1354 const JS::CallArgs &args, |
|
1355 js::Wrapper& baseInstance) |
|
1356 { |
|
1357 // Run the resolve hook of the wrapped native. |
|
1358 XPCWrappedNative *wn = getWN(wrapper); |
|
1359 if (NATIVE_HAS_FLAG(wn, WantCall)) { |
|
1360 XPCCallContext ccx(JS_CALLER, cx, wrapper, NullPtr(), JSID_VOIDHANDLE, args.length(), |
|
1361 args.array(), args.rval().address()); |
|
1362 if (!ccx.IsValid()) |
|
1363 return false; |
|
1364 bool ok = true; |
|
1365 nsresult rv = wn->GetScriptableInfo()->GetCallback()->Call( |
|
1366 wn, cx, wrapper, args, &ok); |
|
1367 if (NS_FAILED(rv)) { |
|
1368 if (ok) |
|
1369 XPCThrower::Throw(rv, cx); |
|
1370 return false; |
|
1371 } |
|
1372 } |
|
1373 |
|
1374 return true; |
|
1375 |
|
1376 } |
|
1377 |
|
1378 bool |
|
1379 XPCWrappedNativeXrayTraits::construct(JSContext *cx, HandleObject wrapper, |
|
1380 const JS::CallArgs &args, |
|
1381 js::Wrapper& baseInstance) |
|
1382 { |
|
1383 // Run the resolve hook of the wrapped native. |
|
1384 XPCWrappedNative *wn = getWN(wrapper); |
|
1385 if (NATIVE_HAS_FLAG(wn, WantConstruct)) { |
|
1386 XPCCallContext ccx(JS_CALLER, cx, wrapper, NullPtr(), JSID_VOIDHANDLE, args.length(), |
|
1387 args.array(), args.rval().address()); |
|
1388 if (!ccx.IsValid()) |
|
1389 return false; |
|
1390 bool ok = true; |
|
1391 nsresult rv = wn->GetScriptableInfo()->GetCallback()->Construct( |
|
1392 wn, cx, wrapper, args, &ok); |
|
1393 if (NS_FAILED(rv)) { |
|
1394 if (ok) |
|
1395 XPCThrower::Throw(rv, cx); |
|
1396 return false; |
|
1397 } |
|
1398 } |
|
1399 |
|
1400 return true; |
|
1401 |
|
1402 } |
|
1403 |
|
1404 bool |
|
1405 DOMXrayTraits::resolveNativeProperty(JSContext *cx, HandleObject wrapper, |
|
1406 HandleObject holder, HandleId id, |
|
1407 MutableHandle<JSPropertyDescriptor> desc) |
|
1408 { |
|
1409 RootedObject obj(cx, getTargetObject(wrapper)); |
|
1410 if (!XrayResolveNativeProperty(cx, wrapper, obj, id, desc)) |
|
1411 return false; |
|
1412 |
|
1413 MOZ_ASSERT(!desc.object() || desc.object() == wrapper, "What did we resolve this on?"); |
|
1414 |
|
1415 return true; |
|
1416 } |
|
1417 |
|
1418 bool |
|
1419 DOMXrayTraits::resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper, HandleObject wrapper, |
|
1420 HandleObject holder, HandleId id, |
|
1421 MutableHandle<JSPropertyDescriptor> desc) |
|
1422 { |
|
1423 // Call the common code. |
|
1424 bool ok = XrayTraits::resolveOwnProperty(cx, jsWrapper, wrapper, holder, id, desc); |
|
1425 if (!ok || desc.object()) |
|
1426 return ok; |
|
1427 |
|
1428 // Check for indexed access on a window. |
|
1429 int32_t index = GetArrayIndexFromId(cx, id); |
|
1430 if (IsArrayIndex(index)) { |
|
1431 nsGlobalWindow* win = AsWindow(cx, wrapper); |
|
1432 // Note: As() unwraps outer windows to get to the inner window. |
|
1433 if (win) { |
|
1434 bool unused; |
|
1435 nsCOMPtr<nsIDOMWindow> subframe = win->IndexedGetter(index, unused); |
|
1436 if (subframe) { |
|
1437 nsGlobalWindow* global = static_cast<nsGlobalWindow*>(subframe.get()); |
|
1438 global->EnsureInnerWindow(); |
|
1439 JSObject* obj = global->FastGetGlobalJSObject(); |
|
1440 if (MOZ_UNLIKELY(!obj)) { |
|
1441 // It's gone? |
|
1442 return xpc::Throw(cx, NS_ERROR_FAILURE); |
|
1443 } |
|
1444 desc.value().setObject(*obj); |
|
1445 FillPropertyDescriptor(desc, wrapper, true); |
|
1446 return JS_WrapPropertyDescriptor(cx, desc); |
|
1447 } |
|
1448 } |
|
1449 } |
|
1450 |
|
1451 RootedObject obj(cx, getTargetObject(wrapper)); |
|
1452 if (!XrayResolveOwnProperty(cx, wrapper, obj, id, desc)) |
|
1453 return false; |
|
1454 |
|
1455 MOZ_ASSERT(!desc.object() || desc.object() == wrapper, "What did we resolve this on?"); |
|
1456 |
|
1457 return true; |
|
1458 } |
|
1459 |
|
1460 bool |
|
1461 DOMXrayTraits::defineProperty(JSContext *cx, HandleObject wrapper, HandleId id, |
|
1462 MutableHandle<JSPropertyDescriptor> desc, |
|
1463 Handle<JSPropertyDescriptor> existingDesc, bool *defined) |
|
1464 { |
|
1465 // Check for an indexed property on a Window. If that's happening, do |
|
1466 // nothing but claim we defined it so it won't get added as an expando. |
|
1467 if (IsWindow(cx, wrapper)) { |
|
1468 int32_t index = GetArrayIndexFromId(cx, id); |
|
1469 if (IsArrayIndex(index)) { |
|
1470 *defined = true; |
|
1471 return true; |
|
1472 } |
|
1473 } |
|
1474 |
|
1475 if (!existingDesc.object()) |
|
1476 return true; |
|
1477 |
|
1478 JS::Rooted<JSObject*> obj(cx, getTargetObject(wrapper)); |
|
1479 return XrayDefineProperty(cx, wrapper, obj, id, desc, defined); |
|
1480 } |
|
1481 |
|
1482 bool |
|
1483 DOMXrayTraits::set(JSContext *cx, HandleObject wrapper, HandleObject receiver, HandleId id, |
|
1484 bool strict, MutableHandleValue vp) |
|
1485 { |
|
1486 MOZ_ASSERT(xpc::WrapperFactory::IsXrayWrapper(wrapper)); |
|
1487 RootedObject obj(cx, getTargetObject(wrapper)); |
|
1488 if (IsDOMProxy(obj)) { |
|
1489 DOMProxyHandler* handler = GetDOMProxyHandler(obj); |
|
1490 |
|
1491 bool done; |
|
1492 if (!handler->setCustom(cx, obj, id, vp, &done)) |
|
1493 return false; |
|
1494 if (done) |
|
1495 return true; |
|
1496 } |
|
1497 return XrayTraits::set(cx, wrapper, receiver, id, strict, vp); |
|
1498 } |
|
1499 |
|
1500 bool |
|
1501 DOMXrayTraits::enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags, |
|
1502 AutoIdVector &props) |
|
1503 { |
|
1504 JS::Rooted<JSObject*> obj(cx, getTargetObject(wrapper)); |
|
1505 return XrayEnumerateProperties(cx, wrapper, obj, flags, props); |
|
1506 } |
|
1507 |
|
1508 bool |
|
1509 DOMXrayTraits::call(JSContext *cx, HandleObject wrapper, |
|
1510 const JS::CallArgs &args, js::Wrapper& baseInstance) |
|
1511 { |
|
1512 RootedObject obj(cx, getTargetObject(wrapper)); |
|
1513 const js::Class* clasp = js::GetObjectClass(obj); |
|
1514 // What we have is either a WebIDL interface object, a WebIDL prototype |
|
1515 // object, or a WebIDL instance object. WebIDL prototype objects never have |
|
1516 // a clasp->call. WebIDL interface objects we want to invoke on the xray |
|
1517 // compartment. WebIDL instance objects either don't have a clasp->call or |
|
1518 // are using "legacycaller", which basically means plug-ins. We want to |
|
1519 // call those on the content compartment. |
|
1520 if (clasp->flags & JSCLASS_IS_DOMIFACEANDPROTOJSCLASS) { |
|
1521 if (!clasp->call) { |
|
1522 RootedValue v(cx, ObjectValue(*wrapper)); |
|
1523 js_ReportIsNotFunction(cx, v); |
|
1524 return false; |
|
1525 } |
|
1526 // call it on the Xray compartment |
|
1527 if (!clasp->call(cx, args.length(), args.base())) |
|
1528 return false; |
|
1529 } else { |
|
1530 // This is only reached for WebIDL instance objects, and in practice |
|
1531 // only for plugins. Just call them on the content compartment. |
|
1532 if (!baseInstance.call(cx, wrapper, args)) |
|
1533 return false; |
|
1534 } |
|
1535 return JS_WrapValue(cx, args.rval()); |
|
1536 } |
|
1537 |
|
1538 bool |
|
1539 DOMXrayTraits::construct(JSContext *cx, HandleObject wrapper, |
|
1540 const JS::CallArgs &args, js::Wrapper& baseInstance) |
|
1541 { |
|
1542 RootedObject obj(cx, getTargetObject(wrapper)); |
|
1543 MOZ_ASSERT(mozilla::dom::HasConstructor(obj)); |
|
1544 const js::Class* clasp = js::GetObjectClass(obj); |
|
1545 // See comments in DOMXrayTraits::call() explaining what's going on here. |
|
1546 if (clasp->flags & JSCLASS_IS_DOMIFACEANDPROTOJSCLASS) { |
|
1547 if (!clasp->construct) { |
|
1548 RootedValue v(cx, ObjectValue(*wrapper)); |
|
1549 js_ReportIsNotFunction(cx, v); |
|
1550 return false; |
|
1551 } |
|
1552 if (!clasp->construct(cx, args.length(), args.base())) |
|
1553 return false; |
|
1554 } else { |
|
1555 if (!baseInstance.construct(cx, wrapper, args)) |
|
1556 return false; |
|
1557 } |
|
1558 if (!args.rval().isObject() || !JS_WrapValue(cx, args.rval())) |
|
1559 return false; |
|
1560 return true; |
|
1561 } |
|
1562 |
|
1563 void |
|
1564 DOMXrayTraits::preserveWrapper(JSObject *target) |
|
1565 { |
|
1566 nsISupports *identity = mozilla::dom::UnwrapDOMObjectToISupports(target); |
|
1567 if (!identity) |
|
1568 return; |
|
1569 nsWrapperCache* cache = nullptr; |
|
1570 CallQueryInterface(identity, &cache); |
|
1571 if (cache) |
|
1572 cache->PreserveWrapper(identity); |
|
1573 } |
|
1574 |
|
1575 JSObject* |
|
1576 DOMXrayTraits::createHolder(JSContext *cx, JSObject *wrapper) |
|
1577 { |
|
1578 RootedObject global(cx, JS_GetGlobalForObject(cx, wrapper)); |
|
1579 return JS_NewObjectWithGivenProto(cx, nullptr, JS::NullPtr(), global); |
|
1580 } |
|
1581 |
|
1582 template <typename Base, typename Traits> |
|
1583 XrayWrapper<Base, Traits>::XrayWrapper(unsigned flags) |
|
1584 : Base(flags | WrapperFactory::IS_XRAY_WRAPPER_FLAG) |
|
1585 { |
|
1586 Base::setHasPrototype(Traits::HasPrototype); |
|
1587 } |
|
1588 |
|
1589 template <typename Base, typename Traits> |
|
1590 XrayWrapper<Base, Traits>::~XrayWrapper() |
|
1591 { |
|
1592 } |
|
1593 |
|
1594 namespace XrayUtils { |
|
1595 |
|
1596 JSObject * |
|
1597 GetNativePropertiesObject(JSContext *cx, JSObject *wrapper) |
|
1598 { |
|
1599 MOZ_ASSERT(js::IsWrapper(wrapper) && WrapperFactory::IsXrayWrapper(wrapper), |
|
1600 "bad object passed in"); |
|
1601 |
|
1602 JSObject *holder = GetHolder(wrapper); |
|
1603 MOZ_ASSERT(holder, "uninitialized wrapper being used?"); |
|
1604 return holder; |
|
1605 } |
|
1606 |
|
1607 bool |
|
1608 IsXrayResolving(JSContext *cx, HandleObject wrapper, HandleId id) |
|
1609 { |
|
1610 if (!WrapperFactory::IsXrayWrapper(wrapper) || |
|
1611 GetXrayType(wrapper) != XrayForWrappedNative) |
|
1612 { |
|
1613 return false; |
|
1614 } |
|
1615 JSObject *holder = |
|
1616 XPCWrappedNativeXrayTraits::singleton.ensureHolder(cx, wrapper); |
|
1617 return XPCWrappedNativeXrayTraits::isResolving(cx, holder, id); |
|
1618 } |
|
1619 |
|
1620 bool |
|
1621 HasNativeProperty(JSContext *cx, HandleObject wrapper, HandleId id, bool *hasProp) |
|
1622 { |
|
1623 MOZ_ASSERT(WrapperFactory::IsXrayWrapper(wrapper)); |
|
1624 XrayTraits *traits = GetXrayTraits(wrapper); |
|
1625 MOZ_ASSERT(traits); |
|
1626 RootedObject holder(cx, traits->ensureHolder(cx, wrapper)); |
|
1627 NS_ENSURE_TRUE(holder, false); |
|
1628 *hasProp = false; |
|
1629 Rooted<JSPropertyDescriptor> desc(cx); |
|
1630 Wrapper *handler = Wrapper::wrapperHandler(wrapper); |
|
1631 |
|
1632 // Try resolveOwnProperty. |
|
1633 Maybe<ResolvingId> resolvingId; |
|
1634 if (traits == &XPCWrappedNativeXrayTraits::singleton) |
|
1635 resolvingId.construct(cx, wrapper, id); |
|
1636 if (!traits->resolveOwnProperty(cx, *handler, wrapper, holder, id, &desc)) |
|
1637 return false; |
|
1638 if (desc.object()) { |
|
1639 *hasProp = true; |
|
1640 return true; |
|
1641 } |
|
1642 |
|
1643 // Try the holder. |
|
1644 bool found = false; |
|
1645 if (!JS_AlreadyHasOwnPropertyById(cx, holder, id, &found)) |
|
1646 return false; |
|
1647 if (found) { |
|
1648 *hasProp = true; |
|
1649 return true; |
|
1650 } |
|
1651 |
|
1652 // Try resolveNativeProperty. |
|
1653 if (!traits->resolveNativeProperty(cx, wrapper, holder, id, &desc)) |
|
1654 return false; |
|
1655 *hasProp = !!desc.object(); |
|
1656 return true; |
|
1657 } |
|
1658 |
|
1659 } // namespace XrayUtils |
|
1660 |
|
1661 static bool |
|
1662 XrayToString(JSContext *cx, unsigned argc, Value *vp) |
|
1663 { |
|
1664 CallArgs args = CallArgsFromVp(argc, vp); |
|
1665 |
|
1666 if (!args.thisv().isObject()) { |
|
1667 JS_ReportError(cx, "XrayToString called on an incompatible object"); |
|
1668 return false; |
|
1669 } |
|
1670 |
|
1671 RootedObject wrapper(cx, &args.thisv().toObject()); |
|
1672 if (!wrapper) |
|
1673 return false; |
|
1674 if (IsWrapper(wrapper) && |
|
1675 GetProxyHandler(wrapper) == &sandboxCallableProxyHandler) { |
|
1676 wrapper = xpc::SandboxCallableProxyHandler::wrappedObject(wrapper); |
|
1677 } |
|
1678 if (!IsWrapper(wrapper) || !WrapperFactory::IsXrayWrapper(wrapper)) { |
|
1679 JS_ReportError(cx, "XrayToString called on an incompatible object"); |
|
1680 return false; |
|
1681 } |
|
1682 |
|
1683 static const char start[] = "[object XrayWrapper "; |
|
1684 static const char end[] = "]"; |
|
1685 |
|
1686 RootedObject obj(cx, XrayTraits::getTargetObject(wrapper)); |
|
1687 XrayType type = GetXrayType(obj); |
|
1688 if (type == XrayForDOMObject) |
|
1689 return NativeToString(cx, wrapper, obj, start, end, args.rval()); |
|
1690 |
|
1691 if (type != XrayForWrappedNative) { |
|
1692 JS_ReportError(cx, "XrayToString called on an incompatible object"); |
|
1693 return false; |
|
1694 } |
|
1695 |
|
1696 nsAutoString result; |
|
1697 result.AppendASCII(start); |
|
1698 |
|
1699 XPCCallContext ccx(JS_CALLER, cx, obj); |
|
1700 XPCWrappedNative *wn = XPCWrappedNativeXrayTraits::getWN(wrapper); |
|
1701 char *wrapperStr = wn->ToString(); |
|
1702 if (!wrapperStr) { |
|
1703 JS_ReportOutOfMemory(cx); |
|
1704 return false; |
|
1705 } |
|
1706 result.AppendASCII(wrapperStr); |
|
1707 JS_smprintf_free(wrapperStr); |
|
1708 |
|
1709 result.AppendASCII(end); |
|
1710 |
|
1711 JSString *str = JS_NewUCStringCopyN(cx, result.get(), result.Length()); |
|
1712 if (!str) |
|
1713 return false; |
|
1714 |
|
1715 args.rval().setString(str); |
|
1716 return true; |
|
1717 } |
|
1718 |
|
1719 #ifdef DEBUG |
|
1720 |
|
1721 static void |
|
1722 DEBUG_CheckXBLCallable(JSContext *cx, JSObject *obj) |
|
1723 { |
|
1724 // In general, we shouldn't have cross-compartment wrappers here, because |
|
1725 // we should be running in an XBL scope, and the content prototype should |
|
1726 // contain wrappers to functions defined in the XBL scope. But if the node |
|
1727 // has been adopted into another compartment, those prototypes will now point |
|
1728 // to a different XBL scope (which is ok). |
|
1729 MOZ_ASSERT_IF(js::IsCrossCompartmentWrapper(obj), |
|
1730 xpc::IsXBLScope(js::GetObjectCompartment(js::UncheckedUnwrap(obj)))); |
|
1731 MOZ_ASSERT(JS_ObjectIsCallable(cx, obj)); |
|
1732 } |
|
1733 |
|
1734 static void |
|
1735 DEBUG_CheckXBLLookup(JSContext *cx, JSPropertyDescriptor *desc) |
|
1736 { |
|
1737 if (!desc->obj) |
|
1738 return; |
|
1739 if (!desc->value.isUndefined()) { |
|
1740 MOZ_ASSERT(desc->value.isObject()); |
|
1741 DEBUG_CheckXBLCallable(cx, &desc->value.toObject()); |
|
1742 } |
|
1743 if (desc->getter) { |
|
1744 MOZ_ASSERT(desc->attrs & JSPROP_GETTER); |
|
1745 DEBUG_CheckXBLCallable(cx, JS_FUNC_TO_DATA_PTR(JSObject *, desc->getter)); |
|
1746 } |
|
1747 if (desc->setter) { |
|
1748 MOZ_ASSERT(desc->attrs & JSPROP_SETTER); |
|
1749 DEBUG_CheckXBLCallable(cx, JS_FUNC_TO_DATA_PTR(JSObject *, desc->setter)); |
|
1750 } |
|
1751 } |
|
1752 #else |
|
1753 #define DEBUG_CheckXBLLookup(a, b) {} |
|
1754 #endif |
|
1755 |
|
1756 template <typename Base, typename Traits> |
|
1757 bool |
|
1758 XrayWrapper<Base, Traits>::isExtensible(JSContext *cx, JS::Handle<JSObject*> wrapper, bool *extensible) |
|
1759 { |
|
1760 // Xray wrappers are supposed to provide a clean view of the target |
|
1761 // reflector, hiding any modifications by script in the target scope. So |
|
1762 // even if that script freezes the reflector, we don't want to make that |
|
1763 // visible to the caller. DOM reflectors are always extensible by default, |
|
1764 // so we can just return true here. |
|
1765 *extensible = true; |
|
1766 return true; |
|
1767 } |
|
1768 |
|
1769 template <typename Base, typename Traits> |
|
1770 bool |
|
1771 XrayWrapper<Base, Traits>::preventExtensions(JSContext *cx, HandleObject wrapper) |
|
1772 { |
|
1773 // See above. |
|
1774 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CHANGE_EXTENSIBILITY); |
|
1775 return false; |
|
1776 } |
|
1777 |
|
1778 template <typename Base, typename Traits> |
|
1779 bool |
|
1780 XrayWrapper<Base, Traits>::getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, |
|
1781 JS::MutableHandle<JSPropertyDescriptor> desc) |
|
1782 { |
|
1783 assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET); |
|
1784 RootedObject holder(cx, Traits::singleton.ensureHolder(cx, wrapper)); |
|
1785 if (Traits::isResolving(cx, holder, id)) { |
|
1786 desc.object().set(nullptr); |
|
1787 return true; |
|
1788 } |
|
1789 |
|
1790 typename Traits::ResolvingIdImpl resolving(cx, wrapper, id); |
|
1791 |
|
1792 if (!holder) |
|
1793 return false; |
|
1794 |
|
1795 // Ordering is important here. |
|
1796 // |
|
1797 // We first need to call resolveOwnProperty, even before checking the holder, |
|
1798 // because there might be a new dynamic |own| property that appears and |
|
1799 // shadows a previously-resolved non-own property that we cached on the |
|
1800 // holder. This can happen with indexed properties on NodeLists, for example, |
|
1801 // which are |own| value props. |
|
1802 // |
|
1803 // resolveOwnProperty may or may not cache what it finds on the holder, |
|
1804 // depending on how ephemeral it decides the property is. XPCWN |own| |
|
1805 // properties generally end up on the holder via NewResolve, whereas |
|
1806 // NodeList |own| properties don't get defined on the holder, since they're |
|
1807 // supposed to be dynamic. This means that we have to first check the result |
|
1808 // of resolveOwnProperty, and _then_, if that comes up blank, check the |
|
1809 // holder for any cached native properties. |
|
1810 // |
|
1811 // Finally, we call resolveNativeProperty, which checks non-own properties, |
|
1812 // and unconditionally caches what it finds on the holder. |
|
1813 |
|
1814 // Check resolveOwnProperty. |
|
1815 if (!Traits::singleton.resolveOwnProperty(cx, *this, wrapper, holder, id, desc)) |
|
1816 return false; |
|
1817 |
|
1818 // Check the holder. |
|
1819 if (!desc.object() && !JS_GetPropertyDescriptorById(cx, holder, id, desc)) |
|
1820 return false; |
|
1821 if (desc.object()) { |
|
1822 desc.object().set(wrapper); |
|
1823 return true; |
|
1824 } |
|
1825 |
|
1826 // Nothing in the cache. Call through, and cache the result. |
|
1827 if (!Traits::singleton.resolveNativeProperty(cx, wrapper, holder, id, desc)) |
|
1828 return false; |
|
1829 |
|
1830 // We need to handle named access on the Window somewhere other than |
|
1831 // Traits::resolveOwnProperty, because per spec it happens on the Global |
|
1832 // Scope Polluter and thus the resulting properties are non-|own|. However, |
|
1833 // we're set up (above) to cache (on the holder) anything that comes out of |
|
1834 // resolveNativeProperty, which we don't want for something dynamic like |
|
1835 // named access. So we just handle it separately here. |
|
1836 nsGlobalWindow *win = nullptr; |
|
1837 if (!desc.object() && |
|
1838 JSID_IS_STRING(id) && |
|
1839 (win = AsWindow(cx, wrapper))) |
|
1840 { |
|
1841 nsDependentJSString name(id); |
|
1842 nsCOMPtr<nsIDOMWindow> childDOMWin = win->GetChildWindow(name); |
|
1843 if (childDOMWin) { |
|
1844 nsGlobalWindow *cwin = static_cast<nsGlobalWindow*>(childDOMWin.get()); |
|
1845 JSObject *childObj = cwin->FastGetGlobalJSObject(); |
|
1846 if (MOZ_UNLIKELY(!childObj)) |
|
1847 return xpc::Throw(cx, NS_ERROR_FAILURE); |
|
1848 FillPropertyDescriptor(desc, wrapper, ObjectValue(*childObj), |
|
1849 /* readOnly = */ true); |
|
1850 return JS_WrapPropertyDescriptor(cx, desc); |
|
1851 } |
|
1852 } |
|
1853 |
|
1854 if (!desc.object() && |
|
1855 id == nsXPConnect::GetRuntimeInstance()->GetStringID(XPCJSRuntime::IDX_TO_STRING)) |
|
1856 { |
|
1857 |
|
1858 JSFunction *toString = JS_NewFunction(cx, XrayToString, 0, 0, wrapper, "toString"); |
|
1859 if (!toString) |
|
1860 return false; |
|
1861 |
|
1862 desc.object().set(wrapper); |
|
1863 desc.setAttributes(0); |
|
1864 desc.setGetter(nullptr); |
|
1865 desc.setSetter(nullptr); |
|
1866 desc.value().setObject(*JS_GetFunctionObject(toString)); |
|
1867 } |
|
1868 |
|
1869 // If we're a special scope for in-content XBL, our script expects to see |
|
1870 // the bound XBL methods and attributes when accessing content. However, |
|
1871 // these members are implemented in content via custom-spliced prototypes, |
|
1872 // and thus aren't visible through Xray wrappers unless we handle them |
|
1873 // explicitly. So we check if we're running in such a scope, and if so, |
|
1874 // whether the wrappee is a bound element. If it is, we do a lookup via |
|
1875 // specialized XBL machinery. |
|
1876 // |
|
1877 // While we have to do some sketchy walking through content land, we should |
|
1878 // be protected by read-only/non-configurable properties, and any functions |
|
1879 // we end up with should _always_ be living in an XBL scope (usually ours, |
|
1880 // but could be another if the node has been adopted). |
|
1881 // |
|
1882 // Make sure to assert this. |
|
1883 nsCOMPtr<nsIContent> content; |
|
1884 if (!desc.object() && |
|
1885 EnsureCompartmentPrivate(wrapper)->scope->IsXBLScope() && |
|
1886 (content = do_QueryInterfaceNative(cx, wrapper))) |
|
1887 { |
|
1888 if (!nsContentUtils::LookupBindingMember(cx, content, id, desc)) |
|
1889 return false; |
|
1890 DEBUG_CheckXBLLookup(cx, desc.address()); |
|
1891 } |
|
1892 |
|
1893 // If we still have nothing, we're done. |
|
1894 if (!desc.object()) |
|
1895 return true; |
|
1896 |
|
1897 if (!JS_DefinePropertyById(cx, holder, id, desc.value(), desc.getter(), |
|
1898 desc.setter(), desc.attributes()) || |
|
1899 !JS_GetPropertyDescriptorById(cx, holder, id, desc)) |
|
1900 { |
|
1901 return false; |
|
1902 } |
|
1903 MOZ_ASSERT(desc.object()); |
|
1904 desc.object().set(wrapper); |
|
1905 return true; |
|
1906 } |
|
1907 |
|
1908 template <typename Base, typename Traits> |
|
1909 bool |
|
1910 XrayWrapper<Base, Traits>::getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id, |
|
1911 JS::MutableHandle<JSPropertyDescriptor> desc) |
|
1912 { |
|
1913 assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET); |
|
1914 RootedObject holder(cx, Traits::singleton.ensureHolder(cx, wrapper)); |
|
1915 if (Traits::isResolving(cx, holder, id)) { |
|
1916 desc.object().set(nullptr); |
|
1917 return true; |
|
1918 } |
|
1919 |
|
1920 typename Traits::ResolvingIdImpl resolving(cx, wrapper, id); |
|
1921 |
|
1922 // NB: Nothing we do here acts on the wrapped native itself, so we don't |
|
1923 // enter our policy. |
|
1924 |
|
1925 if (!Traits::singleton.resolveOwnProperty(cx, *this, wrapper, holder, id, desc)) |
|
1926 return false; |
|
1927 if (desc.object()) |
|
1928 desc.object().set(wrapper); |
|
1929 return true; |
|
1930 } |
|
1931 |
|
1932 // Consider what happens when chrome does |xray.expando = xray.wrappedJSObject|. |
|
1933 // |
|
1934 // Since the expando comes from the target compartment, wrapping it back into |
|
1935 // the target compartment to define it on the expando object ends up stripping |
|
1936 // off the Xray waiver that gives |xray| and |xray.wrappedJSObject| different |
|
1937 // identities. This is generally the right thing to do when wrapping across |
|
1938 // compartments, but is incorrect in the special case of the Xray expando |
|
1939 // object. Manually re-apply Xrays if necessary. |
|
1940 // |
|
1941 // NB: In order to satisfy the invariants of WaiveXray, we need to pass |
|
1942 // in an object sans security wrapper, which means we need to strip off any |
|
1943 // potential same-compartment security wrapper that may have been applied |
|
1944 // to the content object. This is ok, because the the expando object is only |
|
1945 // ever accessed by code across the compartment boundary. |
|
1946 static bool |
|
1947 RecreateLostWaivers(JSContext *cx, JSPropertyDescriptor *orig, |
|
1948 MutableHandle<JSPropertyDescriptor> wrapped) |
|
1949 { |
|
1950 // Compute whether the original objects were waived, and implicitly, whether |
|
1951 // they were objects at all. |
|
1952 bool valueWasWaived = |
|
1953 orig->value.isObject() && |
|
1954 WrapperFactory::HasWaiveXrayFlag(&orig->value.toObject()); |
|
1955 bool getterWasWaived = |
|
1956 (orig->attrs & JSPROP_GETTER) && |
|
1957 WrapperFactory::HasWaiveXrayFlag(JS_FUNC_TO_DATA_PTR(JSObject*, orig->getter)); |
|
1958 bool setterWasWaived = |
|
1959 (orig->attrs & JSPROP_SETTER) && |
|
1960 WrapperFactory::HasWaiveXrayFlag(JS_FUNC_TO_DATA_PTR(JSObject*, orig->setter)); |
|
1961 |
|
1962 // Recreate waivers. Note that for value, we need an extra UncheckedUnwrap |
|
1963 // to handle same-compartment security wrappers (see above). This should |
|
1964 // never happen for getters/setters. |
|
1965 |
|
1966 RootedObject rewaived(cx); |
|
1967 if (valueWasWaived && !IsCrossCompartmentWrapper(&wrapped.value().toObject())) { |
|
1968 rewaived = &wrapped.value().toObject(); |
|
1969 rewaived = WrapperFactory::WaiveXray(cx, UncheckedUnwrap(rewaived)); |
|
1970 NS_ENSURE_TRUE(rewaived, false); |
|
1971 wrapped.value().set(ObjectValue(*rewaived)); |
|
1972 } |
|
1973 if (getterWasWaived && !IsCrossCompartmentWrapper(wrapped.getterObject())) { |
|
1974 MOZ_ASSERT(CheckedUnwrap(wrapped.getterObject())); |
|
1975 rewaived = WrapperFactory::WaiveXray(cx, wrapped.getterObject()); |
|
1976 NS_ENSURE_TRUE(rewaived, false); |
|
1977 wrapped.setGetterObject(rewaived); |
|
1978 } |
|
1979 if (setterWasWaived && !IsCrossCompartmentWrapper(wrapped.setterObject())) { |
|
1980 MOZ_ASSERT(CheckedUnwrap(wrapped.setterObject())); |
|
1981 rewaived = WrapperFactory::WaiveXray(cx, wrapped.setterObject()); |
|
1982 NS_ENSURE_TRUE(rewaived, false); |
|
1983 wrapped.setSetterObject(rewaived); |
|
1984 } |
|
1985 |
|
1986 return true; |
|
1987 } |
|
1988 |
|
1989 template <typename Base, typename Traits> |
|
1990 bool |
|
1991 XrayWrapper<Base, Traits>::defineProperty(JSContext *cx, HandleObject wrapper, |
|
1992 HandleId id, MutableHandle<JSPropertyDescriptor> desc) |
|
1993 { |
|
1994 assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::SET); |
|
1995 |
|
1996 Rooted<JSPropertyDescriptor> existing_desc(cx); |
|
1997 if (!getOwnPropertyDescriptor(cx, wrapper, id, &existing_desc)) |
|
1998 return false; |
|
1999 |
|
2000 if (existing_desc.object() && existing_desc.isPermanent()) |
|
2001 return true; // silently ignore attempt to overwrite native property |
|
2002 |
|
2003 bool defined = false; |
|
2004 if (!Traits::defineProperty(cx, wrapper, id, desc, existing_desc, &defined)) |
|
2005 return false; |
|
2006 if (defined) |
|
2007 return true; |
|
2008 |
|
2009 // We're placing an expando. The expando objects live in the target |
|
2010 // compartment, so we need to enter it. |
|
2011 RootedObject target(cx, Traits::singleton.getTargetObject(wrapper)); |
|
2012 JSAutoCompartment ac(cx, target); |
|
2013 |
|
2014 // Grab the relevant expando object. |
|
2015 RootedObject expandoObject(cx, Traits::singleton.ensureExpandoObject(cx, wrapper, |
|
2016 target)); |
|
2017 if (!expandoObject) |
|
2018 return false; |
|
2019 |
|
2020 // Wrap the property descriptor for the target compartment. |
|
2021 Rooted<JSPropertyDescriptor> wrappedDesc(cx, desc); |
|
2022 if (!JS_WrapPropertyDescriptor(cx, &wrappedDesc)) |
|
2023 return false; |
|
2024 |
|
2025 // Fix up Xray waivers. |
|
2026 if (!RecreateLostWaivers(cx, desc.address(), &wrappedDesc)) |
|
2027 return false; |
|
2028 |
|
2029 return JS_DefinePropertyById(cx, expandoObject, id, wrappedDesc.value(), |
|
2030 wrappedDesc.getter(), wrappedDesc.setter(), |
|
2031 wrappedDesc.get().attrs); |
|
2032 } |
|
2033 |
|
2034 template <typename Base, typename Traits> |
|
2035 bool |
|
2036 XrayWrapper<Base, Traits>::getOwnPropertyNames(JSContext *cx, HandleObject wrapper, |
|
2037 AutoIdVector &props) |
|
2038 { |
|
2039 assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE); |
|
2040 return enumerate(cx, wrapper, JSITER_OWNONLY | JSITER_HIDDEN, props); |
|
2041 } |
|
2042 |
|
2043 template <typename Base, typename Traits> |
|
2044 bool |
|
2045 XrayWrapper<Base, Traits>::delete_(JSContext *cx, HandleObject wrapper, |
|
2046 HandleId id, bool *bp) |
|
2047 { |
|
2048 assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::SET); |
|
2049 |
|
2050 // Check the expando object. |
|
2051 RootedObject target(cx, Traits::getTargetObject(wrapper)); |
|
2052 RootedObject expando(cx, Traits::singleton.getExpandoObject(cx, target, wrapper)); |
|
2053 if (expando) { |
|
2054 JSAutoCompartment ac(cx, expando); |
|
2055 return JS_DeletePropertyById2(cx, expando, id, bp); |
|
2056 } |
|
2057 *bp = true; |
|
2058 return true; |
|
2059 } |
|
2060 |
|
2061 template <typename Base, typename Traits> |
|
2062 bool |
|
2063 XrayWrapper<Base, Traits>::enumerate(JSContext *cx, HandleObject wrapper, unsigned flags, |
|
2064 AutoIdVector &props) |
|
2065 { |
|
2066 assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE); |
|
2067 if (!AccessCheck::wrapperSubsumes(wrapper)) { |
|
2068 JS_ReportError(cx, "Not allowed to enumerate cross origin objects"); |
|
2069 return false; |
|
2070 } |
|
2071 |
|
2072 // Enumerate expando properties first. Note that the expando object lives |
|
2073 // in the target compartment. |
|
2074 RootedObject target(cx, Traits::singleton.getTargetObject(wrapper)); |
|
2075 RootedObject expando(cx, Traits::singleton.getExpandoObject(cx, target, wrapper)); |
|
2076 if (expando) { |
|
2077 JSAutoCompartment ac(cx, expando); |
|
2078 if (!js::GetPropertyNames(cx, expando, flags, &props)) |
|
2079 return false; |
|
2080 } |
|
2081 if (!JS_WrapAutoIdVector(cx, props)) |
|
2082 return false; |
|
2083 |
|
2084 return Traits::singleton.enumerateNames(cx, wrapper, flags, props); |
|
2085 } |
|
2086 |
|
2087 template <typename Base, typename Traits> |
|
2088 bool |
|
2089 XrayWrapper<Base, Traits>::enumerate(JSContext *cx, HandleObject wrapper, |
|
2090 AutoIdVector &props) |
|
2091 { |
|
2092 return enumerate(cx, wrapper, 0, props); |
|
2093 } |
|
2094 |
|
2095 template <typename Base, typename Traits> |
|
2096 bool |
|
2097 XrayWrapper<Base, Traits>::get(JSContext *cx, HandleObject wrapper, |
|
2098 HandleObject receiver, HandleId id, |
|
2099 MutableHandleValue vp) |
|
2100 { |
|
2101 // Skip our Base if it isn't already ProxyHandler. |
|
2102 // NB: None of the functions we call are prepared for the receiver not |
|
2103 // being the wrapper, so ignore the receiver here. |
|
2104 return js::BaseProxyHandler::get(cx, wrapper, Traits::HasPrototype ? receiver : wrapper, id, vp); |
|
2105 } |
|
2106 |
|
2107 template <typename Base, typename Traits> |
|
2108 bool |
|
2109 XrayWrapper<Base, Traits>::set(JSContext *cx, HandleObject wrapper, |
|
2110 HandleObject receiver, HandleId id, |
|
2111 bool strict, MutableHandleValue vp) |
|
2112 { |
|
2113 // Delegate to Traits. |
|
2114 // NB: None of the functions we call are prepared for the receiver not |
|
2115 // being the wrapper, so ignore the receiver here. |
|
2116 return Traits::set(cx, wrapper, Traits::HasPrototype ? receiver : wrapper, id, strict, vp); |
|
2117 } |
|
2118 |
|
2119 template <typename Base, typename Traits> |
|
2120 bool |
|
2121 XrayWrapper<Base, Traits>::has(JSContext *cx, HandleObject wrapper, |
|
2122 HandleId id, bool *bp) |
|
2123 { |
|
2124 // Skip our Base if it isn't already ProxyHandler. |
|
2125 return js::BaseProxyHandler::has(cx, wrapper, id, bp); |
|
2126 } |
|
2127 |
|
2128 template <typename Base, typename Traits> |
|
2129 bool |
|
2130 XrayWrapper<Base, Traits>::hasOwn(JSContext *cx, HandleObject wrapper, |
|
2131 HandleId id, bool *bp) |
|
2132 { |
|
2133 // Skip our Base if it isn't already ProxyHandler. |
|
2134 return js::BaseProxyHandler::hasOwn(cx, wrapper, id, bp); |
|
2135 } |
|
2136 |
|
2137 template <typename Base, typename Traits> |
|
2138 bool |
|
2139 XrayWrapper<Base, Traits>::keys(JSContext *cx, HandleObject wrapper, |
|
2140 AutoIdVector &props) |
|
2141 { |
|
2142 // Skip our Base if it isn't already ProxyHandler. |
|
2143 return js::BaseProxyHandler::keys(cx, wrapper, props); |
|
2144 } |
|
2145 |
|
2146 template <typename Base, typename Traits> |
|
2147 bool |
|
2148 XrayWrapper<Base, Traits>::iterate(JSContext *cx, HandleObject wrapper, |
|
2149 unsigned flags, MutableHandleValue vp) |
|
2150 { |
|
2151 // Skip our Base if it isn't already ProxyHandler. |
|
2152 return js::BaseProxyHandler::iterate(cx, wrapper, flags, vp); |
|
2153 } |
|
2154 |
|
2155 template <typename Base, typename Traits> |
|
2156 bool |
|
2157 XrayWrapper<Base, Traits>::call(JSContext *cx, HandleObject wrapper, const JS::CallArgs &args) |
|
2158 { |
|
2159 assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::CALL); |
|
2160 return Traits::call(cx, wrapper, args, Base::singleton); |
|
2161 } |
|
2162 |
|
2163 template <typename Base, typename Traits> |
|
2164 bool |
|
2165 XrayWrapper<Base, Traits>::construct(JSContext *cx, HandleObject wrapper, const JS::CallArgs &args) |
|
2166 { |
|
2167 assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::CALL); |
|
2168 return Traits::construct(cx, wrapper, args, Base::singleton); |
|
2169 } |
|
2170 |
|
2171 template <typename Base, typename Traits> |
|
2172 bool |
|
2173 XrayWrapper<Base, Traits>::defaultValue(JSContext *cx, HandleObject wrapper, |
|
2174 JSType hint, MutableHandleValue vp) |
|
2175 { |
|
2176 // Even if this isn't a security wrapper, Xray semantics dictate that we |
|
2177 // run the DefaultValue algorithm directly on the Xray wrapper. |
|
2178 // |
|
2179 // NB: We don't have to worry about things with special [[DefaultValue]] |
|
2180 // behavior like Date because we'll never have an XrayWrapper to them. |
|
2181 return js::DefaultValue(cx, wrapper, hint, vp); |
|
2182 } |
|
2183 |
|
2184 template <typename Base, typename Traits> |
|
2185 bool |
|
2186 XrayWrapper<Base, Traits>::getPrototypeOf(JSContext *cx, JS::HandleObject wrapper, |
|
2187 JS::MutableHandleObject protop) |
|
2188 { |
|
2189 // We really only want this override for non-SecurityWrapper-inheriting |
|
2190 // |Base|. But doing that statically with templates requires partial method |
|
2191 // specializations (and therefore a helper class), which is all more trouble |
|
2192 // than it's worth. Do a dynamic check. |
|
2193 if (Base::hasSecurityPolicy()) |
|
2194 return Base::getPrototypeOf(cx, wrapper, protop); |
|
2195 |
|
2196 RootedObject target(cx, Traits::getTargetObject(wrapper)); |
|
2197 RootedObject expando(cx, Traits::singleton.getExpandoObject(cx, target, wrapper)); |
|
2198 |
|
2199 // We want to keep the Xray's prototype distinct from that of content, but |
|
2200 // only if there's been a set. If there's not an expando, or the expando |
|
2201 // slot is |undefined|, hand back the default proto, appropriately wrapped. |
|
2202 |
|
2203 RootedValue v(cx); |
|
2204 if (expando) { |
|
2205 JSAutoCompartment ac(cx, expando); |
|
2206 v = JS_GetReservedSlot(expando, JSSLOT_EXPANDO_PROTOTYPE); |
|
2207 } |
|
2208 if (v.isUndefined()) |
|
2209 return getPrototypeOfHelper(cx, wrapper, target, protop); |
|
2210 |
|
2211 protop.set(v.toObjectOrNull()); |
|
2212 return JS_WrapObject(cx, protop); |
|
2213 } |
|
2214 |
|
2215 template <typename Base, typename Traits> |
|
2216 bool |
|
2217 XrayWrapper<Base, Traits>::setPrototypeOf(JSContext *cx, JS::HandleObject wrapper, |
|
2218 JS::HandleObject proto, bool *bp) |
|
2219 { |
|
2220 // Do this only for non-SecurityWrapper-inheriting |Base|. See the comment |
|
2221 // in getPrototypeOf(). |
|
2222 if (Base::hasSecurityPolicy()) |
|
2223 return Base::setPrototypeOf(cx, wrapper, proto, bp); |
|
2224 |
|
2225 RootedObject target(cx, Traits::getTargetObject(wrapper)); |
|
2226 RootedObject expando(cx, Traits::singleton.ensureExpandoObject(cx, wrapper, target)); |
|
2227 |
|
2228 // The expando lives in the target's compartment, so do our installation there. |
|
2229 JSAutoCompartment ac(cx, target); |
|
2230 |
|
2231 RootedValue v(cx, ObjectOrNullValue(proto)); |
|
2232 if (!JS_WrapValue(cx, &v)) |
|
2233 return false; |
|
2234 JS_SetReservedSlot(expando, JSSLOT_EXPANDO_PROTOTYPE, v); |
|
2235 *bp = true; |
|
2236 return true; |
|
2237 } |
|
2238 |
|
2239 |
|
2240 /* |
|
2241 * The Permissive / Security variants should be used depending on whether the |
|
2242 * compartment of the wrapper is guranteed to subsume the compartment of the |
|
2243 * wrapped object (i.e. - whether it is safe from a security perspective to |
|
2244 * unwrap the wrapper). |
|
2245 */ |
|
2246 |
|
2247 template<> |
|
2248 PermissiveXrayXPCWN PermissiveXrayXPCWN::singleton(0); |
|
2249 template class PermissiveXrayXPCWN; |
|
2250 |
|
2251 template<> |
|
2252 SecurityXrayXPCWN SecurityXrayXPCWN::singleton(0); |
|
2253 template class SecurityXrayXPCWN; |
|
2254 |
|
2255 template<> |
|
2256 PermissiveXrayDOM PermissiveXrayDOM::singleton(0); |
|
2257 template class PermissiveXrayDOM; |
|
2258 |
|
2259 template<> |
|
2260 SecurityXrayDOM SecurityXrayDOM::singleton(0); |
|
2261 template class SecurityXrayDOM; |
|
2262 |
|
2263 template<> |
|
2264 PermissiveXrayJS PermissiveXrayJS::singleton(0); |
|
2265 template class PermissiveXrayJS; |
|
2266 |
|
2267 template<> |
|
2268 SCSecurityXrayXPCWN SCSecurityXrayXPCWN::singleton(0); |
|
2269 template class SCSecurityXrayXPCWN; |
|
2270 |
|
2271 static nsQueryInterface |
|
2272 do_QueryInterfaceNative(JSContext* cx, HandleObject wrapper) |
|
2273 { |
|
2274 nsISupports* nativeSupports = nullptr; |
|
2275 if (IsWrapper(wrapper) && WrapperFactory::IsXrayWrapper(wrapper)) { |
|
2276 RootedObject target(cx, XrayTraits::getTargetObject(wrapper)); |
|
2277 XrayType type = GetXrayType(target); |
|
2278 if (type == XrayForDOMObject) { |
|
2279 nativeSupports = UnwrapDOMObjectToISupports(target); |
|
2280 } else if (type == XrayForWrappedNative) { |
|
2281 XPCWrappedNative *wn = XPCWrappedNative::Get(target); |
|
2282 nativeSupports = wn->Native(); |
|
2283 } |
|
2284 } else { |
|
2285 nsIXPConnect *xpc = nsXPConnect::XPConnect(); |
|
2286 nativeSupports = xpc->GetNativeOfWrapper(cx, wrapper); |
|
2287 } |
|
2288 |
|
2289 return nsQueryInterface(nativeSupports); |
|
2290 } |
|
2291 |
|
2292 } |