dom/bindings/BindingUtils.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
     2 /* vim: set ts=2 sw=2 et tw=79: */
     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 file,
     5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "BindingUtils.h"
     9 #include <algorithm>
    10 #include <stdarg.h>
    12 #include "JavaScriptParent.h"
    14 #include "mozilla/DebugOnly.h"
    15 #include "mozilla/FloatingPoint.h"
    16 #include "mozilla/Assertions.h"
    18 #include "AccessCheck.h"
    19 #include "jsfriendapi.h"
    20 #include "js/OldDebugAPI.h"
    21 #include "nsContentUtils.h"
    22 #include "nsIDOMGlobalPropertyInitializer.h"
    23 #include "nsIPrincipal.h"
    24 #include "nsIXPConnect.h"
    25 #include "WrapperFactory.h"
    26 #include "xpcprivate.h"
    27 #include "XPCQuickStubs.h"
    28 #include "XrayWrapper.h"
    29 #include "nsPrintfCString.h"
    30 #include "prprf.h"
    32 #include "mozilla/dom/ScriptSettings.h"
    33 #include "mozilla/dom/DOMError.h"
    34 #include "mozilla/dom/DOMErrorBinding.h"
    35 #include "mozilla/dom/HTMLObjectElement.h"
    36 #include "mozilla/dom/HTMLObjectElementBinding.h"
    37 #include "mozilla/dom/HTMLSharedObjectElement.h"
    38 #include "mozilla/dom/HTMLEmbedElementBinding.h"
    39 #include "mozilla/dom/HTMLAppletElementBinding.h"
    40 #include "mozilla/dom/Promise.h"
    41 #include "WorkerPrivate.h"
    43 namespace mozilla {
    44 namespace dom {
    46 JSErrorFormatString ErrorFormatString[] = {
    47 #define MSG_DEF(_name, _argc, _str) \
    48   { _str, _argc, JSEXN_TYPEERR },
    49 #include "mozilla/dom/Errors.msg"
    50 #undef MSG_DEF
    51 };
    53 const JSErrorFormatString*
    54 GetErrorMessage(void* aUserRef, const char* aLocale,
    55                 const unsigned aErrorNumber)
    56 {
    57   MOZ_ASSERT(aErrorNumber < ArrayLength(ErrorFormatString));
    58   return &ErrorFormatString[aErrorNumber];
    59 }
    61 bool
    62 ThrowErrorMessage(JSContext* aCx, const ErrNum aErrorNumber, ...)
    63 {
    64   va_list ap;
    65   va_start(ap, aErrorNumber);
    66   JS_ReportErrorNumberVA(aCx, GetErrorMessage, nullptr,
    67                          static_cast<const unsigned>(aErrorNumber), ap);
    68   va_end(ap);
    69   return false;
    70 }
    72 bool
    73 ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
    74                  const ErrNum aErrorNumber,
    75                  const char* aInterfaceName)
    76 {
    77   NS_ConvertASCIItoUTF16 ifaceName(aInterfaceName);
    78   // This should only be called for DOM methods/getters/setters, which
    79   // are JSNative-backed functions, so we can assume that
    80   // JS_ValueToFunction and JS_GetFunctionDisplayId will both return
    81   // non-null and that JS_GetStringCharsZ returns non-null.
    82   JS::Rooted<JSFunction*> func(aCx, JS_ValueToFunction(aCx, aArgs.calleev()));
    83   MOZ_ASSERT(func);
    84   JS::Rooted<JSString*> funcName(aCx, JS_GetFunctionDisplayId(func));
    85   MOZ_ASSERT(funcName);
    86   JS_ReportErrorNumberUC(aCx, GetErrorMessage, nullptr,
    87                          static_cast<const unsigned>(aErrorNumber),
    88                          JS_GetStringCharsZ(aCx, funcName),
    89                          ifaceName.get());
    90   return false;
    91 }
    93 bool
    94 ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
    95                  const ErrNum aErrorNumber,
    96                  prototypes::ID aProtoId)
    97 {
    98   return ThrowInvalidThis(aCx, aArgs, aErrorNumber,
    99                           NamesOfInterfacesWithProtos(aProtoId));
   100 }
   102 bool
   103 ThrowNoSetterArg(JSContext* aCx, prototypes::ID aProtoId)
   104 {
   105   nsPrintfCString errorMessage("%s attribute setter",
   106                                NamesOfInterfacesWithProtos(aProtoId));
   107   return ThrowErrorMessage(aCx, MSG_MISSING_ARGUMENTS, errorMessage.get());
   108 }
   110 } // namespace dom
   112 struct ErrorResult::Message {
   113   nsTArray<nsString> mArgs;
   114   dom::ErrNum mErrorNumber;
   115 };
   117 void
   118 ErrorResult::ThrowTypeError(const dom::ErrNum errorNumber, ...)
   119 {
   120   va_list ap;
   121   va_start(ap, errorNumber);
   122   if (IsJSException()) {
   123     // We have rooted our mJSException, and we don't have the info
   124     // needed to unroot here, so just bail.
   125     va_end(ap);
   126     MOZ_ASSERT(false,
   127                "Ignoring ThrowTypeError call because we have a JS exception");
   128     return;
   129   }
   130   if (IsTypeError()) {
   131     delete mMessage;
   132   }
   133   mResult = NS_ERROR_TYPE_ERR;
   134   Message* message = new Message();
   135   message->mErrorNumber = errorNumber;
   136   uint16_t argCount =
   137     dom::GetErrorMessage(nullptr, nullptr, errorNumber)->argCount;
   138   MOZ_ASSERT(argCount <= 10);
   139   argCount = std::min<uint16_t>(argCount, 10);
   140   while (argCount--) {
   141     message->mArgs.AppendElement(*va_arg(ap, nsString*));
   142   }
   143   mMessage = message;
   144   va_end(ap);
   145 }
   147 void
   148 ErrorResult::ReportTypeError(JSContext* aCx)
   149 {
   150   MOZ_ASSERT(mMessage, "ReportTypeError() can be called only once");
   152   Message* message = mMessage;
   153   const uint32_t argCount = message->mArgs.Length();
   154   const jschar* args[11];
   155   for (uint32_t i = 0; i < argCount; ++i) {
   156     args[i] = message->mArgs.ElementAt(i).get();
   157   }
   158   args[argCount] = nullptr;
   160   JS_ReportErrorNumberUCArray(aCx, dom::GetErrorMessage, nullptr,
   161                               static_cast<const unsigned>(message->mErrorNumber),
   162                               argCount > 0 ? args : nullptr);
   164   ClearMessage();
   165 }
   167 void
   168 ErrorResult::ClearMessage()
   169 {
   170   if (IsTypeError()) {
   171     delete mMessage;
   172     mMessage = nullptr;
   173   }
   174 }
   176 void
   177 ErrorResult::ThrowJSException(JSContext* cx, JS::Handle<JS::Value> exn)
   178 {
   179   MOZ_ASSERT(mMightHaveUnreportedJSException,
   180              "Why didn't you tell us you planned to throw a JS exception?");
   182   if (IsTypeError()) {
   183     delete mMessage;
   184   }
   186   // Make sure mJSException is initialized _before_ we try to root it.  But
   187   // don't set it to exn yet, because we don't want to do that until after we
   188   // root.
   189   mJSException = JS::UndefinedValue();
   190   if (!js::AddRawValueRoot(cx, &mJSException, "ErrorResult::mJSException")) {
   191     // Don't use NS_ERROR_DOM_JS_EXCEPTION, because that indicates we have
   192     // in fact rooted mJSException.
   193     mResult = NS_ERROR_OUT_OF_MEMORY;
   194   } else {
   195     mJSException = exn;
   196     mResult = NS_ERROR_DOM_JS_EXCEPTION;
   197   }
   198 }
   200 void
   201 ErrorResult::ReportJSException(JSContext* cx)
   202 {
   203   MOZ_ASSERT(!mMightHaveUnreportedJSException,
   204              "Why didn't you tell us you planned to handle JS exceptions?");
   206   JS::Rooted<JS::Value> exception(cx, mJSException);
   207   if (JS_WrapValue(cx, &exception)) {
   208     JS_SetPendingException(cx, exception);
   209   }
   210   mJSException = exception;
   211   // If JS_WrapValue failed, not much we can do about it...  No matter
   212   // what, go ahead and unroot mJSException.
   213   js::RemoveRawValueRoot(cx, &mJSException);
   214 }
   216 void
   217 ErrorResult::ReportJSExceptionFromJSImplementation(JSContext* aCx)
   218 {
   219   MOZ_ASSERT(!mMightHaveUnreportedJSException,
   220              "Why didn't you tell us you planned to handle JS exceptions?");
   222   dom::DOMError* domError;
   223   nsresult rv = UNWRAP_OBJECT(DOMError, &mJSException.toObject(), domError);
   224   if (NS_FAILED(rv)) {
   225     // Unwrapping really shouldn't fail here, if mExceptionHandling is set to
   226     // eRethrowContentExceptions then the CallSetup destructor only stores an
   227     // exception if it unwraps to DOMError. If we reach this then either
   228     // mExceptionHandling wasn't set to eRethrowContentExceptions and we
   229     // shouldn't be calling ReportJSExceptionFromJSImplementation or something
   230     // went really wrong.
   231     NS_RUNTIMEABORT("We stored a non-DOMError exception!");
   232   }
   234   nsString message;
   235   domError->GetMessage(message);
   237   JS_ReportError(aCx, "%hs", message.get());
   238   js::RemoveRawValueRoot(aCx, &mJSException);
   240   // We no longer have a useful exception but we do want to signal that an error
   241   // occured.
   242   mResult = NS_ERROR_FAILURE;
   243 }
   245 void
   246 ErrorResult::StealJSException(JSContext* cx,
   247                               JS::MutableHandle<JS::Value> value)
   248 {
   249   MOZ_ASSERT(!mMightHaveUnreportedJSException,
   250              "Must call WouldReportJSException unconditionally in all codepaths that might call StealJSException");
   251   MOZ_ASSERT(IsJSException(), "No exception to steal");
   253   value.set(mJSException);
   254   js::RemoveRawValueRoot(cx, &mJSException);
   255   mResult = NS_OK;
   256 }
   258 void
   259 ErrorResult::ReportNotEnoughArgsError(JSContext* cx,
   260                                       const char* ifaceName,
   261                                       const char* memberName)
   262 {
   263   MOZ_ASSERT(ErrorCode() == NS_ERROR_XPC_NOT_ENOUGH_ARGS);
   265   nsPrintfCString errorMessage("%s.%s", ifaceName, memberName);
   266   ThrowErrorMessage(cx, dom::MSG_MISSING_ARGUMENTS, errorMessage.get());
   267 }
   269 namespace dom {
   271 bool
   272 DefineConstants(JSContext* cx, JS::Handle<JSObject*> obj,
   273                 const ConstantSpec* cs)
   274 {
   275   JS::Rooted<JS::Value> value(cx);
   276   for (; cs->name; ++cs) {
   277     value = cs->value;
   278     bool ok =
   279       JS_DefineProperty(cx, obj, cs->name, value,
   280                         JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
   281     if (!ok) {
   282       return false;
   283     }
   284   }
   285   return true;
   286 }
   288 static inline bool
   289 Define(JSContext* cx, JS::Handle<JSObject*> obj, const JSFunctionSpec* spec) {
   290   return JS_DefineFunctions(cx, obj, spec);
   291 }
   292 static inline bool
   293 Define(JSContext* cx, JS::Handle<JSObject*> obj, const JSPropertySpec* spec) {
   294   return JS_DefineProperties(cx, obj, spec);
   295 }
   296 static inline bool
   297 Define(JSContext* cx, JS::Handle<JSObject*> obj, const ConstantSpec* spec) {
   298   return DefineConstants(cx, obj, spec);
   299 }
   301 template<typename T>
   302 bool
   303 DefinePrefable(JSContext* cx, JS::Handle<JSObject*> obj,
   304                const Prefable<T>* props)
   305 {
   306   MOZ_ASSERT(props);
   307   MOZ_ASSERT(props->specs);
   308   do {
   309     // Define if enabled
   310     if (props->isEnabled(cx, obj)) {
   311       if (!Define(cx, obj, props->specs)) {
   312         return false;
   313       }
   314     }
   315   } while ((++props)->specs);
   316   return true;
   317 }
   319 bool
   320 DefineUnforgeableAttributes(JSContext* cx, JS::Handle<JSObject*> obj,
   321                             const Prefable<const JSPropertySpec>* props)
   322 {
   323   return DefinePrefable(cx, obj, props);
   324 }
   327 // We should use JSFunction objects for interface objects, but we need a custom
   328 // hasInstance hook because we have new interface objects on prototype chains of
   329 // old (XPConnect-based) bindings. Because Function.prototype.toString throws if
   330 // passed a non-Function object we also need to provide our own toString method
   331 // for interface objects.
   333 enum {
   334   TOSTRING_CLASS_RESERVED_SLOT = 0,
   335   TOSTRING_NAME_RESERVED_SLOT = 1
   336 };
   338 static bool
   339 InterfaceObjectToString(JSContext* cx, unsigned argc, JS::Value *vp)
   340 {
   341   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
   342   JS::Rooted<JSObject*> callee(cx, &args.callee());
   344   if (!args.thisv().isObject()) {
   345     JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
   346                          JSMSG_CANT_CONVERT_TO, "null", "object");
   347     return false;
   348   }
   350   JS::Value v = js::GetFunctionNativeReserved(callee,
   351                                               TOSTRING_CLASS_RESERVED_SLOT);
   352   const JSClass* clasp = static_cast<const JSClass*>(v.toPrivate());
   354   v = js::GetFunctionNativeReserved(callee, TOSTRING_NAME_RESERVED_SLOT);
   355   JSString* jsname = static_cast<JSString*>(JSVAL_TO_STRING(v));
   356   size_t length;
   357   const jschar* name = JS_GetInternedStringCharsAndLength(jsname, &length);
   359   if (js::GetObjectJSClass(&args.thisv().toObject()) != clasp) {
   360     JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
   361                          JSMSG_INCOMPATIBLE_PROTO,
   362                          NS_ConvertUTF16toUTF8(name).get(), "toString",
   363                          "object");
   364     return false;
   365   }
   367   nsString str;
   368   str.AppendLiteral("function ");
   369   str.Append(name, length);
   370   str.AppendLiteral("() {");
   371   str.Append('\n');
   372   str.AppendLiteral("    [native code]");
   373   str.Append('\n');
   374   str.AppendLiteral("}");
   376   return xpc::NonVoidStringToJsval(cx, str, args.rval());
   377 }
   379 bool
   380 Constructor(JSContext* cx, unsigned argc, JS::Value* vp)
   381 {
   382   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
   383   const JS::Value& v =
   384     js::GetFunctionNativeReserved(&args.callee(),
   385                                   CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT);
   386   const JSNativeHolder* nativeHolder =
   387     static_cast<const JSNativeHolder*>(v.toPrivate());
   388   return (nativeHolder->mNative)(cx, argc, vp);
   389 }
   391 static JSObject*
   392 CreateConstructor(JSContext* cx, JS::Handle<JSObject*> global, const char* name,
   393                   const JSNativeHolder* nativeHolder, unsigned ctorNargs)
   394 {
   395   JSFunction* fun = js::NewFunctionWithReserved(cx, Constructor, ctorNargs,
   396                                                 JSFUN_CONSTRUCTOR, global,
   397                                                 name);
   398   if (!fun) {
   399     return nullptr;
   400   }
   402   JSObject* constructor = JS_GetFunctionObject(fun);
   403   js::SetFunctionNativeReserved(constructor,
   404                                 CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT,
   405                                 js::PrivateValue(const_cast<JSNativeHolder*>(nativeHolder)));
   406   return constructor;
   407 }
   409 static bool
   410 DefineConstructor(JSContext* cx, JS::Handle<JSObject*> global, const char* name,
   411                   JS::Handle<JSObject*> constructor)
   412 {
   413   bool alreadyDefined;
   414   if (!JS_AlreadyHasOwnProperty(cx, global, name, &alreadyDefined)) {
   415     return false;
   416   }
   418   // This is Enumerable: False per spec.
   419   return alreadyDefined ||
   420          JS_DefineProperty(cx, global, name, constructor, 0);
   421 }
   423 static JSObject*
   424 CreateInterfaceObject(JSContext* cx, JS::Handle<JSObject*> global,
   425                       JS::Handle<JSObject*> constructorProto,
   426                       const JSClass* constructorClass,
   427                       const JSNativeHolder* constructorNative,
   428                       unsigned ctorNargs, const NamedConstructor* namedConstructors,
   429                       JS::Handle<JSObject*> proto,
   430                       const NativeProperties* properties,
   431                       const NativeProperties* chromeOnlyProperties,
   432                       const char* name, bool defineOnGlobal)
   433 {
   434   JS::Rooted<JSObject*> constructor(cx);
   435   if (constructorClass) {
   436     MOZ_ASSERT(constructorProto);
   437     constructor = JS_NewObject(cx, constructorClass, constructorProto, global);
   438   } else {
   439     MOZ_ASSERT(constructorNative);
   440     MOZ_ASSERT(constructorProto == JS_GetFunctionPrototype(cx, global));
   441     constructor = CreateConstructor(cx, global, name, constructorNative,
   442                                     ctorNargs);
   443   }
   444   if (!constructor) {
   445     return nullptr;
   446   }
   448   if (constructorClass) {
   449     // Have to shadow Function.prototype.toString, since that throws
   450     // on things that are not js::FunctionClass.
   451     JS::Rooted<JSFunction*> toString(cx,
   452       js::DefineFunctionWithReserved(cx, constructor,
   453                                      "toString",
   454                                      InterfaceObjectToString,
   455                                      0, 0));
   456     if (!toString) {
   457       return nullptr;
   458     }
   460     JSString *str = ::JS_InternString(cx, name);
   461     if (!str) {
   462       return nullptr;
   463     }
   464     JSObject* toStringObj = JS_GetFunctionObject(toString);
   465     js::SetFunctionNativeReserved(toStringObj, TOSTRING_CLASS_RESERVED_SLOT,
   466                                   PRIVATE_TO_JSVAL(const_cast<JSClass *>(constructorClass)));
   468     js::SetFunctionNativeReserved(toStringObj, TOSTRING_NAME_RESERVED_SLOT,
   469                                   STRING_TO_JSVAL(str));
   471     if (!JS_DefineProperty(cx, constructor, "length", ctorNargs,
   472                            JSPROP_READONLY | JSPROP_PERMANENT)) {
   473       return nullptr;
   474     }
   475   }
   477   if (properties) {
   478     if (properties->staticMethods &&
   479         !DefinePrefable(cx, constructor, properties->staticMethods)) {
   480       return nullptr;
   481     }
   483     if (properties->staticAttributes &&
   484         !DefinePrefable(cx, constructor, properties->staticAttributes)) {
   485       return nullptr;
   486     }
   488     if (properties->constants &&
   489         !DefinePrefable(cx, constructor, properties->constants)) {
   490       return nullptr;
   491     }
   492   }
   494   if (chromeOnlyProperties) {
   495     if (chromeOnlyProperties->staticMethods &&
   496         !DefinePrefable(cx, constructor, chromeOnlyProperties->staticMethods)) {
   497       return nullptr;
   498     }
   500     if (chromeOnlyProperties->staticAttributes &&
   501         !DefinePrefable(cx, constructor,
   502                         chromeOnlyProperties->staticAttributes)) {
   503       return nullptr;
   504     }
   506     if (chromeOnlyProperties->constants &&
   507         !DefinePrefable(cx, constructor, chromeOnlyProperties->constants)) {
   508       return nullptr;
   509     }
   510   }
   512   if (proto && !JS_LinkConstructorAndPrototype(cx, constructor, proto)) {
   513     return nullptr;
   514   }
   516   if (defineOnGlobal && !DefineConstructor(cx, global, name, constructor)) {
   517     return nullptr;
   518   }
   520   if (namedConstructors) {
   521     int namedConstructorSlot = DOM_INTERFACE_SLOTS_BASE;
   522     while (namedConstructors->mName) {
   523       JS::Rooted<JSObject*> namedConstructor(cx,
   524         CreateConstructor(cx, global, namedConstructors->mName,
   525                           &namedConstructors->mHolder,
   526                           namedConstructors->mNargs));
   527       if (!namedConstructor ||
   528           !JS_DefineProperty(cx, namedConstructor, "prototype",
   529                              proto, JSPROP_PERMANENT | JSPROP_READONLY,
   530                              JS_PropertyStub, JS_StrictPropertyStub) ||
   531           (defineOnGlobal &&
   532            !DefineConstructor(cx, global, namedConstructors->mName,
   533                               namedConstructor))) {
   534         return nullptr;
   535       }
   536       js::SetReservedSlot(constructor, namedConstructorSlot++,
   537                           JS::ObjectValue(*namedConstructor));
   538       ++namedConstructors;
   539     }
   540   }
   542   return constructor;
   543 }
   545 bool
   546 DefineWebIDLBindingPropertiesOnXPCObject(JSContext* cx,
   547                                          JS::Handle<JSObject*> obj,
   548                                          const NativeProperties* properties,
   549                                          bool defineUnforgeableAttributes)
   550 {
   551   if (properties->methods &&
   552       !DefinePrefable(cx, obj, properties->methods)) {
   553     return false;
   554   }
   556   if (properties->attributes &&
   557       !DefinePrefable(cx, obj, properties->attributes)) {
   558     return false;
   559   }
   561   if (defineUnforgeableAttributes && properties->unforgeableAttributes &&
   562       !DefinePrefable(cx, obj, properties->unforgeableAttributes)) {
   563     return false;
   564   }
   566   return true;
   567 }
   569 static JSObject*
   570 CreateInterfacePrototypeObject(JSContext* cx, JS::Handle<JSObject*> global,
   571                                JS::Handle<JSObject*> parentProto,
   572                                const JSClass* protoClass,
   573                                const NativeProperties* properties,
   574                                const NativeProperties* chromeOnlyProperties)
   575 {
   576   JS::Rooted<JSObject*> ourProto(cx,
   577     JS_NewObjectWithUniqueType(cx, protoClass, parentProto, global));
   578   if (!ourProto) {
   579     return nullptr;
   580   }
   582   if (properties) {
   583     if (properties->methods &&
   584         !DefinePrefable(cx, ourProto, properties->methods)) {
   585       return nullptr;
   586     }
   588     if (properties->attributes &&
   589         !DefinePrefable(cx, ourProto, properties->attributes)) {
   590       return nullptr;
   591     }
   593     if (properties->constants &&
   594         !DefinePrefable(cx, ourProto, properties->constants)) {
   595       return nullptr;
   596     }
   597   }
   599   if (chromeOnlyProperties) {
   600     if (chromeOnlyProperties->methods &&
   601         !DefinePrefable(cx, ourProto, chromeOnlyProperties->methods)) {
   602       return nullptr;
   603     }
   605     if (chromeOnlyProperties->attributes &&
   606         !DefinePrefable(cx, ourProto, chromeOnlyProperties->attributes)) {
   607       return nullptr;
   608     }
   610     if (chromeOnlyProperties->constants &&
   611         !DefinePrefable(cx, ourProto, chromeOnlyProperties->constants)) {
   612       return nullptr;
   613     }
   614   }
   616   return ourProto;
   617 }
   619 void
   620 CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global,
   621                        JS::Handle<JSObject*> protoProto,
   622                        const JSClass* protoClass, JS::Heap<JSObject*>* protoCache,
   623                        JS::Handle<JSObject*> constructorProto,
   624                        const JSClass* constructorClass, const JSNativeHolder* constructor,
   625                        unsigned ctorNargs, const NamedConstructor* namedConstructors,
   626                        JS::Heap<JSObject*>* constructorCache, const DOMClass* domClass,
   627                        const NativeProperties* properties,
   628                        const NativeProperties* chromeOnlyProperties,
   629                        const char* name, bool defineOnGlobal)
   630 {
   631   MOZ_ASSERT(protoClass || constructorClass || constructor,
   632              "Need at least one class or a constructor!");
   633   MOZ_ASSERT(!((properties &&
   634                 (properties->methods || properties->attributes)) ||
   635                (chromeOnlyProperties &&
   636                 (chromeOnlyProperties->methods ||
   637                  chromeOnlyProperties->attributes))) || protoClass,
   638              "Methods or properties but no protoClass!");
   639   MOZ_ASSERT(!((properties &&
   640                 (properties->staticMethods || properties->staticAttributes)) ||
   641                (chromeOnlyProperties &&
   642                 (chromeOnlyProperties->staticMethods ||
   643                  chromeOnlyProperties->staticAttributes))) ||
   644              constructorClass || constructor,
   645              "Static methods but no constructorClass or constructor!");
   646   MOZ_ASSERT(bool(name) == bool(constructorClass || constructor),
   647              "Must have name precisely when we have an interface object");
   648   MOZ_ASSERT(!constructorClass || !constructor);
   649   MOZ_ASSERT(!protoClass == !protoCache,
   650              "If, and only if, there is an interface prototype object we need "
   651              "to cache it");
   652   MOZ_ASSERT(!(constructorClass || constructor) == !constructorCache,
   653              "If, and only if, there is an interface object we need to cache "
   654              "it");
   656   JS::Rooted<JSObject*> proto(cx);
   657   if (protoClass) {
   658     proto =
   659       CreateInterfacePrototypeObject(cx, global, protoProto, protoClass,
   660                                      properties, chromeOnlyProperties);
   661     if (!proto) {
   662       return;
   663     }
   665     js::SetReservedSlot(proto, DOM_PROTO_INSTANCE_CLASS_SLOT,
   666                         JS::PrivateValue(const_cast<DOMClass*>(domClass)));
   668     *protoCache = proto;
   669   }
   670   else {
   671     MOZ_ASSERT(!proto);
   672   }
   674   JSObject* interface;
   675   if (constructorClass || constructor) {
   676     interface = CreateInterfaceObject(cx, global, constructorProto,
   677                                       constructorClass, constructor,
   678                                       ctorNargs, namedConstructors, proto,
   679                                       properties, chromeOnlyProperties, name,
   680                                       defineOnGlobal);
   681     if (!interface) {
   682       if (protoCache) {
   683         // If we fail we need to make sure to clear the value of protoCache we
   684         // set above.
   685         *protoCache = nullptr;
   686       }
   687       return;
   688     }
   689     *constructorCache = interface;
   690   }
   691 }
   693 bool
   694 NativeInterface2JSObjectAndThrowIfFailed(JSContext* aCx,
   695                                          JS::Handle<JSObject*> aScope,
   696                                          JS::MutableHandle<JS::Value> aRetval,
   697                                          xpcObjectHelper& aHelper,
   698                                          const nsIID* aIID,
   699                                          bool aAllowNativeWrapper)
   700 {
   701   js::AssertSameCompartment(aCx, aScope);
   702   nsresult rv;
   703   // Inline some logic from XPCConvert::NativeInterfaceToJSObject that we need
   704   // on all threads.
   705   nsWrapperCache *cache = aHelper.GetWrapperCache();
   707   if (cache && cache->IsDOMBinding()) {
   708       JS::Rooted<JSObject*> obj(aCx, cache->GetWrapper());
   709       if (!obj) {
   710           obj = cache->WrapObject(aCx);
   711       }
   713       if (obj && aAllowNativeWrapper && !JS_WrapObject(aCx, &obj)) {
   714         return false;
   715       }
   717       if (obj) {
   718         aRetval.setObject(*obj);
   719         return true;
   720       }
   721   }
   723   MOZ_ASSERT(NS_IsMainThread());
   725   if (!XPCConvert::NativeInterface2JSObject(aRetval, nullptr, aHelper, aIID,
   726                                             nullptr, aAllowNativeWrapper, &rv)) {
   727     // I can't tell if NativeInterface2JSObject throws JS exceptions
   728     // or not.  This is a sloppy stab at the right semantics; the
   729     // method really ought to be fixed to behave consistently.
   730     if (!JS_IsExceptionPending(aCx)) {
   731       Throw(aCx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED);
   732     }
   733     return false;
   734   }
   735   return true;
   736 }
   738 bool
   739 TryPreserveWrapper(JSObject* obj)
   740 {
   741   MOZ_ASSERT(IsDOMObject(obj));
   743   if (nsISupports* native = UnwrapDOMObjectToISupports(obj)) {
   744     nsWrapperCache* cache = nullptr;
   745     CallQueryInterface(native, &cache);
   746     if (cache) {
   747       cache->PreserveWrapper(native);
   748     }
   749     return true;
   750   }
   752   // If this DOMClass is not cycle collected, then it isn't wrappercached,
   753   // so it does not need to be preserved. If it is cycle collected, then
   754   // we can't tell if it is wrappercached or not, so we just return false.
   755   const DOMClass* domClass = GetDOMClass(obj);
   756   return domClass && !domClass->mParticipant;
   757 }
   759 // Can only be called with the immediate prototype of the instance object. Can
   760 // only be called on the prototype of an object known to be a DOM instance.
   761 bool
   762 InstanceClassHasProtoAtDepth(JSObject* protoObject, uint32_t protoID,
   763                              uint32_t depth)
   764 {
   765   const DOMClass* domClass = static_cast<const DOMClass*>(
   766     js::GetReservedSlot(protoObject, DOM_PROTO_INSTANCE_CLASS_SLOT).toPrivate());
   767   return (uint32_t)domClass->mInterfaceChain[depth] == protoID;
   768 }
   770 // Only set allowNativeWrapper to false if you really know you need it, if in
   771 // doubt use true. Setting it to false disables security wrappers.
   772 bool
   773 XPCOMObjectToJsval(JSContext* cx, JS::Handle<JSObject*> scope,
   774                    xpcObjectHelper& helper, const nsIID* iid,
   775                    bool allowNativeWrapper, JS::MutableHandle<JS::Value> rval)
   776 {
   777   if (!NativeInterface2JSObjectAndThrowIfFailed(cx, scope, rval, helper, iid,
   778                                                 allowNativeWrapper)) {
   779     return false;
   780   }
   782 #ifdef DEBUG
   783   JSObject* jsobj = rval.toObjectOrNull();
   784   if (jsobj && !js::GetObjectParent(jsobj))
   785     NS_ASSERTION(js::GetObjectClass(jsobj)->flags & JSCLASS_IS_GLOBAL,
   786                  "Why did we recreate this wrapper?");
   787 #endif
   789   return true;
   790 }
   792 bool
   793 VariantToJsval(JSContext* aCx, nsIVariant* aVariant,
   794                JS::MutableHandle<JS::Value> aRetval)
   795 {
   796   nsresult rv;
   797   if (!XPCVariant::VariantDataToJS(aVariant, &rv, aRetval)) {
   798     // Does it throw?  Who knows
   799     if (!JS_IsExceptionPending(aCx)) {
   800       Throw(aCx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED);
   801     }
   802     return false;
   803   }
   805   return true;
   806 }
   808 bool
   809 QueryInterface(JSContext* cx, unsigned argc, JS::Value* vp)
   810 {
   811   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
   812   JS::Rooted<JS::Value> thisv(cx, JS_THIS(cx, vp));
   813   if (thisv.isNull())
   814     return false;
   816   // Get the object. It might be a security wrapper, in which case we do a checked
   817   // unwrap.
   818   JS::Rooted<JSObject*> origObj(cx, &thisv.toObject());
   819   JSObject* obj = js::CheckedUnwrap(origObj, /* stopAtOuter = */ false);
   820   if (!obj) {
   821       JS_ReportError(cx, "Permission denied to access object");
   822       return false;
   823   }
   825   // Switch this to UnwrapDOMObjectToISupports once our global objects are
   826   // using new bindings.
   827   JS::Rooted<JS::Value> val(cx, JS::ObjectValue(*obj));
   828   nsISupports* native = nullptr;
   829   nsCOMPtr<nsISupports> nativeRef;
   830   xpc_qsUnwrapArg<nsISupports>(cx, val, &native,
   831                                static_cast<nsISupports**>(getter_AddRefs(nativeRef)),
   832                                &val);
   833   if (!native) {
   834     return Throw(cx, NS_ERROR_FAILURE);
   835   }
   837   if (argc < 1) {
   838     return Throw(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);
   839   }
   841   if (!args[0].isObject()) {
   842     return Throw(cx, NS_ERROR_XPC_BAD_CONVERT_JS);
   843   }
   845   nsIJSID* iid;
   846   SelfRef iidRef;
   847   if (NS_FAILED(xpc_qsUnwrapArg<nsIJSID>(cx, args[0], &iid, &iidRef.ptr,
   848                                          args[0]))) {
   849     return Throw(cx, NS_ERROR_XPC_BAD_CONVERT_JS);
   850   }
   851   MOZ_ASSERT(iid);
   853   if (iid->GetID()->Equals(NS_GET_IID(nsIClassInfo))) {
   854     nsresult rv;
   855     nsCOMPtr<nsIClassInfo> ci = do_QueryInterface(native, &rv);
   856     if (NS_FAILED(rv)) {
   857       return Throw(cx, rv);
   858     }
   860     return WrapObject(cx, ci, &NS_GET_IID(nsIClassInfo), args.rval());
   861   }
   863   nsCOMPtr<nsISupports> unused;
   864   nsresult rv = native->QueryInterface(*iid->GetID(), getter_AddRefs(unused));
   865   if (NS_FAILED(rv)) {
   866     return Throw(cx, rv);
   867   }
   869   *vp = thisv;
   870   return true;
   871 }
   873 void
   874 GetInterfaceImpl(JSContext* aCx, nsIInterfaceRequestor* aRequestor,
   875                  nsWrapperCache* aCache, nsIJSID* aIID,
   876                  JS::MutableHandle<JS::Value> aRetval, ErrorResult& aError)
   877 {
   878   const nsID* iid = aIID->GetID();
   880   nsRefPtr<nsISupports> result;
   881   aError = aRequestor->GetInterface(*iid, getter_AddRefs(result));
   882   if (aError.Failed()) {
   883     return;
   884   }
   886   if (!WrapObject(aCx, result, iid, aRetval)) {
   887     aError.Throw(NS_ERROR_FAILURE);
   888   }
   889 }
   891 bool
   892 ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp)
   893 {
   894   return ThrowErrorMessage(cx, MSG_ILLEGAL_CONSTRUCTOR);
   895 }
   897 bool
   898 ThrowConstructorWithoutNew(JSContext* cx, const char* name)
   899 {
   900   return ThrowErrorMessage(cx, MSG_CONSTRUCTOR_WITHOUT_NEW, name);
   901 }
   903 inline const NativePropertyHooks*
   904 GetNativePropertyHooks(JSContext *cx, JS::Handle<JSObject*> obj,
   905                        DOMObjectType& type)
   906 {
   907   const DOMClass* domClass = GetDOMClass(obj);
   908   if (domClass) {
   909     type = eInstance;
   910     return domClass->mNativeHooks;
   911   }
   913   if (JS_ObjectIsFunction(cx, obj)) {
   914     MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor));
   915     type = eInterface;
   916     const JS::Value& v =
   917       js::GetFunctionNativeReserved(obj,
   918                                     CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT);
   919     const JSNativeHolder* nativeHolder =
   920       static_cast<const JSNativeHolder*>(v.toPrivate());
   921     return nativeHolder->mPropertyHooks;
   922   }
   924   MOZ_ASSERT(IsDOMIfaceAndProtoClass(js::GetObjectClass(obj)));
   925   const DOMIfaceAndProtoJSClass* ifaceAndProtoJSClass =
   926     DOMIfaceAndProtoJSClass::FromJSClass(js::GetObjectClass(obj));
   927   type = ifaceAndProtoJSClass->mType;
   928   return ifaceAndProtoJSClass->mNativeHooks;
   929 }
   931 // Try to resolve a property as an unforgeable property from the given
   932 // NativeProperties, if it's there.  nativeProperties is allowed to be null (in
   933 // which case we of course won't resolve anything).
   934 static bool
   935 XrayResolveUnforgeableProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
   936                                JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
   937                                JS::MutableHandle<JSPropertyDescriptor> desc,
   938                                const NativeProperties* nativeProperties);
   940 static bool
   941 XrayResolveNativeProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
   942                           const NativePropertyHooks* nativePropertyHooks,
   943                           DOMObjectType type, JS::Handle<JSObject*> obj,
   944                           JS::Handle<jsid> id,
   945                           JS::MutableHandle<JSPropertyDescriptor> desc);
   947 bool
   948 XrayResolveOwnProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
   949                        JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
   950                        JS::MutableHandle<JSPropertyDescriptor> desc)
   951 {
   952   DOMObjectType type;
   953   const NativePropertyHooks *nativePropertyHooks =
   954     GetNativePropertyHooks(cx, obj, type);
   956   if (type != eInstance) {
   957     // For prototype objects and interface objects, just return their
   958     // normal set of properties.
   959     return XrayResolveNativeProperty(cx, wrapper, nativePropertyHooks, type,
   960                                      obj, id, desc);
   961   }
   963   // Check for unforgeable properties before doing mResolveOwnProperty weirdness
   964   const NativePropertiesHolder& nativeProperties =
   965     nativePropertyHooks->mNativeProperties;
   966   if (!XrayResolveUnforgeableProperty(cx, wrapper, obj, id, desc,
   967                                       nativeProperties.regular)) {
   968     return false;
   969   }
   970   if (desc.object()) {
   971     return true;
   972   }
   973   if (!XrayResolveUnforgeableProperty(cx, wrapper, obj, id, desc,
   974                                       nativeProperties.chromeOnly)) {
   975     return false;
   976   }
   977   if (desc.object()) {
   978     return true;
   979   }
   981   return !nativePropertyHooks->mResolveOwnProperty ||
   982          nativePropertyHooks->mResolveOwnProperty(cx, wrapper, obj, id, desc);
   983 }
   985 static bool
   986 XrayResolveAttribute(JSContext* cx, JS::Handle<JSObject*> wrapper,
   987                      JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
   988                      const Prefable<const JSPropertySpec>* attributes, jsid* attributeIds,
   989                      const JSPropertySpec* attributeSpecs, JS::MutableHandle<JSPropertyDescriptor> desc)
   990 {
   991   for (; attributes->specs; ++attributes) {
   992     if (attributes->isEnabled(cx, obj)) {
   993       // Set i to be the index into our full list of ids/specs that we're
   994       // looking at now.
   995       size_t i = attributes->specs - attributeSpecs;
   996       for ( ; attributeIds[i] != JSID_VOID; ++i) {
   997         if (id == attributeIds[i]) {
   998           const JSPropertySpec& attrSpec = attributeSpecs[i];
   999           // Because of centralization, we need to make sure we fault in the
  1000           // JitInfos as well. At present, until the JSAPI changes, the easiest
  1001           // way to do this is wrap them up as functions ourselves.
  1002           desc.setAttributes(attrSpec.flags & ~JSPROP_NATIVE_ACCESSORS);
  1003           // They all have getters, so we can just make it.
  1004           JS::Rooted<JSFunction*> fun(cx,
  1005                                       JS_NewFunctionById(cx, (JSNative)attrSpec.getter.propertyOp.op,
  1006                                                          0, 0, wrapper, id));
  1007           if (!fun)
  1008             return false;
  1009           SET_JITINFO(fun, attrSpec.getter.propertyOp.info);
  1010           JSObject *funobj = JS_GetFunctionObject(fun);
  1011           desc.setGetterObject(funobj);
  1012           desc.attributesRef() |= JSPROP_GETTER;
  1013           if (attrSpec.setter.propertyOp.op) {
  1014             // We have a setter! Make it.
  1015             fun = JS_NewFunctionById(cx, (JSNative)attrSpec.setter.propertyOp.op, 1, 0,
  1016                                      wrapper, id);
  1017             if (!fun)
  1018               return false;
  1019             SET_JITINFO(fun, attrSpec.setter.propertyOp.info);
  1020             funobj = JS_GetFunctionObject(fun);
  1021             desc.setSetterObject(funobj);
  1022             desc.attributesRef() |= JSPROP_SETTER;
  1023           } else {
  1024             desc.setSetter(nullptr);
  1026           desc.object().set(wrapper);
  1027           return true;
  1032   return true;
  1035 /* static */ bool
  1036 XrayResolveUnforgeableProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
  1037                                JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
  1038                                JS::MutableHandle<JSPropertyDescriptor> desc,
  1039                                const NativeProperties* nativeProperties)
  1041   return !nativeProperties || !nativeProperties->unforgeableAttributes ||
  1042          XrayResolveAttribute(cx, wrapper, obj, id,
  1043                               nativeProperties->unforgeableAttributes,
  1044                               nativeProperties->unforgeableAttributeIds,
  1045                               nativeProperties->unforgeableAttributeSpecs,
  1046                               desc);
  1049 static bool
  1050 XrayResolveProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
  1051                     JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
  1052                     JS::MutableHandle<JSPropertyDescriptor> desc, DOMObjectType type,
  1053                     const NativeProperties* nativeProperties)
  1055   const Prefable<const JSFunctionSpec>* methods;
  1056   jsid* methodIds;
  1057   const JSFunctionSpec* methodsSpecs;
  1058   if (type == eInterface) {
  1059     methods = nativeProperties->staticMethods;
  1060     methodIds = nativeProperties->staticMethodIds;
  1061     methodsSpecs = nativeProperties->staticMethodsSpecs;
  1062   } else {
  1063     methods = nativeProperties->methods;
  1064     methodIds = nativeProperties->methodIds;
  1065     methodsSpecs = nativeProperties->methodsSpecs;
  1067   if (methods) {
  1068     const Prefable<const JSFunctionSpec>* method;
  1069     for (method = methods; method->specs; ++method) {
  1070       if (method->isEnabled(cx, obj)) {
  1071         // Set i to be the index into our full list of ids/specs that we're
  1072         // looking at now.
  1073         size_t i = method->specs - methodsSpecs;
  1074         for ( ; methodIds[i] != JSID_VOID; ++i) {
  1075           if (id == methodIds[i]) {
  1076             const JSFunctionSpec& methodSpec = methodsSpecs[i];
  1077             JSFunction *fun;
  1078             if (methodSpec.selfHostedName) {
  1079               fun = JS::GetSelfHostedFunction(cx, methodSpec.selfHostedName, id, methodSpec.nargs);
  1080               if (!fun) {
  1081                 return false;
  1083               MOZ_ASSERT(!methodSpec.call.op, "Bad FunctionSpec declaration: non-null native");
  1084               MOZ_ASSERT(!methodSpec.call.info, "Bad FunctionSpec declaration: non-null jitinfo");
  1085             } else {
  1086               fun = JS_NewFunctionById(cx, methodSpec.call.op, methodSpec.nargs, 0, wrapper, id);
  1087               if (!fun) {
  1088                 return false;
  1090               SET_JITINFO(fun, methodSpec.call.info);
  1092             JSObject *funobj = JS_GetFunctionObject(fun);
  1093             desc.value().setObject(*funobj);
  1094             desc.setAttributes(methodSpec.flags);
  1095             desc.object().set(wrapper);
  1096             desc.setSetter(nullptr);
  1097             desc.setGetter(nullptr);
  1098            return true;
  1105   if (type == eInterface) {
  1106     if (nativeProperties->staticAttributes) {
  1107       if (!XrayResolveAttribute(cx, wrapper, obj, id,
  1108                                 nativeProperties->staticAttributes,
  1109                                 nativeProperties->staticAttributeIds,
  1110                                 nativeProperties->staticAttributeSpecs, desc)) {
  1111         return false;
  1113       if (desc.object()) {
  1114         return true;
  1117   } else {
  1118     if (nativeProperties->attributes) {
  1119       if (!XrayResolveAttribute(cx, wrapper, obj, id,
  1120                                 nativeProperties->attributes,
  1121                                 nativeProperties->attributeIds,
  1122                                 nativeProperties->attributeSpecs, desc)) {
  1123         return false;
  1125       if (desc.object()) {
  1126         return true;
  1131   if (nativeProperties->constants) {
  1132     const Prefable<const ConstantSpec>* constant;
  1133     for (constant = nativeProperties->constants; constant->specs; ++constant) {
  1134       if (constant->isEnabled(cx, obj)) {
  1135         // Set i to be the index into our full list of ids/specs that we're
  1136         // looking at now.
  1137         size_t i = constant->specs - nativeProperties->constantSpecs;
  1138         for ( ; nativeProperties->constantIds[i] != JSID_VOID; ++i) {
  1139           if (id == nativeProperties->constantIds[i]) {
  1140             desc.setAttributes(JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
  1141             desc.object().set(wrapper);
  1142             desc.value().set(nativeProperties->constantSpecs[i].value);
  1143             return true;
  1150   return true;
  1153 static bool
  1154 ResolvePrototypeOrConstructor(JSContext* cx, JS::Handle<JSObject*> wrapper,
  1155                               JS::Handle<JSObject*> obj,
  1156                               size_t protoAndIfaceCacheIndex, unsigned attrs,
  1157                               JS::MutableHandle<JSPropertyDescriptor> desc)
  1159   JS::Rooted<JSObject*> global(cx, js::GetGlobalForObjectCrossCompartment(obj));
  1161     JSAutoCompartment ac(cx, global);
  1162     ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(global);
  1163     JSObject* protoOrIface =
  1164       protoAndIfaceCache.EntrySlotIfExists(protoAndIfaceCacheIndex);
  1165     if (!protoOrIface) {
  1166       return false;
  1168     desc.object().set(wrapper);
  1169     desc.setAttributes(attrs);
  1170     desc.setGetter(JS_PropertyStub);
  1171     desc.setSetter(JS_StrictPropertyStub);
  1172     desc.value().set(JS::ObjectValue(*protoOrIface));
  1174   return JS_WrapPropertyDescriptor(cx, desc);
  1177 /* static */ bool
  1178 XrayResolveNativeProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
  1179                           const NativePropertyHooks* nativePropertyHooks,
  1180                           DOMObjectType type, JS::Handle<JSObject*> obj,
  1181                           JS::Handle<jsid> id,
  1182                           JS::MutableHandle<JSPropertyDescriptor> desc)
  1184   if (type == eInterface && IdEquals(id, "prototype")) {
  1185     return nativePropertyHooks->mPrototypeID == prototypes::id::_ID_Count ||
  1186            ResolvePrototypeOrConstructor(cx, wrapper, obj,
  1187                                          nativePropertyHooks->mPrototypeID,
  1188                                          JSPROP_PERMANENT | JSPROP_READONLY,
  1189                                          desc);
  1192   if (type == eInterfacePrototype && IdEquals(id, "constructor")) {
  1193     return nativePropertyHooks->mConstructorID == constructors::id::_ID_Count ||
  1194            ResolvePrototypeOrConstructor(cx, wrapper, obj,
  1195                                          nativePropertyHooks->mConstructorID,
  1196                                          0, desc);
  1199   const NativePropertiesHolder& nativeProperties =
  1200     nativePropertyHooks->mNativeProperties;
  1202   if (nativeProperties.regular &&
  1203       !XrayResolveProperty(cx, wrapper, obj, id, desc, type,
  1204                            nativeProperties.regular)) {
  1205     return false;
  1208   if (!desc.object() &&
  1209       nativeProperties.chromeOnly &&
  1210       xpc::AccessCheck::isChrome(js::GetObjectCompartment(wrapper)) &&
  1211       !XrayResolveProperty(cx, wrapper, obj, id, desc, type,
  1212                            nativeProperties.chromeOnly)) {
  1213     return false;
  1216   return true;
  1219 bool
  1220 XrayResolveNativeProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
  1221                           JS::Handle<JSObject*> obj,
  1222                           JS::Handle<jsid> id, JS::MutableHandle<JSPropertyDescriptor> desc)
  1224   DOMObjectType type;
  1225   const NativePropertyHooks* nativePropertyHooks =
  1226     GetNativePropertyHooks(cx, obj, type);
  1228   if (type == eInstance) {
  1229     // Force the type to be eInterfacePrototype, since we need to walk the
  1230     // prototype chain.
  1231     type = eInterfacePrototype;
  1234   if (type == eInterfacePrototype) {
  1235     do {
  1236       if (!XrayResolveNativeProperty(cx, wrapper, nativePropertyHooks, type,
  1237                                      obj, id, desc)) {
  1238         return false;
  1241       if (desc.object()) {
  1242         return true;
  1244     } while ((nativePropertyHooks = nativePropertyHooks->mProtoHooks));
  1246     return true;
  1249   return XrayResolveNativeProperty(cx, wrapper, nativePropertyHooks, type, obj,
  1250                                    id, desc);
  1253 bool
  1254 XrayDefineProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
  1255                    JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
  1256                    JS::MutableHandle<JSPropertyDescriptor> desc, bool* defined)
  1258   if (!js::IsProxy(obj))
  1259       return true;
  1261   MOZ_ASSERT(IsDOMProxy(obj), "What kind of proxy is this?");
  1263   DOMProxyHandler* handler =
  1264     static_cast<DOMProxyHandler*>(js::GetProxyHandler(obj));
  1265   return handler->defineProperty(cx, wrapper, id, desc, defined);
  1268 bool
  1269 XrayEnumerateAttributes(JSContext* cx, JS::Handle<JSObject*> wrapper,
  1270                         JS::Handle<JSObject*> obj,
  1271                         const Prefable<const JSPropertySpec>* attributes,
  1272                         jsid* attributeIds, const JSPropertySpec* attributeSpecs,
  1273                         unsigned flags, JS::AutoIdVector& props)
  1275   for (; attributes->specs; ++attributes) {
  1276     if (attributes->isEnabled(cx, obj)) {
  1277       // Set i to be the index into our full list of ids/specs that we're
  1278       // looking at now.
  1279       size_t i = attributes->specs - attributeSpecs;
  1280       for ( ; attributeIds[i] != JSID_VOID; ++i) {
  1281         if (((flags & JSITER_HIDDEN) ||
  1282              (attributeSpecs[i].flags & JSPROP_ENUMERATE)) &&
  1283             !props.append(attributeIds[i])) {
  1284           return false;
  1289   return true;
  1292 bool
  1293 XrayEnumerateProperties(JSContext* cx, JS::Handle<JSObject*> wrapper,
  1294                         JS::Handle<JSObject*> obj,
  1295                         unsigned flags, JS::AutoIdVector& props,
  1296                         DOMObjectType type,
  1297                         const NativeProperties* nativeProperties)
  1299   const Prefable<const JSFunctionSpec>* methods;
  1300   jsid* methodIds;
  1301   const JSFunctionSpec* methodsSpecs;
  1302   if (type == eInterface) {
  1303     methods = nativeProperties->staticMethods;
  1304     methodIds = nativeProperties->staticMethodIds;
  1305     methodsSpecs = nativeProperties->staticMethodsSpecs;
  1306   } else {
  1307     methods = nativeProperties->methods;
  1308     methodIds = nativeProperties->methodIds;
  1309     methodsSpecs = nativeProperties->methodsSpecs;
  1311   if (methods) {
  1312     const Prefable<const JSFunctionSpec>* method;
  1313     for (method = methods; method->specs; ++method) {
  1314       if (method->isEnabled(cx, obj)) {
  1315         // Set i to be the index into our full list of ids/specs that we're
  1316         // looking at now.
  1317         size_t i = method->specs - methodsSpecs;
  1318         for ( ; methodIds[i] != JSID_VOID; ++i) {
  1319           if (((flags & JSITER_HIDDEN) ||
  1320                (methodsSpecs[i].flags & JSPROP_ENUMERATE)) &&
  1321               !props.append(methodIds[i])) {
  1322             return false;
  1329   if (type == eInterface) {
  1330     if (nativeProperties->staticAttributes &&
  1331         !XrayEnumerateAttributes(cx, wrapper, obj,
  1332                                  nativeProperties->staticAttributes,
  1333                                  nativeProperties->staticAttributeIds,
  1334                                  nativeProperties->staticAttributeSpecs,
  1335                                  flags, props)) {
  1336       return false;
  1338   } else {
  1339     if (nativeProperties->attributes &&
  1340         !XrayEnumerateAttributes(cx, wrapper, obj,
  1341                                  nativeProperties->attributes,
  1342                                  nativeProperties->attributeIds,
  1343                                  nativeProperties->attributeSpecs,
  1344                                  flags, props)) {
  1345       return false;
  1347     if (nativeProperties->unforgeableAttributes &&
  1348         !XrayEnumerateAttributes(cx, wrapper, obj,
  1349                                  nativeProperties->unforgeableAttributes,
  1350                                  nativeProperties->unforgeableAttributeIds,
  1351                                  nativeProperties->unforgeableAttributeSpecs,
  1352                                  flags, props)) {
  1353       return false;
  1357   if (nativeProperties->constants) {
  1358     const Prefable<const ConstantSpec>* constant;
  1359     for (constant = nativeProperties->constants; constant->specs; ++constant) {
  1360       if (constant->isEnabled(cx, obj)) {
  1361         // Set i to be the index into our full list of ids/specs that we're
  1362         // looking at now.
  1363         size_t i = constant->specs - nativeProperties->constantSpecs;
  1364         for ( ; nativeProperties->constantIds[i] != JSID_VOID; ++i) {
  1365           if (!props.append(nativeProperties->constantIds[i])) {
  1366             return false;
  1373   return true;
  1376 bool
  1377 XrayEnumerateNativeProperties(JSContext* cx, JS::Handle<JSObject*> wrapper,
  1378                               const NativePropertyHooks* nativePropertyHooks,
  1379                               DOMObjectType type, JS::Handle<JSObject*> obj,
  1380                               unsigned flags, JS::AutoIdVector& props)
  1382   if (type == eInterface &&
  1383       nativePropertyHooks->mPrototypeID != prototypes::id::_ID_Count &&
  1384       !AddStringToIDVector(cx, props, "prototype")) {
  1385     return false;
  1388   if (type == eInterfacePrototype &&
  1389       nativePropertyHooks->mConstructorID != constructors::id::_ID_Count &&
  1390       (flags & JSITER_HIDDEN) &&
  1391       !AddStringToIDVector(cx, props, "constructor")) {
  1392     return false;
  1395   const NativePropertiesHolder& nativeProperties =
  1396     nativePropertyHooks->mNativeProperties;
  1398   if (nativeProperties.regular &&
  1399       !XrayEnumerateProperties(cx, wrapper, obj, flags, props, type,
  1400                                nativeProperties.regular)) {
  1401     return false;
  1404   if (nativeProperties.chromeOnly &&
  1405       xpc::AccessCheck::isChrome(js::GetObjectCompartment(wrapper)) &&
  1406       !XrayEnumerateProperties(cx, wrapper, obj, flags, props, type,
  1407                                nativeProperties.chromeOnly)) {
  1408     return false;
  1411   return true;
  1414 bool
  1415 XrayEnumerateProperties(JSContext* cx, JS::Handle<JSObject*> wrapper,
  1416                         JS::Handle<JSObject*> obj,
  1417                         unsigned flags, JS::AutoIdVector& props)
  1419   DOMObjectType type;
  1420   const NativePropertyHooks* nativePropertyHooks =
  1421     GetNativePropertyHooks(cx, obj, type);
  1423   if (type == eInstance) {
  1424     if (nativePropertyHooks->mEnumerateOwnProperties &&
  1425         !nativePropertyHooks->mEnumerateOwnProperties(cx, wrapper, obj,
  1426                                                       props)) {
  1427       return false;
  1430     if (flags & JSITER_OWNONLY) {
  1431       return true;
  1434     // Force the type to be eInterfacePrototype, since we need to walk the
  1435     // prototype chain.
  1436     type = eInterfacePrototype;
  1439   if (type == eInterfacePrototype) {
  1440     do {
  1441       if (!XrayEnumerateNativeProperties(cx, wrapper, nativePropertyHooks, type,
  1442                                          obj, flags, props)) {
  1443         return false;
  1446       if (flags & JSITER_OWNONLY) {
  1447         return true;
  1449     } while ((nativePropertyHooks = nativePropertyHooks->mProtoHooks));
  1451     return true;
  1454   return XrayEnumerateNativeProperties(cx, wrapper, nativePropertyHooks, type,
  1455                                        obj, flags, props);
  1458 NativePropertyHooks sWorkerNativePropertyHooks = {
  1459   nullptr,
  1460   nullptr,
  1462     nullptr,
  1463     nullptr
  1464   },
  1465   prototypes::id::_ID_Count,
  1466   constructors::id::_ID_Count,
  1467   nullptr
  1468 };
  1470 bool
  1471 GetPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
  1472                        JS::Handle<jsid> id, bool* found,
  1473                        JS::Value* vp)
  1475   JS::Rooted<JSObject*> proto(cx);
  1476   if (!js::GetObjectProto(cx, proxy, &proto)) {
  1477     return false;
  1479   if (!proto) {
  1480     *found = false;
  1481     return true;
  1484   bool hasProp;
  1485   if (!JS_HasPropertyById(cx, proto, id, &hasProp)) {
  1486     return false;
  1489   *found = hasProp;
  1490   if (!hasProp || !vp) {
  1491     return true;
  1494   JS::Rooted<JS::Value> value(cx);
  1495   if (!JS_ForwardGetPropertyTo(cx, proto, id, proxy, &value)) {
  1496     return false;
  1499   *vp = value;
  1500   return true;
  1503 bool
  1504 HasPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
  1505                        JS::Handle<jsid> id)
  1507   JS::Rooted<JSObject*> obj(cx, proxy);
  1508   Maybe<JSAutoCompartment> ac;
  1509   if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
  1510     obj = js::UncheckedUnwrap(obj);
  1511     ac.construct(cx, obj);
  1514   bool found;
  1515   // We ignore an error from GetPropertyOnPrototype.  We pass nullptr
  1516   // for vp so that GetPropertyOnPrototype won't actually do a get.
  1517   return !GetPropertyOnPrototype(cx, obj, id, &found, nullptr) || found;
  1520 bool
  1521 AppendNamedPropertyIds(JSContext* cx, JS::Handle<JSObject*> proxy,
  1522                        nsTArray<nsString>& names,
  1523                        bool shadowPrototypeProperties,
  1524                        JS::AutoIdVector& props)
  1526   for (uint32_t i = 0; i < names.Length(); ++i) {
  1527     JS::Rooted<JS::Value> v(cx);
  1528     if (!xpc::NonVoidStringToJsval(cx, names[i], &v)) {
  1529       return false;
  1532     JS::Rooted<jsid> id(cx);
  1533     if (!JS_ValueToId(cx, v, &id)) {
  1534       return false;
  1537     if (shadowPrototypeProperties || !HasPropertyOnPrototype(cx, proxy, id)) {
  1538       if (!props.append(id)) {
  1539         return false;
  1544   return true;
  1547 bool
  1548 DictionaryBase::ParseJSON(JSContext* aCx,
  1549                           const nsAString& aJSON,
  1550                           JS::MutableHandle<JS::Value> aVal)
  1552   if (aJSON.IsEmpty()) {
  1553     return true;
  1555   return JS_ParseJSON(aCx,
  1556                       static_cast<const jschar*>(PromiseFlatString(aJSON).get()),
  1557                       aJSON.Length(), aVal);
  1560 static JSString*
  1561 ConcatJSString(JSContext* cx, const char* pre, JS::Handle<JSString*> str, const char* post)
  1563   if (!str) {
  1564     return nullptr;
  1567   JS::Rooted<JSString*> preString(cx, JS_NewStringCopyN(cx, pre, strlen(pre)));
  1568   JS::Rooted<JSString*> postString(cx, JS_NewStringCopyN(cx, post, strlen(post)));
  1569   if (!preString || !postString) {
  1570     return nullptr;
  1573   preString = JS_ConcatStrings(cx, preString, str);
  1574   if (!preString) {
  1575     return nullptr;
  1578   return JS_ConcatStrings(cx, preString, postString);
  1581 bool
  1582 NativeToString(JSContext* cx, JS::Handle<JSObject*> wrapper,
  1583                JS::Handle<JSObject*> obj, const char* pre,
  1584                const char* post,
  1585                JS::MutableHandle<JS::Value> v)
  1587   JS::Rooted<JSPropertyDescriptor> toStringDesc(cx);
  1588   toStringDesc.object().set(nullptr);
  1589   toStringDesc.setAttributes(0);
  1590   toStringDesc.setGetter(nullptr);
  1591   toStringDesc.setSetter(nullptr);
  1592   toStringDesc.value().set(JS::UndefinedValue());
  1593   JS::Rooted<jsid> id(cx,
  1594     nsXPConnect::GetRuntimeInstance()->GetStringID(XPCJSRuntime::IDX_TO_STRING));
  1595   if (!XrayResolveNativeProperty(cx, wrapper, obj, id, &toStringDesc)) {
  1596     return false;
  1599   JS::Rooted<JSString*> str(cx);
  1601     JSAutoCompartment ac(cx, obj);
  1602     if (toStringDesc.object()) {
  1603       JS::Rooted<JS::Value> toString(cx, toStringDesc.value());
  1604       if (!JS_WrapValue(cx, &toString)) {
  1605         return false;
  1607       MOZ_ASSERT(JS_ObjectIsCallable(cx, &toString.toObject()));
  1608       JS::Rooted<JS::Value> toStringResult(cx);
  1609       if (JS_CallFunctionValue(cx, obj, toString, JS::HandleValueArray::empty(),
  1610                                &toStringResult)) {
  1611         str = toStringResult.toString();
  1612       } else {
  1613         str = nullptr;
  1615     } else {
  1616       const js::Class* clasp = js::GetObjectClass(obj);
  1617       if (IsDOMClass(clasp)) {
  1618         str = JS_NewStringCopyZ(cx, clasp->name);
  1619         str = ConcatJSString(cx, "[object ", str, "]");
  1620       } else if (IsDOMIfaceAndProtoClass(clasp)) {
  1621         const DOMIfaceAndProtoJSClass* ifaceAndProtoJSClass =
  1622           DOMIfaceAndProtoJSClass::FromJSClass(clasp);
  1623         str = JS_NewStringCopyZ(cx, ifaceAndProtoJSClass->mToString);
  1624       } else {
  1625         MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor));
  1626         JS::Rooted<JSFunction*> fun(cx, JS_GetObjectFunction(obj));
  1627         str = JS_DecompileFunction(cx, fun, 0);
  1629       str = ConcatJSString(cx, pre, str, post);
  1633   if (!str) {
  1634     return false;
  1637   v.setString(str);
  1638   return JS_WrapValue(cx, v);
  1641 // Dynamically ensure that two objects don't end up with the same reserved slot.
  1642 class MOZ_STACK_CLASS AutoCloneDOMObjectSlotGuard
  1644 public:
  1645   AutoCloneDOMObjectSlotGuard(JSContext* aCx, JSObject* aOld, JSObject* aNew)
  1646     : mOldReflector(aCx, aOld), mNewReflector(aCx, aNew)
  1648     MOZ_ASSERT(js::GetReservedSlot(aOld, DOM_OBJECT_SLOT) ==
  1649                  js::GetReservedSlot(aNew, DOM_OBJECT_SLOT));
  1652   ~AutoCloneDOMObjectSlotGuard()
  1654     if (js::GetReservedSlot(mOldReflector, DOM_OBJECT_SLOT).toPrivate()) {
  1655       js::SetReservedSlot(mNewReflector, DOM_OBJECT_SLOT,
  1656                           JS::PrivateValue(nullptr));
  1660 private:
  1661   JS::Rooted<JSObject*> mOldReflector;
  1662   JS::Rooted<JSObject*> mNewReflector;
  1663 };
  1665 nsresult
  1666 ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg)
  1668   js::AssertSameCompartment(aCx, aObjArg);
  1670   // Check if we're near the stack limit before we get anywhere near the
  1671   // transplanting code.
  1672   JS_CHECK_RECURSION(aCx, return NS_ERROR_FAILURE);
  1674   JS::Rooted<JSObject*> aObj(aCx, aObjArg);
  1675   const DOMClass* domClass = GetDOMClass(aObj);
  1677   JS::Rooted<JSObject*> oldParent(aCx, JS_GetParent(aObj));
  1678   JS::Rooted<JSObject*> newParent(aCx, domClass->mGetParent(aCx, aObj));
  1680   JSAutoCompartment oldAc(aCx, oldParent);
  1682   JSCompartment* oldCompartment = js::GetObjectCompartment(oldParent);
  1683   JSCompartment* newCompartment = js::GetObjectCompartment(newParent);
  1684   if (oldCompartment == newCompartment) {
  1685     if (!JS_SetParent(aCx, aObj, newParent)) {
  1686       MOZ_CRASH();
  1688     return NS_OK;
  1691   // Telemetry.
  1692   xpc::RecordDonatedNode(oldCompartment);
  1693   xpc::RecordAdoptedNode(newCompartment);
  1695   nsISupports* native = UnwrapDOMObjectToISupports(aObj);
  1696   if (!native) {
  1697     return NS_OK;
  1700   bool isProxy = js::IsProxy(aObj);
  1701   JS::Rooted<JSObject*> expandoObject(aCx);
  1702   if (isProxy) {
  1703     expandoObject = DOMProxyHandler::GetAndClearExpandoObject(aObj);
  1706   JSAutoCompartment newAc(aCx, newParent);
  1708   // First we clone the reflector. We get a copy of its properties and clone its
  1709   // expando chain. The only part that is dangerous here is that if we have to
  1710   // return early we must avoid ending up with two reflectors pointing to the
  1711   // same native. Other than that, the objects we create will just go away.
  1713   JS::Rooted<JSObject*> global(aCx,
  1714                                js::GetGlobalForObjectCrossCompartment(newParent));
  1715   JS::Handle<JSObject*> proto = (domClass->mGetProto)(aCx, global);
  1716   if (!proto) {
  1717     return NS_ERROR_FAILURE;
  1720   JS::Rooted<JSObject*> newobj(aCx, JS_CloneObject(aCx, aObj, proto, newParent));
  1721   if (!newobj) {
  1722     return NS_ERROR_FAILURE;
  1725   js::SetReservedSlot(newobj, DOM_OBJECT_SLOT,
  1726                       js::GetReservedSlot(aObj, DOM_OBJECT_SLOT));
  1728   // At this point, both |aObj| and |newobj| point to the same native
  1729   // which is bad, because one of them will end up being finalized with a
  1730   // native it does not own. |cloneGuard| ensures that if we exit before
  1731   // clearing |aObj|'s reserved slot the reserved slot of |newobj| will be
  1732   // set to null. |aObj| will go away soon, because we swap it with
  1733   // another object during the transplant and let that object die.
  1734   JS::Rooted<JSObject*> propertyHolder(aCx);
  1736     AutoCloneDOMObjectSlotGuard cloneGuard(aCx, aObj, newobj);
  1738     JS::Rooted<JSObject*> copyFrom(aCx, isProxy ? expandoObject : aObj);
  1739     if (copyFrom) {
  1740       propertyHolder = JS_NewObjectWithGivenProto(aCx, nullptr, JS::NullPtr(),
  1741                                                   newParent);
  1742       if (!propertyHolder) {
  1743         return NS_ERROR_OUT_OF_MEMORY;
  1746       if (!JS_CopyPropertiesFrom(aCx, propertyHolder, copyFrom)) {
  1747         return NS_ERROR_FAILURE;
  1749     } else {
  1750       propertyHolder = nullptr;
  1753     // Expandos from other compartments are attached to the target JS object.
  1754     // Copy them over, and let the old ones die a natural death.
  1755     if (!xpc::XrayUtils::CloneExpandoChain(aCx, newobj, aObj)) {
  1756       return NS_ERROR_FAILURE;
  1759     // We've set up |newobj|, so we make it own the native by nulling
  1760     // out the reserved slot of |obj|.
  1761     //
  1762     // NB: It's important to do this _after_ copying the properties to
  1763     // propertyHolder. Otherwise, an object with |foo.x === foo| will
  1764     // crash when JS_CopyPropertiesFrom tries to call wrap() on foo.x.
  1765     js::SetReservedSlot(aObj, DOM_OBJECT_SLOT, JS::PrivateValue(nullptr));
  1768   aObj = xpc::TransplantObject(aCx, aObj, newobj);
  1769   if (!aObj) {
  1770     MOZ_CRASH();
  1773   nsWrapperCache* cache = nullptr;
  1774   CallQueryInterface(native, &cache);
  1775   bool preserving = cache->PreservingWrapper();
  1776   cache->SetPreservingWrapper(false);
  1777   cache->SetWrapper(aObj);
  1778   cache->SetPreservingWrapper(preserving);
  1780   if (propertyHolder) {
  1781     JS::Rooted<JSObject*> copyTo(aCx);
  1782     if (isProxy) {
  1783       copyTo = DOMProxyHandler::EnsureExpandoObject(aCx, aObj);
  1784     } else {
  1785       copyTo = aObj;
  1788     if (!copyTo || !JS_CopyPropertiesFrom(aCx, copyTo, propertyHolder)) {
  1789       MOZ_CRASH();
  1793   nsObjectLoadingContent* htmlobject;
  1794   nsresult rv = UNWRAP_OBJECT(HTMLObjectElement, aObj, htmlobject);
  1795   if (NS_FAILED(rv)) {
  1796     rv = UnwrapObject<prototypes::id::HTMLEmbedElement,
  1797                       HTMLSharedObjectElement>(aObj, htmlobject);
  1798     if (NS_FAILED(rv)) {
  1799       rv = UnwrapObject<prototypes::id::HTMLAppletElement,
  1800                         HTMLSharedObjectElement>(aObj, htmlobject);
  1801       if (NS_FAILED(rv)) {
  1802         htmlobject = nullptr;
  1806   if (htmlobject) {
  1807     htmlobject->SetupProtoChain(aCx, aObj);
  1810   // Now we can just fix up the parent and return the wrapper
  1812   if (newParent && !JS_SetParent(aCx, aObj, newParent)) {
  1813     MOZ_CRASH();
  1816   return NS_OK;
  1819 GlobalObject::GlobalObject(JSContext* aCx, JSObject* aObject)
  1820   : mGlobalJSObject(aCx),
  1821     mCx(aCx),
  1822     mGlobalObject(nullptr)
  1824   JS::Rooted<JSObject*> obj(aCx, aObject);
  1825   if (js::IsWrapper(obj)) {
  1826     obj = js::CheckedUnwrap(obj, /* stopAtOuter = */ false);
  1827     if (!obj) {
  1828       // We should never end up here on a worker thread, since there shouldn't
  1829       // be any security wrappers to worry about.
  1830       if (!MOZ_LIKELY(NS_IsMainThread())) {
  1831         MOZ_CRASH();
  1834       Throw(aCx, NS_ERROR_XPC_SECURITY_MANAGER_VETO);
  1835       return;
  1839   mGlobalJSObject = js::GetGlobalForObjectCrossCompartment(obj);
  1842 nsISupports*
  1843 GlobalObject::GetAsSupports() const
  1845   if (mGlobalObject) {
  1846     return mGlobalObject;
  1849   if (!NS_IsMainThread()) {
  1850     mGlobalObject = UnwrapDOMObjectToISupports(mGlobalJSObject);
  1851     return mGlobalObject;
  1854   JS::Rooted<JS::Value> val(mCx, JS::ObjectValue(*mGlobalJSObject));
  1856   // Switch this to UnwrapDOMObjectToISupports once our global objects are
  1857   // using new bindings.
  1858   nsresult rv = xpc_qsUnwrapArg<nsISupports>(mCx, val, &mGlobalObject,
  1859                                              static_cast<nsISupports**>(getter_AddRefs(mGlobalObjectRef)),
  1860                                              &val);
  1861   if (NS_FAILED(rv)) {
  1862     mGlobalObject = nullptr;
  1863     Throw(mCx, NS_ERROR_XPC_BAD_CONVERT_JS);
  1866   return mGlobalObject;
  1869 bool
  1870 InterfaceHasInstance(JSContext* cx, JS::Handle<JSObject*> obj,
  1871                      JS::Handle<JSObject*> instance,
  1872                      bool* bp)
  1874   const DOMIfaceAndProtoJSClass* clasp =
  1875     DOMIfaceAndProtoJSClass::FromJSClass(js::GetObjectClass(obj));
  1877   const DOMClass* domClass = GetDOMClass(js::UncheckedUnwrap(instance));
  1879   MOZ_ASSERT(!domClass || clasp->mPrototypeID != prototypes::id::_ID_Count,
  1880              "Why do we have a hasInstance hook if we don't have a prototype "
  1881              "ID?");
  1883   if (domClass &&
  1884       domClass->mInterfaceChain[clasp->mDepth] == clasp->mPrototypeID) {
  1885     *bp = true;
  1886     return true;
  1889   JS::Rooted<JSObject*> unwrapped(cx, js::CheckedUnwrap(instance, true));
  1890   if (unwrapped && jsipc::JavaScriptParent::IsCPOW(unwrapped)) {
  1891     bool boolp = false;
  1892     if (!jsipc::JavaScriptParent::DOMInstanceOf(cx, unwrapped, clasp->mPrototypeID,
  1893                                                 clasp->mDepth, &boolp)) {
  1894       return false;
  1896     *bp = boolp;
  1897     return true;
  1900   JS::Rooted<JS::Value> protov(cx);
  1901   DebugOnly<bool> ok = JS_GetProperty(cx, obj, "prototype", &protov);
  1902   MOZ_ASSERT(ok, "Someone messed with our prototype property?");
  1904   JS::Rooted<JSObject*> interfacePrototype(cx, &protov.toObject());
  1905   MOZ_ASSERT(IsDOMIfaceAndProtoClass(js::GetObjectClass(interfacePrototype)),
  1906              "Someone messed with our prototype property?");
  1908   JS::Rooted<JSObject*> proto(cx);
  1909   if (!JS_GetPrototype(cx, instance, &proto)) {
  1910     return false;
  1913   while (proto) {
  1914     if (proto == interfacePrototype) {
  1915       *bp = true;
  1916       return true;
  1919     if (!JS_GetPrototype(cx, proto, &proto)) {
  1920       return false;
  1924   *bp = false;
  1925   return true;
  1928 bool
  1929 InterfaceHasInstance(JSContext* cx, JS::Handle<JSObject*> obj, JS::MutableHandle<JS::Value> vp,
  1930                      bool* bp)
  1932   if (!vp.isObject()) {
  1933     *bp = false;
  1934     return true;
  1937   JS::Rooted<JSObject*> instanceObject(cx, &vp.toObject());
  1938   return InterfaceHasInstance(cx, obj, instanceObject, bp);
  1941 bool
  1942 InterfaceHasInstance(JSContext* cx, int prototypeID, int depth,
  1943                      JS::Handle<JSObject*> instance,
  1944                      bool* bp)
  1946   const DOMClass* domClass = GetDOMClass(js::UncheckedUnwrap(instance));
  1948   MOZ_ASSERT(!domClass || prototypeID != prototypes::id::_ID_Count,
  1949              "Why do we have a hasInstance hook if we don't have a prototype "
  1950              "ID?");
  1952   *bp = (domClass && domClass->mInterfaceChain[depth] == prototypeID);
  1953   return true;
  1956 bool
  1957 ReportLenientThisUnwrappingFailure(JSContext* cx, JSObject* obj)
  1959   JS::Rooted<JSObject*> rootedObj(cx, obj);
  1960   GlobalObject global(cx, rootedObj);
  1961   if (global.Failed()) {
  1962     return false;
  1964   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(global.GetAsSupports());
  1965   if (window && window->GetDoc()) {
  1966     window->GetDoc()->WarnOnceAbout(nsIDocument::eLenientThis);
  1968   return true;
  1971 bool
  1972 GetWindowForJSImplementedObject(JSContext* cx, JS::Handle<JSObject*> obj,
  1973                                 nsPIDOMWindow** window)
  1975   // Be very careful to not get tricked here.
  1976   MOZ_ASSERT(NS_IsMainThread());
  1977   if (!xpc::AccessCheck::isChrome(js::GetObjectCompartment(obj))) {
  1978     NS_RUNTIMEABORT("Should have a chrome object here");
  1981   // Look up the content-side object.
  1982   JS::Rooted<JS::Value> domImplVal(cx);
  1983   if (!JS_GetProperty(cx, obj, "__DOM_IMPL__", &domImplVal)) {
  1984     return false;
  1987   if (!domImplVal.isObject()) {
  1988     ThrowErrorMessage(cx, MSG_NOT_OBJECT, "Value");
  1989     return false;
  1992   // Go ahead and get the global from it.  GlobalObject will handle
  1993   // doing unwrapping as needed.
  1994   GlobalObject global(cx, &domImplVal.toObject());
  1995   if (global.Failed()) {
  1996     return false;
  1999   // It's OK if we have null here: that just means the content-side
  2000   // object really wasn't associated with any window.
  2001   nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(global.GetAsSupports()));
  2002   win.forget(window);
  2003   return true;
  2006 already_AddRefed<nsPIDOMWindow>
  2007 ConstructJSImplementation(JSContext* aCx, const char* aContractId,
  2008                           const GlobalObject& aGlobal,
  2009                           JS::MutableHandle<JSObject*> aObject,
  2010                           ErrorResult& aRv)
  2012   // Get the window to use as a parent and for initialization.
  2013   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
  2014   if (!window) {
  2015     aRv.Throw(NS_ERROR_FAILURE);
  2016     return nullptr;
  2019   ConstructJSImplementation(aCx, aContractId, window, aObject, aRv);
  2021   if (aRv.Failed()) {
  2022     return nullptr;
  2024   return window.forget();
  2027 void
  2028 ConstructJSImplementation(JSContext* aCx, const char* aContractId,
  2029                           nsPIDOMWindow* aWindow,
  2030                           JS::MutableHandle<JSObject*> aObject,
  2031                           ErrorResult& aRv)
  2033   // Make sure to divorce ourselves from the calling JS while creating and
  2034   // initializing the object, so exceptions from that will get reported
  2035   // properly, since those are never exceptions that a spec wants to be thrown.
  2037     AutoNoJSAPI nojsapi;
  2039     // Get the XPCOM component containing the JS implementation.
  2040     nsCOMPtr<nsISupports> implISupports = do_CreateInstance(aContractId);
  2041     if (!implISupports) {
  2042       NS_WARNING("Failed to get JS implementation for contract");
  2043       aRv.Throw(NS_ERROR_FAILURE);
  2044       return;
  2046     // Initialize the object, if it implements nsIDOMGlobalPropertyInitializer.
  2047     nsCOMPtr<nsIDOMGlobalPropertyInitializer> gpi =
  2048       do_QueryInterface(implISupports);
  2049     if (gpi) {
  2050       JS::Rooted<JS::Value> initReturn(aCx);
  2051       nsresult rv = gpi->Init(aWindow, &initReturn);
  2052       if (NS_FAILED(rv)) {
  2053         aRv.Throw(rv);
  2054         return;
  2056       // With JS-implemented WebIDL, the return value of init() is not used to determine
  2057       // if init() failed, so init() should only return undefined. Any kind of permission
  2058       // or pref checking must happen by adding an attribute to the WebIDL interface.
  2059       if (!initReturn.isUndefined()) {
  2060         MOZ_ASSERT(false, "The init() method for JS-implemented WebIDL should not return anything");
  2061         MOZ_CRASH();
  2064     // Extract the JS implementation from the XPCOM object.
  2065     nsCOMPtr<nsIXPConnectWrappedJS> implWrapped =
  2066       do_QueryInterface(implISupports);
  2067     MOZ_ASSERT(implWrapped, "Failed to get wrapped JS from XPCOM component.");
  2068     if (!implWrapped) {
  2069       aRv.Throw(NS_ERROR_FAILURE);
  2070       return;
  2072     aObject.set(implWrapped->GetJSObject());
  2073     if (!aObject) {
  2074       aRv.Throw(NS_ERROR_FAILURE);
  2079 bool
  2080 NonVoidByteStringToJsval(JSContext *cx, const nsACString &str,
  2081                          JS::MutableHandle<JS::Value> rval)
  2083     // ByteStrings are not UTF-8 encoded.
  2084     JSString* jsStr = JS_NewStringCopyN(cx, str.Data(), str.Length());
  2086     if (!jsStr)
  2087         return false;
  2089     rval.setString(jsStr);
  2090     return true;
  2093 bool
  2094 ConvertJSValueToByteString(JSContext* cx, JS::Handle<JS::Value> v,
  2095                            JS::MutableHandle<JS::Value> pval, bool nullable,
  2096                            nsACString& result)
  2098   JS::Rooted<JSString*> s(cx);
  2099   if (v.isString()) {
  2100     s = v.toString();
  2101   } else {
  2103     if (nullable && v.isNullOrUndefined()) {
  2104       result.SetIsVoid(true);
  2105       return true;
  2108     s = JS::ToString(cx, v);
  2109     if (!s) {
  2110       return false;
  2112     pval.set(JS::StringValue(s));  // Root the new string.
  2115   size_t length;
  2116   const jschar *chars = JS_GetStringCharsZAndLength(cx, s, &length);
  2117   if (!chars) {
  2118     return false;
  2121   // Conversion from Javascript string to ByteString is only valid if all
  2122   // characters < 256.
  2123   for (size_t i = 0; i < length; i++) {
  2124     if (chars[i] > 255) {
  2125       // The largest unsigned 64 bit number (18,446,744,073,709,551,615) has
  2126       // 20 digits, plus one more for the null terminator.
  2127       char index[21];
  2128       static_assert(sizeof(size_t) <= 8, "index array too small");
  2129       PR_snprintf(index, sizeof(index), "%d", i);
  2130       // A jschar is 16 bits long.  The biggest unsigned 16 bit
  2131       // number (65,535) has 5 digits, plus one more for the null
  2132       // terminator.
  2133       char badChar[6];
  2134       static_assert(sizeof(jschar) <= 2, "badChar array too small");
  2135       PR_snprintf(badChar, sizeof(badChar), "%d", chars[i]);
  2136       ThrowErrorMessage(cx, MSG_INVALID_BYTESTRING, index, badChar);
  2137       return false;
  2141   if (length >= UINT32_MAX) {
  2142     return false;
  2144   result.SetCapacity(length+1);
  2145   JS_EncodeStringToBuffer(cx, s, result.BeginWriting(), length);
  2146   result.BeginWriting()[length] = '\0';
  2147   result.SetLength(length);
  2149   return true;
  2152 bool
  2153 IsInPrivilegedApp(JSContext* aCx, JSObject* aObj)
  2155   using mozilla::dom::workers::GetWorkerPrivateFromContext;
  2156   if (!NS_IsMainThread()) {
  2157     return GetWorkerPrivateFromContext(aCx)->IsInPrivilegedApp();
  2160   nsIPrincipal* principal = nsContentUtils::GetObjectPrincipal(aObj);
  2161   uint16_t appStatus = principal->GetAppStatus();
  2162   return (appStatus == nsIPrincipal::APP_STATUS_CERTIFIED ||
  2163           appStatus == nsIPrincipal::APP_STATUS_PRIVILEGED);
  2166 bool
  2167 IsInCertifiedApp(JSContext* aCx, JSObject* aObj)
  2169   using mozilla::dom::workers::GetWorkerPrivateFromContext;
  2170   if (!NS_IsMainThread()) {
  2171     return GetWorkerPrivateFromContext(aCx)->IsInCertifiedApp();
  2174   nsIPrincipal* principal = nsContentUtils::GetObjectPrincipal(aObj);
  2175   return principal->GetAppStatus() == nsIPrincipal::APP_STATUS_CERTIFIED;
  2178 void
  2179 TraceGlobal(JSTracer* aTrc, JSObject* aObj)
  2181   MOZ_ASSERT(js::GetObjectClass(aObj)->flags & JSCLASS_DOM_GLOBAL);
  2182   mozilla::dom::TraceProtoAndIfaceCache(aTrc, aObj);
  2185 void
  2186 FinalizeGlobal(JSFreeOp* aFreeOp, JSObject* aObj)
  2188   MOZ_ASSERT(js::GetObjectClass(aObj)->flags & JSCLASS_DOM_GLOBAL);
  2189   mozilla::dom::DestroyProtoAndIfaceCache(aObj);
  2192 bool
  2193 ResolveGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj,
  2194               JS::Handle<jsid> aId, JS::MutableHandle<JSObject*> aObjp)
  2196   bool resolved;
  2197   if (!JS_ResolveStandardClass(aCx, aObj, aId, &resolved)) {
  2198     return false;
  2201   aObjp.set(resolved ? aObj.get() : nullptr);
  2202   return true;
  2205 bool
  2206 EnumerateGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj)
  2208   return JS_EnumerateStandardClasses(aCx, aObj);
  2211 bool
  2212 GenericBindingGetter(JSContext* cx, unsigned argc, JS::Value* vp)
  2214   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
  2215   const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
  2216   prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID);
  2217   if (!args.thisv().isObject()) {
  2218     return ThrowInvalidThis(cx, args,
  2219                             MSG_GETTER_THIS_DOES_NOT_IMPLEMENT_INTERFACE,
  2220                             protoID);
  2222   JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
  2224   void* self;
  2226     nsresult rv = UnwrapObject<void>(obj, self, protoID, info->depth);
  2227     if (NS_FAILED(rv)) {
  2228       return ThrowInvalidThis(cx, args,
  2229                               GetInvalidThisErrorForGetter(rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO),
  2230                               protoID);
  2234   MOZ_ASSERT(info->type() == JSJitInfo::Getter);
  2235   JSJitGetterOp getter = info->getter;
  2236   return getter(cx, obj, self, JSJitGetterCallArgs(args));
  2239 bool
  2240 GenericBindingSetter(JSContext* cx, unsigned argc, JS::Value* vp)
  2242   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
  2243   const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
  2244   prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID);
  2245   if (!args.thisv().isObject()) {
  2246     return ThrowInvalidThis(cx, args,
  2247                             MSG_SETTER_THIS_DOES_NOT_IMPLEMENT_INTERFACE,
  2248                             protoID);
  2250   JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
  2252   void* self;
  2254     nsresult rv = UnwrapObject<void>(obj, self, protoID, info->depth);
  2255     if (NS_FAILED(rv)) {
  2256       return ThrowInvalidThis(cx, args,
  2257                               GetInvalidThisErrorForSetter(rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO),
  2258                               protoID);
  2261   if (args.length() == 0) {
  2262     return ThrowNoSetterArg(cx, protoID);
  2264   MOZ_ASSERT(info->type() == JSJitInfo::Setter);
  2265   JSJitSetterOp setter = info->setter;
  2266   if (!setter(cx, obj, self, JSJitSetterCallArgs(args))) {
  2267     return false;
  2269   args.rval().set(JSVAL_VOID);
  2270   return true;
  2273 bool
  2274 GenericBindingMethod(JSContext* cx, unsigned argc, JS::Value* vp)
  2276   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
  2277   const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
  2278   prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID);
  2279   if (!args.thisv().isObject()) {
  2280     return ThrowInvalidThis(cx, args,
  2281                             MSG_METHOD_THIS_DOES_NOT_IMPLEMENT_INTERFACE,
  2282                             protoID);
  2284   JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
  2286   void* self;
  2288     nsresult rv = UnwrapObject<void>(obj, self, protoID, info->depth);
  2289     if (NS_FAILED(rv)) {
  2290       return ThrowInvalidThis(cx, args,
  2291                               GetInvalidThisErrorForMethod(rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO),
  2292                               protoID);
  2295   MOZ_ASSERT(info->type() == JSJitInfo::Method);
  2296   JSJitMethodOp method = info->method;
  2297   return method(cx, obj, self, JSJitMethodCallArgs(args));
  2300 bool
  2301 GenericPromiseReturningBindingMethod(JSContext* cx, unsigned argc, JS::Value* vp)
  2303   // Make sure to save the callee before someone maybe messes with rval().
  2304   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
  2305   JS::Rooted<JSObject*> callee(cx, &args.callee());
  2307   // We could invoke GenericBindingMethod here, but that involves an
  2308   // extra call.  Manually inline it instead.
  2309   const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
  2310   prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID);
  2311   if (!args.thisv().isObject()) {
  2312     ThrowInvalidThis(cx, args,
  2313                      MSG_METHOD_THIS_DOES_NOT_IMPLEMENT_INTERFACE,
  2314                      protoID);
  2315     return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
  2316                                      args.rval());
  2318   JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
  2320   void* self;
  2322     nsresult rv = UnwrapObject<void>(obj, self, protoID, info->depth);
  2323     if (NS_FAILED(rv)) {
  2324       ThrowInvalidThis(cx, args,
  2325                        GetInvalidThisErrorForMethod(rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO),
  2326                        protoID);
  2327       return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
  2328                                        args.rval());
  2331   MOZ_ASSERT(info->type() == JSJitInfo::Method);
  2332   JSJitMethodOp method = info->method;
  2333   bool ok = method(cx, obj, self, JSJitMethodCallArgs(args));
  2334   if (ok) {
  2335     return true;
  2338   return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
  2339                                    args.rval());
  2342 bool
  2343 StaticMethodPromiseWrapper(JSContext* cx, unsigned argc, JS::Value* vp)
  2345   // Make sure to save the callee before someone maybe messes with rval().
  2346   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
  2347   JS::Rooted<JSObject*> callee(cx, &args.callee());
  2349   const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
  2350   MOZ_ASSERT(info);
  2351   MOZ_ASSERT(info->type() == JSJitInfo::StaticMethod);
  2353   bool ok = info->staticMethod(cx, argc, vp);
  2354   if (ok) {
  2355     return true;
  2358   return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
  2359                                    args.rval());
  2362 bool
  2363 ConvertExceptionToPromise(JSContext* cx,
  2364                           JSObject* promiseScope,
  2365                           JS::MutableHandle<JS::Value> rval)
  2367   GlobalObject global(cx, promiseScope);
  2368   if (global.Failed()) {
  2369     return false;
  2372   JS::Rooted<JS::Value> exn(cx);
  2373   if (!JS_GetPendingException(cx, &exn)) {
  2374     return false;
  2377   JS_ClearPendingException(cx);
  2378   ErrorResult rv;
  2379   nsRefPtr<Promise> promise = Promise::Reject(global, cx, exn, rv);
  2380   if (rv.Failed()) {
  2381     // We just give up.  Make sure to not leak memory on the
  2382     // ErrorResult, but then just put the original exception back.
  2383     ThrowMethodFailedWithDetails(cx, rv, "", "");
  2384     JS_SetPendingException(cx, exn);
  2385     return false;
  2388   return WrapNewBindingObject(cx, promise, rval);
  2391 } // namespace dom
  2392 } // namespace mozilla

mercurial