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.

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

mercurial