js/xpconnect/wrappers/XrayWrapper.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial