Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
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/. */
7 #include "XrayWrapper.h"
8 #include "AccessCheck.h"
9 #include "WrapperFactory.h"
11 #include "nsIContent.h"
12 #include "nsIControllers.h"
13 #include "nsContentUtils.h"
15 #include "XPCWrapper.h"
16 #include "xpcprivate.h"
18 #include "jsapi.h"
19 #include "jsprf.h"
20 #include "nsJSUtils.h"
22 #include "mozilla/dom/BindingUtils.h"
23 #include "mozilla/dom/WindowBinding.h"
24 #include "nsGlobalWindow.h"
26 using namespace mozilla::dom;
27 using namespace JS;
28 using namespace mozilla;
30 using js::Wrapper;
31 using js::BaseProxyHandler;
32 using js::IsCrossCompartmentWrapper;
33 using js::UncheckedUnwrap;
34 using js::CheckedUnwrap;
36 namespace xpc {
38 using namespace XrayUtils;
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 }
52 XrayType
53 GetXrayType(JSObject *obj)
54 {
55 obj = js::UncheckedUnwrap(obj, /* stopAtOuter = */ false);
56 if (mozilla::dom::UseDOMXray(obj))
57 return XrayForDOMObject;
59 const js::Class* clasp = js::GetObjectClass(obj);
60 if (IS_WN_CLASS(clasp) || clasp->ext.innerObject)
61 return XrayForWrappedNative;
63 JSProtoKey standardProto = IdentifyStandardInstanceOrPrototype(obj);
64 if (IsJSXraySupported(standardProto))
65 return XrayForJSObject;
67 return NotXray;
68 }
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 }
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 }
90 ResolvingId::~ResolvingId()
91 {
92 MOZ_ASSERT(getResolvingId(mHolder) == this, "unbalanced ResolvingIds");
93 js::SetReservedSlot(mHolder, JSSLOT_RESOLVING, js::PrivateValue(mPrev));
94 }
96 bool
97 ResolvingId::isXrayShadowing(jsid id)
98 {
99 if (!mXrayShadowing)
100 return false;
102 return mId == id;
103 }
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 }
113 return false;
114 }
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 }
123 JSObject *
124 ResolvingId::getHolderObject(JSObject *wrapper)
125 {
126 return &js::GetProxyExtra(wrapper, 0).toObject();
127 }
129 ResolvingId *
130 ResolvingId::getResolvingIdFromWrapper(JSObject *wrapper)
131 {
132 return getResolvingId(getHolderObject(wrapper));
133 }
135 class MOZ_STACK_CLASS ResolvingIdDummy
136 {
137 public:
138 ResolvingIdDummy(JSContext *cx, HandleObject wrapper, HandleId id)
139 {
140 }
141 };
143 class XrayTraits
144 {
145 public:
146 static JSObject* getTargetObject(JSObject *wrapper) {
147 return js::UncheckedUnwrap(wrapper, /* stopAtOuter = */ false);
148 }
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);
161 virtual void preserveWrapper(JSObject *target) = 0;
163 static bool set(JSContext *cx, HandleObject wrapper, HandleObject receiver, HandleId id,
164 bool strict, MutableHandleValue vp);
166 JSObject* getExpandoObject(JSContext *cx, HandleObject target,
167 HandleObject consumer);
168 JSObject* ensureExpandoObject(JSContext *cx, HandleObject wrapper,
169 HandleObject target);
171 JSObject* getHolder(JSObject *wrapper);
172 JSObject* ensureHolder(JSContext *cx, HandleObject wrapper);
173 virtual JSObject* createHolder(JSContext *cx, JSObject *wrapper) = 0;
175 JSObject* getExpandoChain(HandleObject obj) {
176 return GetObjectScope(obj)->GetExpandoChain(obj);
177 }
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);
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 };
196 class XPCWrappedNativeXrayTraits : public XrayTraits
197 {
198 public:
199 enum {
200 HasPrototype = 0
201 };
203 static const XrayType Type = XrayForWrappedNative;
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);
221 static bool isResolving(JSContext *cx, JSObject *holder, jsid id);
223 static bool resolveDOMCollectionProperty(JSContext *cx, HandleObject wrapper,
224 HandleObject holder, HandleId id,
225 MutableHandle<JSPropertyDescriptor> desc);
227 static XPCWrappedNative* getWN(JSObject *wrapper) {
228 return XPCWrappedNative::Get(getTargetObject(wrapper));
229 }
231 virtual void preserveWrapper(JSObject *target) MOZ_OVERRIDE;
233 typedef ResolvingId ResolvingIdImpl;
235 virtual JSObject* createHolder(JSContext *cx, JSObject *wrapper) MOZ_OVERRIDE;
237 static const JSClass HolderClass;
238 static XPCWrappedNativeXrayTraits singleton;
239 };
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 };
247 class DOMXrayTraits : public XrayTraits
248 {
249 public:
250 enum {
251 HasPrototype = 0
252 };
254 static const XrayType Type = XrayForDOMObject;
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);
274 static bool isResolving(JSContext *cx, JSObject *holder, jsid id)
275 {
276 return false;
277 }
279 typedef ResolvingIdDummy ResolvingIdImpl;
281 virtual void preserveWrapper(JSObject *target) MOZ_OVERRIDE;
283 virtual JSObject* createHolder(JSContext *cx, JSObject *wrapper) MOZ_OVERRIDE;
285 static DOMXrayTraits singleton;
286 };
288 class JSXrayTraits : public XrayTraits
289 {
290 public:
291 enum {
292 HasPrototype = 1
293 };
294 static const XrayType Type = XrayForJSObject;
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 }
303 virtual bool resolveOwnProperty(JSContext *cx, Wrapper &jsWrapper, HandleObject wrapper,
304 HandleObject holder, HandleId id,
305 MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE;
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 }
316 virtual bool enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags,
317 AutoIdVector &props);
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 }
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 }
337 static bool isResolving(JSContext *cx, JSObject *holder, jsid id)
338 {
339 return false;
340 }
342 typedef ResolvingIdDummy ResolvingIdImpl;
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 }
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 }
366 enum {
367 SLOT_PROTOKEY = 0,
368 SLOT_ISPROTOTYPE,
369 SLOT_COUNT
370 };
371 virtual JSObject* createHolder(JSContext *cx, JSObject *wrapper) MOZ_OVERRIDE;
373 static JSProtoKey getProtoKey(JSObject *holder) {
374 int32_t key = js::GetReservedSlot(holder, SLOT_PROTOKEY).toInt32();
375 return static_cast<JSProtoKey>(key);
376 }
378 static bool isPrototype(JSObject *holder) {
379 return js::GetReservedSlot(holder, SLOT_ISPROTOTYPE).toBoolean();
380 }
382 static const JSClass HolderClass;
383 static JSXrayTraits singleton;
384 };
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 };
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;
405 // Non-prototypes don't have anything on them yet.
406 if (!isPrototype(holder))
407 return true;
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 }
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());
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 }
446 // Find the properties available, if any.
447 const JSFunctionSpec *fs = clasp->spec.prototypeFunctions;
448 if (!fs)
449 return true;
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));
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;
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;
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 }
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;
492 // Non-prototypes don't have anything on them yet.
493 if (!isPrototype(holder))
494 return true;
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());
502 // Find the properties available, if any.
503 const JSFunctionSpec *fs = clasp->spec.prototypeFunctions;
504 if (!fs)
505 return true;
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 }
519 // Add the 'constructor' property.
520 return props.append(GetRTIdByIndex(cx, XPCJSRuntime::IDX_CONSTRUCTOR));
521 }
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;
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);
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);
549 return holder;
550 }
552 XPCWrappedNativeXrayTraits XPCWrappedNativeXrayTraits::singleton;
553 DOMXrayTraits DOMXrayTraits::singleton;
554 JSXrayTraits JSXrayTraits::singleton;
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 }
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 */
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 };
594 static nsIPrincipal*
595 ObjectPrincipal(JSObject *obj)
596 {
597 return GetCompartmentPrincipal(js::GetObjectCompartment(obj));
598 }
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 }
607 static void
608 ExpandoObjectFinalize(JSFreeOp *fop, JSObject *obj)
609 {
610 // Release the principal.
611 nsIPrincipal *principal = GetExpandoObjectPrincipal(obj);
612 NS_RELEASE(principal);
613 }
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 };
622 bool
623 XrayTraits::expandoObjectMatchesConsumer(JSContext *cx,
624 HandleObject expandoObject,
625 nsIPrincipal *consumerOrigin,
626 HandleObject exclusiveGlobal)
627 {
628 MOZ_ASSERT(js::IsObjectInContextCompartment(expandoObject, cx));
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;
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;
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 }
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;
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 }
675 // Not found.
676 return nullptr;
677 }
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 }
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));
696 // No duplicates allowed.
697 MOZ_ASSERT(!getExpandoObjectInternal(cx, target, origin, exclusiveGlobal));
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;
705 // AddRef and store the principal.
706 NS_ADDREF(origin);
707 JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_ORIGIN, PRIVATE_TO_JSVAL(origin));
709 // Note the exclusive global, if any.
710 JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL,
711 OBJECT_TO_JSVAL(exclusiveGlobal));
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);
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);
724 return expandoObject;
725 }
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 }
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);
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 }
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 }
781 static JSObject *
782 GetHolder(JSObject *obj)
783 {
784 return &js::GetProxyExtra(obj, 0).toObject();
785 }
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 }
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 }
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 }
817 namespace XrayUtils {
819 bool
820 IsXPCWNHolderClass(const JSClass *clasp)
821 {
822 return clasp == &XPCWrappedNativeXrayTraits::HolderClass;
823 }
825 }
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);
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 }
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 }
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 }
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 }
893 ~AutoSetWrapperNotShadowing()
894 {
895 mResolvingId->mXrayShadowing = false;
896 }
898 private:
899 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
900 ResolvingId *mResolvingId;
901 };
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;
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;
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 }
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);
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 }
950 if (pobj && !JS_GetPropertyDescriptorById(cx, holder, id, desc))
951 return false;
953 return true;
954 }
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;
967 nsCOMPtr<nsPIDOMWindow> piWin = do_QueryInterface(
968 nsContentUtils::XPConnect()->GetNativeOfWrapper(cx, target));
969 return static_cast<nsGlobalWindow*>(piWin.get());
970 }
972 static bool
973 IsWindow(JSContext *cx, JSObject *wrapper)
974 {
975 return !!AsWindow(cx, wrapper);
976 }
978 static nsQueryInterface
979 do_QueryInterfaceNative(JSContext* cx, HandleObject wrapper);
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 }
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);
998 desc.object().set(nullptr);
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);
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 }
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 }
1030 if (NS_FAILED(rv) || !c) {
1031 JS_ReportError(cx, "Failed to invoke GetControllers via Xrays");
1032 return false;
1033 }
1035 desc.object().set(wrapper);
1036 return true;
1037 }
1039 XPCNativeInterface *iface;
1040 XPCNativeMember *member;
1041 XPCWrappedNative *wn = getWN(wrapper);
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 }
1053 desc.object().set(holder);
1054 desc.setAttributes(JSPROP_ENUMERATE);
1055 desc.setGetter(nullptr);
1056 desc.setSetter(nullptr);
1057 desc.value().set(JSVAL_VOID);
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 }
1072 unsigned attrs = desc.attributes();
1073 attrs |= JSPROP_GETTER;
1074 if (member->IsWritableAttribute())
1075 attrs |= JSPROP_SETTER;
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 }
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 }
1096 if (!JS_WrapValue(cx, desc.value()) || !JS_WrapValue(cx, &fval))
1097 return false;
1099 if (desc.hasGetterObject())
1100 desc.setGetterObject(&fval.toObject());
1101 if (desc.hasSetterObject())
1102 desc.setSetterObject(&fval.toObject());
1104 // Define the property.
1105 return JS_DefinePropertyById(cx, holder, id, desc.value(),
1106 desc.getter(), desc.setter(), desc.attributes());
1107 }
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 }
1117 vp.set(OBJECT_TO_JSVAL(wrapper));
1119 return WrapperFactory::WaiveXrayAndWrap(cx, vp);
1120 }
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));
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 }
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 }
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 }
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 }
1188 return true;
1189 }
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 }
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;
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 }
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));
1239 bool hasProp;
1240 if (!JS_HasPropertyById(cx, holder, id, &hasProp)) {
1241 return false;
1242 }
1243 if (!hasProp) {
1244 XPCWrappedNative *wn = getWN(wrapper);
1246 // Run the resolve hook of the wrapped native.
1247 if (!NATIVE_HAS_FLAG(wn, WantNewResolve)) {
1248 return true;
1249 }
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 }
1262 MOZ_ASSERT(!pobj || (JS_HasPropertyById(cx, holder, id, &hasProp) &&
1263 hasProp), "id got defined somewhere else?");
1264 }
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 }
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 }
1296 *defined = true;
1297 return JS_DefinePropertyById(cx, holder, id, desc.value(), desc.getter(), desc.setter(),
1298 desc.attributes());
1299 }
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 }
1309 return true;
1310 }
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;
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 }
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;
1348 js::SetReservedSlot(holder, JSSLOT_RESOLVING, PrivateValue(nullptr));
1349 return holder;
1350 }
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 }
1374 return true;
1376 }
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 }
1400 return true;
1402 }
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;
1413 MOZ_ASSERT(!desc.object() || desc.object() == wrapper, "What did we resolve this on?");
1415 return true;
1416 }
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;
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 }
1451 RootedObject obj(cx, getTargetObject(wrapper));
1452 if (!XrayResolveOwnProperty(cx, wrapper, obj, id, desc))
1453 return false;
1455 MOZ_ASSERT(!desc.object() || desc.object() == wrapper, "What did we resolve this on?");
1457 return true;
1458 }
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 }
1475 if (!existingDesc.object())
1476 return true;
1478 JS::Rooted<JSObject*> obj(cx, getTargetObject(wrapper));
1479 return XrayDefineProperty(cx, wrapper, obj, id, desc, defined);
1480 }
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);
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 }
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 }
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 }
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 }
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 }
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 }
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 }
1589 template <typename Base, typename Traits>
1590 XrayWrapper<Base, Traits>::~XrayWrapper()
1591 {
1592 }
1594 namespace XrayUtils {
1596 JSObject *
1597 GetNativePropertiesObject(JSContext *cx, JSObject *wrapper)
1598 {
1599 MOZ_ASSERT(js::IsWrapper(wrapper) && WrapperFactory::IsXrayWrapper(wrapper),
1600 "bad object passed in");
1602 JSObject *holder = GetHolder(wrapper);
1603 MOZ_ASSERT(holder, "uninitialized wrapper being used?");
1604 return holder;
1605 }
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 }
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);
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 }
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 }
1652 // Try resolveNativeProperty.
1653 if (!traits->resolveNativeProperty(cx, wrapper, holder, id, &desc))
1654 return false;
1655 *hasProp = !!desc.object();
1656 return true;
1657 }
1659 } // namespace XrayUtils
1661 static bool
1662 XrayToString(JSContext *cx, unsigned argc, Value *vp)
1663 {
1664 CallArgs args = CallArgsFromVp(argc, vp);
1666 if (!args.thisv().isObject()) {
1667 JS_ReportError(cx, "XrayToString called on an incompatible object");
1668 return false;
1669 }
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 }
1683 static const char start[] = "[object XrayWrapper ";
1684 static const char end[] = "]";
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());
1691 if (type != XrayForWrappedNative) {
1692 JS_ReportError(cx, "XrayToString called on an incompatible object");
1693 return false;
1694 }
1696 nsAutoString result;
1697 result.AppendASCII(start);
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);
1709 result.AppendASCII(end);
1711 JSString *str = JS_NewUCStringCopyN(cx, result.get(), result.Length());
1712 if (!str)
1713 return false;
1715 args.rval().setString(str);
1716 return true;
1717 }
1719 #ifdef DEBUG
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 }
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
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 }
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 }
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 }
1790 typename Traits::ResolvingIdImpl resolving(cx, wrapper, id);
1792 if (!holder)
1793 return false;
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.
1814 // Check resolveOwnProperty.
1815 if (!Traits::singleton.resolveOwnProperty(cx, *this, wrapper, holder, id, desc))
1816 return false;
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 }
1826 // Nothing in the cache. Call through, and cache the result.
1827 if (!Traits::singleton.resolveNativeProperty(cx, wrapper, holder, id, desc))
1828 return false;
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 }
1854 if (!desc.object() &&
1855 id == nsXPConnect::GetRuntimeInstance()->GetStringID(XPCJSRuntime::IDX_TO_STRING))
1856 {
1858 JSFunction *toString = JS_NewFunction(cx, XrayToString, 0, 0, wrapper, "toString");
1859 if (!toString)
1860 return false;
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 }
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 }
1893 // If we still have nothing, we're done.
1894 if (!desc.object())
1895 return true;
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 }
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 }
1920 typename Traits::ResolvingIdImpl resolving(cx, wrapper, id);
1922 // NB: Nothing we do here acts on the wrapped native itself, so we don't
1923 // enter our policy.
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 }
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));
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.
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 }
1986 return true;
1987 }
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);
1996 Rooted<JSPropertyDescriptor> existing_desc(cx);
1997 if (!getOwnPropertyDescriptor(cx, wrapper, id, &existing_desc))
1998 return false;
2000 if (existing_desc.object() && existing_desc.isPermanent())
2001 return true; // silently ignore attempt to overwrite native property
2003 bool defined = false;
2004 if (!Traits::defineProperty(cx, wrapper, id, desc, existing_desc, &defined))
2005 return false;
2006 if (defined)
2007 return true;
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);
2014 // Grab the relevant expando object.
2015 RootedObject expandoObject(cx, Traits::singleton.ensureExpandoObject(cx, wrapper,
2016 target));
2017 if (!expandoObject)
2018 return false;
2020 // Wrap the property descriptor for the target compartment.
2021 Rooted<JSPropertyDescriptor> wrappedDesc(cx, desc);
2022 if (!JS_WrapPropertyDescriptor(cx, &wrappedDesc))
2023 return false;
2025 // Fix up Xray waivers.
2026 if (!RecreateLostWaivers(cx, desc.address(), &wrappedDesc))
2027 return false;
2029 return JS_DefinePropertyById(cx, expandoObject, id, wrappedDesc.value(),
2030 wrappedDesc.getter(), wrappedDesc.setter(),
2031 wrappedDesc.get().attrs);
2032 }
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 }
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);
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 }
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 }
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;
2084 return Traits::singleton.enumerateNames(cx, wrapper, flags, props);
2085 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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);
2196 RootedObject target(cx, Traits::getTargetObject(wrapper));
2197 RootedObject expando(cx, Traits::singleton.getExpandoObject(cx, target, wrapper));
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.
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);
2211 protop.set(v.toObjectOrNull());
2212 return JS_WrapObject(cx, protop);
2213 }
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);
2225 RootedObject target(cx, Traits::getTargetObject(wrapper));
2226 RootedObject expando(cx, Traits::singleton.ensureExpandoObject(cx, wrapper, target));
2228 // The expando lives in the target's compartment, so do our installation there.
2229 JSAutoCompartment ac(cx, target);
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 }
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 */
2247 template<>
2248 PermissiveXrayXPCWN PermissiveXrayXPCWN::singleton(0);
2249 template class PermissiveXrayXPCWN;
2251 template<>
2252 SecurityXrayXPCWN SecurityXrayXPCWN::singleton(0);
2253 template class SecurityXrayXPCWN;
2255 template<>
2256 PermissiveXrayDOM PermissiveXrayDOM::singleton(0);
2257 template class PermissiveXrayDOM;
2259 template<>
2260 SecurityXrayDOM SecurityXrayDOM::singleton(0);
2261 template class SecurityXrayDOM;
2263 template<>
2264 PermissiveXrayJS PermissiveXrayJS::singleton(0);
2265 template class PermissiveXrayJS;
2267 template<>
2268 SCSecurityXrayXPCWN SCSecurityXrayXPCWN::singleton(0);
2269 template class SCSecurityXrayXPCWN;
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 }
2289 return nsQueryInterface(nativeSupports);
2290 }
2292 }