LCOV - code coverage report
Current view: top level - js/xpconnect/wrappers - XrayWrapper.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 264 1187 22.2 %
Date: 2017-07-14 16:53:18 Functions: 38 198 19.2 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /* vim: set ts=8 sts=4 et sw=4 tw=99: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "XrayWrapper.h"
       8             : #include "AccessCheck.h"
       9             : #include "WrapperFactory.h"
      10             : 
      11             : #include "nsDependentString.h"
      12             : #include "nsIScriptError.h"
      13             : #include "mozilla/dom/Element.h"
      14             : #include "mozilla/dom/ScriptSettings.h"
      15             : 
      16             : #include "XPCWrapper.h"
      17             : #include "xpcprivate.h"
      18             : 
      19             : #include "jsapi.h"
      20             : #include "jsprf.h"
      21             : #include "nsJSUtils.h"
      22             : #include "nsPrintfCString.h"
      23             : 
      24             : #include "mozilla/dom/BindingUtils.h"
      25             : #include "mozilla/dom/WindowBinding.h"
      26             : #include "mozilla/dom/XrayExpandoClass.h"
      27             : #include "nsGlobalWindow.h"
      28             : 
      29             : using namespace mozilla::dom;
      30             : using namespace JS;
      31             : using namespace mozilla;
      32             : 
      33             : using js::Wrapper;
      34             : using js::BaseProxyHandler;
      35             : using js::IsCrossCompartmentWrapper;
      36             : using js::UncheckedUnwrap;
      37             : using js::CheckedUnwrap;
      38             : 
      39             : namespace xpc {
      40             : 
      41             : using namespace XrayUtils;
      42             : 
      43             : #define Between(x, a, b) (a <= x && x <= b)
      44             : 
      45             : static_assert(JSProto_URIError - JSProto_Error == 7, "New prototype added in error object range");
      46             : #define AssertErrorObjectKeyInBounds(key) \
      47             :     static_assert(Between(key, JSProto_Error, JSProto_URIError), "We depend on jsprototypes.h ordering here");
      48             : MOZ_FOR_EACH(AssertErrorObjectKeyInBounds, (),
      49             :              (JSProto_Error, JSProto_InternalError, JSProto_EvalError, JSProto_RangeError,
      50             :               JSProto_ReferenceError, JSProto_SyntaxError, JSProto_TypeError, JSProto_URIError));
      51             : 
      52             : static_assert(JSProto_Uint8ClampedArray - JSProto_Int8Array == 8, "New prototype added in typed array range");
      53             : #define AssertTypedArrayKeyInBounds(key) \
      54             :     static_assert(Between(key, JSProto_Int8Array, JSProto_Uint8ClampedArray), "We depend on jsprototypes.h ordering here");
      55             : MOZ_FOR_EACH(AssertTypedArrayKeyInBounds, (),
      56             :              (JSProto_Int8Array, JSProto_Uint8Array, JSProto_Int16Array, JSProto_Uint16Array,
      57             :               JSProto_Int32Array, JSProto_Uint32Array, JSProto_Float32Array, JSProto_Float64Array, JSProto_Uint8ClampedArray));
      58             : 
      59             : #undef Between
      60             : 
      61             : inline bool
      62        1138 : IsErrorObjectKey(JSProtoKey key)
      63             : {
      64        1138 :     return key >= JSProto_Error && key <= JSProto_URIError;
      65             : }
      66             : 
      67             : inline bool
      68        1138 : IsTypedArrayKey(JSProtoKey key)
      69             : {
      70        1138 :     return key >= JSProto_Int8Array && key <= JSProto_Uint8ClampedArray;
      71             : }
      72             : 
      73             : // Whitelist for the standard ES classes we can Xray to.
      74             : static bool
      75        1138 : IsJSXraySupported(JSProtoKey key)
      76             : {
      77        1138 :     if (IsTypedArrayKey(key))
      78           0 :         return true;
      79        1138 :     if (IsErrorObjectKey(key))
      80           0 :         return true;
      81        1138 :     switch (key) {
      82             :       case JSProto_Date:
      83             :       case JSProto_DataView:
      84             :       case JSProto_Object:
      85             :       case JSProto_Array:
      86             :       case JSProto_Function:
      87             :       case JSProto_TypedArray:
      88             :       case JSProto_SavedFrame:
      89             :       case JSProto_RegExp:
      90             :       case JSProto_Promise:
      91             :       case JSProto_ArrayBuffer:
      92             :       case JSProto_SharedArrayBuffer:
      93             :       case JSProto_Map:
      94             :       case JSProto_Set:
      95          12 :         return true;
      96             :       default:
      97        1126 :         return false;
      98             :     }
      99             : }
     100             : 
     101             : XrayType
     102        1272 : GetXrayType(JSObject* obj)
     103             : {
     104        1272 :     obj = js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false);
     105        1272 :     if (mozilla::dom::UseDOMXray(obj))
     106         134 :         return XrayForDOMObject;
     107             : 
     108        1138 :     const js::Class* clasp = js::GetObjectClass(obj);
     109        1138 :     if (IS_WN_CLASS(clasp) || js::IsWindowProxy(obj))
     110           0 :         return XrayForWrappedNative;
     111             : 
     112        1138 :     JSProtoKey standardProto = IdentifyStandardInstanceOrPrototype(obj);
     113        1138 :     if (IsJSXraySupported(standardProto))
     114          12 :         return XrayForJSObject;
     115             : 
     116             :     // Modulo a few exceptions, everything else counts as an XrayWrapper to an
     117             :     // opaque object, which means that more-privileged code sees nothing from
     118             :     // the underlying object. This is very important for security. In some cases
     119             :     // though, we need to make an exception for compatibility.
     120        1126 :     if (IsSandbox(obj))
     121           0 :         return NotXray;
     122             : 
     123        1126 :     return XrayForOpaqueObject;
     124             : }
     125             : 
     126             : JSObject*
     127           1 : XrayAwareCalleeGlobal(JSObject* fun)
     128             : {
     129           1 :   MOZ_ASSERT(js::IsFunctionObject(fun));
     130             : 
     131           1 :   if (!js::FunctionHasNativeReserved(fun)) {
     132             :       // Just a normal function, no Xrays involved.
     133           1 :       return js::GetGlobalForObjectCrossCompartment(fun);
     134             :   }
     135             : 
     136             :   // The functions we expect here have the Xray wrapper they're associated with
     137             :   // in their XRAY_DOM_FUNCTION_PARENT_WRAPPER_SLOT and, in a debug build,
     138             :   // themselves in their XRAY_DOM_FUNCTION_NATIVE_SLOT_FOR_SELF.  Assert that
     139             :   // last bit.
     140           0 :   MOZ_ASSERT(&js::GetFunctionNativeReserved(fun, XRAY_DOM_FUNCTION_NATIVE_SLOT_FOR_SELF).toObject() ==
     141             :              fun);
     142             : 
     143             :   Value v =
     144           0 :       js::GetFunctionNativeReserved(fun, XRAY_DOM_FUNCTION_PARENT_WRAPPER_SLOT);
     145           0 :   MOZ_ASSERT(IsXrayWrapper(&v.toObject()));
     146             : 
     147           0 :   JSObject* xrayTarget = js::UncheckedUnwrap(&v.toObject());
     148           0 :   return js::GetGlobalForObjectCrossCompartment(xrayTarget);
     149             : }
     150             : 
     151             : bool
     152           0 : XrayAwareCalleeGlobalForSpecializedGetters(JSContext* cx,
     153             :                                            JS::Handle<JSObject*> thisObj,
     154             :                                            JS::MutableHandle<JSObject*> global)
     155             : {
     156           0 :     JS::Rooted<JSObject*> wrappedObj(cx, thisObj);
     157           0 :     if (!JS_WrapObject(cx, &wrappedObj)) {
     158           0 :         return false;
     159             :     }
     160             : 
     161           0 :     if (xpc::WrapperFactory::IsXrayWrapper(wrappedObj)) {
     162             :         // Our current compartment would generaly get xrays to thisObj.  That
     163             :         // means we're presumably doing a call over Xrays, an the compartment of
     164             :         // the callee is presumably that of thisObj.  This isn't _necessarily_
     165             :         // true (e.g. chrome code could be using a chrome-side getter and doing
     166             :         // .call() with a content-side this value), but people shouldn't do
     167             :         // that!
     168             :         //
     169             :         // If someoen does do something weird here, the only impact is that we
     170             :         // will create the rejected promise that a promise-returning getter
     171             :         // creates around any exceptions it throws in the "wrong" compartment.
     172             :         // In particular, we might create it in the content compartment even
     173             :         // though we should really have created it in the chrome compartment
     174             :         // (for the case when a chrome getter is invoked with a content object
     175             :         // instead of just invoking the xrayed getter).
     176           0 :         global.set(js::GetGlobalForObjectCrossCompartment(thisObj));
     177           0 :         return true;
     178             :     }
     179             : 
     180           0 :     global.set(JS::CurrentGlobalOrNull(cx));
     181           0 :     return true;
     182             : }
     183             : 
     184             : JSObject*
     185        1181 : XrayTraits::getExpandoChain(HandleObject obj)
     186             : {
     187        1181 :     return ObjectScope(obj)->GetExpandoChain(obj);
     188             : }
     189             : 
     190             : bool
     191           0 : XrayTraits::setExpandoChain(JSContext* cx, HandleObject obj, HandleObject chain)
     192             : {
     193           0 :     return ObjectScope(obj)->SetExpandoChain(cx, obj, chain);
     194             : }
     195             : 
     196             : // static
     197             : XPCWrappedNative*
     198           0 : XPCWrappedNativeXrayTraits::getWN(JSObject* wrapper)
     199             : {
     200           0 :     return XPCWrappedNative::Get(getTargetObject(wrapper));
     201             : }
     202             : 
     203             : const JSClass XPCWrappedNativeXrayTraits::HolderClass = {
     204             :     "NativePropertyHolder", JSCLASS_HAS_RESERVED_SLOTS(HOLDER_SHARED_SLOT_COUNT)
     205             : };
     206             : 
     207             : const JSClass XrayTraits::HolderClass = {
     208             :     "XrayHolder", JSCLASS_HAS_RESERVED_SLOTS(HOLDER_SHARED_SLOT_COUNT)
     209             : };
     210             : 
     211             : const JSClass JSXrayTraits::HolderClass = {
     212             :     "JSXrayHolder", JSCLASS_HAS_RESERVED_SLOTS(SLOT_COUNT)
     213             : };
     214             : 
     215             : bool
     216           0 : OpaqueXrayTraits::resolveOwnProperty(JSContext* cx, HandleObject wrapper, HandleObject target,
     217             :                                      HandleObject holder, HandleId id,
     218             :                                      MutableHandle<PropertyDescriptor> desc)
     219             : {
     220           0 :     bool ok = XrayTraits::resolveOwnProperty(cx, wrapper, target, holder, id, desc);
     221           0 :     if (!ok || desc.object())
     222           0 :         return ok;
     223             : 
     224           0 :     return ReportWrapperDenial(cx, id, WrapperDenialForXray, "object is not safely Xrayable");
     225             : }
     226             : 
     227             : bool
     228           0 : ReportWrapperDenial(JSContext* cx, HandleId id, WrapperDenialType type, const char* reason)
     229             : {
     230           0 :     CompartmentPrivate* priv = CompartmentPrivate::Get(CurrentGlobalOrNull(cx));
     231           0 :     bool alreadyWarnedOnce = priv->wrapperDenialWarnings[type];
     232           0 :     priv->wrapperDenialWarnings[type] = true;
     233             : 
     234             :     // The browser console warning is only emitted for the first violation,
     235             :     // whereas the (debug-only) NS_WARNING is emitted for each violation.
     236             : #ifndef DEBUG
     237             :     if (alreadyWarnedOnce)
     238             :         return true;
     239             : #endif
     240             : 
     241           0 :     nsAutoJSString propertyName;
     242           0 :     RootedValue idval(cx);
     243           0 :     if (!JS_IdToValue(cx, id, &idval))
     244           0 :         return false;
     245           0 :     JSString* str = JS_ValueToSource(cx, idval);
     246           0 :     if (!str)
     247           0 :         return false;
     248           0 :     if (!propertyName.init(cx, str))
     249           0 :         return false;
     250           0 :     AutoFilename filename;
     251           0 :     unsigned line = 0, column = 0;
     252           0 :     DescribeScriptedCaller(cx, &filename, &line, &column);
     253             : 
     254             :     // Warn to the terminal for the logs.
     255           0 :     NS_WARNING(nsPrintfCString("Silently denied access to property %s: %s (@%s:%u:%u)",
     256             :                                NS_LossyConvertUTF16toASCII(propertyName).get(), reason,
     257           0 :                                filename.get(), line, column).get());
     258             : 
     259             :     // If this isn't the first warning on this topic for this global, we've
     260             :     // already bailed out in opt builds. Now that the NS_WARNING is done, bail
     261             :     // out in debug builds as well.
     262           0 :     if (alreadyWarnedOnce)
     263           0 :         return true;
     264             : 
     265             :     //
     266             :     // Log a message to the console service.
     267             :     //
     268             : 
     269             :     // Grab the pieces.
     270           0 :     nsCOMPtr<nsIConsoleService> consoleService = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
     271           0 :     NS_ENSURE_TRUE(consoleService, true);
     272           0 :     nsCOMPtr<nsIScriptError> errorObject = do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
     273           0 :     NS_ENSURE_TRUE(errorObject, true);
     274             : 
     275             :     // Compute the current window id if any.
     276           0 :     uint64_t windowId = 0;
     277           0 :     nsGlobalWindow* win = WindowGlobalOrNull(CurrentGlobalOrNull(cx));
     278           0 :     if (win)
     279           0 :       windowId = win->WindowID();
     280             : 
     281             : 
     282           0 :     Maybe<nsPrintfCString> errorMessage;
     283           0 :     if (type == WrapperDenialForXray) {
     284             :         errorMessage.emplace("XrayWrapper denied access to property %s (reason: %s). "
     285             :                              "See https://developer.mozilla.org/en-US/docs/Xray_vision "
     286             :                              "for more information. Note that only the first denied "
     287             :                              "property access from a given global object will be reported.",
     288           0 :                              NS_LossyConvertUTF16toASCII(propertyName).get(),
     289           0 :                              reason);
     290             :     } else {
     291           0 :         MOZ_ASSERT(type == WrapperDenialForCOW);
     292             :         errorMessage.emplace("Security wrapper denied access to property %s on privileged "
     293             :                              "Javascript object. Support for exposing privileged objects "
     294             :                              "to untrusted content via __exposedProps__ is being gradually "
     295             :                              "removed - use WebIDL bindings or Components.utils.cloneInto "
     296             :                              "instead. Note that only the first denied property access from a "
     297             :                              "given global object will be reported.",
     298           0 :                              NS_LossyConvertUTF16toASCII(propertyName).get());
     299             :     }
     300           0 :     nsString filenameStr(NS_ConvertASCIItoUTF16(filename.get()));
     301           0 :     nsresult rv = errorObject->InitWithWindowID(NS_ConvertASCIItoUTF16(errorMessage.ref()),
     302             :                                                 filenameStr,
     303           0 :                                                 EmptyString(),
     304             :                                                 line, column,
     305             :                                                 nsIScriptError::warningFlag,
     306             :                                                 "XPConnect",
     307           0 :                                                 windowId);
     308           0 :     NS_ENSURE_SUCCESS(rv, true);
     309           0 :     rv = consoleService->LogMessage(errorObject);
     310           0 :     NS_ENSURE_SUCCESS(rv, true);
     311             : 
     312           0 :     return true;
     313             : }
     314             : 
     315          36 : bool JSXrayTraits::getOwnPropertyFromWrapperIfSafe(JSContext* cx,
     316             :                                                    HandleObject wrapper,
     317             :                                                    HandleId id,
     318             :                                                    MutableHandle<PropertyDescriptor> outDesc)
     319             : {
     320          36 :     MOZ_ASSERT(js::IsObjectInContextCompartment(wrapper, cx));
     321          72 :     RootedObject target(cx, getTargetObject(wrapper));
     322             :     {
     323          72 :         JSAutoCompartment ac(cx, target);
     324          36 :         JS_MarkCrossZoneId(cx, id);
     325          36 :         if (!getOwnPropertyFromTargetIfSafe(cx, target, wrapper, id, outDesc))
     326           0 :             return false;
     327             :     }
     328          36 :     return JS_WrapPropertyDescriptor(cx, outDesc);
     329             : }
     330             : 
     331          43 : bool JSXrayTraits::getOwnPropertyFromTargetIfSafe(JSContext* cx,
     332             :                                                   HandleObject target,
     333             :                                                   HandleObject wrapper,
     334             :                                                   HandleId id,
     335             :                                                   MutableHandle<PropertyDescriptor> outDesc)
     336             : {
     337             :     // Note - This function operates in the target compartment, because it
     338             :     // avoids a bunch of back-and-forth wrapping in enumerateNames.
     339          43 :     MOZ_ASSERT(getTargetObject(wrapper) == target);
     340          43 :     MOZ_ASSERT(js::IsObjectInContextCompartment(target, cx));
     341          43 :     MOZ_ASSERT(WrapperFactory::IsXrayWrapper(wrapper));
     342          43 :     MOZ_ASSERT(outDesc.object() == nullptr);
     343             : 
     344          86 :     Rooted<PropertyDescriptor> desc(cx);
     345          43 :     if (!JS_GetOwnPropertyDescriptorById(cx, target, id, &desc))
     346           0 :         return false;
     347             : 
     348             :     // If the property doesn't exist at all, we're done.
     349          43 :     if (!desc.object())
     350           2 :         return true;
     351             : 
     352             :     // Disallow accessor properties.
     353          41 :     if (desc.hasGetterOrSetter()) {
     354           0 :         JSAutoCompartment ac(cx, wrapper);
     355           0 :         JS_MarkCrossZoneId(cx, id);
     356           0 :         return ReportWrapperDenial(cx, id, WrapperDenialForXray, "property has accessor");
     357             :     }
     358             : 
     359             :     // Apply extra scrutiny to objects.
     360          41 :     if (desc.value().isObject()) {
     361           8 :         RootedObject propObj(cx, js::UncheckedUnwrap(&desc.value().toObject()));
     362           8 :         JSAutoCompartment ac(cx, propObj);
     363             : 
     364             :         // Disallow non-subsumed objects.
     365           4 :         if (!AccessCheck::subsumes(target, propObj)) {
     366           0 :             JSAutoCompartment ac(cx, wrapper);
     367           0 :             JS_MarkCrossZoneId(cx, id);
     368           0 :             return ReportWrapperDenial(cx, id, WrapperDenialForXray, "value not same-origin with target");
     369             :         }
     370             : 
     371             :         // Disallow non-Xrayable objects.
     372           4 :         XrayType xrayType = GetXrayType(propObj);
     373           4 :         if (xrayType == NotXray || xrayType == XrayForOpaqueObject) {
     374           0 :             JSAutoCompartment ac(cx, wrapper);
     375           0 :             JS_MarkCrossZoneId(cx, id);
     376           0 :             return ReportWrapperDenial(cx, id, WrapperDenialForXray, "value not Xrayable");
     377             :         }
     378             : 
     379             :         // Disallow callables.
     380           4 :         if (JS::IsCallable(propObj)) {
     381           0 :             JSAutoCompartment ac(cx, wrapper);
     382           0 :             JS_MarkCrossZoneId(cx, id);
     383           0 :             return ReportWrapperDenial(cx, id, WrapperDenialForXray, "value is callable");
     384             :         }
     385             :     }
     386             : 
     387             :     // Disallow any property that shadows something on its (Xrayed)
     388             :     // prototype chain.
     389          82 :     JSAutoCompartment ac2(cx, wrapper);
     390          41 :     JS_MarkCrossZoneId(cx, id);
     391          82 :     RootedObject proto(cx);
     392          41 :     bool foundOnProto = false;
     393         123 :     if (!JS_GetPrototype(cx, wrapper, &proto) ||
     394         123 :         (proto && !JS_HasPropertyById(cx, proto, id, &foundOnProto)))
     395             :     {
     396           0 :         return false;
     397             :     }
     398          41 :     if (foundOnProto)
     399           0 :         return ReportWrapperDenial(cx, id, WrapperDenialForXray, "value shadows a property on the standard prototype");
     400             : 
     401             :     // We made it! Assign over the descriptor, and don't forget to wrap.
     402          41 :     outDesc.assign(desc.get());
     403          41 :     return true;
     404             : }
     405             : 
     406             : // Returns true on success (in the JSAPI sense), false on failure.  If true is
     407             : // returned, desc.object() will indicate whether we actually resolved
     408             : // the property.
     409             : //
     410             : // id is the property id we're looking for.
     411             : // holder is the object to define the property on.
     412             : // fs is the relevant JSFunctionSpec*.
     413             : // ps is the relevant JSPropertySpec*.
     414             : // desc is the descriptor we're resolving into.
     415             : static bool
     416          43 : TryResolvePropertyFromSpecs(JSContext* cx, HandleId id, HandleObject holder,
     417             :                             const JSFunctionSpec* fs,
     418             :                             const JSPropertySpec* ps,
     419             :                             MutableHandle<PropertyDescriptor> desc)
     420             : {
     421             :     // Scan through the functions.
     422          43 :     const JSFunctionSpec* fsMatch = nullptr;
     423        1161 :     for ( ; fs && fs->name; ++fs) {
     424         559 :         if (PropertySpecNameEqualsId(fs->name, id)) {
     425           0 :             fsMatch = fs;
     426           0 :             break;
     427             :         }
     428             :     }
     429          43 :     if (fsMatch) {
     430             :         // Generate an Xrayed version of the method.
     431           0 :         RootedFunction fun(cx, JS::NewFunctionFromSpec(cx, fsMatch, id));
     432           0 :         if (!fun)
     433           0 :             return false;
     434             : 
     435             :         // The generic Xray machinery only defines non-own properties of the target on
     436             :         // the holder. This is broken, and will be fixed at some point, but for now we
     437             :         // need to cache the value explicitly. See the corresponding call to
     438             :         // JS_GetOwnPropertyDescriptorById at the top of
     439             :         // JSXrayTraits::resolveOwnProperty.
     440           0 :         RootedObject funObj(cx, JS_GetFunctionObject(fun));
     441           0 :         return JS_DefinePropertyById(cx, holder, id, funObj, 0) &&
     442           0 :                JS_GetOwnPropertyDescriptorById(cx, holder, id, desc);
     443             :     }
     444             : 
     445             :     // Scan through the properties.
     446          43 :     const JSPropertySpec* psMatch = nullptr;
     447         129 :     for ( ; ps && ps->name; ++ps) {
     448          43 :         if (PropertySpecNameEqualsId(ps->name, id)) {
     449           0 :             psMatch = ps;
     450           0 :             break;
     451             :         }
     452             :     }
     453          43 :     if (psMatch) {
     454           0 :         desc.value().setUndefined();
     455           0 :         RootedFunction getterObj(cx);
     456           0 :         RootedFunction setterObj(cx);
     457           0 :         unsigned flags = psMatch->flags;
     458           0 :         if (psMatch->isAccessor()) {
     459           0 :             if (psMatch->isSelfHosted()) {
     460           0 :                 getterObj = JS::GetSelfHostedFunction(cx, psMatch->accessors.getter.selfHosted.funname, id, 0);
     461           0 :                 if (!getterObj)
     462           0 :                     return false;
     463           0 :                 desc.setGetterObject(JS_GetFunctionObject(getterObj));
     464           0 :                 if (psMatch->accessors.setter.selfHosted.funname) {
     465           0 :                     MOZ_ASSERT(flags & JSPROP_SETTER);
     466           0 :                     setterObj = JS::GetSelfHostedFunction(cx, psMatch->accessors.setter.selfHosted.funname, id, 0);
     467           0 :                     if (!setterObj)
     468           0 :                         return false;
     469           0 :                     desc.setSetterObject(JS_GetFunctionObject(setterObj));
     470             :                 }
     471             :             } else {
     472           0 :                 desc.setGetter(JS_CAST_NATIVE_TO(psMatch->accessors.getter.native.op,
     473           0 :                                                  JSGetterOp));
     474           0 :                 desc.setSetter(JS_CAST_NATIVE_TO(psMatch->accessors.setter.native.op,
     475           0 :                                                  JSSetterOp));
     476             :             }
     477           0 :             desc.setAttributes(flags);
     478             :         } else {
     479           0 :             RootedValue v(cx);
     480           0 :             if (!psMatch->getValue(cx, &v))
     481           0 :                 return false;
     482           0 :             desc.value().set(v);
     483           0 :             desc.setAttributes(flags & ~JSPROP_INTERNAL_USE_BIT);
     484             :         }
     485             : 
     486             :         // The generic Xray machinery only defines non-own properties on the holder.
     487             :         // This is broken, and will be fixed at some point, but for now we need to
     488             :         // cache the value explicitly. See the corresponding call to
     489             :         // JS_GetPropertyById at the top of JSXrayTraits::resolveOwnProperty.
     490             :         //
     491             :         // Note also that the public-facing API here doesn't give us a way to
     492             :         // pass along JITInfo. It's probably ok though, since Xrays are already
     493             :         // pretty slow.
     494           0 :         return JS_DefinePropertyById(cx, holder, id,
     495             :                                      desc.value(),
     496             :                                      // This particular descriptor, unlike most,
     497             :                                      // actually stores JSNatives directly,
     498             :                                      // since we just set it up.  Do NOT pass
     499             :                                      // JSPROP_PROPOP_ACCESSORS here!
     500             :                                      desc.attributes(),
     501           0 :                                      JS_PROPERTYOP_GETTER(desc.getter()),
     502           0 :                                      JS_PROPERTYOP_SETTER(desc.setter())) &&
     503           0 :                JS_GetOwnPropertyDescriptorById(cx, holder, id, desc);
     504             :     }
     505             : 
     506          43 :     return true;
     507             : }
     508             : 
     509             : static bool
     510           0 : ShouldResolveStaticProperties(JSProtoKey key)
     511             : {
     512             :     // Don't try to resolve static properties on RegExp, because they
     513             :     // have issues.  In particular, some of them grab state off the
     514             :     // global of the RegExp constructor that describes the last regexp
     515             :     // evaluation in that global, which is not a useful thing to do
     516             :     // over Xrays.
     517           0 :     return key != JSProto_RegExp;
     518             : }
     519             : 
     520             : bool
     521          79 : JSXrayTraits::resolveOwnProperty(JSContext* cx, HandleObject wrapper,
     522             :                                  HandleObject target, HandleObject holder,
     523             :                                  HandleId id,
     524             :                                  MutableHandle<PropertyDescriptor> desc)
     525             : {
     526             :     // Call the common code.
     527          79 :     bool ok = XrayTraits::resolveOwnProperty(cx, wrapper, target, holder,
     528          79 :                                              id, desc);
     529          79 :     if (!ok || desc.object())
     530           0 :         return ok;
     531             : 
     532             :     // The non-HasPrototypes semantics implemented by traditional Xrays are kind
     533             :     // of broken with respect to |own|-ness and the holder. The common code
     534             :     // muddles through by only checking the holder for non-|own| lookups, but
     535             :     // that doesn't work for us. So we do an explicit holder check here, and hope
     536             :     // that this mess gets fixed up soon.
     537          79 :     if (!JS_GetOwnPropertyDescriptorById(cx, holder, id, desc))
     538           0 :         return false;
     539          79 :     if (desc.object()) {
     540           0 :         desc.object().set(wrapper);
     541           0 :         return true;
     542             :     }
     543             : 
     544          79 :     JSProtoKey key = getProtoKey(holder);
     545          79 :     if (!isPrototype(holder)) {
     546             :         // For Object and Array instances, we expose some properties from the
     547             :         // underlying object, but only after filtering them carefully.
     548             :         //
     549             :         // Note that, as far as JS observables go, Arrays are just Objects with
     550             :         // a different prototype and a magic (own, non-configurable) |.length| that
     551             :         // serves as a non-tight upper bound on |own| indexed properties. So while
     552             :         // it's tempting to try to impose some sort of structure on what Arrays
     553             :         // "should" look like over Xrays, the underlying object is squishy enough
     554             :         // that it makes sense to just treat them like Objects for Xray purposes.
     555          36 :         if (key == JSProto_Object || key == JSProto_Array) {
     556          36 :             return getOwnPropertyFromWrapperIfSafe(cx, wrapper, id, desc);
     557             :         }
     558           0 :         if (IsTypedArrayKey(key)) {
     559           0 :             if (IsArrayIndex(GetArrayIndexFromId(cx, id))) {
     560             :                 // WebExtensions can't use cloneInto(), so we just let them do
     561             :                 // the slow thing to maximize compatibility.
     562           0 :                 if (CompartmentPrivate::Get(CurrentGlobalOrNull(cx))->isWebExtensionContentScript) {
     563           0 :                     Rooted<PropertyDescriptor> innerDesc(cx);
     564             :                     {
     565           0 :                         JSAutoCompartment ac(cx, target);
     566           0 :                         JS_MarkCrossZoneId(cx, id);
     567           0 :                         if (!JS_GetOwnPropertyDescriptorById(cx, target, id, &innerDesc))
     568           0 :                             return false;
     569             :                     }
     570           0 :                     if (innerDesc.isDataDescriptor() && innerDesc.value().isNumber()) {
     571           0 :                         desc.setValue(innerDesc.value());
     572           0 :                         desc.object().set(wrapper);
     573             :                     }
     574           0 :                     return true;
     575             :                 }
     576             :                 JS_ReportErrorASCII(cx, "Accessing TypedArray data over Xrays is slow, and forbidden "
     577             :                                     "in order to encourage performant code. To copy TypedArrays "
     578           0 :                                     "across origin boundaries, consider using Components.utils.cloneInto().");
     579           0 :                 return false;
     580             :             }
     581           0 :         } else if (key == JSProto_Function) {
     582           0 :             if (id == GetJSIDByIndex(cx, XPCJSContext::IDX_LENGTH)) {
     583           0 :                 FillPropertyDescriptor(desc, wrapper, JSPROP_PERMANENT | JSPROP_READONLY,
     584           0 :                                        NumberValue(JS_GetFunctionArity(JS_GetObjectFunction(target))));
     585           0 :                 return true;
     586             :             }
     587           0 :             if (id == GetJSIDByIndex(cx, XPCJSContext::IDX_NAME)) {
     588           0 :                 RootedString fname(cx, JS_GetFunctionId(JS_GetObjectFunction(target)));
     589           0 :                 if (fname)
     590           0 :                     JS_MarkCrossZoneIdValue(cx, StringValue(fname));
     591           0 :                 FillPropertyDescriptor(desc, wrapper, JSPROP_PERMANENT | JSPROP_READONLY,
     592           0 :                                        fname ? StringValue(fname) : JS_GetEmptyStringValue(cx));
     593             :             } else {
     594             :                 // Look for various static properties/methods and the
     595             :                 // 'prototype' property.
     596           0 :                 JSProtoKey standardConstructor = constructorFor(holder);
     597           0 :                 if (standardConstructor != JSProto_Null) {
     598             :                     // Handle the 'prototype' property to make
     599             :                     // xrayedGlobal.StandardClass.prototype work.
     600           0 :                     if (id == GetJSIDByIndex(cx, XPCJSContext::IDX_PROTOTYPE)) {
     601           0 :                         RootedObject standardProto(cx);
     602             :                         {
     603           0 :                             JSAutoCompartment ac(cx, target);
     604           0 :                             if (!JS_GetClassPrototype(cx, standardConstructor, &standardProto))
     605           0 :                                 return false;
     606           0 :                             MOZ_ASSERT(standardProto);
     607             :                         }
     608             : 
     609           0 :                         if (!JS_WrapObject(cx, &standardProto))
     610           0 :                             return false;
     611           0 :                         FillPropertyDescriptor(desc, wrapper, JSPROP_PERMANENT | JSPROP_READONLY,
     612           0 :                                                ObjectValue(*standardProto));
     613           0 :                         return true;
     614             :                     }
     615             : 
     616           0 :                     if (ShouldResolveStaticProperties(standardConstructor)) {
     617           0 :                         const js::Class* clasp = js::ProtoKeyToClass(standardConstructor);
     618           0 :                         MOZ_ASSERT(clasp->specDefined());
     619             : 
     620           0 :                         if (!TryResolvePropertyFromSpecs(cx, id, holder,
     621             :                                clasp->specConstructorFunctions(),
     622             :                                clasp->specConstructorProperties(), desc)) {
     623           0 :                             return false;
     624             :                         }
     625             : 
     626           0 :                         if (desc.object()) {
     627           0 :                             desc.object().set(wrapper);
     628           0 :                             return true;
     629             :                         }
     630             :                     }
     631             :                 }
     632             :             }
     633           0 :         } else if (IsErrorObjectKey(key)) {
     634             :             // The useful state of error objects (except for .stack) is
     635             :             // (unfortunately) represented as own data properties per-spec. This
     636             :             // means that we can't have a a clean representation of the data
     637             :             // (free from tampering) without doubling the slots of Error
     638             :             // objects, which isn't great. So we forward these properties to the
     639             :             // underlying object and then just censor any values with the wrong
     640             :             // type. This limits the ability of content to do anything all that
     641             :             // confusing.
     642             :             bool isErrorIntProperty =
     643           0 :                 id == GetJSIDByIndex(cx, XPCJSContext::IDX_LINENUMBER) ||
     644           0 :                 id == GetJSIDByIndex(cx, XPCJSContext::IDX_COLUMNNUMBER);
     645             :             bool isErrorStringProperty =
     646           0 :                 id == GetJSIDByIndex(cx, XPCJSContext::IDX_FILENAME) ||
     647           0 :                 id == GetJSIDByIndex(cx, XPCJSContext::IDX_MESSAGE);
     648           0 :             if (isErrorIntProperty || isErrorStringProperty) {
     649           0 :                 RootedObject waiver(cx, wrapper);
     650           0 :                 if (!WrapperFactory::WaiveXrayAndWrap(cx, &waiver))
     651           0 :                     return false;
     652           0 :                 if (!JS_GetOwnPropertyDescriptorById(cx, waiver, id, desc))
     653           0 :                     return false;
     654           0 :                 bool valueMatchesType = (isErrorIntProperty && desc.value().isInt32()) ||
     655           0 :                                         (isErrorStringProperty && desc.value().isString());
     656           0 :                 if (desc.hasGetterOrSetter() || !valueMatchesType)
     657           0 :                     FillPropertyDescriptor(desc, nullptr, 0, UndefinedValue());
     658           0 :                 return true;
     659             :             }
     660           0 :         } else if (key == JSProto_RegExp) {
     661           0 :             if (id == GetJSIDByIndex(cx, XPCJSContext::IDX_LASTINDEX))
     662           0 :                 return getOwnPropertyFromWrapperIfSafe(cx, wrapper, id, desc);
     663             :         }
     664             : 
     665             :         // The rest of this function applies only to prototypes.
     666           0 :         return true;
     667             :     }
     668             : 
     669             :     // Handle the 'constructor' property.
     670          43 :     if (id == GetJSIDByIndex(cx, XPCJSContext::IDX_CONSTRUCTOR)) {
     671           0 :         RootedObject constructor(cx);
     672             :         {
     673           0 :             JSAutoCompartment ac(cx, target);
     674           0 :             if (!JS_GetClassObject(cx, key, &constructor))
     675           0 :                 return false;
     676             :         }
     677           0 :         if (!JS_WrapObject(cx, &constructor))
     678           0 :             return false;
     679           0 :         desc.object().set(wrapper);
     680           0 :         desc.setAttributes(0);
     681           0 :         desc.setGetter(nullptr);
     682           0 :         desc.setSetter(nullptr);
     683           0 :         desc.value().setObject(*constructor);
     684           0 :         return true;
     685             :     }
     686             : 
     687             :     // Grab the JSClass. We require all Xrayable classes to have a ClassSpec.
     688          43 :     const js::Class* clasp = js::GetObjectClass(target);
     689          43 :     MOZ_ASSERT(clasp->specDefined());
     690             : 
     691             :     // Indexed array properties are handled above, so we can just work with the
     692             :     // class spec here.
     693          43 :     if (!TryResolvePropertyFromSpecs(cx, id, holder,
     694             :                                      clasp->specPrototypeFunctions(),
     695             :                                      clasp->specPrototypeProperties(),
     696             :                                      desc)) {
     697           0 :         return false;
     698             :     }
     699             : 
     700          43 :     if (desc.object()) {
     701           0 :         desc.object().set(wrapper);
     702             :     }
     703             : 
     704          43 :     return true;
     705             : }
     706             : 
     707             : bool
     708           0 : JSXrayTraits::delete_(JSContext* cx, HandleObject wrapper, HandleId id, ObjectOpResult& result)
     709             : {
     710           0 :     RootedObject holder(cx, ensureHolder(cx, wrapper));
     711             : 
     712             :     // If we're using Object Xrays, we allow callers to attempt to delete any
     713             :     // property from the underlying object that they are able to resolve. Note
     714             :     // that this deleting may fail if the property is non-configurable.
     715           0 :     JSProtoKey key = getProtoKey(holder);
     716           0 :     bool isObjectOrArrayInstance = (key == JSProto_Object || key == JSProto_Array) &&
     717           0 :                                    !isPrototype(holder);
     718           0 :     if (isObjectOrArrayInstance) {
     719           0 :         RootedObject target(cx, getTargetObject(wrapper));
     720           0 :         JSAutoCompartment ac(cx, target);
     721           0 :         JS_MarkCrossZoneId(cx, id);
     722           0 :         Rooted<PropertyDescriptor> desc(cx);
     723           0 :         if (!getOwnPropertyFromTargetIfSafe(cx, target, wrapper, id, &desc))
     724           0 :             return false;
     725           0 :         if (desc.object())
     726           0 :             return JS_DeletePropertyById(cx, target, id, result);
     727             :     }
     728           0 :     return result.succeed();
     729             : }
     730             : 
     731             : bool
     732           0 : JSXrayTraits::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
     733             :                              Handle<PropertyDescriptor> desc,
     734             :                              Handle<PropertyDescriptor> existingDesc,
     735             :                              ObjectOpResult& result,
     736             :                              bool* defined)
     737             : {
     738           0 :     *defined = false;
     739           0 :     RootedObject holder(cx, ensureHolder(cx, wrapper));
     740           0 :     if (!holder)
     741           0 :         return false;
     742             : 
     743             : 
     744             :     // Object and Array instances are special. For those cases, we forward property
     745             :     // definitions to the underlying object if the following conditions are met:
     746             :     // * The property being defined is a value-prop.
     747             :     // * The property being defined is either a primitive or subsumed by the target.
     748             :     // * As seen from the Xray, any existing property that we would overwrite is an
     749             :     //   |own| value-prop.
     750             :     //
     751             :     // To avoid confusion, we disallow expandos on Object and Array instances, and
     752             :     // therefore raise an exception here if the above conditions aren't met.
     753           0 :     JSProtoKey key = getProtoKey(holder);
     754           0 :     bool isInstance = !isPrototype(holder);
     755           0 :     bool isObjectOrArray = (key == JSProto_Object || key == JSProto_Array);
     756           0 :     if (isObjectOrArray && isInstance) {
     757           0 :         RootedObject target(cx, getTargetObject(wrapper));
     758           0 :         if (desc.hasGetterOrSetter()) {
     759           0 :             JS_ReportErrorASCII(cx, "Not allowed to define accessor property on [Object] or [Array] XrayWrapper");
     760           0 :             return false;
     761             :         }
     762           0 :         if (desc.value().isObject() &&
     763           0 :             !AccessCheck::subsumes(target, js::UncheckedUnwrap(&desc.value().toObject())))
     764             :         {
     765           0 :             JS_ReportErrorASCII(cx, "Not allowed to define cross-origin object as property on [Object] or [Array] XrayWrapper");
     766           0 :             return false;
     767             :         }
     768           0 :         if (existingDesc.hasGetterOrSetter()) {
     769           0 :             JS_ReportErrorASCII(cx, "Not allowed to overwrite accessor property on [Object] or [Array] XrayWrapper");
     770           0 :             return false;
     771             :         }
     772           0 :         if (existingDesc.object() && existingDesc.object() != wrapper) {
     773           0 :             JS_ReportErrorASCII(cx, "Not allowed to shadow non-own Xray-resolved property on [Object] or [Array] XrayWrapper");
     774           0 :             return false;
     775             :         }
     776             : 
     777           0 :         Rooted<PropertyDescriptor> wrappedDesc(cx, desc);
     778           0 :         JSAutoCompartment ac(cx, target);
     779           0 :         JS_MarkCrossZoneId(cx, id);
     780           0 :         if (!JS_WrapPropertyDescriptor(cx, &wrappedDesc) ||
     781           0 :             !JS_DefinePropertyById(cx, target, id, wrappedDesc, result))
     782             :         {
     783           0 :             return false;
     784             :         }
     785           0 :         *defined = true;
     786           0 :         return true;
     787             :     }
     788             : 
     789             :     // For WebExtensions content scripts, we forward the definition of indexed properties. By
     790             :     // validating that the key and value are both numbers, we can avoid doing any wrapping.
     791           0 :     if (isInstance && IsTypedArrayKey(key) &&
     792           0 :         CompartmentPrivate::Get(JS::CurrentGlobalOrNull(cx))->isWebExtensionContentScript &&
     793           0 :         desc.isDataDescriptor() && (desc.value().isNumber() || desc.value().isUndefined()) &&
     794           0 :         IsArrayIndex(GetArrayIndexFromId(cx, id)))
     795             :     {
     796           0 :         RootedObject target(cx, getTargetObject(wrapper));
     797           0 :         JSAutoCompartment ac(cx, target);
     798           0 :         JS_MarkCrossZoneId(cx, id);
     799           0 :         if (!JS_DefinePropertyById(cx, target, id, desc, result))
     800           0 :             return false;
     801           0 :         *defined = true;
     802           0 :         return true;
     803             :     }
     804             : 
     805           0 :     return true;
     806             : }
     807             : 
     808             : static bool
     809           0 : MaybeAppend(jsid id, unsigned flags, AutoIdVector& props)
     810             : {
     811           0 :     MOZ_ASSERT(!(flags & JSITER_SYMBOLSONLY));
     812           0 :     if (!(flags & JSITER_SYMBOLS) && JSID_IS_SYMBOL(id))
     813           0 :         return true;
     814           0 :     return props.append(id);
     815             : }
     816             : 
     817             : // Append the names from the given function and property specs to props.
     818             : static bool
     819           0 : AppendNamesFromFunctionAndPropertySpecs(JSContext* cx,
     820             :                                         const JSFunctionSpec* fs,
     821             :                                         const JSPropertySpec* ps,
     822             :                                         unsigned flags,
     823             :                                         AutoIdVector& props)
     824             : {
     825             :     // Convert the method and property names to jsids and pass them to the caller.
     826           0 :     for ( ; fs && fs->name; ++fs) {
     827             :         jsid id;
     828           0 :         if (!PropertySpecNameToPermanentId(cx, fs->name, &id))
     829           0 :             return false;
     830           0 :         if (!MaybeAppend(id, flags, props))
     831           0 :             return false;
     832             :     }
     833           0 :     for ( ; ps && ps->name; ++ps) {
     834             :         jsid id;
     835           0 :         if (!PropertySpecNameToPermanentId(cx, ps->name, &id))
     836           0 :             return false;
     837           0 :         if (!MaybeAppend(id, flags, props))
     838           0 :             return false;
     839             :     }
     840             : 
     841           0 :     return true;
     842             : }
     843             : 
     844             : bool
     845           2 : JSXrayTraits::enumerateNames(JSContext* cx, HandleObject wrapper, unsigned flags,
     846             :                              AutoIdVector& props)
     847             : {
     848           4 :     RootedObject target(cx, getTargetObject(wrapper));
     849           4 :     RootedObject holder(cx, ensureHolder(cx, wrapper));
     850           2 :     if (!holder)
     851           0 :         return false;
     852             : 
     853           2 :     JSProtoKey key = getProtoKey(holder);
     854           2 :     if (!isPrototype(holder)) {
     855             :         // For Object and Array instances, we expose some properties from the underlying
     856             :         // object, but only after filtering them carefully.
     857           2 :         if (key == JSProto_Object || key == JSProto_Array) {
     858           2 :             MOZ_ASSERT(props.empty());
     859             :             {
     860           4 :                 JSAutoCompartment ac(cx, target);
     861           4 :                 AutoIdVector targetProps(cx);
     862           2 :                 if (!js::GetPropertyKeys(cx, target, flags | JSITER_OWNONLY, &targetProps))
     863           0 :                     return false;
     864             :                 // Loop over the properties, and only pass along the ones that
     865             :                 // we determine to be safe.
     866           2 :                 if (!props.reserve(targetProps.length()))
     867           0 :                     return false;
     868           9 :                 for (size_t i = 0; i < targetProps.length(); ++i) {
     869          14 :                     Rooted<PropertyDescriptor> desc(cx);
     870          14 :                     RootedId id(cx, targetProps[i]);
     871           7 :                     if (!getOwnPropertyFromTargetIfSafe(cx, target, wrapper, id, &desc))
     872           0 :                         return false;
     873           7 :                     if (desc.object())
     874           7 :                         props.infallibleAppend(id);
     875             :                 }
     876             :             }
     877           9 :             for (size_t i = 0; i < props.length(); ++i)
     878           7 :                 JS_MarkCrossZoneId(cx, props[i]);
     879           2 :             return true;
     880             :         }
     881           0 :         if (IsTypedArrayKey(key)) {
     882           0 :             uint32_t length = JS_GetTypedArrayLength(target);
     883             :             // TypedArrays enumerate every indexed property in range, but
     884             :             // |length| is a getter that lives on the proto, like it should be.
     885           0 :             if (!props.reserve(length))
     886           0 :                 return false;
     887           0 :             for (int32_t i = 0; i <= int32_t(length - 1); ++i)
     888           0 :                 props.infallibleAppend(INT_TO_JSID(i));
     889           0 :         } else if (key == JSProto_Function) {
     890           0 :             if (!props.append(GetJSIDByIndex(cx, XPCJSContext::IDX_LENGTH)))
     891           0 :                 return false;
     892           0 :             if (!props.append(GetJSIDByIndex(cx, XPCJSContext::IDX_NAME)))
     893           0 :                 return false;
     894             :             // Handle the .prototype property and static properties on standard
     895             :             // constructors.
     896           0 :             JSProtoKey standardConstructor = constructorFor(holder);
     897           0 :             if (standardConstructor != JSProto_Null) {
     898           0 :                 if (!props.append(GetJSIDByIndex(cx, XPCJSContext::IDX_PROTOTYPE)))
     899           0 :                     return false;
     900             : 
     901           0 :                 if (ShouldResolveStaticProperties(standardConstructor)) {
     902           0 :                     const js::Class* clasp = js::ProtoKeyToClass(standardConstructor);
     903           0 :                     MOZ_ASSERT(clasp->specDefined());
     904             : 
     905           0 :                     if (!AppendNamesFromFunctionAndPropertySpecs(
     906             :                            cx, clasp->specConstructorFunctions(),
     907             :                            clasp->specConstructorProperties(), flags, props)) {
     908           0 :                         return false;
     909             :                     }
     910             :                 }
     911             :             }
     912           0 :         } else if (IsErrorObjectKey(key)) {
     913           0 :             if (!props.append(GetJSIDByIndex(cx, XPCJSContext::IDX_FILENAME)) ||
     914           0 :                 !props.append(GetJSIDByIndex(cx, XPCJSContext::IDX_LINENUMBER)) ||
     915           0 :                 !props.append(GetJSIDByIndex(cx, XPCJSContext::IDX_COLUMNNUMBER)) ||
     916           0 :                 !props.append(GetJSIDByIndex(cx, XPCJSContext::IDX_STACK)) ||
     917           0 :                 !props.append(GetJSIDByIndex(cx, XPCJSContext::IDX_MESSAGE)))
     918             :             {
     919           0 :                 return false;
     920             :             }
     921           0 :         } else if (key == JSProto_RegExp) {
     922           0 :             if (!props.append(GetJSIDByIndex(cx, XPCJSContext::IDX_LASTINDEX)))
     923           0 :                 return false;
     924             :         }
     925             : 
     926             :         // The rest of this function applies only to prototypes.
     927           0 :         return true;
     928             :     }
     929             : 
     930             :     // Add the 'constructor' property.
     931           0 :     if (!props.append(GetJSIDByIndex(cx, XPCJSContext::IDX_CONSTRUCTOR)))
     932           0 :         return false;
     933             : 
     934             :     // Grab the JSClass. We require all Xrayable classes to have a ClassSpec.
     935           0 :     const js::Class* clasp = js::GetObjectClass(target);
     936           0 :     MOZ_ASSERT(clasp->specDefined());
     937             : 
     938           0 :     return AppendNamesFromFunctionAndPropertySpecs(
     939             :         cx, clasp->specPrototypeFunctions(),
     940           0 :         clasp->specPrototypeProperties(), flags, props);
     941             : }
     942             : 
     943             : bool
     944           0 : JSXrayTraits::construct(JSContext* cx, HandleObject wrapper,
     945             :                         const JS::CallArgs& args, const js::Wrapper& baseInstance)
     946             : {
     947           0 :     JSXrayTraits& self = JSXrayTraits::singleton;
     948           0 :     JS::RootedObject holder(cx, self.ensureHolder(cx, wrapper));
     949           0 :     if (self.getProtoKey(holder) == JSProto_Function) {
     950           0 :         JSProtoKey standardConstructor = constructorFor(holder);
     951           0 :         if (standardConstructor == JSProto_Null)
     952           0 :             return baseInstance.construct(cx, wrapper, args);
     953             : 
     954           0 :         const js::Class* clasp = js::ProtoKeyToClass(standardConstructor);
     955           0 :         MOZ_ASSERT(clasp);
     956           0 :         if (!(clasp->flags & JSCLASS_HAS_XRAYED_CONSTRUCTOR))
     957           0 :             return baseInstance.construct(cx, wrapper, args);
     958             : 
     959             :         // If the JSCLASS_HAS_XRAYED_CONSTRUCTOR flag is set on the Class,
     960             :         // we don't use the constructor at hand. Instead, we retrieve the
     961             :         // equivalent standard constructor in the xray compartment and run
     962             :         // it in that compartment. The newTarget isn't unwrapped, and the
     963             :         // constructor has to be able to detect and handle this situation.
     964             :         // See the comments in js/public/Class.h and PromiseConstructor for
     965             :         // details and an example.
     966           0 :         RootedObject ctor(cx);
     967           0 :         if (!JS_GetClassObject(cx, standardConstructor, &ctor))
     968           0 :             return false;
     969             : 
     970           0 :         RootedValue ctorVal(cx, ObjectValue(*ctor));
     971           0 :         HandleValueArray vals(args);
     972           0 :         RootedObject result(cx);
     973           0 :         if (!JS::Construct(cx, ctorVal, wrapper, vals, &result))
     974           0 :             return false;
     975           0 :         AssertSameCompartment(cx, result);
     976           0 :         args.rval().setObject(*result);
     977           0 :         return true;
     978             :     }
     979             : 
     980           0 :     JS::RootedValue v(cx, JS::ObjectValue(*wrapper));
     981           0 :     js::ReportIsNotFunction(cx, v);
     982           0 :     return false;
     983             : }
     984             : 
     985             : JSObject*
     986           6 : JSXrayTraits::createHolder(JSContext* cx, JSObject* wrapper)
     987             : {
     988          12 :     RootedObject target(cx, getTargetObject(wrapper));
     989          12 :     RootedObject holder(cx, JS_NewObjectWithGivenProto(cx, &HolderClass,
     990          12 :                                                        nullptr));
     991           6 :     if (!holder)
     992           0 :         return nullptr;
     993             : 
     994             :     // Compute information about the target.
     995           6 :     bool isPrototype = false;
     996           6 :     JSProtoKey key = IdentifyStandardInstance(target);
     997           6 :     if (key == JSProto_Null) {
     998           2 :         isPrototype = true;
     999           2 :         key = IdentifyStandardPrototype(target);
    1000             :     }
    1001           6 :     MOZ_ASSERT(key != JSProto_Null);
    1002             : 
    1003             :     // Store it on the holder.
    1004          12 :     RootedValue v(cx);
    1005           6 :     v.setNumber(static_cast<uint32_t>(key));
    1006           6 :     js::SetReservedSlot(holder, SLOT_PROTOKEY, v);
    1007           6 :     v.setBoolean(isPrototype);
    1008           6 :     js::SetReservedSlot(holder, SLOT_ISPROTOTYPE, v);
    1009             : 
    1010             :     // If this is a function, also compute whether it serves as a constructor
    1011             :     // for a standard class.
    1012           6 :     if (key == JSProto_Function) {
    1013           0 :         v.setNumber(static_cast<uint32_t>(IdentifyStandardConstructor(target)));
    1014           0 :         js::SetReservedSlot(holder, SLOT_CONSTRUCTOR_FOR, v);
    1015             :     }
    1016             : 
    1017           6 :     return holder;
    1018             : }
    1019             : 
    1020             : XPCWrappedNativeXrayTraits XPCWrappedNativeXrayTraits::singleton;
    1021             : DOMXrayTraits DOMXrayTraits::singleton;
    1022             : JSXrayTraits JSXrayTraits::singleton;
    1023             : OpaqueXrayTraits OpaqueXrayTraits::singleton;
    1024             : 
    1025             : XrayTraits*
    1026          22 : GetXrayTraits(JSObject* obj)
    1027             : {
    1028          22 :     switch (GetXrayType(obj)) {
    1029             :       case XrayForDOMObject:
    1030          22 :         return &DOMXrayTraits::singleton;
    1031             :       case XrayForWrappedNative:
    1032           0 :         return &XPCWrappedNativeXrayTraits::singleton;
    1033             :       case XrayForJSObject:
    1034           0 :         return &JSXrayTraits::singleton;
    1035             :       case XrayForOpaqueObject:
    1036           0 :         return &OpaqueXrayTraits::singleton;
    1037             :       default:
    1038           0 :         return nullptr;
    1039             :     }
    1040             : }
    1041             : 
    1042             : /*
    1043             :  * Xray expando handling.
    1044             :  *
    1045             :  * We hang expandos for Xray wrappers off a reserved slot on the target object
    1046             :  * so that same-origin compartments can share expandos for a given object. We
    1047             :  * have a linked list of expando objects, one per origin. The properties on these
    1048             :  * objects are generally wrappers pointing back to the compartment that applied
    1049             :  * them.
    1050             :  *
    1051             :  * The expando objects should _never_ be exposed to script. The fact that they
    1052             :  * live in the target compartment is a detail of the implementation, and does
    1053             :  * not imply that code in the target compartment should be allowed to inspect
    1054             :  * them. They are private to the origin that placed them.
    1055             :  */
    1056             : 
    1057             : static nsIPrincipal*
    1058           0 : ObjectPrincipal(JSObject* obj)
    1059             : {
    1060           0 :     return GetCompartmentPrincipal(js::GetObjectCompartment(obj));
    1061             : }
    1062             : 
    1063             : static nsIPrincipal*
    1064           0 : GetExpandoObjectPrincipal(JSObject* expandoObject)
    1065             : {
    1066           0 :     Value v = JS_GetReservedSlot(expandoObject, JSSLOT_EXPANDO_ORIGIN);
    1067           0 :     return static_cast<nsIPrincipal*>(v.toPrivate());
    1068             : }
    1069             : 
    1070             : static void
    1071           0 : ExpandoObjectFinalize(JSFreeOp* fop, JSObject* obj)
    1072             : {
    1073             :     // Release the principal.
    1074           0 :     nsIPrincipal* principal = GetExpandoObjectPrincipal(obj);
    1075           0 :     NS_RELEASE(principal);
    1076           0 : }
    1077             : 
    1078             : const JSClassOps XrayExpandoObjectClassOps = {
    1079             :     nullptr, nullptr, nullptr, nullptr,
    1080             :     nullptr, nullptr, nullptr, nullptr,
    1081             :     ExpandoObjectFinalize
    1082             : };
    1083             : 
    1084             : bool
    1085           0 : XrayTraits::expandoObjectMatchesConsumer(JSContext* cx,
    1086             :                                          HandleObject expandoObject,
    1087             :                                          nsIPrincipal* consumerOrigin,
    1088             :                                          HandleObject exclusiveGlobal)
    1089             : {
    1090           0 :     MOZ_ASSERT(js::IsObjectInContextCompartment(expandoObject, cx));
    1091             : 
    1092             :     // First, compare the principals.
    1093           0 :     nsIPrincipal* o = GetExpandoObjectPrincipal(expandoObject);
    1094             :     // Note that it's very important here to ignore document.domain. We
    1095             :     // pull the principal for the expando object off of the first consumer
    1096             :     // for a given origin, and freely share the expandos amongst multiple
    1097             :     // same-origin consumers afterwards. However, this means that we have
    1098             :     // no way to know whether _all_ consumers have opted in to collaboration
    1099             :     // by explicitly setting document.domain. So we just mandate that expando
    1100             :     // sharing is unaffected by it.
    1101           0 :     if (!consumerOrigin->Equals(o))
    1102           0 :       return false;
    1103             : 
    1104             :     // Sandboxes want exclusive expando objects.
    1105           0 :     JSObject* owner = JS_GetReservedSlot(expandoObject,
    1106           0 :                                          JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL)
    1107           0 :                                         .toObjectOrNull();
    1108           0 :     if (!owner && !exclusiveGlobal)
    1109           0 :         return true;
    1110             : 
    1111             :     // The exclusive global should always be wrapped in the target's compartment.
    1112           0 :     MOZ_ASSERT(!exclusiveGlobal || js::IsObjectInContextCompartment(exclusiveGlobal, cx));
    1113           0 :     MOZ_ASSERT(!owner || js::IsObjectInContextCompartment(owner, cx));
    1114           0 :     return owner == exclusiveGlobal;
    1115             : }
    1116             : 
    1117             : bool
    1118           0 : XrayTraits::getExpandoObjectInternal(JSContext* cx, JSObject* expandoChain,
    1119             :                                      nsIPrincipal* origin,
    1120             :                                      JSObject* exclusiveGlobalArg,
    1121             :                                      MutableHandleObject expandoObject)
    1122             : {
    1123           0 :     MOZ_ASSERT(!JS_IsExceptionPending(cx));
    1124           0 :     expandoObject.set(nullptr);
    1125             : 
    1126             :     // The expando object lives in the compartment of the target, so all our
    1127             :     // work needs to happen there.
    1128           0 :     RootedObject exclusiveGlobal(cx, exclusiveGlobalArg);
    1129           0 :     RootedObject head(cx, expandoChain);
    1130           0 :     JSAutoCompartment ac(cx, head);
    1131           0 :     if (!JS_WrapObject(cx, &exclusiveGlobal))
    1132           0 :         return false;
    1133             : 
    1134             :     // Iterate through the chain, looking for a same-origin object.
    1135           0 :     while (head) {
    1136           0 :         if (expandoObjectMatchesConsumer(cx, head, origin, exclusiveGlobal)) {
    1137           0 :             expandoObject.set(head);
    1138           0 :             return true;
    1139             :         }
    1140           0 :         head = JS_GetReservedSlot(head, JSSLOT_EXPANDO_NEXT).toObjectOrNull();
    1141             :     }
    1142             : 
    1143             :     // Not found.
    1144           0 :     return true;
    1145             : }
    1146             : 
    1147             : bool
    1148        1159 : XrayTraits::getExpandoObject(JSContext* cx, HandleObject target, HandleObject consumer,
    1149             :                              MutableHandleObject expandoObject)
    1150             : {
    1151             :     // Return early if no expando object has ever been attached, which is
    1152             :     // usually the case.
    1153        1159 :     JSObject* chain = getExpandoChain(target);
    1154        1159 :     if (!chain)
    1155        1159 :         return true;
    1156             : 
    1157           0 :     JSObject* consumerGlobal = js::GetGlobalForObjectCrossCompartment(consumer);
    1158           0 :     bool isSandbox = !strcmp(js::GetObjectJSClass(consumerGlobal)->name, "Sandbox");
    1159           0 :     return getExpandoObjectInternal(cx, chain, ObjectPrincipal(consumer),
    1160             :                                     isSandbox ? consumerGlobal : nullptr,
    1161           0 :                                     expandoObject);
    1162             : }
    1163             : 
    1164             : JSObject*
    1165           0 : XrayTraits::attachExpandoObject(JSContext* cx, HandleObject target,
    1166             :                                 nsIPrincipal* origin, HandleObject exclusiveGlobal)
    1167             : {
    1168             :     // Make sure the compartments are sane.
    1169           0 :     MOZ_ASSERT(js::IsObjectInContextCompartment(target, cx));
    1170           0 :     MOZ_ASSERT(!exclusiveGlobal || js::IsObjectInContextCompartment(exclusiveGlobal, cx));
    1171             : 
    1172             :     // No duplicates allowed.
    1173             : #ifdef DEBUG
    1174             :     {
    1175           0 :         JSObject* chain = getExpandoChain(target);
    1176           0 :         if (chain) {
    1177           0 :             RootedObject existingExpandoObject(cx);
    1178           0 :             if (getExpandoObjectInternal(cx, chain, origin, exclusiveGlobal, &existingExpandoObject))
    1179           0 :                 MOZ_ASSERT(!existingExpandoObject);
    1180             :             else
    1181           0 :                 JS_ClearPendingException(cx);
    1182             :         }
    1183             :     }
    1184             : #endif
    1185             : 
    1186             :     // Create the expando object.
    1187           0 :     const JSClass* expandoClass = getExpandoClass(cx, target);
    1188           0 :     MOZ_ASSERT(!strcmp(expandoClass->name, "XrayExpandoObject"));
    1189             :     RootedObject expandoObject(cx,
    1190           0 :       JS_NewObjectWithGivenProto(cx, expandoClass, nullptr));
    1191           0 :     if (!expandoObject)
    1192           0 :         return nullptr;
    1193             : 
    1194             :     // AddRef and store the principal.
    1195           0 :     NS_ADDREF(origin);
    1196           0 :     JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_ORIGIN, JS::PrivateValue(origin));
    1197             : 
    1198             :     // Note the exclusive global, if any.
    1199           0 :     JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL,
    1200           0 :                        ObjectOrNullValue(exclusiveGlobal));
    1201             : 
    1202             :     // If this is our first expando object, take the opportunity to preserve
    1203             :     // the wrapper. This keeps our expandos alive even if the Xray wrapper gets
    1204             :     // collected.
    1205           0 :     RootedObject chain(cx, getExpandoChain(target));
    1206           0 :     if (!chain)
    1207           0 :         preserveWrapper(target);
    1208             : 
    1209             :     // Insert it at the front of the chain.
    1210           0 :     JS_SetReservedSlot(expandoObject, JSSLOT_EXPANDO_NEXT, ObjectOrNullValue(chain));
    1211           0 :     setExpandoChain(cx, target, expandoObject);
    1212             : 
    1213           0 :     return expandoObject;
    1214             : }
    1215             : 
    1216             : JSObject*
    1217           0 : XrayTraits::ensureExpandoObject(JSContext* cx, HandleObject wrapper,
    1218             :                                 HandleObject target)
    1219             : {
    1220             :     // Expando objects live in the target compartment.
    1221           0 :     JSAutoCompartment ac(cx, target);
    1222           0 :     RootedObject expandoObject(cx);
    1223           0 :     if (!getExpandoObject(cx, target, wrapper, &expandoObject))
    1224           0 :         return nullptr;
    1225           0 :     if (!expandoObject) {
    1226             :         // If the object is a sandbox, we don't want it to share expandos with
    1227             :         // anyone else, so we tag it with the sandbox global.
    1228             :         //
    1229             :         // NB: We first need to check the class, _then_ wrap for the target's
    1230             :         // compartment.
    1231           0 :         RootedObject consumerGlobal(cx, js::GetGlobalForObjectCrossCompartment(wrapper));
    1232           0 :         bool isSandbox = !strcmp(js::GetObjectJSClass(consumerGlobal)->name, "Sandbox");
    1233           0 :         if (!JS_WrapObject(cx, &consumerGlobal))
    1234           0 :             return nullptr;
    1235           0 :         expandoObject = attachExpandoObject(cx, target, ObjectPrincipal(wrapper),
    1236           0 :                                             isSandbox ? (HandleObject)consumerGlobal : nullptr);
    1237             :     }
    1238           0 :     return expandoObject;
    1239             : }
    1240             : 
    1241             : bool
    1242           0 : XrayTraits::cloneExpandoChain(JSContext* cx, HandleObject dst, HandleObject src)
    1243             : {
    1244           0 :     MOZ_ASSERT(js::IsObjectInContextCompartment(dst, cx));
    1245           0 :     MOZ_ASSERT(getExpandoChain(dst) == nullptr);
    1246             : 
    1247           0 :     RootedObject oldHead(cx, getExpandoChain(src));
    1248             : 
    1249             : #ifdef DEBUG
    1250             :     // When this is called from dom::ReparentWrapper() there will be no native
    1251             :     // set for |dst|. Eventually it will be set to that of |src|.  This will
    1252             :     // prevent attachExpandoObject() from preserving the wrapper, but this is
    1253             :     // not a problem because in this case the wrapper will already have been
    1254             :     // preserved when expandos were originally added to |src|. Assert the
    1255             :     // wrapper for |src| has been preserved if it has expandos set.
    1256           0 :     if (oldHead) {
    1257           0 :         nsISupports* identity = mozilla::dom::UnwrapDOMObjectToISupports(src);
    1258           0 :         if (identity) {
    1259           0 :             nsWrapperCache* cache = nullptr;
    1260           0 :             CallQueryInterface(identity, &cache);
    1261           0 :             MOZ_ASSERT_IF(cache, cache->PreservingWrapper());
    1262             :         }
    1263             :     }
    1264             : #endif
    1265             : 
    1266           0 :     while (oldHead) {
    1267           0 :         RootedObject exclusive(cx, JS_GetReservedSlot(oldHead,
    1268           0 :                                                       JSSLOT_EXPANDO_EXCLUSIVE_GLOBAL)
    1269           0 :                                                      .toObjectOrNull());
    1270           0 :         if (!JS_WrapObject(cx, &exclusive))
    1271           0 :             return false;
    1272           0 :         RootedObject newHead(cx, attachExpandoObject(cx, dst, GetExpandoObjectPrincipal(oldHead),
    1273           0 :                                                      exclusive));
    1274           0 :         if (!JS_CopyPropertiesFrom(cx, newHead, oldHead))
    1275           0 :             return false;
    1276           0 :         oldHead = JS_GetReservedSlot(oldHead, JSSLOT_EXPANDO_NEXT).toObjectOrNull();
    1277             :     }
    1278           0 :     return true;
    1279             : }
    1280             : 
    1281             : void
    1282          39 : ClearXrayExpandoSlots(JSObject* target, size_t slotIndex)
    1283             : {
    1284          39 :     if (!NS_IsMainThread()) {
    1285             :         // No Xrays
    1286          17 :         return;
    1287             :     }
    1288             : 
    1289          22 :     MOZ_ASSERT(GetXrayTraits(target) == &DOMXrayTraits::singleton);
    1290          22 :     RootingContext* rootingCx = RootingCx();
    1291          44 :     RootedObject rootedTarget(rootingCx, target);
    1292             :     RootedObject head(rootingCx,
    1293          44 :                       DOMXrayTraits::singleton.getExpandoChain(rootedTarget));
    1294          22 :     while (head) {
    1295           0 :         MOZ_ASSERT(JSCLASS_RESERVED_SLOTS(js::GetObjectClass(head)) > slotIndex);
    1296           0 :         js::SetReservedSlot(head, slotIndex, UndefinedValue());
    1297           0 :         head = js::GetReservedSlot(head, JSSLOT_EXPANDO_NEXT).toObjectOrNull();
    1298             :     }
    1299             : }
    1300             : 
    1301             : JSObject*
    1302           0 : EnsureXrayExpandoObject(JSContext* cx, JS::HandleObject wrapper)
    1303             : {
    1304           0 :     MOZ_ASSERT(NS_IsMainThread());
    1305           0 :     MOZ_ASSERT(GetXrayTraits(wrapper) == &DOMXrayTraits::singleton);
    1306           0 :     MOZ_ASSERT(IsXrayWrapper(wrapper));
    1307             : 
    1308           0 :     RootedObject target(cx, DOMXrayTraits::singleton.getTargetObject(wrapper));
    1309           0 :     return DOMXrayTraits::singleton.ensureExpandoObject(cx, wrapper, target);
    1310             : }
    1311             : 
    1312             : const JSClass*
    1313           0 : XrayTraits::getExpandoClass(JSContext* cx, HandleObject target) const
    1314             : {
    1315           0 :     return &DefaultXrayExpandoObjectClass;
    1316             : }
    1317             : 
    1318             : namespace XrayUtils {
    1319           0 : bool CloneExpandoChain(JSContext* cx, JSObject* dstArg, JSObject* srcArg)
    1320             : {
    1321           0 :     RootedObject dst(cx, dstArg);
    1322           0 :     RootedObject src(cx, srcArg);
    1323           0 :     return GetXrayTraits(src)->cloneExpandoChain(cx, dst, src);
    1324             : }
    1325             : } // namespace XrayUtils
    1326             : 
    1327             : static JSObject*
    1328           0 : GetHolder(JSObject* obj)
    1329             : {
    1330           0 :     return &js::GetProxyReservedSlot(obj, 0).toObject();
    1331             : }
    1332             : 
    1333             : JSObject*
    1334        1165 : XrayTraits::getHolder(JSObject* wrapper)
    1335             : {
    1336        1165 :     MOZ_ASSERT(WrapperFactory::IsXrayWrapper(wrapper));
    1337        1165 :     js::Value v = js::GetProxyReservedSlot(wrapper, 0);
    1338        1165 :     return v.isObject() ? &v.toObject() : nullptr;
    1339             : }
    1340             : 
    1341             : JSObject*
    1342        1165 : XrayTraits::ensureHolder(JSContext* cx, HandleObject wrapper)
    1343             : {
    1344        2330 :     RootedObject holder(cx, getHolder(wrapper));
    1345        1165 :     if (holder)
    1346        1071 :         return holder;
    1347          94 :     holder = createHolder(cx, wrapper); // virtual trap.
    1348          94 :     if (holder)
    1349          94 :         js::SetProxyReservedSlot(wrapper, 0, ObjectValue(*holder));
    1350          94 :     return holder;
    1351             : }
    1352             : 
    1353             : namespace XrayUtils {
    1354             : 
    1355             : bool
    1356        9311 : IsXPCWNHolderClass(const JSClass* clasp)
    1357             : {
    1358        9311 :   return clasp == &XPCWrappedNativeXrayTraits::HolderClass;
    1359             : }
    1360             : 
    1361             : } // namespace XrayUtils
    1362             : 
    1363             : static nsGlobalWindow*
    1364           0 : AsWindow(JSContext* cx, JSObject* wrapper)
    1365             : {
    1366             :   // We want to use our target object here, since we don't want to be
    1367             :   // doing a security check while unwrapping.
    1368           0 :   JSObject* target = XrayTraits::getTargetObject(wrapper);
    1369           0 :   return WindowOrNull(target);
    1370             : }
    1371             : 
    1372             : static bool
    1373           0 : IsWindow(JSContext* cx, JSObject* wrapper)
    1374             : {
    1375           0 :     return !!AsWindow(cx, wrapper);
    1376             : }
    1377             : 
    1378             : void
    1379           0 : XPCWrappedNativeXrayTraits::preserveWrapper(JSObject* target)
    1380             : {
    1381           0 :     XPCWrappedNative* wn = XPCWrappedNative::Get(target);
    1382           0 :     RefPtr<nsXPCClassInfo> ci;
    1383           0 :     CallQueryInterface(wn->Native(), getter_AddRefs(ci));
    1384           0 :     if (ci)
    1385           0 :         ci->PreserveWrapper(wn->Native());
    1386           0 : }
    1387             : 
    1388             : static bool
    1389             : XrayToString(JSContext* cx, unsigned argc, JS::Value* vp);
    1390             : 
    1391             : bool
    1392           0 : XPCWrappedNativeXrayTraits::resolveNativeProperty(JSContext* cx, HandleObject wrapper,
    1393             :                                                   HandleObject holder, HandleId id,
    1394             :                                                   MutableHandle<PropertyDescriptor> desc)
    1395             : {
    1396           0 :     MOZ_ASSERT(js::GetObjectJSClass(holder) == &HolderClass);
    1397             : 
    1398           0 :     desc.object().set(nullptr);
    1399             : 
    1400             :     // This will do verification and the method lookup for us.
    1401           0 :     RootedObject target(cx, getTargetObject(wrapper));
    1402           0 :     XPCCallContext ccx(cx, target, nullptr, id);
    1403             : 
    1404             :     // There are no native numeric (or symbol-keyed) properties, so we can
    1405             :     // shortcut here. We will not find the property.
    1406           0 :     if (!JSID_IS_STRING(id))
    1407           0 :         return true;
    1408             : 
    1409             :     XPCNativeInterface* iface;
    1410             :     XPCNativeMember* member;
    1411           0 :     XPCWrappedNative* wn = getWN(wrapper);
    1412             : 
    1413           0 :     if (ccx.GetWrapper() != wn || !wn->IsValid()) {
    1414           0 :         return true;
    1415             :     }
    1416             : 
    1417           0 :     if (!(iface = ccx.GetInterface()) || !(member = ccx.GetMember())) {
    1418           0 :         if (id != XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_TO_STRING))
    1419           0 :             return true;
    1420             : 
    1421           0 :         JSFunction* toString = JS_NewFunction(cx, XrayToString, 0, 0, "toString");
    1422           0 :         if (!toString)
    1423           0 :             return false;
    1424             : 
    1425           0 :         FillPropertyDescriptor(desc, wrapper, 0,
    1426           0 :                                ObjectValue(*JS_GetFunctionObject(toString)));
    1427             : 
    1428           0 :         return JS_DefinePropertyById(cx, holder, id, desc) &&
    1429           0 :                JS_GetOwnPropertyDescriptorById(cx, holder, id, desc);
    1430             :     }
    1431             : 
    1432           0 :     desc.object().set(holder);
    1433           0 :     desc.setAttributes(JSPROP_ENUMERATE);
    1434           0 :     desc.setGetter(nullptr);
    1435           0 :     desc.setSetter(nullptr);
    1436           0 :     desc.value().setUndefined();
    1437             : 
    1438           0 :     RootedValue fval(cx, JS::UndefinedValue());
    1439           0 :     if (member->IsConstant()) {
    1440           0 :         if (!member->GetConstantValue(ccx, iface, desc.value().address())) {
    1441           0 :             JS_ReportErrorASCII(cx, "Failed to convert constant native property to JS value");
    1442           0 :             return false;
    1443             :         }
    1444           0 :     } else if (member->IsAttribute()) {
    1445             :         // This is a getter/setter. Clone a function for it.
    1446           0 :         if (!member->NewFunctionObject(ccx, iface, wrapper, fval.address())) {
    1447           0 :             JS_ReportErrorASCII(cx, "Failed to clone function object for native getter/setter");
    1448           0 :             return false;
    1449             :         }
    1450             : 
    1451           0 :         unsigned attrs = desc.attributes();
    1452           0 :         attrs |= JSPROP_GETTER;
    1453           0 :         if (member->IsWritableAttribute())
    1454           0 :             attrs |= JSPROP_SETTER;
    1455             : 
    1456             :         // Make the property shared on the holder so no slot is allocated
    1457             :         // for it. This avoids keeping garbage alive through that slot.
    1458           0 :         attrs |= JSPROP_SHARED;
    1459           0 :         desc.setAttributes(attrs);
    1460             :     } else {
    1461             :         // This is a method. Clone a function for it.
    1462           0 :         if (!member->NewFunctionObject(ccx, iface, wrapper, desc.value().address())) {
    1463           0 :             JS_ReportErrorASCII(cx, "Failed to clone function object for native function");
    1464           0 :             return false;
    1465             :         }
    1466             : 
    1467             :         // Without a wrapper the function would live on the prototype. Since we
    1468             :         // don't have one, we have to avoid calling the scriptable helper's
    1469             :         // GetProperty method for this property, so null out the getter and
    1470             :         // setter here explicitly.
    1471           0 :         desc.setGetter(nullptr);
    1472           0 :         desc.setSetter(nullptr);
    1473             :     }
    1474             : 
    1475           0 :     if (!JS_WrapValue(cx, desc.value()) || !JS_WrapValue(cx, &fval))
    1476           0 :         return false;
    1477             : 
    1478           0 :     if (desc.hasGetterObject())
    1479           0 :         desc.setGetterObject(&fval.toObject());
    1480           0 :     if (desc.hasSetterObject())
    1481           0 :         desc.setSetterObject(&fval.toObject());
    1482             : 
    1483           0 :     return JS_DefinePropertyById(cx, holder, id, desc);
    1484             : }
    1485             : 
    1486             : static bool
    1487           0 : wrappedJSObject_getter(JSContext* cx, unsigned argc, Value* vp)
    1488             : {
    1489           0 :     CallArgs args = CallArgsFromVp(argc, vp);
    1490           0 :     if (!args.thisv().isObject()) {
    1491           0 :         JS_ReportErrorASCII(cx, "This value not an object");
    1492           0 :         return false;
    1493             :     }
    1494           0 :     RootedObject wrapper(cx, &args.thisv().toObject());
    1495           0 :     if (!IsWrapper(wrapper) || !WrapperFactory::IsXrayWrapper(wrapper) ||
    1496           0 :         !WrapperFactory::AllowWaiver(wrapper)) {
    1497           0 :         JS_ReportErrorASCII(cx, "Unexpected object");
    1498           0 :         return false;
    1499             :     }
    1500             : 
    1501           0 :     args.rval().setObject(*wrapper);
    1502             : 
    1503           0 :     return WrapperFactory::WaiveXrayAndWrap(cx, args.rval());
    1504             : }
    1505             : 
    1506             : bool
    1507         777 : XrayTraits::resolveOwnProperty(JSContext* cx, HandleObject wrapper, HandleObject target,
    1508             :                                HandleObject holder, HandleId id,
    1509             :                                MutableHandle<PropertyDescriptor> desc)
    1510             : {
    1511         777 :     desc.object().set(nullptr);
    1512        1554 :     RootedObject expando(cx);
    1513         777 :     if (!getExpandoObject(cx, target, wrapper, &expando))
    1514           0 :         return false;
    1515             : 
    1516             :     // Check for expando properties first. Note that the expando object lives
    1517             :     // in the target compartment.
    1518         777 :     bool found = false;
    1519         777 :     if (expando) {
    1520           0 :         JSAutoCompartment ac(cx, expando);
    1521           0 :         JS_MarkCrossZoneId(cx, id);
    1522           0 :         if (!JS_GetOwnPropertyDescriptorById(cx, expando, id, desc))
    1523           0 :             return false;
    1524           0 :         found = !!desc.object();
    1525             :     }
    1526             : 
    1527             :     // Next, check for ES builtins.
    1528         777 :     if (!found && JS_IsGlobalObject(target)) {
    1529         181 :         JSProtoKey key = JS_IdToProtoKey(cx, id);
    1530         362 :         JSAutoCompartment ac(cx, target);
    1531         181 :         if (key != JSProto_Null) {
    1532           0 :             MOZ_ASSERT(key < JSProto_LIMIT);
    1533           0 :             RootedObject constructor(cx);
    1534           0 :             if (!JS_GetClassObject(cx, key, &constructor))
    1535           0 :                 return false;
    1536           0 :             MOZ_ASSERT(constructor);
    1537           0 :             desc.value().set(ObjectValue(*constructor));
    1538           0 :             found = true;
    1539         181 :         } else if (id == GetJSIDByIndex(cx, XPCJSContext::IDX_EVAL)) {
    1540           0 :             RootedObject eval(cx);
    1541           0 :             if (!js::GetOriginalEval(cx, target, &eval))
    1542           0 :                 return false;
    1543           0 :             desc.value().set(ObjectValue(*eval));
    1544           0 :             found = true;
    1545             :         }
    1546             :     }
    1547             : 
    1548         777 :     if (found) {
    1549           0 :         if (!JS_WrapPropertyDescriptor(cx, desc))
    1550           0 :             return false;
    1551             :         // Pretend the property lives on the wrapper.
    1552           0 :         desc.object().set(wrapper);
    1553           0 :         return true;
    1554             :     }
    1555             : 
    1556             :     // Handle .wrappedJSObject for subsuming callers. This should move once we
    1557             :     // sort out own-ness for the holder.
    1558         777 :     if (id == GetJSIDByIndex(cx, XPCJSContext::IDX_WRAPPED_JSOBJECT) &&
    1559           0 :         WrapperFactory::AllowWaiver(wrapper))
    1560             :     {
    1561           0 :         if (!JS_AlreadyHasOwnPropertyById(cx, holder, id, &found))
    1562           0 :             return false;
    1563           0 :         if (!found && !JS_DefinePropertyById(cx, holder, id, UndefinedHandleValue,
    1564             :                                              JSPROP_ENUMERATE | JSPROP_SHARED,
    1565             :                                              wrappedJSObject_getter)) {
    1566           0 :             return false;
    1567             :         }
    1568           0 :         if (!JS_GetOwnPropertyDescriptorById(cx, holder, id, desc))
    1569           0 :             return false;
    1570           0 :         desc.object().set(wrapper);
    1571           0 :         return true;
    1572             :     }
    1573             : 
    1574         777 :     return true;
    1575             : }
    1576             : 
    1577             : bool
    1578           0 : XPCWrappedNativeXrayTraits::resolveOwnProperty(JSContext* cx, HandleObject wrapper,
    1579             :                                                HandleObject target, HandleObject holder,
    1580             :                                                HandleId id,
    1581             :                                                MutableHandle<PropertyDescriptor> desc)
    1582             : {
    1583             :     // Call the common code.
    1584           0 :     bool ok = XrayTraits::resolveOwnProperty(cx, wrapper, target, holder,
    1585           0 :                                              id, desc);
    1586           0 :     if (!ok || desc.object())
    1587           0 :         return ok;
    1588             : 
    1589             :     // Xray wrappers don't use the regular wrapper hierarchy, so we should be
    1590             :     // in the wrapper's compartment here, not the wrappee.
    1591           0 :     MOZ_ASSERT(js::IsObjectInContextCompartment(wrapper, cx));
    1592             : 
    1593           0 :     return JS_GetOwnPropertyDescriptorById(cx, holder, id, desc);
    1594             : }
    1595             : 
    1596             : bool
    1597           0 : XPCWrappedNativeXrayTraits::enumerateNames(JSContext* cx, HandleObject wrapper, unsigned flags,
    1598             :                                            AutoIdVector& props)
    1599             : {
    1600             :     // Force all native properties to be materialized onto the wrapped native.
    1601           0 :     AutoIdVector wnProps(cx);
    1602             :     {
    1603           0 :         RootedObject target(cx, singleton.getTargetObject(wrapper));
    1604           0 :         JSAutoCompartment ac(cx, target);
    1605           0 :         if (!js::GetPropertyKeys(cx, target, flags, &wnProps))
    1606           0 :             return false;
    1607             :     }
    1608             : 
    1609             :     // Go through the properties we found on the underlying object and see if
    1610             :     // they appear on the XrayWrapper. If it throws (which may happen if the
    1611             :     // wrapper is a SecurityWrapper), just clear the exception and move on.
    1612           0 :     MOZ_ASSERT(!JS_IsExceptionPending(cx));
    1613           0 :     if (!props.reserve(wnProps.length()))
    1614           0 :         return false;
    1615           0 :     for (size_t n = 0; n < wnProps.length(); ++n) {
    1616           0 :         RootedId id(cx, wnProps[n]);
    1617           0 :         JS_MarkCrossZoneId(cx, id);
    1618             :         bool hasProp;
    1619           0 :         if (JS_HasPropertyById(cx, wrapper, id, &hasProp) && hasProp)
    1620           0 :             props.infallibleAppend(id);
    1621           0 :         JS_ClearPendingException(cx);
    1622             :     }
    1623           0 :     return true;
    1624             : }
    1625             : 
    1626             : JSObject*
    1627           0 : XPCWrappedNativeXrayTraits::createHolder(JSContext* cx, JSObject* wrapper)
    1628             : {
    1629           0 :     return JS_NewObjectWithGivenProto(cx, &HolderClass, nullptr);
    1630             : }
    1631             : 
    1632             : bool
    1633           0 : XPCWrappedNativeXrayTraits::call(JSContext* cx, HandleObject wrapper,
    1634             :                                  const JS::CallArgs& args,
    1635             :                                  const js::Wrapper& baseInstance)
    1636             : {
    1637             :     // Run the call hook of the wrapped native.
    1638           0 :     XPCWrappedNative* wn = getWN(wrapper);
    1639           0 :     if (wn->GetScriptable() && wn->GetScriptable()->WantCall()) {
    1640             :         XPCCallContext ccx(cx, wrapper, nullptr, JSID_VOIDHANDLE, args.length(),
    1641           0 :                            args.array(), args.rval().address());
    1642           0 :         if (!ccx.IsValid())
    1643           0 :             return false;
    1644           0 :         bool ok = true;
    1645           0 :         nsresult rv = wn->GetScriptable()->Call(wn, cx, wrapper, args, &ok);
    1646           0 :         if (NS_FAILED(rv)) {
    1647           0 :             if (ok)
    1648           0 :                 XPCThrower::Throw(rv, cx);
    1649           0 :             return false;
    1650             :         }
    1651             :     }
    1652             : 
    1653           0 :     return true;
    1654             : 
    1655             : }
    1656             : 
    1657             : bool
    1658           0 : XPCWrappedNativeXrayTraits::construct(JSContext* cx, HandleObject wrapper,
    1659             :                                       const JS::CallArgs& args,
    1660             :                                       const js::Wrapper& baseInstance)
    1661             : {
    1662             :     // Run the construct hook of the wrapped native.
    1663           0 :     XPCWrappedNative* wn = getWN(wrapper);
    1664           0 :     if (wn->GetScriptable() && wn->GetScriptable()->WantConstruct()) {
    1665             :         XPCCallContext ccx(cx, wrapper, nullptr, JSID_VOIDHANDLE, args.length(),
    1666           0 :                            args.array(), args.rval().address());
    1667           0 :         if (!ccx.IsValid())
    1668           0 :             return false;
    1669           0 :         bool ok = true;
    1670             :         nsresult rv =
    1671           0 :             wn->GetScriptable()->Construct(wn, cx, wrapper, args, &ok);
    1672           0 :         if (NS_FAILED(rv)) {
    1673           0 :             if (ok)
    1674           0 :                 XPCThrower::Throw(rv, cx);
    1675           0 :             return false;
    1676             :         }
    1677             :     }
    1678             : 
    1679           0 :     return true;
    1680             : 
    1681             : }
    1682             : 
    1683             : bool
    1684         698 : DOMXrayTraits::resolveOwnProperty(JSContext* cx, HandleObject wrapper, HandleObject target,
    1685             :                                   HandleObject holder, HandleId id,
    1686             :                                   MutableHandle<PropertyDescriptor> desc)
    1687             : {
    1688             :     // Call the common code.
    1689         698 :     bool ok = XrayTraits::resolveOwnProperty(cx, wrapper, target, holder, id, desc);
    1690         698 :     if (!ok || desc.object())
    1691           0 :         return ok;
    1692             : 
    1693             :     // Check for indexed access on a window.
    1694         698 :     uint32_t index = GetArrayIndexFromId(cx, id);
    1695         698 :     if (IsArrayIndex(index)) {
    1696           0 :         nsGlobalWindow* win = AsWindow(cx, wrapper);
    1697             :         // Note: As() unwraps outer windows to get to the inner window.
    1698           0 :         if (win) {
    1699           0 :             nsCOMPtr<nsPIDOMWindowOuter> subframe = win->IndexedGetter(index);
    1700           0 :             if (subframe) {
    1701           0 :                 subframe->EnsureInnerWindow();
    1702           0 :                 nsGlobalWindow* global = nsGlobalWindow::Cast(subframe);
    1703           0 :                 JSObject* obj = global->FastGetGlobalJSObject();
    1704           0 :                 if (MOZ_UNLIKELY(!obj)) {
    1705             :                     // It's gone?
    1706           0 :                     return xpc::Throw(cx, NS_ERROR_FAILURE);
    1707             :                 }
    1708           0 :                 ExposeObjectToActiveJS(obj);
    1709           0 :                 desc.value().setObject(*obj);
    1710           0 :                 FillPropertyDescriptor(desc, wrapper, true);
    1711           0 :                 return JS_WrapPropertyDescriptor(cx, desc);
    1712             :             }
    1713             :         }
    1714             :     }
    1715             : 
    1716         698 :     if (!JS_GetOwnPropertyDescriptorById(cx, holder, id, desc))
    1717           0 :         return false;
    1718         698 :     if (desc.object()) {
    1719         349 :         desc.object().set(wrapper);
    1720         349 :         return true;
    1721             :     }
    1722             : 
    1723             :     bool cacheOnHolder;
    1724         349 :     if (!XrayResolveOwnProperty(cx, wrapper, target, id, desc, cacheOnHolder))
    1725           0 :         return false;
    1726             : 
    1727         349 :     MOZ_ASSERT(!desc.object() || desc.object() == wrapper, "What did we resolve this on?");
    1728             : 
    1729         349 :     if (!desc.object() || !cacheOnHolder)
    1730         247 :         return true;
    1731             : 
    1732         408 :     return JS_DefinePropertyById(cx, holder, id, desc) &&
    1733         204 :            JS_GetOwnPropertyDescriptorById(cx, holder, id, desc);
    1734             : }
    1735             : 
    1736             : bool
    1737           0 : DOMXrayTraits::delete_(JSContext* cx, JS::HandleObject wrapper,
    1738             :                        JS::HandleId id, JS::ObjectOpResult& result)
    1739             : {
    1740           0 :     RootedObject target(cx, getTargetObject(wrapper));
    1741           0 :     return XrayDeleteNamedProperty(cx, wrapper, target, id, result);
    1742             : }
    1743             : 
    1744             : bool
    1745           0 : DOMXrayTraits::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
    1746             :                               Handle<PropertyDescriptor> desc,
    1747             :                               Handle<PropertyDescriptor> existingDesc,
    1748             :                               JS::ObjectOpResult& result, bool* defined)
    1749             : {
    1750             :     // Check for an indexed property on a Window.  If that's happening, do
    1751             :     // nothing but claim we defined it so it won't get added as an expando.
    1752           0 :     if (IsWindow(cx, wrapper)) {
    1753           0 :         if (IsArrayIndex(GetArrayIndexFromId(cx, id))) {
    1754           0 :             *defined = true;
    1755           0 :             return result.succeed();
    1756             :         }
    1757             :     }
    1758             : 
    1759           0 :     JS::Rooted<JSObject*> obj(cx, getTargetObject(wrapper));
    1760           0 :     return XrayDefineProperty(cx, wrapper, obj, id, desc, result, defined);
    1761             : }
    1762             : 
    1763             : bool
    1764           0 : DOMXrayTraits::enumerateNames(JSContext* cx, HandleObject wrapper, unsigned flags,
    1765             :                               AutoIdVector& props)
    1766             : {
    1767             :     // Put the indexed properties for a window first.
    1768           0 :     nsGlobalWindow* win = AsWindow(cx, wrapper);
    1769           0 :     if (win) {
    1770           0 :         uint32_t length = win->Length();
    1771           0 :         if (!props.reserve(props.length() + length)) {
    1772           0 :             return false;
    1773             :         }
    1774           0 :         JS::RootedId indexId(cx);
    1775           0 :         for (uint32_t i = 0; i < length; ++i) {
    1776           0 :             if (!JS_IndexToId(cx, i, &indexId)) {
    1777           0 :                 return false;
    1778             :             }
    1779           0 :             props.infallibleAppend(indexId);
    1780             :         }
    1781             :     }
    1782             : 
    1783           0 :     JS::Rooted<JSObject*> obj(cx, getTargetObject(wrapper));
    1784           0 :     return XrayOwnPropertyKeys(cx, wrapper, obj, flags, props);
    1785             : }
    1786             : 
    1787             : bool
    1788           0 : DOMXrayTraits::call(JSContext* cx, HandleObject wrapper,
    1789             :                     const JS::CallArgs& args, const js::Wrapper& baseInstance)
    1790             : {
    1791           0 :     RootedObject obj(cx, getTargetObject(wrapper));
    1792           0 :     const js::Class* clasp = js::GetObjectClass(obj);
    1793             :     // What we have is either a WebIDL interface object, a WebIDL prototype
    1794             :     // object, or a WebIDL instance object.  WebIDL prototype objects never have
    1795             :     // a clasp->call.  WebIDL interface objects we want to invoke on the xray
    1796             :     // compartment.  WebIDL instance objects either don't have a clasp->call or
    1797             :     // are using "legacycaller", which basically means plug-ins.  We want to
    1798             :     // call those on the content compartment.
    1799           0 :     if (clasp->flags & JSCLASS_IS_DOMIFACEANDPROTOJSCLASS) {
    1800           0 :         if (JSNative call = clasp->getCall()) {
    1801             :             // call it on the Xray compartment
    1802           0 :             if (!call(cx, args.length(), args.base()))
    1803           0 :                 return false;
    1804             :         } else {
    1805           0 :             RootedValue v(cx, ObjectValue(*wrapper));
    1806           0 :             js::ReportIsNotFunction(cx, v);
    1807           0 :             return false;
    1808             :         }
    1809             :     } else {
    1810             :         // This is only reached for WebIDL instance objects, and in practice
    1811             :         // only for plugins.  Just call them on the content compartment.
    1812           0 :         if (!baseInstance.call(cx, wrapper, args))
    1813           0 :             return false;
    1814             :     }
    1815           0 :     return JS_WrapValue(cx, args.rval());
    1816             : }
    1817             : 
    1818             : bool
    1819           1 : DOMXrayTraits::construct(JSContext* cx, HandleObject wrapper,
    1820             :                          const JS::CallArgs& args, const js::Wrapper& baseInstance)
    1821             : {
    1822           2 :     RootedObject obj(cx, getTargetObject(wrapper));
    1823           1 :     MOZ_ASSERT(mozilla::dom::HasConstructor(obj));
    1824           1 :     const js::Class* clasp = js::GetObjectClass(obj);
    1825             :     // See comments in DOMXrayTraits::call() explaining what's going on here.
    1826           1 :     if (clasp->flags & JSCLASS_IS_DOMIFACEANDPROTOJSCLASS) {
    1827           1 :         if (JSNative construct = clasp->getConstruct()) {
    1828           1 :             if (!construct(cx, args.length(), args.base()))
    1829           0 :                 return false;
    1830             :         } else {
    1831           0 :             RootedValue v(cx, ObjectValue(*wrapper));
    1832           0 :             js::ReportIsNotFunction(cx, v);
    1833           0 :             return false;
    1834             :         }
    1835             :     } else {
    1836           0 :         if (!baseInstance.construct(cx, wrapper, args))
    1837           0 :             return false;
    1838             :     }
    1839           1 :     if (!args.rval().isObject() || !JS_WrapValue(cx, args.rval()))
    1840           0 :         return false;
    1841           1 :     return true;
    1842             : }
    1843             : 
    1844             : bool
    1845          46 : DOMXrayTraits::getPrototype(JSContext* cx, JS::HandleObject wrapper,
    1846             :                             JS::HandleObject target,
    1847             :                             JS::MutableHandleObject protop)
    1848             : {
    1849          46 :     return mozilla::dom::XrayGetNativeProto(cx, target, protop);
    1850             : }
    1851             : 
    1852             : void
    1853           0 : DOMXrayTraits::preserveWrapper(JSObject* target)
    1854             : {
    1855           0 :     nsISupports* identity = mozilla::dom::UnwrapDOMObjectToISupports(target);
    1856           0 :     if (!identity)
    1857           0 :         return;
    1858           0 :     nsWrapperCache* cache = nullptr;
    1859           0 :     CallQueryInterface(identity, &cache);
    1860           0 :     if (cache)
    1861           0 :         cache->PreserveWrapper(identity);
    1862             : }
    1863             : 
    1864             : JSObject*
    1865          88 : DOMXrayTraits::createHolder(JSContext* cx, JSObject* wrapper)
    1866             : {
    1867          88 :     return JS_NewObjectWithGivenProto(cx, &HolderClass, nullptr);
    1868             : }
    1869             : 
    1870             : const JSClass*
    1871           0 : DOMXrayTraits::getExpandoClass(JSContext* cx, HandleObject target) const
    1872             : {
    1873           0 :     return XrayGetExpandoClass(cx, target);
    1874             : }
    1875             : 
    1876             : namespace XrayUtils {
    1877             : 
    1878             : JSObject*
    1879           0 : GetNativePropertiesObject(JSContext* cx, JSObject* wrapper)
    1880             : {
    1881           0 :     MOZ_ASSERT(js::IsWrapper(wrapper) && WrapperFactory::IsXrayWrapper(wrapper),
    1882             :                "bad object passed in");
    1883             : 
    1884           0 :     JSObject* holder = GetHolder(wrapper);
    1885           0 :     MOZ_ASSERT(holder, "uninitialized wrapper being used?");
    1886           0 :     return holder;
    1887             : }
    1888             : 
    1889             : bool
    1890           0 : HasNativeProperty(JSContext* cx, HandleObject wrapper, HandleId id, bool* hasProp)
    1891             : {
    1892           0 :     MOZ_ASSERT(WrapperFactory::IsXrayWrapper(wrapper));
    1893           0 :     XrayTraits* traits = GetXrayTraits(wrapper);
    1894           0 :     MOZ_ASSERT(traits);
    1895           0 :     RootedObject target(cx, XrayTraits::getTargetObject(wrapper));
    1896           0 :     RootedObject holder(cx, traits->ensureHolder(cx, wrapper));
    1897           0 :     NS_ENSURE_TRUE(holder, false);
    1898           0 :     *hasProp = false;
    1899           0 :     Rooted<PropertyDescriptor> desc(cx);
    1900             : 
    1901             :     // Try resolveOwnProperty.
    1902           0 :     if (!traits->resolveOwnProperty(cx, wrapper, target, holder, id, &desc))
    1903           0 :         return false;
    1904           0 :     if (desc.object()) {
    1905           0 :         *hasProp = true;
    1906           0 :         return true;
    1907             :     }
    1908             : 
    1909             :     // Try the holder.
    1910           0 :     bool found = false;
    1911           0 :     if (!JS_AlreadyHasOwnPropertyById(cx, holder, id, &found))
    1912           0 :         return false;
    1913           0 :     if (found) {
    1914           0 :         *hasProp = true;
    1915           0 :         return true;
    1916             :     }
    1917             : 
    1918             :     // Try resolveNativeProperty.
    1919           0 :     if (!traits->resolveNativeProperty(cx, wrapper, holder, id, &desc))
    1920           0 :         return false;
    1921           0 :     *hasProp = !!desc.object();
    1922           0 :     return true;
    1923             : }
    1924             : 
    1925             : } // namespace XrayUtils
    1926             : 
    1927             : static bool
    1928           0 : XrayToString(JSContext* cx, unsigned argc, Value* vp)
    1929             : {
    1930           0 :     CallArgs args = CallArgsFromVp(argc, vp);
    1931             : 
    1932           0 :     if (!args.thisv().isObject()) {
    1933           0 :         JS_ReportErrorASCII(cx, "XrayToString called on an incompatible object");
    1934           0 :         return false;
    1935             :     }
    1936             : 
    1937           0 :     RootedObject wrapper(cx, &args.thisv().toObject());
    1938           0 :     if (!wrapper)
    1939           0 :         return false;
    1940           0 :     if (IsWrapper(wrapper) &&
    1941           0 :         GetProxyHandler(wrapper) == &sandboxCallableProxyHandler) {
    1942           0 :         wrapper = xpc::SandboxCallableProxyHandler::wrappedObject(wrapper);
    1943             :     }
    1944           0 :     if (!IsWrapper(wrapper) || !WrapperFactory::IsXrayWrapper(wrapper)) {
    1945           0 :         JS_ReportErrorASCII(cx, "XrayToString called on an incompatible object");
    1946           0 :         return false;
    1947             :     }
    1948             : 
    1949           0 :     RootedObject obj(cx, XrayTraits::getTargetObject(wrapper));
    1950           0 :     if (GetXrayType(obj) != XrayForWrappedNative) {
    1951           0 :         JS_ReportErrorASCII(cx, "XrayToString called on an incompatible object");
    1952           0 :         return false;
    1953             :     }
    1954             : 
    1955             :     static const char start[] = "[object XrayWrapper ";
    1956             :     static const char end[] = "]";
    1957           0 :     nsAutoString result;
    1958           0 :     result.AppendASCII(start);
    1959             : 
    1960           0 :     XPCCallContext ccx(cx, obj);
    1961           0 :     XPCWrappedNative* wn = XPCWrappedNativeXrayTraits::getWN(wrapper);
    1962           0 :     char* wrapperStr = wn->ToString();
    1963           0 :     if (!wrapperStr) {
    1964           0 :         JS_ReportOutOfMemory(cx);
    1965           0 :         return false;
    1966             :     }
    1967           0 :     result.AppendASCII(wrapperStr);
    1968           0 :     JS_smprintf_free(wrapperStr);
    1969             : 
    1970           0 :     result.AppendASCII(end);
    1971             : 
    1972           0 :     JSString* str = JS_NewUCStringCopyN(cx, result.get(), result.Length());
    1973           0 :     if (!str)
    1974           0 :         return false;
    1975             : 
    1976           0 :     args.rval().setString(str);
    1977           0 :     return true;
    1978             : }
    1979             : 
    1980             : template <typename Base, typename Traits>
    1981             : bool
    1982           0 : XrayWrapper<Base, Traits>::preventExtensions(JSContext* cx, HandleObject wrapper,
    1983             :                                              ObjectOpResult& result) const
    1984             : {
    1985             :     // Xray wrappers are supposed to provide a clean view of the target
    1986             :     // reflector, hiding any modifications by script in the target scope.  So
    1987             :     // even if that script freezes the reflector, we don't want to make that
    1988             :     // visible to the caller. DOM reflectors are always extensible by default,
    1989             :     // so we can just return failure here.
    1990           0 :     return result.failCantPreventExtensions();
    1991             : }
    1992             : 
    1993             : template <typename Base, typename Traits>
    1994             : bool
    1995           0 : XrayWrapper<Base, Traits>::isExtensible(JSContext* cx, JS::Handle<JSObject*> wrapper,
    1996             :                                         bool* extensible) const
    1997             : {
    1998             :     // See above.
    1999           0 :     *extensible = true;
    2000           0 :     return true;
    2001             : }
    2002             : 
    2003             : template <typename Base, typename Traits>
    2004             : bool
    2005         212 : XrayWrapper<Base, Traits>::getPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
    2006             :                                                  JS::MutableHandle<PropertyDescriptor> desc)
    2007             :                                                  const
    2008             : {
    2009         212 :     assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET |
    2010             :                                          BaseProxyHandler::GET_PROPERTY_DESCRIPTOR);
    2011         424 :     RootedObject target(cx, XrayTraits::getTargetObject(wrapper));
    2012         424 :     RootedObject holder(cx, Traits::singleton.ensureHolder(cx, wrapper));
    2013             : 
    2014         212 :     if (!holder)
    2015           0 :         return false;
    2016             : 
    2017             :     // Ordering is important here.
    2018             :     //
    2019             :     // We first need to call resolveOwnProperty, even before checking the holder,
    2020             :     // because there might be a new dynamic |own| property that appears and
    2021             :     // shadows a previously-resolved non-own property that we cached on the
    2022             :     // holder. This can happen with indexed properties on NodeLists, for example,
    2023             :     // which are |own| value props.
    2024             :     //
    2025             :     // resolveOwnProperty may or may not cache what it finds on the holder,
    2026             :     // depending on how ephemeral it decides the property is. XPCWN |own|
    2027             :     // properties generally end up on the holder via Resolve, whereas
    2028             :     // NodeList |own| properties don't get defined on the holder, since they're
    2029             :     // supposed to be dynamic. This means that we have to first check the result
    2030             :     // of resolveOwnProperty, and _then_, if that comes up blank, check the
    2031             :     // holder for any cached native properties.
    2032             :     //
    2033             :     // Finally, we call resolveNativeProperty, which checks non-own properties,
    2034             :     // and unconditionally caches what it finds on the holder.
    2035             : 
    2036             :     // Check resolveOwnProperty.
    2037         212 :     if (!Traits::singleton.resolveOwnProperty(cx, wrapper, target, holder, id, desc))
    2038           0 :         return false;
    2039             : 
    2040             :     // Check the holder.
    2041         212 :     if (!desc.object() && !JS_GetOwnPropertyDescriptorById(cx, holder, id, desc))
    2042           0 :         return false;
    2043         212 :     if (desc.object()) {
    2044         212 :         desc.object().set(wrapper);
    2045         212 :         return true;
    2046             :     }
    2047             : 
    2048             :     // Nothing in the cache. Call through, and cache the result.
    2049           0 :     if (!Traits::singleton.resolveNativeProperty(cx, wrapper, holder, id, desc))
    2050           0 :         return false;
    2051             : 
    2052             :     // We need to handle named access on the Window somewhere other than
    2053             :     // Traits::resolveOwnProperty, because per spec it happens on the Global
    2054             :     // Scope Polluter and thus the resulting properties are non-|own|. However,
    2055             :     // we're set up (above) to cache (on the holder) anything that comes out of
    2056             :     // resolveNativeProperty, which we don't want for something dynamic like
    2057             :     // named access. So we just handle it separately here.
    2058           0 :     nsGlobalWindow* win = nullptr;
    2059           0 :     if (!desc.object() &&
    2060           0 :         JSID_IS_STRING(id) &&
    2061           0 :         (win = AsWindow(cx, wrapper)))
    2062             :     {
    2063           0 :         nsAutoJSString name;
    2064           0 :         if (!name.init(cx, JSID_TO_STRING(id)))
    2065           0 :             return false;
    2066           0 :         if (nsCOMPtr<nsPIDOMWindowOuter> childDOMWin = win->GetChildWindow(name)) {
    2067           0 :             auto* cwin = nsGlobalWindow::Cast(childDOMWin);
    2068           0 :             JSObject* childObj = cwin->FastGetGlobalJSObject();
    2069           0 :             if (MOZ_UNLIKELY(!childObj))
    2070           0 :                 return xpc::Throw(cx, NS_ERROR_FAILURE);
    2071           0 :             ExposeObjectToActiveJS(childObj);
    2072           0 :             FillPropertyDescriptor(desc, wrapper, ObjectValue(*childObj),
    2073             :                                    /* readOnly = */ true);
    2074           0 :             return JS_WrapPropertyDescriptor(cx, desc);
    2075             :         }
    2076             :     }
    2077             : 
    2078             :     // If we still have nothing, we're done.
    2079           0 :     if (!desc.object())
    2080           0 :         return true;
    2081             : 
    2082           0 :     if (!JS_DefinePropertyById(cx, holder, id, desc) ||
    2083           0 :         !JS_GetOwnPropertyDescriptorById(cx, holder, id, desc))
    2084             :     {
    2085           0 :         return false;
    2086             :     }
    2087           0 :     MOZ_ASSERT(desc.object());
    2088           0 :     desc.object().set(wrapper);
    2089           0 :     return true;
    2090             : }
    2091             : 
    2092             : template <typename Base, typename Traits>
    2093             : bool
    2094         565 : XrayWrapper<Base, Traits>::getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
    2095             :                                                     JS::MutableHandle<PropertyDescriptor> desc)
    2096             :                                                     const
    2097             : {
    2098         565 :     assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::GET | BaseProxyHandler::SET |
    2099             :                                          BaseProxyHandler::GET_PROPERTY_DESCRIPTOR);
    2100        1130 :     RootedObject target(cx, XrayTraits::getTargetObject(wrapper));
    2101        1130 :     RootedObject holder(cx, Traits::singleton.ensureHolder(cx, wrapper));
    2102             : 
    2103         565 :     if (!Traits::singleton.resolveOwnProperty(cx, wrapper, target, holder, id, desc))
    2104           0 :         return false;
    2105         565 :     if (desc.object())
    2106         279 :         desc.object().set(wrapper);
    2107         565 :     return true;
    2108             : }
    2109             : 
    2110             : // Consider what happens when chrome does |xray.expando = xray.wrappedJSObject|.
    2111             : //
    2112             : // Since the expando comes from the target compartment, wrapping it back into
    2113             : // the target compartment to define it on the expando object ends up stripping
    2114             : // off the Xray waiver that gives |xray| and |xray.wrappedJSObject| different
    2115             : // identities. This is generally the right thing to do when wrapping across
    2116             : // compartments, but is incorrect in the special case of the Xray expando
    2117             : // object. Manually re-apply Xrays if necessary.
    2118             : //
    2119             : // NB: In order to satisfy the invariants of WaiveXray, we need to pass
    2120             : // in an object sans security wrapper, which means we need to strip off any
    2121             : // potential same-compartment security wrapper that may have been applied
    2122             : // to the content object. This is ok, because the the expando object is only
    2123             : // ever accessed by code across the compartment boundary.
    2124             : static bool
    2125           0 : RecreateLostWaivers(JSContext* cx, const PropertyDescriptor* orig,
    2126             :                     MutableHandle<PropertyDescriptor> wrapped)
    2127             : {
    2128             :     // Compute whether the original objects were waived, and implicitly, whether
    2129             :     // they were objects at all.
    2130             :     bool valueWasWaived =
    2131           0 :         orig->value.isObject() &&
    2132           0 :         WrapperFactory::HasWaiveXrayFlag(&orig->value.toObject());
    2133             :     bool getterWasWaived =
    2134           0 :         (orig->attrs & JSPROP_GETTER) &&
    2135           0 :         WrapperFactory::HasWaiveXrayFlag(JS_FUNC_TO_DATA_PTR(JSObject*, orig->getter));
    2136             :     bool setterWasWaived =
    2137           0 :         (orig->attrs & JSPROP_SETTER) &&
    2138           0 :         WrapperFactory::HasWaiveXrayFlag(JS_FUNC_TO_DATA_PTR(JSObject*, orig->setter));
    2139             : 
    2140             :     // Recreate waivers. Note that for value, we need an extra UncheckedUnwrap
    2141             :     // to handle same-compartment security wrappers (see above). This should
    2142             :     // never happen for getters/setters.
    2143             : 
    2144           0 :     RootedObject rewaived(cx);
    2145           0 :     if (valueWasWaived && !IsCrossCompartmentWrapper(&wrapped.value().toObject())) {
    2146           0 :         rewaived = &wrapped.value().toObject();
    2147           0 :         rewaived = WrapperFactory::WaiveXray(cx, UncheckedUnwrap(rewaived));
    2148           0 :         NS_ENSURE_TRUE(rewaived, false);
    2149           0 :         wrapped.value().set(ObjectValue(*rewaived));
    2150             :     }
    2151           0 :     if (getterWasWaived && !IsCrossCompartmentWrapper(wrapped.getterObject())) {
    2152           0 :         MOZ_ASSERT(CheckedUnwrap(wrapped.getterObject()));
    2153           0 :         rewaived = WrapperFactory::WaiveXray(cx, wrapped.getterObject());
    2154           0 :         NS_ENSURE_TRUE(rewaived, false);
    2155           0 :         wrapped.setGetterObject(rewaived);
    2156             :     }
    2157           0 :     if (setterWasWaived && !IsCrossCompartmentWrapper(wrapped.setterObject())) {
    2158           0 :         MOZ_ASSERT(CheckedUnwrap(wrapped.setterObject()));
    2159           0 :         rewaived = WrapperFactory::WaiveXray(cx, wrapped.setterObject());
    2160           0 :         NS_ENSURE_TRUE(rewaived, false);
    2161           0 :         wrapped.setSetterObject(rewaived);
    2162             :     }
    2163             : 
    2164           0 :     return true;
    2165             : }
    2166             : 
    2167             : template <typename Base, typename Traits>
    2168             : bool
    2169           0 : XrayWrapper<Base, Traits>::defineProperty(JSContext* cx, HandleObject wrapper,
    2170             :                                           HandleId id, Handle<PropertyDescriptor> desc,
    2171             :                                           ObjectOpResult& result) const
    2172             : {
    2173           0 :     assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::SET);
    2174             : 
    2175           0 :     Rooted<PropertyDescriptor> existing_desc(cx);
    2176           0 :     if (!JS_GetPropertyDescriptorById(cx, wrapper, id, &existing_desc))
    2177           0 :         return false;
    2178             : 
    2179             :     // Note that the check here is intended to differentiate between own and
    2180             :     // non-own properties, since the above lookup is not limited to own
    2181             :     // properties. At present, this may not always do the right thing because
    2182             :     // we often lie (sloppily) about where we found properties and set
    2183             :     // desc.object() to |wrapper|. Once we fully fix our Xray prototype semantics,
    2184             :     // this should work as intended.
    2185           0 :     if (existing_desc.object() == wrapper && !existing_desc.configurable()) {
    2186             :         // We have a non-configurable property. See if the caller is trying to
    2187             :         // re-configure it in any way other than making it non-writable.
    2188           0 :         if (existing_desc.isAccessorDescriptor() || desc.isAccessorDescriptor() ||
    2189           0 :             (desc.hasEnumerable() && existing_desc.enumerable() != desc.enumerable()) ||
    2190           0 :             (desc.hasWritable() && !existing_desc.writable() && desc.writable()))
    2191             :         {
    2192             :             // We should technically report non-configurability in strict mode, but
    2193             :             // doing that via JSAPI used to be a lot of trouble. See bug 1135997.
    2194           0 :             return result.succeed();
    2195             :         }
    2196           0 :         if (!existing_desc.writable()) {
    2197             :             // Same as the above for non-writability.
    2198           0 :             return result.succeed();
    2199             :         }
    2200             :     }
    2201             : 
    2202           0 :     bool defined = false;
    2203           0 :     if (!Traits::singleton.defineProperty(cx, wrapper, id, desc, existing_desc, result, &defined))
    2204           0 :         return false;
    2205           0 :     if (defined)
    2206           0 :         return true;
    2207             : 
    2208             :     // We're placing an expando. The expando objects live in the target
    2209             :     // compartment, so we need to enter it.
    2210           0 :     RootedObject target(cx, Traits::singleton.getTargetObject(wrapper));
    2211           0 :     JSAutoCompartment ac(cx, target);
    2212           0 :     JS_MarkCrossZoneId(cx, id);
    2213             : 
    2214             :     // Grab the relevant expando object.
    2215             :     RootedObject expandoObject(cx, Traits::singleton.ensureExpandoObject(cx, wrapper,
    2216           0 :                                                                          target));
    2217           0 :     if (!expandoObject)
    2218           0 :         return false;
    2219             : 
    2220             :     // Wrap the property descriptor for the target compartment.
    2221           0 :     Rooted<PropertyDescriptor> wrappedDesc(cx, desc);
    2222           0 :     if (!JS_WrapPropertyDescriptor(cx, &wrappedDesc))
    2223           0 :         return false;
    2224             : 
    2225             :     // Fix up Xray waivers.
    2226           0 :     if (!RecreateLostWaivers(cx, desc.address(), &wrappedDesc))
    2227           0 :         return false;
    2228             : 
    2229           0 :     return JS_DefinePropertyById(cx, expandoObject, id, wrappedDesc, result);
    2230             : }
    2231             : 
    2232             : template <typename Base, typename Traits>
    2233             : bool
    2234           2 : XrayWrapper<Base, Traits>::ownPropertyKeys(JSContext* cx, HandleObject wrapper,
    2235             :                                            AutoIdVector& props) const
    2236             : {
    2237           2 :     assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE);
    2238           2 :     return getPropertyKeys(cx, wrapper, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props);
    2239             : }
    2240             : 
    2241             : template <typename Base, typename Traits>
    2242             : bool
    2243           0 : XrayWrapper<Base, Traits>::delete_(JSContext* cx, HandleObject wrapper,
    2244             :                                    HandleId id, ObjectOpResult& result) const
    2245             : {
    2246           0 :     assertEnteredPolicy(cx, wrapper, id, BaseProxyHandler::SET);
    2247             : 
    2248             :     // Check the expando object.
    2249           0 :     RootedObject target(cx, Traits::getTargetObject(wrapper));
    2250           0 :     RootedObject expando(cx);
    2251           0 :     if (!Traits::singleton.getExpandoObject(cx, target, wrapper, &expando))
    2252           0 :         return false;
    2253             : 
    2254           0 :     if (expando) {
    2255           0 :         JSAutoCompartment ac(cx, expando);
    2256           0 :         JS_MarkCrossZoneId(cx, id);
    2257             :         bool hasProp;
    2258           0 :         if (!JS_HasPropertyById(cx, expando, id, &hasProp)) {
    2259           0 :             return false;
    2260             :         }
    2261           0 :         if (hasProp) {
    2262           0 :             return JS_DeletePropertyById(cx, expando, id, result);
    2263             :         }
    2264             :     }
    2265             : 
    2266           0 :     return Traits::singleton.delete_(cx, wrapper, id, result);
    2267             : }
    2268             : 
    2269             : template <typename Base, typename Traits>
    2270             : bool
    2271         212 : XrayWrapper<Base, Traits>::get(JSContext* cx, HandleObject wrapper,
    2272             :                                HandleValue receiver, HandleId id,
    2273             :                                MutableHandleValue vp) const
    2274             : {
    2275             :     // Skip our Base if it isn't already ProxyHandler.
    2276             :     // NB: None of the functions we call are prepared for the receiver not
    2277             :     // being the wrapper, so ignore the receiver here.
    2278         424 :     RootedValue thisv(cx);
    2279             :     if (Traits::HasPrototype)
    2280         212 :       thisv = receiver;
    2281             :     else
    2282           0 :       thisv.setObject(*wrapper);
    2283             : 
    2284             :     // This uses getPropertyDescriptor for backward compatibility with
    2285             :     // the old BaseProxyHandler::get implementation.
    2286         424 :     Rooted<PropertyDescriptor> desc(cx);
    2287         212 :     if (!getPropertyDescriptor(cx, wrapper, id, &desc))
    2288           0 :         return false;
    2289         212 :     desc.assertCompleteIfFound();
    2290             : 
    2291         212 :     if (!desc.object()) {
    2292           0 :         vp.setUndefined();
    2293           0 :         return true;
    2294             :     }
    2295             : 
    2296             :     // Everything after here follows [[Get]] for ordinary objects.
    2297         212 :     if (desc.isDataDescriptor()) {
    2298          32 :         vp.set(desc.value());
    2299          32 :         return true;
    2300             :     }
    2301             : 
    2302         180 :     MOZ_ASSERT(desc.isAccessorDescriptor());
    2303         360 :     RootedObject getter(cx, desc.getterObject());
    2304             : 
    2305         180 :     if (!getter) {
    2306           0 :         vp.setUndefined();
    2307           0 :         return true;
    2308             :     }
    2309             : 
    2310         180 :     return Call(cx, thisv, getter, HandleValueArray::empty(), vp);
    2311             : }
    2312             : 
    2313             : template <typename Base, typename Traits>
    2314             : bool
    2315           0 : XrayWrapper<Base, Traits>::set(JSContext* cx, HandleObject wrapper, HandleId id, HandleValue v,
    2316             :                                HandleValue receiver, ObjectOpResult& result) const
    2317             : {
    2318           0 :     MOZ_ASSERT(!Traits::HasPrototype);
    2319             :     // Skip our Base if it isn't already BaseProxyHandler.
    2320             :     // NB: None of the functions we call are prepared for the receiver not
    2321             :     // being the wrapper, so ignore the receiver here.
    2322           0 :     RootedValue wrapperValue(cx, ObjectValue(*wrapper));
    2323           0 :     return js::BaseProxyHandler::set(cx, wrapper, id, v, wrapperValue, result);
    2324             : }
    2325             : 
    2326             : template <typename Base, typename Traits>
    2327             : bool
    2328           0 : XrayWrapper<Base, Traits>::has(JSContext* cx, HandleObject wrapper,
    2329             :                                HandleId id, bool* bp) const
    2330             : {
    2331             :     // This uses getPropertyDescriptor for backward compatibility with
    2332             :     // the old BaseProxyHandler::has implementation.
    2333           0 :     Rooted<PropertyDescriptor> desc(cx);
    2334           0 :     if (!getPropertyDescriptor(cx, wrapper, id, &desc))
    2335           0 :         return false;
    2336             : 
    2337           0 :     *bp = !!desc.object();
    2338           0 :     return true;
    2339             : }
    2340             : 
    2341             : template <typename Base, typename Traits>
    2342             : bool
    2343         558 : XrayWrapper<Base, Traits>::hasOwn(JSContext* cx, HandleObject wrapper,
    2344             :                                   HandleId id, bool* bp) const
    2345             : {
    2346             :     // Skip our Base if it isn't already ProxyHandler.
    2347         558 :     return js::BaseProxyHandler::hasOwn(cx, wrapper, id, bp);
    2348             : }
    2349             : 
    2350             : template <typename Base, typename Traits>
    2351             : bool
    2352           2 : XrayWrapper<Base, Traits>::getOwnEnumerablePropertyKeys(JSContext* cx,
    2353             :                                                         HandleObject wrapper,
    2354             :                                                         AutoIdVector& props) const
    2355             : {
    2356             :     // Skip our Base if it isn't already ProxyHandler.
    2357           2 :     return js::BaseProxyHandler::getOwnEnumerablePropertyKeys(cx, wrapper, props);
    2358             : }
    2359             : 
    2360             : template <typename Base, typename Traits>
    2361             : JSObject*
    2362           0 : XrayWrapper<Base, Traits>::enumerate(JSContext* cx, HandleObject wrapper) const
    2363             : {
    2364             :     // Skip our Base if it isn't already ProxyHandler.
    2365           0 :     return js::BaseProxyHandler::enumerate(cx, wrapper);
    2366             : }
    2367             : 
    2368             : template <typename Base, typename Traits>
    2369             : bool
    2370           0 : XrayWrapper<Base, Traits>::call(JSContext* cx, HandleObject wrapper, const JS::CallArgs& args) const
    2371             : {
    2372           0 :     assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::CALL);
    2373             :     // Hard cast the singleton since SecurityWrapper doesn't have one.
    2374           0 :     return Traits::call(cx, wrapper, args, Base::singleton);
    2375             : }
    2376             : 
    2377             : template <typename Base, typename Traits>
    2378             : bool
    2379           1 : XrayWrapper<Base, Traits>::construct(JSContext* cx, HandleObject wrapper, const JS::CallArgs& args) const
    2380             : {
    2381           1 :     assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::CALL);
    2382             :     // Hard cast the singleton since SecurityWrapper doesn't have one.
    2383           1 :     return Traits::construct(cx, wrapper, args, Base::singleton);
    2384             : }
    2385             : 
    2386             : template <typename Base, typename Traits>
    2387             : bool
    2388          11 : XrayWrapper<Base, Traits>::getBuiltinClass(JSContext* cx, JS::HandleObject wrapper, js::ESClass* cls) const
    2389             : {
    2390          11 :     return Traits::getBuiltinClass(cx, wrapper, Base::singleton, cls);
    2391             : }
    2392             : 
    2393             : template <typename Base, typename Traits>
    2394             : const char*
    2395           0 : XrayWrapper<Base, Traits>::className(JSContext* cx, HandleObject wrapper) const
    2396             : {
    2397           0 :     return Traits::className(cx, wrapper, Base::singleton);
    2398             : }
    2399             : 
    2400             : template <typename Base, typename Traits>
    2401             : bool
    2402         380 : XrayWrapper<Base, Traits>::getPrototype(JSContext* cx, JS::HandleObject wrapper,
    2403             :                                         JS::MutableHandleObject protop) const
    2404             : {
    2405             :     // We really only want this override for non-SecurityWrapper-inheriting
    2406             :     // |Base|. But doing that statically with templates requires partial method
    2407             :     // specializations (and therefore a helper class), which is all more trouble
    2408             :     // than it's worth. Do a dynamic check.
    2409         380 :     if (Base::hasSecurityPolicy())
    2410           0 :         return Base::getPrototype(cx, wrapper, protop);
    2411             : 
    2412         760 :     RootedObject target(cx, Traits::getTargetObject(wrapper));
    2413         760 :     RootedObject expando(cx);
    2414         380 :     if (!Traits::singleton.getExpandoObject(cx, target, wrapper, &expando))
    2415           0 :         return false;
    2416             : 
    2417             :     // We want to keep the Xray's prototype distinct from that of content, but
    2418             :     // only if there's been a set. If there's not an expando, or the expando
    2419             :     // slot is |undefined|, hand back the default proto, appropriately wrapped.
    2420             : 
    2421         380 :     if (expando) {
    2422           0 :         RootedValue v(cx);
    2423             :         { // Scope for JSAutoCompartment
    2424           0 :             JSAutoCompartment ac(cx, expando);
    2425           0 :             v = JS_GetReservedSlot(expando, JSSLOT_EXPANDO_PROTOTYPE);
    2426             :         }
    2427           0 :         if (!v.isUndefined()) {
    2428           0 :             protop.set(v.toObjectOrNull());
    2429           0 :             return JS_WrapObject(cx, protop);
    2430             :         }
    2431             :     }
    2432             : 
    2433             :     // Check our holder, and cache there if we don't have it cached already.
    2434         760 :     RootedObject holder(cx, Traits::singleton.ensureHolder(cx, wrapper));
    2435         380 :     if (!holder)
    2436           0 :         return false;
    2437             : 
    2438         380 :     Value cached = js::GetReservedSlot(holder,
    2439         380 :                                        Traits::HOLDER_SLOT_CACHED_PROTO);
    2440         380 :     if (cached.isUndefined()) {
    2441          52 :         if (!getPrototypeHelper(cx, wrapper, target, protop))
    2442           0 :             return false;
    2443             : 
    2444          52 :         js::SetReservedSlot(holder, Traits::HOLDER_SLOT_CACHED_PROTO,
    2445         104 :                             ObjectOrNullValue(protop));
    2446             :     } else {
    2447         328 :         protop.set(cached.toObjectOrNull());
    2448             :     }
    2449         380 :     return true;
    2450             : }
    2451             : 
    2452             : template <typename Base, typename Traits>
    2453             : bool
    2454           0 : XrayWrapper<Base, Traits>::setPrototype(JSContext* cx, JS::HandleObject wrapper,
    2455             :                                         JS::HandleObject proto, JS::ObjectOpResult& result) const
    2456             : {
    2457             :     // Do this only for non-SecurityWrapper-inheriting |Base|. See the comment
    2458             :     // in getPrototype().
    2459           0 :     if (Base::hasSecurityPolicy())
    2460           0 :         return Base::setPrototype(cx, wrapper, proto, result);
    2461             : 
    2462           0 :     RootedObject target(cx, Traits::getTargetObject(wrapper));
    2463           0 :     RootedObject expando(cx, Traits::singleton.ensureExpandoObject(cx, wrapper, target));
    2464           0 :     if (!expando)
    2465           0 :         return false;
    2466             : 
    2467             :     // The expando lives in the target's compartment, so do our installation there.
    2468           0 :     JSAutoCompartment ac(cx, target);
    2469             : 
    2470           0 :     RootedValue v(cx, ObjectOrNullValue(proto));
    2471           0 :     if (!JS_WrapValue(cx, &v))
    2472           0 :         return false;
    2473           0 :     JS_SetReservedSlot(expando, JSSLOT_EXPANDO_PROTOTYPE, v);
    2474           0 :     return result.succeed();
    2475             : }
    2476             : 
    2477             : template <typename Base, typename Traits>
    2478             : bool
    2479           0 : XrayWrapper<Base, Traits>::getPrototypeIfOrdinary(JSContext* cx, JS::HandleObject wrapper,
    2480             :                                                   bool* isOrdinary,
    2481             :                                                   JS::MutableHandleObject protop) const
    2482             : {
    2483             :     // We want to keep the Xray's prototype distinct from that of content, but
    2484             :     // only if there's been a set.  This different-prototype-over-time behavior
    2485             :     // means that the [[GetPrototypeOf]] trap *can't* be ECMAScript's ordinary
    2486             :     // [[GetPrototypeOf]].  This also covers cross-origin Window behavior that
    2487             :     // per <https://html.spec.whatwg.org/multipage/browsers.html#windowproxy-getprototypeof>
    2488             :     // must be non-ordinary.
    2489           0 :     *isOrdinary = false;
    2490           0 :     return true;
    2491             : }
    2492             : 
    2493             : template <typename Base, typename Traits>
    2494             : bool
    2495           0 : XrayWrapper<Base, Traits>::setImmutablePrototype(JSContext* cx, JS::HandleObject wrapper,
    2496             :                                                  bool* succeeded) const
    2497             : {
    2498             :     // For now, lacking an obvious place to store a bit, prohibit making an
    2499             :     // Xray's [[Prototype]] immutable.  We can revisit this (or maybe give all
    2500             :     // Xrays immutable [[Prototype]], because who does this, really?) later if
    2501             :     // necessary.
    2502           0 :     *succeeded = false;
    2503           0 :     return true;
    2504             : }
    2505             : 
    2506             : template <typename Base, typename Traits>
    2507             : bool
    2508           2 : XrayWrapper<Base, Traits>::getPropertyKeys(JSContext* cx, HandleObject wrapper, unsigned flags,
    2509             :                                            AutoIdVector& props) const
    2510             : {
    2511           2 :     assertEnteredPolicy(cx, wrapper, JSID_VOID, BaseProxyHandler::ENUMERATE);
    2512             : 
    2513             :     // Enumerate expando properties first. Note that the expando object lives
    2514             :     // in the target compartment.
    2515           4 :     RootedObject target(cx, Traits::singleton.getTargetObject(wrapper));
    2516           4 :     RootedObject expando(cx);
    2517           2 :     if (!Traits::singleton.getExpandoObject(cx, target, wrapper, &expando))
    2518           0 :         return false;
    2519             : 
    2520           2 :     if (expando) {
    2521           0 :         JSAutoCompartment ac(cx, expando);
    2522           0 :         if (!js::GetPropertyKeys(cx, expando, flags, &props))
    2523           0 :             return false;
    2524             :     }
    2525           2 :     for (size_t i = 0; i < props.length(); ++i)
    2526           0 :         JS_MarkCrossZoneId(cx, props[i]);
    2527             : 
    2528           2 :     return Traits::singleton.enumerateNames(cx, wrapper, flags, props);
    2529             : }
    2530             : 
    2531             : /*
    2532             :  * The Permissive / Security variants should be used depending on whether the
    2533             :  * compartment of the wrapper is guranteed to subsume the compartment of the
    2534             :  * wrapped object (i.e. - whether it is safe from a security perspective to
    2535             :  * unwrap the wrapper).
    2536             :  */
    2537             : 
    2538             : template<typename Base, typename Traits>
    2539             : const xpc::XrayWrapper<Base, Traits>
    2540             : xpc::XrayWrapper<Base, Traits>::singleton(0);
    2541             : 
    2542             : template class PermissiveXrayXPCWN;
    2543             : template class SecurityXrayXPCWN;
    2544             : template class PermissiveXrayDOM;
    2545             : template class SecurityXrayDOM;
    2546             : template class PermissiveXrayJS;
    2547             : template class PermissiveXrayOpaque;
    2548             : 
    2549             : } // namespace xpc

Generated by: LCOV version 1.13