js/src/jswrapper.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:c20ae2c88d99
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 "jswrapper.h"
8
9 #include "jscntxt.h"
10 #include "jscompartment.h"
11 #include "jsexn.h"
12 #include "jsgc.h"
13 #include "jsiter.h"
14
15 #include "vm/ErrorObject.h"
16 #include "vm/WrapperObject.h"
17
18 #include "jsobjinlines.h"
19
20 using namespace js;
21 using namespace js::gc;
22
23 const char js::sWrapperFamily = 0;
24
25 /*
26 * Wrapper forwards this call directly to the wrapped object for efficiency
27 * and transparency. In particular, the hint is needed to properly stringify
28 * Date objects in certain cases - see bug 646129. Note also the
29 * SecurityWrapper overrides this trap to avoid information leaks. See bug
30 * 720619.
31 */
32 bool
33 Wrapper::defaultValue(JSContext *cx, HandleObject proxy, JSType hint, MutableHandleValue vp)
34 {
35 vp.set(ObjectValue(*proxy->as<ProxyObject>().target()));
36 if (hint == JSTYPE_VOID)
37 return ToPrimitive(cx, vp);
38 return ToPrimitive(cx, hint, vp);
39 }
40
41 JSObject *
42 Wrapper::New(JSContext *cx, JSObject *obj, JSObject *parent, Wrapper *handler,
43 const WrapperOptions *options)
44 {
45 JS_ASSERT(parent);
46
47 AutoMarkInDeadZone amd(cx->zone());
48
49 RootedValue priv(cx, ObjectValue(*obj));
50 mozilla::Maybe<WrapperOptions> opts;
51 if (!options) {
52 opts.construct();
53 opts.ref().selectDefaultClass(obj->isCallable());
54 options = opts.addr();
55 }
56 return NewProxyObject(cx, handler, priv, options->proto(), parent, *options);
57 }
58
59 JSObject *
60 Wrapper::Renew(JSContext *cx, JSObject *existing, JSObject *obj, Wrapper *handler)
61 {
62 JS_ASSERT(!obj->isCallable());
63 existing->as<ProxyObject>().renew(cx, handler, ObjectValue(*obj));
64 return existing;
65 }
66
67 Wrapper *
68 Wrapper::wrapperHandler(JSObject *wrapper)
69 {
70 JS_ASSERT(wrapper->is<WrapperObject>());
71 return static_cast<Wrapper*>(wrapper->as<ProxyObject>().handler());
72 }
73
74 JSObject *
75 Wrapper::wrappedObject(JSObject *wrapper)
76 {
77 JS_ASSERT(wrapper->is<WrapperObject>());
78 return wrapper->as<ProxyObject>().target();
79 }
80
81 JS_FRIEND_API(JSObject *)
82 js::UncheckedUnwrap(JSObject *wrapped, bool stopAtOuter, unsigned *flagsp)
83 {
84 unsigned flags = 0;
85 while (true) {
86 if (!wrapped->is<WrapperObject>() ||
87 MOZ_UNLIKELY(stopAtOuter && wrapped->getClass()->ext.innerObject))
88 {
89 break;
90 }
91 flags |= Wrapper::wrapperHandler(wrapped)->flags();
92 wrapped = wrapped->as<ProxyObject>().private_().toObjectOrNull();
93 }
94 if (flagsp)
95 *flagsp = flags;
96 return wrapped;
97 }
98
99 JS_FRIEND_API(JSObject *)
100 js::CheckedUnwrap(JSObject *obj, bool stopAtOuter)
101 {
102 while (true) {
103 JSObject *wrapper = obj;
104 obj = UnwrapOneChecked(obj, stopAtOuter);
105 if (!obj || obj == wrapper)
106 return obj;
107 }
108 }
109
110 JS_FRIEND_API(JSObject *)
111 js::UnwrapOneChecked(JSObject *obj, bool stopAtOuter)
112 {
113 if (!obj->is<WrapperObject>() ||
114 MOZ_UNLIKELY(!!obj->getClass()->ext.innerObject && stopAtOuter))
115 {
116 return obj;
117 }
118
119 Wrapper *handler = Wrapper::wrapperHandler(obj);
120 return handler->hasSecurityPolicy() ? nullptr : Wrapper::wrappedObject(obj);
121 }
122
123 bool
124 js::IsCrossCompartmentWrapper(JSObject *obj)
125 {
126 return IsWrapper(obj) &&
127 !!(Wrapper::wrapperHandler(obj)->flags() & Wrapper::CROSS_COMPARTMENT);
128 }
129
130 Wrapper::Wrapper(unsigned flags, bool hasPrototype) : DirectProxyHandler(&sWrapperFamily)
131 , mFlags(flags)
132 {
133 setHasPrototype(hasPrototype);
134 }
135
136 Wrapper::~Wrapper()
137 {
138 }
139
140 Wrapper Wrapper::singleton((unsigned)0);
141 Wrapper Wrapper::singletonWithPrototype((unsigned)0, true);
142 JSObject *Wrapper::defaultProto = TaggedProto::LazyProto;
143
144 /* Compartments. */
145
146 extern JSObject *
147 js::TransparentObjectWrapper(JSContext *cx, HandleObject existing, HandleObject obj,
148 HandleObject wrappedProto, HandleObject parent,
149 unsigned flags)
150 {
151 // Allow wrapping outer window proxies.
152 JS_ASSERT(!obj->is<WrapperObject>() || obj->getClass()->ext.innerObject);
153 JS_ASSERT(wrappedProto == TaggedProto::LazyProto);
154 return Wrapper::New(cx, obj, parent, &CrossCompartmentWrapper::singleton);
155 }
156
157 ErrorCopier::~ErrorCopier()
158 {
159 JSContext *cx = ac.ref().context()->asJSContext();
160 if (ac.ref().origin() != cx->compartment() && cx->isExceptionPending()) {
161 RootedValue exc(cx);
162 if (cx->getPendingException(&exc) && exc.isObject() && exc.toObject().is<ErrorObject>()) {
163 cx->clearPendingException();
164 ac.destroy();
165 Rooted<ErrorObject*> errObj(cx, &exc.toObject().as<ErrorObject>());
166 JSObject *copyobj = js_CopyErrorObject(cx, errObj, scope);
167 if (copyobj)
168 cx->setPendingException(ObjectValue(*copyobj));
169 }
170 }
171 }
172
173 /* Cross compartment wrappers. */
174
175 CrossCompartmentWrapper::CrossCompartmentWrapper(unsigned flags, bool hasPrototype)
176 : Wrapper(CROSS_COMPARTMENT | flags, hasPrototype)
177 {
178 }
179
180 CrossCompartmentWrapper::~CrossCompartmentWrapper()
181 {
182 }
183
184 bool Wrapper::finalizeInBackground(Value priv)
185 {
186 if (!priv.isObject())
187 return true;
188
189 /*
190 * Make the 'background-finalized-ness' of the wrapper the same as the
191 * wrapped object, to allow transplanting between them.
192 *
193 * If the wrapped object is in the nursery then we know it doesn't have a
194 * finalizer, and so background finalization is ok.
195 */
196 if (IsInsideNursery(priv.toObject().runtimeFromMainThread(), &priv.toObject()))
197 return true;
198 return IsBackgroundFinalized(priv.toObject().tenuredGetAllocKind());
199 }
200
201 #define PIERCE(cx, wrapper, pre, op, post) \
202 JS_BEGIN_MACRO \
203 bool ok; \
204 { \
205 AutoCompartment call(cx, wrappedObject(wrapper)); \
206 ok = (pre) && (op); \
207 } \
208 return ok && (post); \
209 JS_END_MACRO
210
211 #define NOTHING (true)
212
213 bool
214 CrossCompartmentWrapper::isExtensible(JSContext *cx, HandleObject wrapper, bool *extensible)
215 {
216 PIERCE(cx, wrapper,
217 NOTHING,
218 Wrapper::isExtensible(cx, wrapper, extensible),
219 NOTHING);
220 }
221
222 bool
223 CrossCompartmentWrapper::preventExtensions(JSContext *cx, HandleObject wrapper)
224 {
225 PIERCE(cx, wrapper,
226 NOTHING,
227 Wrapper::preventExtensions(cx, wrapper),
228 NOTHING);
229 }
230
231 bool
232 CrossCompartmentWrapper::getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id,
233 MutableHandle<PropertyDescriptor> desc)
234 {
235 RootedId idCopy(cx, id);
236 PIERCE(cx, wrapper,
237 cx->compartment()->wrapId(cx, idCopy.address()),
238 Wrapper::getPropertyDescriptor(cx, wrapper, idCopy, desc),
239 cx->compartment()->wrap(cx, desc));
240 }
241
242 bool
243 CrossCompartmentWrapper::getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id,
244 MutableHandle<PropertyDescriptor> desc)
245 {
246 RootedId idCopy(cx, id);
247 PIERCE(cx, wrapper,
248 cx->compartment()->wrapId(cx, idCopy.address()),
249 Wrapper::getOwnPropertyDescriptor(cx, wrapper, idCopy, desc),
250 cx->compartment()->wrap(cx, desc));
251 }
252
253 bool
254 CrossCompartmentWrapper::defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
255 MutableHandle<PropertyDescriptor> desc)
256 {
257 RootedId idCopy(cx, id);
258 Rooted<PropertyDescriptor> desc2(cx, desc);
259 PIERCE(cx, wrapper,
260 cx->compartment()->wrapId(cx, idCopy.address()) && cx->compartment()->wrap(cx, &desc2),
261 Wrapper::defineProperty(cx, wrapper, idCopy, &desc2),
262 NOTHING);
263 }
264
265 bool
266 CrossCompartmentWrapper::getOwnPropertyNames(JSContext *cx, HandleObject wrapper,
267 AutoIdVector &props)
268 {
269 PIERCE(cx, wrapper,
270 NOTHING,
271 Wrapper::getOwnPropertyNames(cx, wrapper, props),
272 cx->compartment()->wrap(cx, props));
273 }
274
275 bool
276 CrossCompartmentWrapper::delete_(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp)
277 {
278 RootedId idCopy(cx, id);
279 PIERCE(cx, wrapper,
280 cx->compartment()->wrapId(cx, idCopy.address()),
281 Wrapper::delete_(cx, wrapper, idCopy, bp),
282 NOTHING);
283 }
284
285 bool
286 CrossCompartmentWrapper::enumerate(JSContext *cx, HandleObject wrapper, AutoIdVector &props)
287 {
288 PIERCE(cx, wrapper,
289 NOTHING,
290 Wrapper::enumerate(cx, wrapper, props),
291 cx->compartment()->wrap(cx, props));
292 }
293
294 bool
295 CrossCompartmentWrapper::has(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp)
296 {
297 RootedId idCopy(cx, id);
298 PIERCE(cx, wrapper,
299 cx->compartment()->wrapId(cx, idCopy.address()),
300 Wrapper::has(cx, wrapper, idCopy, bp),
301 NOTHING);
302 }
303
304 bool
305 CrossCompartmentWrapper::hasOwn(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp)
306 {
307 RootedId idCopy(cx, id);
308 PIERCE(cx, wrapper,
309 cx->compartment()->wrapId(cx, idCopy.address()),
310 Wrapper::hasOwn(cx, wrapper, idCopy, bp),
311 NOTHING);
312 }
313
314 bool
315 CrossCompartmentWrapper::get(JSContext *cx, HandleObject wrapper, HandleObject receiver,
316 HandleId id, MutableHandleValue vp)
317 {
318 RootedObject receiverCopy(cx, receiver);
319 RootedId idCopy(cx, id);
320 {
321 AutoCompartment call(cx, wrappedObject(wrapper));
322 if (!cx->compartment()->wrap(cx, &receiverCopy) ||
323 !cx->compartment()->wrapId(cx, idCopy.address()))
324 {
325 return false;
326 }
327
328 if (!Wrapper::get(cx, wrapper, receiverCopy, idCopy, vp))
329 return false;
330 }
331 return cx->compartment()->wrap(cx, vp);
332 }
333
334 bool
335 CrossCompartmentWrapper::set(JSContext *cx, HandleObject wrapper, HandleObject receiver,
336 HandleId id, bool strict, MutableHandleValue vp)
337 {
338 RootedObject receiverCopy(cx, receiver);
339 RootedId idCopy(cx, id);
340 PIERCE(cx, wrapper,
341 cx->compartment()->wrap(cx, &receiverCopy) &&
342 cx->compartment()->wrapId(cx, idCopy.address()) &&
343 cx->compartment()->wrap(cx, vp),
344 Wrapper::set(cx, wrapper, receiverCopy, idCopy, strict, vp),
345 NOTHING);
346 }
347
348 bool
349 CrossCompartmentWrapper::keys(JSContext *cx, HandleObject wrapper, AutoIdVector &props)
350 {
351 PIERCE(cx, wrapper,
352 NOTHING,
353 Wrapper::keys(cx, wrapper, props),
354 cx->compartment()->wrap(cx, props));
355 }
356
357 /*
358 * We can reify non-escaping iterator objects instead of having to wrap them. This
359 * allows fast iteration over objects across a compartment boundary.
360 */
361 static bool
362 CanReify(HandleValue vp)
363 {
364 JSObject *obj;
365 return vp.isObject() &&
366 (obj = &vp.toObject())->is<PropertyIteratorObject>() &&
367 (obj->as<PropertyIteratorObject>().getNativeIterator()->flags & JSITER_ENUMERATE);
368 }
369
370 struct AutoCloseIterator
371 {
372 AutoCloseIterator(JSContext *cx, JSObject *obj) : cx(cx), obj(cx, obj) {}
373
374 ~AutoCloseIterator() { if (obj) CloseIterator(cx, obj); }
375
376 void clear() { obj = nullptr; }
377
378 private:
379 JSContext *cx;
380 RootedObject obj;
381 };
382
383 static bool
384 Reify(JSContext *cx, JSCompartment *origin, MutableHandleValue vp)
385 {
386 Rooted<PropertyIteratorObject*> iterObj(cx, &vp.toObject().as<PropertyIteratorObject>());
387 NativeIterator *ni = iterObj->getNativeIterator();
388
389 AutoCloseIterator close(cx, iterObj);
390
391 /* Wrap the iteratee. */
392 RootedObject obj(cx, ni->obj);
393 if (!origin->wrap(cx, &obj))
394 return false;
395
396 /*
397 * Wrap the elements in the iterator's snapshot.
398 * N.B. the order of closing/creating iterators is important due to the
399 * implicit cx->enumerators state.
400 */
401 size_t length = ni->numKeys();
402 bool isKeyIter = ni->isKeyIter();
403 AutoIdVector keys(cx);
404 if (length > 0) {
405 if (!keys.reserve(length))
406 return false;
407 for (size_t i = 0; i < length; ++i) {
408 RootedId id(cx);
409 RootedValue v(cx, StringValue(ni->begin()[i]));
410 if (!ValueToId<CanGC>(cx, v, &id))
411 return false;
412 keys.infallibleAppend(id);
413 if (!origin->wrapId(cx, &keys[i]))
414 return false;
415 }
416 }
417
418 close.clear();
419 if (!CloseIterator(cx, iterObj))
420 return false;
421
422 if (isKeyIter) {
423 if (!VectorToKeyIterator(cx, obj, ni->flags, keys, vp))
424 return false;
425 } else {
426 if (!VectorToValueIterator(cx, obj, ni->flags, keys, vp))
427 return false;
428 }
429 return true;
430 }
431
432 bool
433 CrossCompartmentWrapper::iterate(JSContext *cx, HandleObject wrapper, unsigned flags,
434 MutableHandleValue vp)
435 {
436 {
437 AutoCompartment call(cx, wrappedObject(wrapper));
438 if (!Wrapper::iterate(cx, wrapper, flags, vp))
439 return false;
440 }
441
442 if (CanReify(vp))
443 return Reify(cx, cx->compartment(), vp);
444 return cx->compartment()->wrap(cx, vp);
445 }
446
447 bool
448 CrossCompartmentWrapper::call(JSContext *cx, HandleObject wrapper, const CallArgs &args)
449 {
450 RootedObject wrapped(cx, wrappedObject(wrapper));
451
452 {
453 AutoCompartment call(cx, wrapped);
454
455 args.setCallee(ObjectValue(*wrapped));
456 if (!cx->compartment()->wrap(cx, args.mutableThisv()))
457 return false;
458
459 for (size_t n = 0; n < args.length(); ++n) {
460 if (!cx->compartment()->wrap(cx, args[n]))
461 return false;
462 }
463
464 if (!Wrapper::call(cx, wrapper, args))
465 return false;
466 }
467
468 return cx->compartment()->wrap(cx, args.rval());
469 }
470
471 bool
472 CrossCompartmentWrapper::construct(JSContext *cx, HandleObject wrapper, const CallArgs &args)
473 {
474 RootedObject wrapped(cx, wrappedObject(wrapper));
475 {
476 AutoCompartment call(cx, wrapped);
477
478 for (size_t n = 0; n < args.length(); ++n) {
479 if (!cx->compartment()->wrap(cx, args[n]))
480 return false;
481 }
482 if (!Wrapper::construct(cx, wrapper, args))
483 return false;
484 }
485 return cx->compartment()->wrap(cx, args.rval());
486 }
487
488 bool
489 CrossCompartmentWrapper::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
490 CallArgs srcArgs)
491 {
492 RootedObject wrapper(cx, &srcArgs.thisv().toObject());
493 JS_ASSERT(srcArgs.thisv().isMagic(JS_IS_CONSTRUCTING) ||
494 !UncheckedUnwrap(wrapper)->is<CrossCompartmentWrapperObject>());
495
496 RootedObject wrapped(cx, wrappedObject(wrapper));
497 {
498 AutoCompartment call(cx, wrapped);
499 InvokeArgs dstArgs(cx);
500 if (!dstArgs.init(srcArgs.length()))
501 return false;
502
503 Value *src = srcArgs.base();
504 Value *srcend = srcArgs.array() + srcArgs.length();
505 Value *dst = dstArgs.base();
506
507 RootedValue source(cx);
508 for (; src < srcend; ++src, ++dst) {
509 source = *src;
510 if (!cx->compartment()->wrap(cx, &source))
511 return false;
512 *dst = source.get();
513
514 // Handle |this| specially. When we rewrap on the other side of the
515 // membrane, we might apply a same-compartment security wrapper that
516 // will stymie this whole process. If that happens, unwrap the wrapper.
517 // This logic can go away when same-compartment security wrappers go away.
518 if ((src == srcArgs.base() + 1) && dst->isObject()) {
519 RootedObject thisObj(cx, &dst->toObject());
520 if (thisObj->is<WrapperObject>() &&
521 Wrapper::wrapperHandler(thisObj)->hasSecurityPolicy())
522 {
523 JS_ASSERT(!thisObj->is<CrossCompartmentWrapperObject>());
524 *dst = ObjectValue(*Wrapper::wrappedObject(thisObj));
525 }
526 }
527 }
528
529 if (!CallNonGenericMethod(cx, test, impl, dstArgs))
530 return false;
531
532 srcArgs.rval().set(dstArgs.rval());
533 }
534 return cx->compartment()->wrap(cx, srcArgs.rval());
535 }
536
537 bool
538 CrossCompartmentWrapper::hasInstance(JSContext *cx, HandleObject wrapper, MutableHandleValue v,
539 bool *bp)
540 {
541 AutoCompartment call(cx, wrappedObject(wrapper));
542 if (!cx->compartment()->wrap(cx, v))
543 return false;
544 return Wrapper::hasInstance(cx, wrapper, v, bp);
545 }
546
547 const char *
548 CrossCompartmentWrapper::className(JSContext *cx, HandleObject wrapper)
549 {
550 AutoCompartment call(cx, wrappedObject(wrapper));
551 return Wrapper::className(cx, wrapper);
552 }
553
554 JSString *
555 CrossCompartmentWrapper::fun_toString(JSContext *cx, HandleObject wrapper, unsigned indent)
556 {
557 RootedString str(cx);
558 {
559 AutoCompartment call(cx, wrappedObject(wrapper));
560 str = Wrapper::fun_toString(cx, wrapper, indent);
561 if (!str)
562 return nullptr;
563 }
564 if (!cx->compartment()->wrap(cx, str.address()))
565 return nullptr;
566 return str;
567 }
568
569 bool
570 CrossCompartmentWrapper::regexp_toShared(JSContext *cx, HandleObject wrapper, RegExpGuard *g)
571 {
572 AutoCompartment call(cx, wrappedObject(wrapper));
573 return Wrapper::regexp_toShared(cx, wrapper, g);
574 }
575
576 bool
577 CrossCompartmentWrapper::defaultValue(JSContext *cx, HandleObject wrapper, JSType hint,
578 MutableHandleValue vp)
579 {
580 PIERCE(cx, wrapper,
581 NOTHING,
582 Wrapper::defaultValue(cx, wrapper, hint, vp),
583 cx->compartment()->wrap(cx, vp));
584 }
585
586 bool
587 CrossCompartmentWrapper::getPrototypeOf(JSContext *cx, HandleObject wrapper,
588 MutableHandleObject protop)
589 {
590 {
591 RootedObject wrapped(cx, wrappedObject(wrapper));
592 AutoCompartment call(cx, wrapped);
593 if (!JSObject::getProto(cx, wrapped, protop))
594 return false;
595 if (protop)
596 protop->setDelegate(cx);
597 }
598
599 return cx->compartment()->wrap(cx, protop);
600 }
601
602 bool
603 CrossCompartmentWrapper::setPrototypeOf(JSContext *cx, HandleObject wrapper,
604 HandleObject proto, bool *bp)
605 {
606 RootedObject protoCopy(cx, proto);
607 PIERCE(cx, wrapper,
608 cx->compartment()->wrap(cx, &protoCopy),
609 Wrapper::setPrototypeOf(cx, wrapper, protoCopy, bp),
610 NOTHING);
611 }
612
613 CrossCompartmentWrapper CrossCompartmentWrapper::singleton(0u);
614
615 /* Security wrappers. */
616
617 template <class Base>
618 SecurityWrapper<Base>::SecurityWrapper(unsigned flags)
619 : Base(flags)
620 {
621 BaseProxyHandler::setHasSecurityPolicy(true);
622 }
623
624 template <class Base>
625 bool
626 SecurityWrapper<Base>::isExtensible(JSContext *cx, HandleObject wrapper, bool *extensible)
627 {
628 // Just like BaseProxyHandler, SecurityWrappers claim by default to always
629 // be extensible, so as not to leak information about the state of the
630 // underlying wrapped thing.
631 *extensible = true;
632 return true;
633 }
634
635 template <class Base>
636 bool
637 SecurityWrapper<Base>::preventExtensions(JSContext *cx, HandleObject wrapper)
638 {
639 // See above.
640 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
641 return false;
642 }
643
644 template <class Base>
645 bool
646 SecurityWrapper<Base>::enter(JSContext *cx, HandleObject wrapper, HandleId id,
647 Wrapper::Action act, bool *bp)
648 {
649 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
650 *bp = false;
651 return false;
652 }
653
654 template <class Base>
655 bool
656 SecurityWrapper<Base>::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
657 CallArgs args)
658 {
659 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
660 return false;
661 }
662
663 template <class Base>
664 bool
665 SecurityWrapper<Base>::setPrototypeOf(JSContext *cx, HandleObject wrapper,
666 HandleObject proto, bool *bp)
667 {
668 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
669 return false;
670 }
671
672 // For security wrappers, we run the DefaultValue algorithm on the wrapper
673 // itself, which means that the existing security policy on operations like
674 // toString() will take effect and do the right thing here.
675 template <class Base>
676 bool
677 SecurityWrapper<Base>::defaultValue(JSContext *cx, HandleObject wrapper,
678 JSType hint, MutableHandleValue vp)
679 {
680 return DefaultValue(cx, wrapper, hint, vp);
681 }
682
683 template <class Base>
684 bool
685 SecurityWrapper<Base>::objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx)
686 {
687 return false;
688 }
689
690 template <class Base>
691 bool
692 SecurityWrapper<Base>::regexp_toShared(JSContext *cx, HandleObject obj, RegExpGuard *g)
693 {
694 return Base::regexp_toShared(cx, obj, g);
695 }
696
697 template <class Base>
698 bool
699 SecurityWrapper<Base>::defineProperty(JSContext *cx, HandleObject wrapper,
700 HandleId id, MutableHandle<PropertyDescriptor> desc)
701 {
702 if (desc.getter() || desc.setter()) {
703 JSString *str = IdToString(cx, id);
704 const jschar *prop = str ? str->getCharsZ(cx) : nullptr;
705 JS_ReportErrorNumberUC(cx, js_GetErrorMessage, nullptr,
706 JSMSG_ACCESSOR_DEF_DENIED, prop);
707 return false;
708 }
709
710 return Base::defineProperty(cx, wrapper, id, desc);
711 }
712
713 template <class Base>
714 bool
715 SecurityWrapper<Base>::watch(JSContext *cx, HandleObject proxy,
716 HandleId id, HandleObject callable)
717 {
718 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
719 return false;
720 }
721
722 template <class Base>
723 bool
724 SecurityWrapper<Base>::unwatch(JSContext *cx, HandleObject proxy,
725 HandleId id)
726 {
727 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
728 return false;
729 }
730
731
732 template class js::SecurityWrapper<Wrapper>;
733 template class js::SecurityWrapper<CrossCompartmentWrapper>;
734
735 DeadObjectProxy::DeadObjectProxy()
736 : BaseProxyHandler(&sDeadObjectFamily)
737 {
738 }
739
740 bool
741 DeadObjectProxy::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible)
742 {
743 // This is kind of meaningless, but dead-object semantics aside,
744 // [[Extensible]] always being true is consistent with other proxy types.
745 *extensible = true;
746 return true;
747 }
748
749 bool
750 DeadObjectProxy::preventExtensions(JSContext *cx, HandleObject proxy)
751 {
752 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
753 return false;
754 }
755
756 bool
757 DeadObjectProxy::getPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id,
758 MutableHandle<PropertyDescriptor> desc)
759 {
760 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
761 return false;
762 }
763
764 bool
765 DeadObjectProxy::getOwnPropertyDescriptor(JSContext *cx, HandleObject wrapper, HandleId id,
766 MutableHandle<PropertyDescriptor> desc)
767 {
768 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
769 return false;
770 }
771
772 bool
773 DeadObjectProxy::defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
774 MutableHandle<PropertyDescriptor> desc)
775 {
776 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
777 return false;
778 }
779
780 bool
781 DeadObjectProxy::getOwnPropertyNames(JSContext *cx, HandleObject wrapper,
782 AutoIdVector &props)
783 {
784 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
785 return false;
786 }
787
788 bool
789 DeadObjectProxy::delete_(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp)
790 {
791 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
792 return false;
793 }
794
795 bool
796 DeadObjectProxy::enumerate(JSContext *cx, HandleObject wrapper, AutoIdVector &props)
797 {
798 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
799 return false;
800 }
801
802 bool
803 DeadObjectProxy::call(JSContext *cx, HandleObject wrapper, const CallArgs &args)
804 {
805 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
806 return false;
807 }
808
809 bool
810 DeadObjectProxy::construct(JSContext *cx, HandleObject wrapper, const CallArgs &args)
811 {
812 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
813 return false;
814 }
815
816 bool
817 DeadObjectProxy::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args)
818 {
819 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
820 return false;
821 }
822
823 bool
824 DeadObjectProxy::hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp)
825 {
826 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
827 return false;
828 }
829
830 bool
831 DeadObjectProxy::objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx)
832 {
833 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
834 return false;
835 }
836
837 const char *
838 DeadObjectProxy::className(JSContext *cx, HandleObject wrapper)
839 {
840 return "DeadObject";
841 }
842
843 JSString *
844 DeadObjectProxy::fun_toString(JSContext *cx, HandleObject proxy, unsigned indent)
845 {
846 return nullptr;
847 }
848
849 bool
850 DeadObjectProxy::regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g)
851 {
852 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
853 return false;
854 }
855
856 bool
857 DeadObjectProxy::defaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp)
858 {
859 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
860 return false;
861 }
862
863 bool
864 DeadObjectProxy::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop)
865 {
866 protop.set(nullptr);
867 return true;
868 }
869
870 DeadObjectProxy DeadObjectProxy::singleton;
871 const char DeadObjectProxy::sDeadObjectFamily = 0;
872
873 bool
874 js::IsDeadProxyObject(JSObject *obj)
875 {
876 return obj->is<ProxyObject>() &&
877 obj->as<ProxyObject>().handler() == &DeadObjectProxy::singleton;
878 }
879
880 void
881 js::NukeCrossCompartmentWrapper(JSContext *cx, JSObject *wrapper)
882 {
883 JS_ASSERT(wrapper->is<CrossCompartmentWrapperObject>());
884
885 NotifyGCNukeWrapper(wrapper);
886
887 wrapper->as<ProxyObject>().nuke(&DeadObjectProxy::singleton);
888
889 JS_ASSERT(IsDeadProxyObject(wrapper));
890 }
891
892 /*
893 * NukeChromeCrossCompartmentWrappersForGlobal reaches into chrome and cuts
894 * all of the cross-compartment wrappers that point to objects parented to
895 * obj's global. The snag here is that we need to avoid cutting wrappers that
896 * point to the window object on page navigation (inner window destruction)
897 * and only do that on tab close (outer window destruction). Thus the
898 * option of how to handle the global object.
899 */
900 JS_FRIEND_API(bool)
901 js::NukeCrossCompartmentWrappers(JSContext* cx,
902 const CompartmentFilter& sourceFilter,
903 const CompartmentFilter& targetFilter,
904 js::NukeReferencesToWindow nukeReferencesToWindow)
905 {
906 CHECK_REQUEST(cx);
907 JSRuntime *rt = cx->runtime();
908
909 // Iterate through scopes looking for system cross compartment wrappers
910 // that point to an object that shares a global with obj.
911
912 for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
913 if (!sourceFilter.match(c))
914 continue;
915
916 // Iterate the wrappers looking for anything interesting.
917 for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
918 // Some cross-compartment wrappers are for strings. We're not
919 // interested in those.
920 const CrossCompartmentKey &k = e.front().key();
921 if (k.kind != CrossCompartmentKey::ObjectWrapper)
922 continue;
923
924 AutoWrapperRooter wobj(cx, WrapperValue(e));
925 JSObject *wrapped = UncheckedUnwrap(wobj);
926
927 if (nukeReferencesToWindow == DontNukeWindowReferences &&
928 wrapped->getClass()->ext.innerObject)
929 continue;
930
931 if (targetFilter.match(wrapped->compartment())) {
932 // We found a wrapper to nuke.
933 e.removeFront();
934 NukeCrossCompartmentWrapper(cx, wobj);
935 }
936 }
937 }
938
939 return true;
940 }
941
942 // Given a cross-compartment wrapper |wobj|, update it to point to
943 // |newTarget|. This recomputes the wrapper with JS_WrapValue, and thus can be
944 // useful even if wrapper already points to newTarget.
945 bool
946 js::RemapWrapper(JSContext *cx, JSObject *wobjArg, JSObject *newTargetArg)
947 {
948 RootedObject wobj(cx, wobjArg);
949 RootedObject newTarget(cx, newTargetArg);
950 JS_ASSERT(wobj->is<CrossCompartmentWrapperObject>());
951 JS_ASSERT(!newTarget->is<CrossCompartmentWrapperObject>());
952 JSObject *origTarget = Wrapper::wrappedObject(wobj);
953 JS_ASSERT(origTarget);
954 Value origv = ObjectValue(*origTarget);
955 JSCompartment *wcompartment = wobj->compartment();
956
957 AutoDisableProxyCheck adpc(cx->runtime());
958
959 // If we're mapping to a different target (as opposed to just recomputing
960 // for the same target), we must not have an existing wrapper for the new
961 // target, otherwise this will break.
962 JS_ASSERT_IF(origTarget != newTarget,
963 !wcompartment->lookupWrapper(ObjectValue(*newTarget)));
964
965 // The old value should still be in the cross-compartment wrapper map, and
966 // the lookup should return wobj.
967 WrapperMap::Ptr p = wcompartment->lookupWrapper(origv);
968 JS_ASSERT(&p->value().unsafeGet()->toObject() == wobj);
969 wcompartment->removeWrapper(p);
970
971 // When we remove origv from the wrapper map, its wrapper, wobj, must
972 // immediately cease to be a cross-compartment wrapper. Neuter it.
973 NukeCrossCompartmentWrapper(cx, wobj);
974
975 // First, we wrap it in the new compartment. We try to use the existing
976 // wrapper, |wobj|, since it's been nuked anyway. The wrap() function has
977 // the choice to reuse |wobj| or not.
978 RootedObject tobj(cx, newTarget);
979 AutoCompartment ac(cx, wobj);
980 if (!wcompartment->wrap(cx, &tobj, wobj))
981 MOZ_CRASH();
982
983 // If wrap() reused |wobj|, it will have overwritten it and returned with
984 // |tobj == wobj|. Otherwise, |tobj| will point to a new wrapper and |wobj|
985 // will still be nuked. In the latter case, we replace |wobj| with the
986 // contents of the new wrapper in |tobj|.
987 if (tobj != wobj) {
988 // Now, because we need to maintain object identity, we do a brain
989 // transplant on the old object so that it contains the contents of the
990 // new one.
991 if (!JSObject::swap(cx, wobj, tobj))
992 MOZ_CRASH();
993 }
994
995 // Before swapping, this wrapper came out of wrap(), which enforces the
996 // invariant that the wrapper in the map points directly to the key.
997 JS_ASSERT(Wrapper::wrappedObject(wobj) == newTarget);
998
999 // Update the entry in the compartment's wrapper map to point to the old
1000 // wrapper, which has now been updated (via reuse or swap).
1001 JS_ASSERT(wobj->is<WrapperObject>());
1002 wcompartment->putWrapper(cx, ObjectValue(*newTarget), ObjectValue(*wobj));
1003 return true;
1004 }
1005
1006 // Remap all cross-compartment wrappers pointing to |oldTarget| to point to
1007 // |newTarget|. All wrappers are recomputed.
1008 JS_FRIEND_API(bool)
1009 js::RemapAllWrappersForObject(JSContext *cx, JSObject *oldTargetArg,
1010 JSObject *newTargetArg)
1011 {
1012 RootedValue origv(cx, ObjectValue(*oldTargetArg));
1013 RootedObject newTarget(cx, newTargetArg);
1014
1015 AutoWrapperVector toTransplant(cx);
1016 if (!toTransplant.reserve(cx->runtime()->numCompartments))
1017 return false;
1018
1019 for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
1020 if (WrapperMap::Ptr wp = c->lookupWrapper(origv)) {
1021 // We found a wrapper. Remember and root it.
1022 toTransplant.infallibleAppend(WrapperValue(wp));
1023 }
1024 }
1025
1026 for (WrapperValue *begin = toTransplant.begin(), *end = toTransplant.end();
1027 begin != end; ++begin)
1028 {
1029 if (!RemapWrapper(cx, &begin->toObject(), newTarget))
1030 MOZ_CRASH();
1031 }
1032
1033 return true;
1034 }
1035
1036 JS_FRIEND_API(bool)
1037 js::RecomputeWrappers(JSContext *cx, const CompartmentFilter &sourceFilter,
1038 const CompartmentFilter &targetFilter)
1039 {
1040 AutoMaybeTouchDeadZones agc(cx);
1041
1042 AutoWrapperVector toRecompute(cx);
1043
1044 for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
1045 // Filter by source compartment.
1046 if (!sourceFilter.match(c))
1047 continue;
1048
1049 // Iterate over the wrappers, filtering appropriately.
1050 for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
1051 // Filter out non-objects.
1052 const CrossCompartmentKey &k = e.front().key();
1053 if (k.kind != CrossCompartmentKey::ObjectWrapper)
1054 continue;
1055
1056 // Filter by target compartment.
1057 if (!targetFilter.match(static_cast<JSObject *>(k.wrapped)->compartment()))
1058 continue;
1059
1060 // Add it to the list.
1061 if (!toRecompute.append(WrapperValue(e)))
1062 return false;
1063 }
1064 }
1065
1066 // Recompute all the wrappers in the list.
1067 for (WrapperValue *begin = toRecompute.begin(), *end = toRecompute.end(); begin != end; ++begin)
1068 {
1069 JSObject *wrapper = &begin->toObject();
1070 JSObject *wrapped = Wrapper::wrappedObject(wrapper);
1071 if (!RemapWrapper(cx, wrapper, wrapped))
1072 MOZ_CRASH();
1073 }
1074
1075 return true;
1076 }

mercurial