BindingUtils.cpp (153965B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "BindingUtils.h" 8 9 #include <stdarg.h> 10 11 #include <algorithm> 12 #include <cstdint> 13 14 #include "AccessCheck.h" 15 #include "WorkerPrivate.h" 16 #include "WorkerRunnable.h" 17 #include "WrapperFactory.h" 18 #include "XrayWrapper.h" 19 #include "ipc/ErrorIPCUtils.h" 20 #include "ipc/IPCMessageUtilsSpecializations.h" 21 #include "js/CallAndConstruct.h" // JS::Call, JS::IsCallable 22 #include "js/Id.h" 23 #include "js/JSON.h" 24 #include "js/MapAndSet.h" 25 #include "js/Object.h" // JS::GetClass, JS::GetCompartment, JS::GetReservedSlot, JS::SetReservedSlot 26 #include "js/PropertyAndElement.h" // JS_AlreadyHasOwnPropertyById, JS_DefineFunction, JS_DefineFunctionById, JS_DefineFunctions, JS_DefineProperties, JS_DefineProperty, JS_DefinePropertyById, JS_ForwardGetPropertyTo, JS_GetProperty, JS_HasProperty, JS_HasPropertyById 27 #include "js/StableStringChars.h" 28 #include "js/String.h" // JS::GetStringLength, JS::MaxStringLength, JS::StringHasLatin1Chars 29 #include "js/Symbol.h" 30 #include "js/experimental/JitInfo.h" // JSJit{Getter,Setter,Method}CallArgs, JSJit{Getter,Setter}Op, JSJitInfo 31 #include "js/friend/StackLimits.h" // js::AutoCheckRecursionLimit 32 #include "jsfriendapi.h" 33 #include "mozilla/Assertions.h" 34 #include "mozilla/DebugOnly.h" 35 #include "mozilla/Encoding.h" 36 #include "mozilla/Preferences.h" 37 #include "mozilla/ScopeExit.h" 38 #include "mozilla/Sprintf.h" 39 #include "mozilla/StaticPrefs_dom.h" 40 #include "mozilla/UniquePtr.h" 41 #include "mozilla/UseCounter.h" 42 #include "mozilla/dom/CustomElementRegistry.h" 43 #include "mozilla/dom/DOMException.h" 44 #include "mozilla/dom/DeprecationReportBody.h" 45 #include "mozilla/dom/DocGroup.h" 46 #include "mozilla/dom/ElementBinding.h" 47 #include "mozilla/dom/Exceptions.h" 48 #include "mozilla/dom/HTMLElementBinding.h" 49 #include "mozilla/dom/HTMLEmbedElement.h" 50 #include "mozilla/dom/HTMLEmbedElementBinding.h" 51 #include "mozilla/dom/HTMLObjectElement.h" 52 #include "mozilla/dom/HTMLObjectElementBinding.h" 53 #include "mozilla/dom/MaybeCrossOriginObject.h" 54 #include "mozilla/dom/ObservableArrayProxyHandler.h" 55 #include "mozilla/dom/Promise.h" 56 #include "mozilla/dom/ReportingUtils.h" 57 #include "mozilla/dom/ScriptSettings.h" 58 #include "mozilla/dom/WebIDLGlobalNameHash.h" 59 #include "mozilla/dom/WindowProxyHolder.h" 60 #include "mozilla/dom/WorkerPrivate.h" 61 #include "mozilla/dom/WorkerScope.h" 62 #include "mozilla/dom/XULElementBinding.h" 63 #include "mozilla/dom/XULFrameElementBinding.h" 64 #include "mozilla/dom/XULMenuElementBinding.h" 65 #include "mozilla/dom/XULPopupElementBinding.h" 66 #include "mozilla/dom/XULResizerElementBinding.h" 67 #include "mozilla/dom/XULTextElementBinding.h" 68 #include "mozilla/dom/XULTreeElementBinding.h" 69 #include "mozilla/dom/XrayExpandoClass.h" 70 #include "nsContentCreatorFunctions.h" 71 #include "nsContentUtils.h" 72 #include "nsGlobalWindowInner.h" 73 #include "nsHTMLTags.h" 74 #include "nsIDOMGlobalPropertyInitializer.h" 75 #include "nsINode.h" 76 #include "nsIOService.h" 77 #include "nsIPrincipal.h" 78 #include "nsIXPConnect.h" 79 #include "nsPrintfCString.h" 80 #include "nsReadableUtils.h" 81 #include "nsUTF8Utils.h" 82 #include "nsWrapperCacheInlines.h" 83 #include "nsXULElement.h" 84 #include "xpcprivate.h" 85 86 namespace mozilla { 87 namespace dom { 88 89 // Forward declare GetConstructorObjectHandle methods. 90 #define HTML_TAG(_tag, _classname, _interfacename) \ 91 namespace HTML##_interfacename##Element_Binding { \ 92 JS::Handle<JSObject*> GetConstructorObjectHandle(JSContext*); \ 93 } 94 #define HTML_OTHER(_tag) 95 #include "nsHTMLTagList.h" 96 #undef HTML_TAG 97 #undef HTML_OTHER 98 99 using constructorGetterCallback = JS::Handle<JSObject*> (*)(JSContext*); 100 101 // Mapping of html tag and GetConstructorObjectHandle methods. 102 #define HTML_TAG(_tag, _classname, _interfacename) \ 103 HTML##_interfacename##Element_Binding::GetConstructorObjectHandle, 104 #define HTML_OTHER(_tag) nullptr, 105 // We use eHTMLTag_foo (where foo is the tag) which is defined in nsHTMLTags.h 106 // to index into this array. 107 static const constructorGetterCallback sConstructorGetterCallback[] = { 108 HTMLUnknownElement_Binding::GetConstructorObjectHandle, 109 #include "nsHTMLTagList.h" 110 #undef HTML_TAG 111 #undef HTML_OTHER 112 }; 113 114 static const JSErrorFormatString ErrorFormatString[] = { 115 #define MSG_DEF(_name, _argc, _has_context, _exn, _str) \ 116 {#_name, _str, _argc, _exn}, 117 #include "mozilla/dom/Errors.msg" 118 #undef MSG_DEF 119 }; 120 121 #define MSG_DEF(_name, _argc, _has_context, _exn, _str) \ 122 static_assert( \ 123 (_argc) < JS::MaxNumErrorArguments, #_name \ 124 " must only have as many error arguments as the JS engine can support"); 125 #include "mozilla/dom/Errors.msg" 126 #undef MSG_DEF 127 128 static const JSErrorFormatString* GetErrorMessage(void* aUserRef, 129 const unsigned aErrorNumber) { 130 MOZ_ASSERT(aErrorNumber < std::size(ErrorFormatString)); 131 return &ErrorFormatString[aErrorNumber]; 132 } 133 134 uint16_t GetErrorArgCount(const ErrNum aErrorNumber) { 135 return GetErrorMessage(nullptr, aErrorNumber)->argCount; 136 } 137 138 // aErrorNumber needs to be unsigned, not an ErrNum, because the latter makes 139 // va_start have undefined behavior, and we do not want undefined behavior. 140 void binding_detail::ThrowErrorMessage(JSContext* aCx, 141 const unsigned aErrorNumber, ...) { 142 va_list ap; 143 va_start(ap, aErrorNumber); 144 145 if (!ErrorFormatHasContext[aErrorNumber]) { 146 JS_ReportErrorNumberUTF8VA(aCx, GetErrorMessage, nullptr, aErrorNumber, ap); 147 va_end(ap); 148 return; 149 } 150 151 // Our first arg is the context arg. We want to replace nullptr with empty 152 // string, leave empty string alone, and for anything else append ": " to the 153 // end. See also the behavior of 154 // TErrorResult::SetPendingExceptionWithMessage, which this is mirroring for 155 // exceptions that are thrown directly, not via an ErrorResult. 156 const char* args[JS::MaxNumErrorArguments + 1]; 157 size_t argCount = GetErrorArgCount(static_cast<ErrNum>(aErrorNumber)); 158 MOZ_ASSERT(argCount > 0, "We have a context arg!"); 159 nsAutoCString firstArg; 160 161 for (size_t i = 0; i < argCount; ++i) { 162 args[i] = va_arg(ap, const char*); 163 if (i == 0) { 164 if (args[0] && *args[0]) { 165 firstArg.Append(args[0]); 166 firstArg.AppendLiteral(": "); 167 } 168 args[0] = firstArg.get(); 169 } 170 } 171 172 JS_ReportErrorNumberUTF8Array(aCx, GetErrorMessage, nullptr, aErrorNumber, 173 args); 174 va_end(ap); 175 } 176 177 static bool ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs, 178 bool aSecurityError, const char* aInterfaceName) { 179 NS_ConvertASCIItoUTF16 ifaceName(aInterfaceName); 180 // This should only be called for DOM methods/getters/setters, which 181 // are JSNative-backed functions, so we can assume that 182 // JS_ValueToFunction and JS_GetFunctionDisplayId will both return 183 // non-null and that JS_GetStringCharsZ returns non-null. 184 JS::Rooted<JSFunction*> func(aCx, JS_ValueToFunction(aCx, aArgs.calleev())); 185 MOZ_ASSERT(func); 186 JS::Rooted<JSString*> funcName(aCx); 187 if (!JS_GetFunctionDisplayId(aCx, func, &funcName)) { 188 return false; 189 } 190 MOZ_ASSERT(funcName); 191 nsAutoJSString funcNameStr; 192 if (!funcNameStr.init(aCx, funcName)) { 193 return false; 194 } 195 if (aSecurityError) { 196 return Throw(aCx, NS_ERROR_DOM_SECURITY_ERR, 197 nsPrintfCString("Permission to call '%s' denied.", 198 NS_ConvertUTF16toUTF8(funcNameStr).get())); 199 } 200 201 const ErrNum errorNumber = MSG_METHOD_THIS_DOES_NOT_IMPLEMENT_INTERFACE; 202 MOZ_RELEASE_ASSERT(GetErrorArgCount(errorNumber) == 2); 203 JS_ReportErrorNumberUC(aCx, GetErrorMessage, nullptr, 204 static_cast<unsigned>(errorNumber), 205 static_cast<const char16_t*>(funcNameStr.get()), 206 static_cast<const char16_t*>(ifaceName.get())); 207 return false; 208 } 209 210 bool ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs, 211 bool aSecurityError, prototypes::ID aProtoId) { 212 return ThrowInvalidThis(aCx, aArgs, aSecurityError, 213 NamesOfInterfacesWithProtos(aProtoId)); 214 } 215 216 } // namespace dom 217 218 namespace binding_danger { 219 220 template <typename CleanupPolicy> 221 struct TErrorResult<CleanupPolicy>::Message { 222 Message() : mErrorNumber(dom::Err_Limit) { 223 MOZ_COUNT_CTOR(TErrorResult::Message); 224 } 225 ~Message() { MOZ_COUNT_DTOR(TErrorResult::Message); } 226 227 // UTF-8 strings (probably ASCII in most cases) in mArgs. 228 nsTArray<nsCString> mArgs; 229 dom::ErrNum mErrorNumber; 230 231 bool HasCorrectNumberOfArguments() { 232 return GetErrorArgCount(mErrorNumber) == mArgs.Length(); 233 } 234 235 bool operator==(const TErrorResult<CleanupPolicy>::Message& aRight) const { 236 return mErrorNumber == aRight.mErrorNumber && mArgs == aRight.mArgs; 237 } 238 }; 239 240 template <typename CleanupPolicy> 241 nsTArray<nsCString>& TErrorResult<CleanupPolicy>::CreateErrorMessageHelper( 242 const dom::ErrNum errorNumber, nsresult errorType) { 243 AssertInOwningThread(); 244 mResult = errorType; 245 246 Message* message = InitMessage(new Message()); 247 message->mErrorNumber = errorNumber; 248 return message->mArgs; 249 } 250 251 template <typename CleanupPolicy> 252 void TErrorResult<CleanupPolicy>::SerializeMessage( 253 IPC::MessageWriter* aWriter) const { 254 using namespace IPC; 255 AssertInOwningThread(); 256 MOZ_ASSERT(mUnionState == HasMessage); 257 MOZ_ASSERT(mExtra.mMessage); 258 WriteParam(aWriter, mExtra.mMessage->mArgs); 259 WriteParam(aWriter, mExtra.mMessage->mErrorNumber); 260 } 261 262 template <typename CleanupPolicy> 263 bool TErrorResult<CleanupPolicy>::DeserializeMessage( 264 IPC::MessageReader* aReader) { 265 using namespace IPC; 266 AssertInOwningThread(); 267 auto readMessage = MakeUnique<Message>(); 268 if (!ReadParam(aReader, &readMessage->mArgs) || 269 !ReadParam(aReader, &readMessage->mErrorNumber)) { 270 return false; 271 } 272 if (!readMessage->HasCorrectNumberOfArguments()) { 273 return false; 274 } 275 276 MOZ_ASSERT(mUnionState == HasNothing); 277 InitMessage(readMessage.release()); 278 #ifdef DEBUG 279 mUnionState = HasMessage; 280 #endif // DEBUG 281 return true; 282 } 283 284 template <typename CleanupPolicy> 285 void TErrorResult<CleanupPolicy>::SetPendingExceptionWithMessage( 286 JSContext* aCx, const char* context) { 287 AssertInOwningThread(); 288 MOZ_ASSERT(mUnionState == HasMessage); 289 MOZ_ASSERT(mExtra.mMessage, 290 "SetPendingExceptionWithMessage() can be called only once"); 291 292 Message* message = mExtra.mMessage; 293 MOZ_RELEASE_ASSERT(message->HasCorrectNumberOfArguments()); 294 if (dom::ErrorFormatHasContext[message->mErrorNumber]) { 295 MOZ_ASSERT(!message->mArgs.IsEmpty(), "How could we have no args here?"); 296 MOZ_ASSERT(message->mArgs[0].IsEmpty(), "Context should not be set yet!"); 297 if (context) { 298 // Prepend our context and ": "; see API documentation. 299 message->mArgs[0].AssignASCII(context); 300 message->mArgs[0].AppendLiteral(": "); 301 } 302 } 303 const uint32_t argCount = message->mArgs.Length(); 304 const char* args[JS::MaxNumErrorArguments + 1]; 305 for (uint32_t i = 0; i < argCount; ++i) { 306 args[i] = message->mArgs.ElementAt(i).get(); 307 } 308 args[argCount] = nullptr; 309 310 JS_ReportErrorNumberUTF8Array(aCx, dom::GetErrorMessage, nullptr, 311 static_cast<unsigned>(message->mErrorNumber), 312 argCount > 0 ? args : nullptr); 313 314 ClearMessage(); 315 mResult = NS_OK; 316 } 317 318 template <typename CleanupPolicy> 319 void TErrorResult<CleanupPolicy>::ClearMessage() { 320 AssertInOwningThread(); 321 MOZ_ASSERT(IsErrorWithMessage()); 322 MOZ_ASSERT(mUnionState == HasMessage); 323 delete mExtra.mMessage; 324 mExtra.mMessage = nullptr; 325 #ifdef DEBUG 326 mUnionState = HasNothing; 327 #endif // DEBUG 328 } 329 330 template <typename CleanupPolicy> 331 void TErrorResult<CleanupPolicy>::ThrowJSException(JSContext* cx, 332 JS::Handle<JS::Value> exn) { 333 AssertInOwningThread(); 334 MOZ_ASSERT(mMightHaveUnreportedJSException, 335 "Why didn't you tell us you planned to throw a JS exception?"); 336 337 ClearUnionData(); 338 339 // Make sure mExtra.mJSException is initialized _before_ we try to root it. 340 // But don't set it to exn yet, because we don't want to do that until after 341 // we root. 342 JS::Value& exc = InitJSException(); 343 if (!js::AddRawValueRoot(cx, &exc, "TErrorResult::mExtra::mJSException")) { 344 // Don't use NS_ERROR_INTERNAL_ERRORRESULT_JS_EXCEPTION, because that 345 // indicates we have in fact rooted mExtra.mJSException. 346 mResult = NS_ERROR_OUT_OF_MEMORY; 347 } else { 348 exc = exn; 349 mResult = NS_ERROR_INTERNAL_ERRORRESULT_JS_EXCEPTION; 350 #ifdef DEBUG 351 mUnionState = HasJSException; 352 #endif // DEBUG 353 } 354 } 355 356 template <typename CleanupPolicy> 357 void TErrorResult<CleanupPolicy>::SetPendingJSException(JSContext* cx) { 358 AssertInOwningThread(); 359 MOZ_ASSERT(!mMightHaveUnreportedJSException, 360 "Why didn't you tell us you planned to handle JS exceptions?"); 361 MOZ_ASSERT(mUnionState == HasJSException); 362 363 JS::Rooted<JS::Value> exception(cx, mExtra.mJSException); 364 if (JS_WrapValue(cx, &exception)) { 365 JS_SetPendingException(cx, exception); 366 } 367 mExtra.mJSException = exception; 368 // If JS_WrapValue failed, not much we can do about it... No matter 369 // what, go ahead and unroot mExtra.mJSException. 370 js::RemoveRawValueRoot(cx, &mExtra.mJSException); 371 372 mResult = NS_OK; 373 #ifdef DEBUG 374 mUnionState = HasNothing; 375 #endif // DEBUG 376 } 377 378 template <typename CleanupPolicy> 379 struct TErrorResult<CleanupPolicy>::DOMExceptionInfo { 380 DOMExceptionInfo(nsresult rv, const nsACString& message) 381 : mMessage(message), mRv(rv) {} 382 383 nsCString mMessage; 384 nsresult mRv; 385 386 bool operator==( 387 const TErrorResult<CleanupPolicy>::DOMExceptionInfo& aRight) const { 388 return mRv == aRight.mRv && mMessage == aRight.mMessage; 389 } 390 }; 391 392 template <typename CleanupPolicy> 393 void TErrorResult<CleanupPolicy>::SerializeDOMExceptionInfo( 394 IPC::MessageWriter* aWriter) const { 395 using namespace IPC; 396 AssertInOwningThread(); 397 MOZ_ASSERT(mUnionState == HasDOMExceptionInfo); 398 MOZ_ASSERT(mExtra.mDOMExceptionInfo); 399 WriteParam(aWriter, mExtra.mDOMExceptionInfo->mMessage); 400 WriteParam(aWriter, mExtra.mDOMExceptionInfo->mRv); 401 } 402 403 template <typename CleanupPolicy> 404 bool TErrorResult<CleanupPolicy>::DeserializeDOMExceptionInfo( 405 IPC::MessageReader* aReader) { 406 using namespace IPC; 407 AssertInOwningThread(); 408 nsCString message; 409 nsresult rv; 410 if (!ReadParam(aReader, &message) || !ReadParam(aReader, &rv)) { 411 return false; 412 } 413 414 MOZ_ASSERT(mUnionState == HasNothing); 415 MOZ_ASSERT(IsDOMException()); 416 InitDOMExceptionInfo(new DOMExceptionInfo(rv, message)); 417 #ifdef DEBUG 418 mUnionState = HasDOMExceptionInfo; 419 #endif // DEBUG 420 return true; 421 } 422 423 template <typename CleanupPolicy> 424 void TErrorResult<CleanupPolicy>::ThrowDOMException(nsresult rv, 425 const nsACString& message) { 426 AssertInOwningThread(); 427 ClearUnionData(); 428 429 mResult = NS_ERROR_INTERNAL_ERRORRESULT_DOMEXCEPTION; 430 InitDOMExceptionInfo(new DOMExceptionInfo(rv, message)); 431 #ifdef DEBUG 432 mUnionState = HasDOMExceptionInfo; 433 #endif 434 } 435 436 template <typename CleanupPolicy> 437 void TErrorResult<CleanupPolicy>::SetPendingDOMException(JSContext* cx, 438 const char* context) { 439 AssertInOwningThread(); 440 MOZ_ASSERT(mUnionState == HasDOMExceptionInfo); 441 MOZ_ASSERT(mExtra.mDOMExceptionInfo, 442 "SetPendingDOMException() can be called only once"); 443 444 if (context && !mExtra.mDOMExceptionInfo->mMessage.IsEmpty()) { 445 // Prepend our context and ": "; see API documentation. 446 nsAutoCString prefix(context); 447 prefix.AppendLiteral(": "); 448 mExtra.mDOMExceptionInfo->mMessage.Insert(prefix, 0); 449 } 450 451 dom::Throw(cx, mExtra.mDOMExceptionInfo->mRv, 452 mExtra.mDOMExceptionInfo->mMessage); 453 454 ClearDOMExceptionInfo(); 455 mResult = NS_OK; 456 } 457 458 template <typename CleanupPolicy> 459 void TErrorResult<CleanupPolicy>::ClearDOMExceptionInfo() { 460 AssertInOwningThread(); 461 MOZ_ASSERT(IsDOMException()); 462 MOZ_ASSERT(mUnionState == HasDOMExceptionInfo); 463 delete mExtra.mDOMExceptionInfo; 464 mExtra.mDOMExceptionInfo = nullptr; 465 #ifdef DEBUG 466 mUnionState = HasNothing; 467 #endif // DEBUG 468 } 469 470 template <typename CleanupPolicy> 471 void TErrorResult<CleanupPolicy>::ClearUnionData() { 472 AssertInOwningThread(); 473 if (IsJSException()) { 474 JSContext* cx = dom::danger::GetJSContext(); 475 MOZ_ASSERT(cx); 476 mExtra.mJSException.setUndefined(); 477 js::RemoveRawValueRoot(cx, &mExtra.mJSException); 478 #ifdef DEBUG 479 mUnionState = HasNothing; 480 #endif // DEBUG 481 } else if (IsErrorWithMessage()) { 482 ClearMessage(); 483 } else if (IsDOMException()) { 484 ClearDOMExceptionInfo(); 485 } 486 } 487 488 template <typename CleanupPolicy> 489 void TErrorResult<CleanupPolicy>::SetPendingGenericErrorException( 490 JSContext* cx) { 491 AssertInOwningThread(); 492 MOZ_ASSERT(!IsErrorWithMessage()); 493 MOZ_ASSERT(!IsJSException()); 494 MOZ_ASSERT(!IsDOMException()); 495 dom::Throw(cx, ErrorCode()); 496 mResult = NS_OK; 497 } 498 499 template <typename CleanupPolicy> 500 TErrorResult<CleanupPolicy>& TErrorResult<CleanupPolicy>::operator=( 501 TErrorResult<CleanupPolicy>&& aRHS) { 502 AssertInOwningThread(); 503 aRHS.AssertInOwningThread(); 504 // Clear out any union members we may have right now, before we 505 // start writing to it. 506 ClearUnionData(); 507 508 #ifdef DEBUG 509 mMightHaveUnreportedJSException = aRHS.mMightHaveUnreportedJSException; 510 aRHS.mMightHaveUnreportedJSException = false; 511 #endif 512 if (aRHS.IsErrorWithMessage()) { 513 InitMessage(aRHS.mExtra.mMessage); 514 aRHS.mExtra.mMessage = nullptr; 515 } else if (aRHS.IsJSException()) { 516 JSContext* cx = dom::danger::GetJSContext(); 517 MOZ_ASSERT(cx); 518 JS::Value& exn = InitJSException(); 519 if (!js::AddRawValueRoot(cx, &exn, "TErrorResult::mExtra::mJSException")) { 520 MOZ_CRASH("Could not root mExtra.mJSException, we're about to OOM"); 521 } 522 mExtra.mJSException = aRHS.mExtra.mJSException; 523 aRHS.mExtra.mJSException.setUndefined(); 524 js::RemoveRawValueRoot(cx, &aRHS.mExtra.mJSException); 525 } else if (aRHS.IsDOMException()) { 526 InitDOMExceptionInfo(aRHS.mExtra.mDOMExceptionInfo); 527 aRHS.mExtra.mDOMExceptionInfo = nullptr; 528 } else { 529 // Null out the union on both sides for hygiene purposes. This is purely 530 // precautionary, so InitMessage/placement-new is unnecessary. 531 mExtra.mMessage = aRHS.mExtra.mMessage = nullptr; 532 } 533 534 #ifdef DEBUG 535 mUnionState = aRHS.mUnionState; 536 aRHS.mUnionState = HasNothing; 537 #endif // DEBUG 538 539 // Note: It's important to do this last, since this affects the condition 540 // checks above! 541 mResult = aRHS.mResult; 542 aRHS.mResult = NS_OK; 543 return *this; 544 } 545 546 template <typename CleanupPolicy> 547 bool TErrorResult<CleanupPolicy>::operator==(const ErrorResult& aRight) const { 548 auto right = reinterpret_cast<const TErrorResult<CleanupPolicy>*>(&aRight); 549 550 if (mResult != right->mResult) { 551 return false; 552 } 553 554 if (IsJSException()) { 555 // js exceptions are always non-equal 556 return false; 557 } 558 559 if (IsErrorWithMessage()) { 560 return *mExtra.mMessage == *right->mExtra.mMessage; 561 } 562 563 if (IsDOMException()) { 564 return *mExtra.mDOMExceptionInfo == *right->mExtra.mDOMExceptionInfo; 565 } 566 567 return true; 568 } 569 570 template <typename CleanupPolicy> 571 void TErrorResult<CleanupPolicy>::CloneTo(TErrorResult& aRv) const { 572 AssertInOwningThread(); 573 aRv.AssertInOwningThread(); 574 aRv.ClearUnionData(); 575 aRv.mResult = mResult; 576 #ifdef DEBUG 577 aRv.mMightHaveUnreportedJSException = mMightHaveUnreportedJSException; 578 #endif 579 580 if (IsErrorWithMessage()) { 581 #ifdef DEBUG 582 aRv.mUnionState = HasMessage; 583 #endif 584 Message* message = aRv.InitMessage(new Message()); 585 message->mArgs = mExtra.mMessage->mArgs.Clone(); 586 message->mErrorNumber = mExtra.mMessage->mErrorNumber; 587 } else if (IsDOMException()) { 588 #ifdef DEBUG 589 aRv.mUnionState = HasDOMExceptionInfo; 590 #endif 591 auto* exnInfo = new DOMExceptionInfo(mExtra.mDOMExceptionInfo->mRv, 592 mExtra.mDOMExceptionInfo->mMessage); 593 aRv.InitDOMExceptionInfo(exnInfo); 594 } else if (IsJSException()) { 595 #ifdef DEBUG 596 aRv.mUnionState = HasJSException; 597 #endif 598 JSContext* cx = dom::danger::GetJSContext(); 599 JS::Rooted<JS::Value> exception(cx, mExtra.mJSException); 600 aRv.ThrowJSException(cx, exception); 601 } 602 } 603 604 template <typename CleanupPolicy> 605 void TErrorResult<CleanupPolicy>::SuppressException() { 606 AssertInOwningThread(); 607 WouldReportJSException(); 608 ClearUnionData(); 609 // We don't use AssignErrorCode, because we want to override existing error 610 // states, which AssignErrorCode is not allowed to do. 611 mResult = NS_OK; 612 } 613 614 template <typename CleanupPolicy> 615 void TErrorResult<CleanupPolicy>::SetPendingException(JSContext* cx, 616 const char* context) { 617 AssertInOwningThread(); 618 if (IsUncatchableException()) { 619 // Note: ReportUncatchableException will clear any existing exception on cx. 620 JS::ReportUncatchableException(cx); 621 mResult = NS_OK; 622 return; 623 } 624 if (IsJSContextException()) { 625 // Whatever we need to throw is on the JSContext already. 626 MOZ_ASSERT(JS_IsExceptionPending(cx)); 627 mResult = NS_OK; 628 return; 629 } 630 if (IsErrorWithMessage()) { 631 SetPendingExceptionWithMessage(cx, context); 632 return; 633 } 634 if (IsJSException()) { 635 SetPendingJSException(cx); 636 return; 637 } 638 if (IsDOMException()) { 639 SetPendingDOMException(cx, context); 640 return; 641 } 642 SetPendingGenericErrorException(cx); 643 } 644 645 template <typename CleanupPolicy> 646 void TErrorResult<CleanupPolicy>::StealExceptionFromJSContext(JSContext* cx) { 647 AssertInOwningThread(); 648 MOZ_ASSERT(mMightHaveUnreportedJSException, 649 "Why didn't you tell us you planned to throw a JS exception?"); 650 651 JS::Rooted<JS::Value> exn(cx); 652 if (!JS_GetPendingException(cx, &exn)) { 653 ThrowUncatchableException(); 654 return; 655 } 656 657 ThrowJSException(cx, exn); 658 JS_ClearPendingException(cx); 659 } 660 661 template <typename CleanupPolicy> 662 void TErrorResult<CleanupPolicy>::NoteJSContextException(JSContext* aCx) { 663 AssertInOwningThread(); 664 if (JS_IsExceptionPending(aCx)) { 665 mResult = NS_ERROR_INTERNAL_ERRORRESULT_EXCEPTION_ON_JSCONTEXT; 666 } else { 667 mResult = NS_ERROR_UNCATCHABLE_EXCEPTION; 668 } 669 } 670 671 /* static */ 672 template <typename CleanupPolicy> 673 void TErrorResult<CleanupPolicy>::EnsureUTF8Validity(nsCString& aValue, 674 size_t aValidUpTo) { 675 nsCString valid; 676 if (NS_SUCCEEDED(UTF_8_ENCODING->DecodeWithoutBOMHandling(aValue, valid, 677 aValidUpTo))) { 678 aValue = valid; 679 } else { 680 aValue.SetLength(aValidUpTo); 681 } 682 } 683 684 template class TErrorResult<JustAssertCleanupPolicy>; 685 template class TErrorResult<AssertAndSuppressCleanupPolicy>; 686 template class TErrorResult<JustSuppressCleanupPolicy>; 687 template class TErrorResult<ThreadSafeJustSuppressCleanupPolicy>; 688 689 } // namespace binding_danger 690 691 namespace dom { 692 693 bool DefineConstants(JSContext* cx, JS::Handle<JSObject*> obj, 694 const ConstantSpec* cs) { 695 JS::Rooted<JS::Value> value(cx); 696 for (; cs->name; ++cs) { 697 value = cs->value; 698 bool ok = JS_DefineProperty( 699 cx, obj, cs->name, value, 700 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT); 701 if (!ok) { 702 return false; 703 } 704 } 705 return true; 706 } 707 708 static inline bool Define(JSContext* cx, JS::Handle<JSObject*> obj, 709 const JSFunctionSpec* spec) { 710 return JS_DefineFunctions(cx, obj, spec); 711 } 712 static inline bool Define(JSContext* cx, JS::Handle<JSObject*> obj, 713 const JSPropertySpec* spec) { 714 return JS_DefineProperties(cx, obj, spec); 715 } 716 static inline bool Define(JSContext* cx, JS::Handle<JSObject*> obj, 717 const ConstantSpec* spec) { 718 return DefineConstants(cx, obj, spec); 719 } 720 721 template <typename T> 722 bool DefinePrefable(JSContext* cx, JS::Handle<JSObject*> obj, 723 const Prefable<T>* props) { 724 MOZ_ASSERT(props); 725 MOZ_ASSERT(props->specs); 726 do { 727 // Define if enabled 728 if (props->isEnabled(cx, obj)) { 729 if (!Define(cx, obj, props->specs)) { 730 return false; 731 } 732 } 733 } while ((++props)->specs); 734 return true; 735 } 736 737 bool DefineLegacyUnforgeableMethods( 738 JSContext* cx, JS::Handle<JSObject*> obj, 739 const Prefable<const JSFunctionSpec>* props) { 740 return DefinePrefable(cx, obj, props); 741 } 742 743 bool DefineLegacyUnforgeableAttributes( 744 JSContext* cx, JS::Handle<JSObject*> obj, 745 const Prefable<const JSPropertySpec>* props) { 746 return DefinePrefable(cx, obj, props); 747 } 748 749 bool InterfaceObjectJSNative(JSContext* cx, unsigned argc, JS::Value* vp) { 750 JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 751 return NativeHolderFromInterfaceObject(&args.callee())->mNative(cx, argc, vp); 752 } 753 754 bool LegacyFactoryFunctionJSNative(JSContext* cx, unsigned argc, 755 JS::Value* vp) { 756 JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 757 return NativeHolderFromLegacyFactoryFunction(&args.callee()) 758 ->mNative(cx, argc, vp); 759 } 760 761 // This creates a JSFunction and sets its length and name properties in the 762 // order that ECMAScript's CreateBuiltinFunction does. 763 static JSObject* CreateBuiltinFunctionForConstructor( 764 JSContext* aCx, JSNative aNative, size_t aNativeReservedSlot, 765 void* aNativeReserved, unsigned int aNargs, jsid aName, 766 JS::Handle<JSObject*> aProto) { 767 JSFunction* fun = js::NewFunctionByIdWithReservedAndProto( 768 aCx, aNative, aProto, aNargs, JSFUN_CONSTRUCTOR, aName); 769 if (!fun) { 770 return nullptr; 771 } 772 773 JS::Rooted<JSObject*> constructor(aCx, JS_GetFunctionObject(fun)); 774 js::SetFunctionNativeReserved(constructor, aNativeReservedSlot, 775 JS::PrivateValue(aNativeReserved)); 776 777 // Eagerly force creation of the .length and .name properties, because 778 // SpiderMonkey creates them lazily (see 779 // https://bugzilla.mozilla.org/show_bug.cgi?id=1629803). 780 bool unused; 781 if (!JS_HasProperty(aCx, constructor, "length", &unused) || 782 !JS_HasProperty(aCx, constructor, "name", &unused)) { 783 return nullptr; 784 } 785 786 return constructor; 787 } 788 789 static bool DefineConstructor(JSContext* cx, JS::Handle<JSObject*> global, 790 JS::Handle<jsid> name, 791 JS::Handle<JSObject*> constructor) { 792 bool alreadyDefined; 793 if (!JS_AlreadyHasOwnPropertyById(cx, global, name, &alreadyDefined)) { 794 return false; 795 } 796 797 // This is Enumerable: False per spec. 798 return alreadyDefined || 799 JS_DefinePropertyById(cx, global, name, constructor, JSPROP_RESOLVING); 800 } 801 802 static bool DefineConstructor(JSContext* cx, JS::Handle<JSObject*> global, 803 const char* name, 804 JS::Handle<JSObject*> constructor) { 805 JSString* nameStr = JS_AtomizeString(cx, name); 806 if (!nameStr) { 807 return false; 808 } 809 JS::Rooted<JS::PropertyKey> nameKey(cx, JS::PropertyKey::NonIntAtom(nameStr)); 810 return DefineConstructor(cx, global, nameKey, constructor); 811 } 812 813 static bool DefineToStringTag(JSContext* cx, JS::Handle<JSObject*> obj, 814 JS::Handle<JSString*> class_name) { 815 JS::Rooted<jsid> toStringTagId( 816 cx, JS::GetWellKnownSymbolKey(cx, JS::SymbolCode::toStringTag)); 817 return JS_DefinePropertyById(cx, obj, toStringTagId, class_name, 818 JSPROP_READONLY); 819 } 820 821 static bool InterfaceIsInstance(JSContext* cx, unsigned argc, JS::Value* vp) { 822 JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 823 // If the thing we were passed is not an object, return false like 824 // OrdinaryHasInstance does. 825 if (!args.get(0).isObject()) { 826 args.rval().setBoolean(false); 827 return true; 828 } 829 830 // If "this" is not an object, likewise return false (again, like 831 // OrdinaryHasInstance). 832 if (!args.thisv().isObject()) { 833 args.rval().setBoolean(false); 834 return true; 835 } 836 837 // If "this" is not an interface object, likewise return false (again, like 838 // OrdinaryHasInstance). But note that we should CheckedUnwrapStatic here, 839 // because otherwise we won't get the right answers. 840 // The static version is OK, because we're looking for interface objects, 841 // which are not cross-origin objects. 842 JS::Rooted<JSObject*> thisObj( 843 cx, js::CheckedUnwrapStatic(&args.thisv().toObject())); 844 if (!thisObj) { 845 // Just fall back on the normal thing, in case it still happens to work. 846 args.rval().setBoolean(false); 847 return true; 848 } 849 850 if (!IsInterfaceObject(thisObj)) { 851 args.rval().setBoolean(false); 852 return true; 853 } 854 855 const DOMInterfaceInfo* interfaceInfo = InterfaceInfoFromObject(thisObj); 856 857 // If "this" is a constructor for an interface without a prototype, just fall 858 // back. 859 if (interfaceInfo->mPrototypeID == prototypes::id::_ID_Count) { 860 args.rval().setBoolean(false); 861 return true; 862 } 863 864 JS::Rooted<JSObject*> instance(cx, &args[0].toObject()); 865 const DOMJSClass* domClass = GetDOMClass( 866 js::UncheckedUnwrap(instance, /* stopAtWindowProxy = */ false)); 867 868 if (domClass && domClass->mInterfaceChain[interfaceInfo->mDepth] == 869 interfaceInfo->mPrototypeID) { 870 args.rval().setBoolean(true); 871 return true; 872 } 873 874 if (IsRemoteObjectProxy(instance, interfaceInfo->mPrototypeID)) { 875 args.rval().setBoolean(true); 876 return true; 877 } 878 879 args.rval().setBoolean(false); 880 return true; 881 } 882 883 bool InitInterfaceOrNamespaceObject( 884 JSContext* cx, JS::Handle<JSObject*> obj, 885 const NativeProperties* properties, 886 const NativeProperties* chromeOnlyProperties, bool isChrome) { 887 if (properties) { 888 if (properties->HasStaticMethods() && 889 !DefinePrefable(cx, obj, properties->StaticMethods())) { 890 return false; 891 } 892 893 if (properties->HasStaticAttributes() && 894 !DefinePrefable(cx, obj, properties->StaticAttributes())) { 895 return false; 896 } 897 898 if (properties->HasConstants() && 899 !DefinePrefable(cx, obj, properties->Constants())) { 900 return false; 901 } 902 } 903 904 if (chromeOnlyProperties && isChrome) { 905 if (chromeOnlyProperties->HasStaticMethods() && 906 !DefinePrefable(cx, obj, chromeOnlyProperties->StaticMethods())) { 907 return false; 908 } 909 910 if (chromeOnlyProperties->HasStaticAttributes() && 911 !DefinePrefable(cx, obj, chromeOnlyProperties->StaticAttributes())) { 912 return false; 913 } 914 915 if (chromeOnlyProperties->HasConstants() && 916 !DefinePrefable(cx, obj, chromeOnlyProperties->Constants())) { 917 return false; 918 } 919 } 920 921 return true; 922 } 923 924 // name must be an atom (or JS::PropertyKey::NonIntAtom will assert). 925 static JSObject* CreateInterfaceObject( 926 JSContext* cx, JS::Handle<JSObject*> global, 927 JS::Handle<JSObject*> interfaceProto, const DOMInterfaceInfo* interfaceInfo, 928 unsigned ctorNargs, 929 const Span<const LegacyFactoryFunction>& legacyFactoryFunctions, 930 JS::Handle<JSObject*> proto, const NativeProperties* properties, 931 const NativeProperties* chromeOnlyProperties, JS::Handle<JSString*> name, 932 bool isChrome, bool defineOnGlobal, 933 const char* const* legacyWindowAliases) { 934 MOZ_ASSERT(interfaceProto); 935 MOZ_ASSERT(interfaceInfo); 936 937 JS::Rooted<jsid> nameId(cx, JS::PropertyKey::NonIntAtom(name)); 938 939 JS::Rooted<JSObject*> constructor( 940 cx, CreateBuiltinFunctionForConstructor( 941 cx, InterfaceObjectJSNative, INTERFACE_OBJECT_INFO_RESERVED_SLOT, 942 const_cast<DOMInterfaceInfo*>(interfaceInfo), ctorNargs, nameId, 943 interfaceProto)); 944 if (!constructor) { 945 return nullptr; 946 } 947 948 if (proto && !JS_LinkConstructorAndPrototype(cx, constructor, proto)) { 949 return nullptr; 950 } 951 952 if (!InitInterfaceOrNamespaceObject(cx, constructor, properties, 953 chromeOnlyProperties, isChrome)) { 954 return nullptr; 955 } 956 957 if (defineOnGlobal && !DefineConstructor(cx, global, nameId, constructor)) { 958 return nullptr; 959 } 960 961 if (interfaceInfo->wantsInterfaceIsInstance && isChrome && 962 !JS_DefineFunction(cx, constructor, "isInstance", InterfaceIsInstance, 1, 963 // Don't bother making it enumerable 964 0)) { 965 return nullptr; 966 } 967 968 if (legacyWindowAliases && NS_IsMainThread()) { 969 for (; *legacyWindowAliases; ++legacyWindowAliases) { 970 if (!DefineConstructor(cx, global, *legacyWindowAliases, constructor)) { 971 return nullptr; 972 } 973 } 974 } 975 976 int legacyFactoryFunctionSlot = 977 INTERFACE_OBJECT_FIRST_LEGACY_FACTORY_FUNCTION; 978 for (const LegacyFactoryFunction& lff : legacyFactoryFunctions) { 979 JSString* fname = JS_AtomizeString(cx, lff.mName); 980 if (!fname) { 981 return nullptr; 982 } 983 984 nameId = JS::PropertyKey::NonIntAtom(fname); 985 986 JS::Rooted<JSObject*> legacyFactoryFunction( 987 cx, CreateBuiltinFunctionForConstructor( 988 cx, LegacyFactoryFunctionJSNative, 989 LEGACY_FACTORY_FUNCTION_RESERVED_SLOT, 990 const_cast<LegacyFactoryFunction*>(&lff), lff.mNargs, nameId, 991 nullptr)); 992 if (!legacyFactoryFunction || 993 !JS_DefineProperty(cx, legacyFactoryFunction, "prototype", proto, 994 JSPROP_PERMANENT | JSPROP_READONLY) || 995 (defineOnGlobal && 996 !DefineConstructor(cx, global, nameId, legacyFactoryFunction))) { 997 return nullptr; 998 } 999 js::SetFunctionNativeReserved(constructor, legacyFactoryFunctionSlot, 1000 JS::ObjectValue(*legacyFactoryFunction)); 1001 ++legacyFactoryFunctionSlot; 1002 } 1003 1004 return constructor; 1005 } 1006 1007 static JSObject* CreateInterfacePrototypeObject( 1008 JSContext* cx, JS::Handle<JSObject*> global, 1009 JS::Handle<JSObject*> parentProto, const JSClass* protoClass, 1010 const NativeProperties* properties, 1011 const NativeProperties* chromeOnlyProperties, 1012 const char* const* unscopableNames, JS::Handle<JSString*> name, 1013 bool isGlobal) { 1014 JS::Rooted<JSObject*> ourProto( 1015 cx, JS_NewObjectWithGivenProto(cx, protoClass, parentProto)); 1016 if (!ourProto || 1017 // We don't try to define properties on the global's prototype; those 1018 // properties go on the global itself. 1019 (!isGlobal && 1020 !DefineProperties(cx, ourProto, properties, chromeOnlyProperties))) { 1021 return nullptr; 1022 } 1023 1024 if (unscopableNames) { 1025 JS::Rooted<JSObject*> unscopableObj( 1026 cx, JS_NewObjectWithGivenProto(cx, nullptr, nullptr)); 1027 if (!unscopableObj) { 1028 return nullptr; 1029 } 1030 1031 for (; *unscopableNames; ++unscopableNames) { 1032 if (!JS_DefineProperty(cx, unscopableObj, *unscopableNames, 1033 JS::TrueHandleValue, JSPROP_ENUMERATE)) { 1034 return nullptr; 1035 } 1036 } 1037 1038 JS::Rooted<jsid> unscopableId( 1039 cx, JS::GetWellKnownSymbolKey(cx, JS::SymbolCode::unscopables)); 1040 // Readonly and non-enumerable to match Array.prototype. 1041 if (!JS_DefinePropertyById(cx, ourProto, unscopableId, unscopableObj, 1042 JSPROP_READONLY)) { 1043 return nullptr; 1044 } 1045 } 1046 1047 if (!DefineToStringTag(cx, ourProto, name)) { 1048 return nullptr; 1049 } 1050 1051 return ourProto; 1052 } 1053 1054 bool DefineProperties(JSContext* cx, JS::Handle<JSObject*> obj, 1055 const NativeProperties* properties, 1056 const NativeProperties* chromeOnlyProperties) { 1057 if (properties) { 1058 if (properties->HasMethods() && 1059 !DefinePrefable(cx, obj, properties->Methods())) { 1060 return false; 1061 } 1062 1063 if (properties->HasAttributes() && 1064 !DefinePrefable(cx, obj, properties->Attributes())) { 1065 return false; 1066 } 1067 1068 if (properties->HasConstants() && 1069 !DefinePrefable(cx, obj, properties->Constants())) { 1070 return false; 1071 } 1072 } 1073 1074 if (chromeOnlyProperties) { 1075 if (chromeOnlyProperties->HasMethods() && 1076 !DefinePrefable(cx, obj, chromeOnlyProperties->Methods())) { 1077 return false; 1078 } 1079 1080 if (chromeOnlyProperties->HasAttributes() && 1081 !DefinePrefable(cx, obj, chromeOnlyProperties->Attributes())) { 1082 return false; 1083 } 1084 1085 if (chromeOnlyProperties->HasConstants() && 1086 !DefinePrefable(cx, obj, chromeOnlyProperties->Constants())) { 1087 return false; 1088 } 1089 } 1090 1091 return true; 1092 } 1093 1094 namespace binding_detail { 1095 1096 void CreateInterfaceObjects( 1097 JSContext* cx, JS::Handle<JSObject*> global, 1098 JS::Handle<JSObject*> protoProto, const DOMIfaceAndProtoJSClass* protoClass, 1099 JS::Heap<JSObject*>* protoCache, JS::Handle<JSObject*> interfaceProto, 1100 const DOMInterfaceInfo* interfaceInfo, unsigned ctorNargs, 1101 bool isConstructorChromeOnly, 1102 const Span<const LegacyFactoryFunction>& legacyFactoryFunctions, 1103 JS::Heap<JSObject*>* constructorCache, const NativeProperties* properties, 1104 const NativeProperties* chromeOnlyProperties, const char* name, 1105 bool defineOnGlobal, const char* const* unscopableNames, bool isGlobal, 1106 const char* const* legacyWindowAliases) { 1107 MOZ_ASSERT(protoClass || interfaceInfo, "Need at least a class or info!"); 1108 MOZ_ASSERT( 1109 !((properties && 1110 (properties->HasMethods() || properties->HasAttributes())) || 1111 (chromeOnlyProperties && (chromeOnlyProperties->HasMethods() || 1112 chromeOnlyProperties->HasAttributes()))) || 1113 protoClass, 1114 "Methods or properties but no protoClass!"); 1115 MOZ_ASSERT(!((properties && (properties->HasStaticMethods() || 1116 properties->HasStaticAttributes())) || 1117 (chromeOnlyProperties && 1118 (chromeOnlyProperties->HasStaticMethods() || 1119 chromeOnlyProperties->HasStaticAttributes()))) || 1120 interfaceInfo, 1121 "Static methods but no info!"); 1122 MOZ_ASSERT(!protoClass == !protoCache, 1123 "If, and only if, there is an interface prototype object we need " 1124 "to cache it"); 1125 MOZ_ASSERT(bool(interfaceInfo) == bool(constructorCache), 1126 "If, and only if, there is an interface object we need to cache " 1127 "it"); 1128 MOZ_ASSERT(interfaceProto || !interfaceInfo, 1129 "Must have a interface proto if we plan to create an interface " 1130 "object"); 1131 1132 bool isChrome = nsContentUtils::ThreadsafeIsSystemCaller(cx); 1133 1134 JS::Rooted<JSString*> nameStr(cx, JS_AtomizeString(cx, name)); 1135 if (!nameStr) { 1136 return; 1137 } 1138 1139 JS::Rooted<JSObject*> proto(cx); 1140 if (protoClass) { 1141 proto = CreateInterfacePrototypeObject( 1142 cx, global, protoProto, protoClass->ToJSClass(), properties, 1143 isChrome ? chromeOnlyProperties : nullptr, unscopableNames, nameStr, 1144 isGlobal); 1145 if (!proto) { 1146 return; 1147 } 1148 1149 *protoCache = proto; 1150 } else { 1151 MOZ_ASSERT(!proto); 1152 } 1153 1154 JSObject* interface; 1155 if (interfaceInfo) { 1156 interface = CreateInterfaceObject( 1157 cx, global, interfaceProto, interfaceInfo, 1158 (isChrome || !isConstructorChromeOnly) ? ctorNargs : 0, 1159 legacyFactoryFunctions, proto, properties, chromeOnlyProperties, 1160 nameStr, isChrome, defineOnGlobal, legacyWindowAliases); 1161 if (!interface) { 1162 if (protoCache) { 1163 // If we fail we need to make sure to clear the value of protoCache we 1164 // set above. 1165 *protoCache = nullptr; 1166 } 1167 return; 1168 } 1169 *constructorCache = interface; 1170 } 1171 } 1172 1173 } // namespace binding_detail 1174 1175 void CreateNamespaceObject(JSContext* cx, JS::Handle<JSObject*> global, 1176 JS::Handle<JSObject*> namespaceProto, 1177 const DOMIfaceAndProtoJSClass& namespaceClass, 1178 JS::Heap<JSObject*>* namespaceCache, 1179 const NativeProperties* properties, 1180 const NativeProperties* chromeOnlyProperties, 1181 const char* name, bool defineOnGlobal) { 1182 JS::Rooted<JSString*> nameStr(cx, JS_AtomizeString(cx, name)); 1183 if (!nameStr) { 1184 return; 1185 } 1186 JS::Rooted<jsid> nameId(cx, JS::PropertyKey::NonIntAtom(nameStr)); 1187 1188 JS::Rooted<JSObject*> namespaceObj( 1189 cx, JS_NewObjectWithGivenProto(cx, namespaceClass.ToJSClass(), 1190 namespaceProto)); 1191 if (!namespaceObj) { 1192 return; 1193 } 1194 1195 if (!InitInterfaceOrNamespaceObject( 1196 cx, namespaceObj, properties, chromeOnlyProperties, 1197 nsContentUtils::ThreadsafeIsSystemCaller(cx))) { 1198 return; 1199 } 1200 1201 if (defineOnGlobal && !DefineConstructor(cx, global, nameId, namespaceObj)) { 1202 return; 1203 } 1204 1205 if (!DefineToStringTag(cx, namespaceObj, nameStr)) { 1206 return; 1207 } 1208 1209 *namespaceCache = namespaceObj; 1210 } 1211 1212 // Only set aAllowNativeWrapper to false if you really know you need it; if in 1213 // doubt use true. Setting it to false disables security wrappers. 1214 static bool NativeInterface2JSObjectAndThrowIfFailed( 1215 JSContext* aCx, JS::Handle<JSObject*> aScope, 1216 JS::MutableHandle<JS::Value> aRetval, xpcObjectHelper& aHelper, 1217 const nsIID* aIID, bool aAllowNativeWrapper) { 1218 js::AssertSameCompartment(aCx, aScope); 1219 nsresult rv; 1220 // Inline some logic from XPCConvert::NativeInterfaceToJSObject that we need 1221 // on all threads. 1222 nsWrapperCache* cache = aHelper.GetWrapperCache(); 1223 1224 if (cache) { 1225 JS::Rooted<JSObject*> obj(aCx, cache->GetWrapper()); 1226 if (!obj) { 1227 obj = cache->WrapObject(aCx, nullptr); 1228 if (!obj) { 1229 return Throw(aCx, NS_ERROR_UNEXPECTED); 1230 } 1231 } 1232 1233 if (aAllowNativeWrapper && !JS_WrapObject(aCx, &obj)) { 1234 return false; 1235 } 1236 1237 aRetval.setObject(*obj); 1238 return true; 1239 } 1240 1241 MOZ_ASSERT(NS_IsMainThread()); 1242 1243 if (!XPCConvert::NativeInterface2JSObject(aCx, aRetval, aHelper, aIID, 1244 aAllowNativeWrapper, &rv)) { 1245 // I can't tell if NativeInterface2JSObject throws JS exceptions 1246 // or not. This is a sloppy stab at the right semantics; the 1247 // method really ought to be fixed to behave consistently. 1248 if (!JS_IsExceptionPending(aCx)) { 1249 Throw(aCx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED); 1250 } 1251 return false; 1252 } 1253 return true; 1254 } 1255 1256 /* static */ 1257 size_t binding_detail::NeedsQIToWrapperCache::ObjectMoved(JSObject* aObj, 1258 JSObject* aOld) { 1259 JS::AutoAssertGCCallback inCallback; 1260 nsWrapperCache* cache = GetWrapperCache(aObj); 1261 if (cache) { 1262 cache->UpdateWrapper(aObj, aOld); 1263 } 1264 1265 return 0; 1266 } 1267 1268 bool TryPreserveWrapper(JS::Handle<JSObject*> obj) { 1269 MOZ_ASSERT(IsDOMObject(obj)); 1270 1271 // nsISupports objects are special cased because DOM proxies are nsISupports 1272 // and have addProperty hooks that do more than wrapper preservation (so we 1273 // don't want to call them). 1274 if (nsISupports* native = UnwrapDOMObjectToISupports(obj)) { 1275 nsWrapperCache* cache = nullptr; 1276 CallQueryInterface(native, &cache); 1277 if (cache) { 1278 cache->PreserveWrapper(native); 1279 } 1280 return true; 1281 } 1282 1283 const JSClass* clasp = JS::GetClass(obj); 1284 const DOMJSClass* domClass = GetDOMClass(clasp); 1285 1286 // We expect all proxies to be nsISupports. 1287 MOZ_RELEASE_ASSERT(clasp->isNativeObject(), 1288 "Should not call addProperty for proxies."); 1289 1290 if (!clasp->preservesWrapper()) { 1291 return true; 1292 } 1293 1294 WrapperCacheGetter getter = domClass->mWrapperCacheGetter; 1295 MOZ_RELEASE_ASSERT(getter); 1296 1297 nsWrapperCache* cache = getter(obj); 1298 // We obviously can't preserve if we're not initialized, we don't want 1299 // to preserve if we don't have a wrapper or if the object is in the 1300 // process of being finalized. 1301 if (cache && cache->GetWrapperPreserveColor()) { 1302 cache->PreserveWrapper( 1303 cache, reinterpret_cast<nsScriptObjectTracer*>(domClass->mParticipant)); 1304 } 1305 1306 return true; 1307 } 1308 1309 bool HasReleasedWrapper(JS::Handle<JSObject*> obj) { 1310 MOZ_ASSERT(obj); 1311 MOZ_ASSERT(IsDOMObject(obj)); 1312 1313 nsWrapperCache* cache = nullptr; 1314 if (nsISupports* native = UnwrapDOMObjectToISupports(obj)) { 1315 CallQueryInterface(native, &cache); 1316 } else { 1317 const JSClass* clasp = JS::GetClass(obj); 1318 const DOMJSClass* domClass = GetDOMClass(clasp); 1319 1320 // We expect all proxies to be nsISupports. 1321 MOZ_RELEASE_ASSERT(clasp->isNativeObject(), 1322 "Should not call getWrapperCache for proxies."); 1323 1324 WrapperCacheGetter getter = domClass->mWrapperCacheGetter; 1325 1326 if (getter) { 1327 // If the class has a wrapper cache getter it must be a CC participant. 1328 MOZ_RELEASE_ASSERT(domClass->mParticipant); 1329 1330 cache = getter(obj); 1331 } 1332 } 1333 1334 return cache && !cache->PreservingWrapper(); 1335 } 1336 1337 // Can only be called with a DOM JSClass. 1338 bool InstanceClassHasProtoAtDepth(const JSClass* clasp, uint32_t protoID, 1339 uint32_t depth) { 1340 const DOMJSClass* domClass = DOMJSClass::FromJSClass(clasp); 1341 return static_cast<uint32_t>(domClass->mInterfaceChain[depth]) == protoID; 1342 } 1343 1344 // Only set allowNativeWrapper to false if you really know you need it; if in 1345 // doubt use true. Setting it to false disables security wrappers. 1346 bool XPCOMObjectToJsval(JSContext* cx, JS::Handle<JSObject*> scope, 1347 xpcObjectHelper& helper, const nsIID* iid, 1348 bool allowNativeWrapper, 1349 JS::MutableHandle<JS::Value> rval) { 1350 return NativeInterface2JSObjectAndThrowIfFailed(cx, scope, rval, helper, iid, 1351 allowNativeWrapper); 1352 } 1353 1354 bool VariantToJsval(JSContext* aCx, nsIVariant* aVariant, 1355 JS::MutableHandle<JS::Value> aRetval) { 1356 nsresult rv; 1357 if (!XPCVariant::VariantDataToJS(aCx, aVariant, &rv, aRetval)) { 1358 // Does it throw? Who knows 1359 if (!JS_IsExceptionPending(aCx)) { 1360 Throw(aCx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED); 1361 } 1362 return false; 1363 } 1364 1365 return true; 1366 } 1367 1368 bool WrapObject(JSContext* cx, const WindowProxyHolder& p, 1369 JS::MutableHandle<JS::Value> rval) { 1370 return ToJSValue(cx, p, rval); 1371 } 1372 1373 // {JSPropertySpec,JSFunctionSpec} use {JSPropertySpec,JSFunctionSpec}::Name 1374 // and ConstantSpec uses `const char*` for name field. 1375 static inline JSPropertySpec::Name ToPropertySpecName( 1376 JSPropertySpec::Name name) { 1377 return name; 1378 } 1379 1380 static inline JSPropertySpec::Name ToPropertySpecName(const char* name) { 1381 return JSPropertySpec::Name(name); 1382 } 1383 1384 template <typename SpecT> 1385 static bool InitPropertyInfos(JSContext* cx, const Prefable<SpecT>* pref, 1386 PropertyInfo* infos, PropertyType type) { 1387 MOZ_ASSERT(pref); 1388 MOZ_ASSERT(pref->specs); 1389 1390 // Index of the Prefable that contains the id for the current PropertyInfo. 1391 uint32_t prefIndex = 0; 1392 1393 do { 1394 // We ignore whether the set of ids is enabled and just intern all the IDs, 1395 // because this is only done once per application runtime. 1396 const SpecT* spec = pref->specs; 1397 // Index of the property/function/constant spec for our current PropertyInfo 1398 // in the "specs" array of the relevant Prefable. 1399 uint32_t specIndex = 0; 1400 do { 1401 jsid id; 1402 if (!JS::PropertySpecNameToPermanentId(cx, ToPropertySpecName(spec->name), 1403 &id)) { 1404 return false; 1405 } 1406 infos->SetId(id); 1407 infos->type = type; 1408 infos->prefIndex = prefIndex; 1409 infos->specIndex = specIndex++; 1410 ++infos; 1411 } while ((++spec)->name); 1412 ++prefIndex; 1413 } while ((++pref)->specs); 1414 1415 return true; 1416 } 1417 1418 #define INIT_PROPERTY_INFOS_IF_DEFINED(TypeName) \ 1419 { \ 1420 if (nativeProperties->Has##TypeName##s() && \ 1421 !InitPropertyInfos(cx, nativeProperties->TypeName##s(), \ 1422 nativeProperties->TypeName##PropertyInfos(), \ 1423 e##TypeName)) { \ 1424 return false; \ 1425 } \ 1426 } 1427 1428 static bool InitPropertyInfos(JSContext* cx, 1429 const NativeProperties* nativeProperties) { 1430 INIT_PROPERTY_INFOS_IF_DEFINED(StaticMethod); 1431 INIT_PROPERTY_INFOS_IF_DEFINED(StaticAttribute); 1432 INIT_PROPERTY_INFOS_IF_DEFINED(Method); 1433 INIT_PROPERTY_INFOS_IF_DEFINED(Attribute); 1434 INIT_PROPERTY_INFOS_IF_DEFINED(UnforgeableMethod); 1435 INIT_PROPERTY_INFOS_IF_DEFINED(UnforgeableAttribute); 1436 INIT_PROPERTY_INFOS_IF_DEFINED(Constant); 1437 1438 // Initialize and sort the index array. 1439 uint16_t* indices = nativeProperties->sortedPropertyIndices; 1440 auto count = nativeProperties->propertyInfoCount; 1441 for (auto i = 0; i < count; ++i) { 1442 indices[i] = i; 1443 } 1444 std::sort(indices, indices + count, 1445 [infos = nativeProperties->PropertyInfos()](const uint16_t left, 1446 const uint16_t right) { 1447 // std::sort may call us with the same element by design but 1448 // PropertyInfo::Compare does not like that. 1449 if (left == right) { 1450 return false; 1451 } 1452 return PropertyInfo::Compare(infos[left], infos[right]) < 0; 1453 }); 1454 1455 return true; 1456 } 1457 1458 #undef INIT_PROPERTY_INFOS_IF_DEFINED 1459 1460 static inline bool InitPropertyInfos( 1461 JSContext* aCx, const NativePropertiesHolder& nativeProperties) { 1462 MOZ_ASSERT(NS_IsMainThread()); 1463 1464 if (!*nativeProperties.inited) { 1465 if (nativeProperties.regular && 1466 !InitPropertyInfos(aCx, nativeProperties.regular)) { 1467 return false; 1468 } 1469 if (nativeProperties.chromeOnly && 1470 !InitPropertyInfos(aCx, nativeProperties.chromeOnly)) { 1471 return false; 1472 } 1473 *nativeProperties.inited = true; 1474 } 1475 1476 return true; 1477 } 1478 1479 void GetInterfaceImpl(JSContext* aCx, nsIInterfaceRequestor* aRequestor, 1480 nsWrapperCache* aCache, JS::Handle<JS::Value> aIID, 1481 JS::MutableHandle<JS::Value> aRetval, 1482 ErrorResult& aError) { 1483 Maybe<nsIID> iid = xpc::JSValue2ID(aCx, aIID); 1484 if (!iid) { 1485 aError.Throw(NS_ERROR_XPC_BAD_CONVERT_JS); 1486 return; 1487 } 1488 1489 RefPtr<nsISupports> result; 1490 aError = aRequestor->GetInterface(*iid, getter_AddRefs(result)); 1491 if (aError.Failed()) { 1492 return; 1493 } 1494 1495 if (!WrapObject(aCx, result, iid.ptr(), aRetval)) { 1496 aError.Throw(NS_ERROR_FAILURE); 1497 } 1498 } 1499 1500 bool ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp) { 1501 // Cast nullptr to void* to work around 1502 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100666 1503 return ThrowErrorMessage<MSG_ILLEGAL_CONSTRUCTOR>(cx, (void*)nullptr); 1504 } 1505 1506 bool ThrowConstructorWithoutNew(JSContext* cx, const char* name) { 1507 return ThrowErrorMessage<MSG_CONSTRUCTOR_WITHOUT_NEW>(cx, name); 1508 } 1509 1510 inline const NativePropertyHooks* GetNativePropertyHooksFromJSNative( 1511 JS::Handle<JSObject*> obj) { 1512 return NativeHolderFromObject(obj)->mPropertyHooks; 1513 } 1514 1515 inline const NativePropertyHooks* GetNativePropertyHooks( 1516 JSContext* cx, JS::Handle<JSObject*> obj, DOMObjectType& type) { 1517 const JSClass* clasp = JS::GetClass(obj); 1518 1519 const DOMJSClass* domClass = GetDOMClass(clasp); 1520 if (domClass) { 1521 bool isGlobal = (clasp->flags & JSCLASS_DOM_GLOBAL) != 0; 1522 type = isGlobal ? eGlobalInstance : eInstance; 1523 return domClass->mNativeHooks; 1524 } 1525 1526 if (JS_ObjectIsFunction(obj)) { 1527 type = eInterface; 1528 return GetNativePropertyHooksFromJSNative(obj); 1529 } 1530 1531 MOZ_ASSERT(IsDOMIfaceAndProtoClass(JS::GetClass(obj))); 1532 const DOMIfaceAndProtoJSClass* ifaceAndProtoJSClass = 1533 DOMIfaceAndProtoJSClass::FromJSClass(JS::GetClass(obj)); 1534 type = ifaceAndProtoJSClass->mType; 1535 return ifaceAndProtoJSClass->mNativeHooks; 1536 } 1537 1538 static JSObject* XrayCreateFunction(JSContext* cx, 1539 JS::Handle<JSObject*> wrapper, 1540 JSNativeWrapper native, unsigned nargs, 1541 JS::Handle<jsid> id) { 1542 JSFunction* fun; 1543 if (id.isString()) { 1544 fun = js::NewFunctionByIdWithReserved(cx, native.op, nargs, 0, id); 1545 } else { 1546 // Can't pass this id (probably a symbol) to NewFunctionByIdWithReserved; 1547 // just use an empty name for lack of anything better. 1548 fun = js::NewFunctionWithReserved(cx, native.op, nargs, 0, nullptr); 1549 } 1550 1551 if (!fun) { 1552 return nullptr; 1553 } 1554 1555 SET_JITINFO(fun, native.info); 1556 JSObject* obj = JS_GetFunctionObject(fun); 1557 js::SetFunctionNativeReserved(obj, XRAY_DOM_FUNCTION_PARENT_WRAPPER_SLOT, 1558 JS::ObjectValue(*wrapper)); 1559 #ifdef DEBUG 1560 js::SetFunctionNativeReserved(obj, XRAY_DOM_FUNCTION_NATIVE_SLOT_FOR_SELF, 1561 JS::ObjectValue(*obj)); 1562 #endif 1563 return obj; 1564 } 1565 1566 struct IdToIndexComparator { 1567 // The id we're searching for. 1568 const jsid& mId; 1569 // Whether we're searching for static operations. 1570 const bool mStatic; 1571 // The list of ids we're searching in. 1572 const PropertyInfo* mInfos; 1573 1574 IdToIndexComparator(const jsid& aId, DOMObjectType aType, 1575 const PropertyInfo* aInfos) 1576 : mId(aId), 1577 mStatic(aType == eInterface || aType == eNamespace), 1578 mInfos(aInfos) {} 1579 int operator()(const uint16_t aIndex) const { 1580 const PropertyInfo& info = mInfos[aIndex]; 1581 if (mId.asRawBits() == info.Id().asRawBits()) { 1582 if (info.type != eMethod && info.type != eStaticMethod) { 1583 return 0; 1584 } 1585 1586 if (mStatic == info.IsStaticMethod()) { 1587 // We're looking for static properties and we've found a static one for 1588 // the right name. 1589 return 0; 1590 } 1591 1592 // Static operations are sorted before others by PropertyInfo::Compare. 1593 return mStatic ? -1 : 1; 1594 } 1595 1596 return mId.asRawBits() < info.Id().asRawBits() ? -1 : 1; 1597 } 1598 }; 1599 1600 static const PropertyInfo* XrayFindOwnPropertyInfo( 1601 JSContext* cx, DOMObjectType type, JS::Handle<jsid> id, 1602 const NativeProperties* nativeProperties) { 1603 if ((type == eInterfacePrototype || type == eGlobalInstance) && 1604 MOZ_UNLIKELY(nativeProperties->iteratorAliasMethodIndex >= 0) && 1605 id.isWellKnownSymbol(JS::SymbolCode::iterator)) { 1606 return nativeProperties->MethodPropertyInfos() + 1607 nativeProperties->iteratorAliasMethodIndex; 1608 } 1609 1610 size_t idx; 1611 const uint16_t* sortedPropertyIndices = 1612 nativeProperties->sortedPropertyIndices; 1613 const PropertyInfo* propertyInfos = nativeProperties->PropertyInfos(); 1614 1615 if (BinarySearchIf(sortedPropertyIndices, 0, 1616 nativeProperties->propertyInfoCount, 1617 IdToIndexComparator(id, type, propertyInfos), &idx)) { 1618 return propertyInfos + sortedPropertyIndices[idx]; 1619 } 1620 1621 return nullptr; 1622 } 1623 1624 static bool XrayResolveAttribute( 1625 JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<JSObject*> obj, 1626 JS::Handle<jsid> id, const Prefable<const JSPropertySpec>& pref, 1627 const JSPropertySpec& attrSpec, 1628 JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc, 1629 bool& cacheOnHolder) { 1630 if (!pref.isEnabled(cx, obj)) { 1631 return true; 1632 } 1633 1634 MOZ_ASSERT(attrSpec.isAccessor()); 1635 1636 MOZ_ASSERT( 1637 !attrSpec.isSelfHosted(), 1638 "Bad JSPropertySpec declaration: unsupported self-hosted accessor"); 1639 1640 cacheOnHolder = true; 1641 1642 JS::Rooted<jsid> getterId(cx); 1643 if (!JS::ToGetterId(cx, id, &getterId)) { 1644 return false; 1645 } 1646 1647 // Because of centralization, we need to make sure we fault in the JitInfos as 1648 // well. At present, until the JSAPI changes, the easiest way to do this is 1649 // wrap them up as functions ourselves. 1650 1651 // They all have getters, so we can just make it. 1652 JS::Rooted<JSObject*> getter( 1653 cx, XrayCreateFunction(cx, wrapper, attrSpec.u.accessors.getter.native, 0, 1654 getterId)); 1655 if (!getter) { 1656 return false; 1657 } 1658 1659 JS::Rooted<JSObject*> setter(cx); 1660 if (attrSpec.u.accessors.setter.native.op) { 1661 JS::Rooted<jsid> setterId(cx); 1662 if (!JS::ToSetterId(cx, id, &setterId)) { 1663 return false; 1664 } 1665 1666 // We have a setter! Make it. 1667 setter = XrayCreateFunction(cx, wrapper, attrSpec.u.accessors.setter.native, 1668 1, setterId); 1669 if (!setter) { 1670 return false; 1671 } 1672 } 1673 1674 desc.set(Some( 1675 JS::PropertyDescriptor::Accessor(getter, setter, attrSpec.attributes()))); 1676 return true; 1677 } 1678 1679 static bool XrayResolveMethod( 1680 JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<JSObject*> obj, 1681 JS::Handle<jsid> id, const Prefable<const JSFunctionSpec>& pref, 1682 const JSFunctionSpec& methodSpec, 1683 JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc, 1684 bool& cacheOnHolder) { 1685 if (!pref.isEnabled(cx, obj)) { 1686 return true; 1687 } 1688 1689 cacheOnHolder = true; 1690 1691 JSObject* funobj; 1692 if (methodSpec.selfHostedName) { 1693 JSFunction* fun = JS::GetSelfHostedFunction(cx, methodSpec.selfHostedName, 1694 id, methodSpec.nargs); 1695 if (!fun) { 1696 return false; 1697 } 1698 MOZ_ASSERT(!methodSpec.call.op, 1699 "Bad FunctionSpec declaration: non-null native"); 1700 MOZ_ASSERT(!methodSpec.call.info, 1701 "Bad FunctionSpec declaration: non-null jitinfo"); 1702 funobj = JS_GetFunctionObject(fun); 1703 } else { 1704 funobj = 1705 XrayCreateFunction(cx, wrapper, methodSpec.call, methodSpec.nargs, id); 1706 if (!funobj) { 1707 return false; 1708 } 1709 } 1710 1711 desc.set(Some(JS::PropertyDescriptor::Data(JS::ObjectValue(*funobj), 1712 methodSpec.flags))); 1713 return true; 1714 } 1715 1716 static bool XrayResolveConstant( 1717 JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<JSObject*> obj, 1718 JS::Handle<jsid>, const Prefable<const ConstantSpec>& pref, 1719 const ConstantSpec& constantSpec, 1720 JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc, 1721 bool& cacheOnHolder) { 1722 if (!pref.isEnabled(cx, obj)) { 1723 return true; 1724 } 1725 1726 cacheOnHolder = true; 1727 1728 desc.set(Some(JS::PropertyDescriptor::Data( 1729 constantSpec.value, {JS::PropertyAttribute::Enumerable}))); 1730 return true; 1731 } 1732 1733 #define RESOLVE_CASE(PropType, SpecType, Resolver) \ 1734 case e##PropType: { \ 1735 MOZ_ASSERT(nativeProperties->Has##PropType##s()); \ 1736 const Prefable<const SpecType>& pref = \ 1737 nativeProperties->PropType##s()[propertyInfo.prefIndex]; \ 1738 return Resolver(cx, wrapper, obj, id, pref, \ 1739 pref.specs[propertyInfo.specIndex], desc, cacheOnHolder); \ 1740 } 1741 1742 static bool XrayResolveProperty( 1743 JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<JSObject*> obj, 1744 JS::Handle<jsid> id, JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc, 1745 bool& cacheOnHolder, DOMObjectType type, 1746 const NativeProperties* nativeProperties, 1747 const PropertyInfo& propertyInfo) { 1748 MOZ_ASSERT(type != eGlobalInterfacePrototype); 1749 1750 // Make sure we resolve for matched object type. 1751 switch (propertyInfo.type) { 1752 case eStaticMethod: 1753 case eStaticAttribute: 1754 if (type != eInterface && type != eNamespace) { 1755 return true; 1756 } 1757 break; 1758 case eMethod: 1759 case eAttribute: 1760 if (type != eGlobalInstance && type != eInterfacePrototype) { 1761 return true; 1762 } 1763 break; 1764 case eUnforgeableMethod: 1765 case eUnforgeableAttribute: 1766 if (!IsInstance(type)) { 1767 return true; 1768 } 1769 break; 1770 case eConstant: 1771 if (IsInstance(type)) { 1772 return true; 1773 } 1774 break; 1775 } 1776 1777 switch (propertyInfo.type) { 1778 RESOLVE_CASE(StaticMethod, JSFunctionSpec, XrayResolveMethod) 1779 RESOLVE_CASE(StaticAttribute, JSPropertySpec, XrayResolveAttribute) 1780 RESOLVE_CASE(Method, JSFunctionSpec, XrayResolveMethod) 1781 RESOLVE_CASE(Attribute, JSPropertySpec, XrayResolveAttribute) 1782 RESOLVE_CASE(UnforgeableMethod, JSFunctionSpec, XrayResolveMethod) 1783 RESOLVE_CASE(UnforgeableAttribute, JSPropertySpec, XrayResolveAttribute) 1784 RESOLVE_CASE(Constant, ConstantSpec, XrayResolveConstant) 1785 } 1786 1787 return true; 1788 } 1789 1790 #undef RESOLVE_CASE 1791 1792 static bool ResolvePrototypeOrConstructor( 1793 JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<JSObject*> obj, 1794 size_t protoAndIfaceCacheIndex, unsigned attrs, 1795 JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc, 1796 bool& cacheOnHolder) { 1797 JS::Rooted<JSObject*> global(cx, JS::GetNonCCWObjectGlobal(obj)); 1798 { 1799 JSAutoRealm ar(cx, global); 1800 ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(global); 1801 // This function is called when resolving the "constructor" and "prototype" 1802 // properties of Xrays for DOM prototypes and constructors respectively. 1803 // This means the relevant Xray exists, which means its _target_ exists. 1804 // And that means we managed to successfullly create the prototype or 1805 // constructor, respectively, and hence must have managed to create the 1806 // thing it's pointing to as well. So our entry slot must exist. 1807 JSObject* protoOrIface = 1808 protoAndIfaceCache.EntrySlotMustExist(protoAndIfaceCacheIndex); 1809 MOZ_RELEASE_ASSERT(protoOrIface, "How can this object not exist?"); 1810 1811 cacheOnHolder = true; 1812 1813 desc.set(Some( 1814 JS::PropertyDescriptor::Data(JS::ObjectValue(*protoOrIface), attrs))); 1815 } 1816 return JS_WrapPropertyDescriptor(cx, desc); 1817 } 1818 1819 /* static */ bool XrayResolveOwnProperty( 1820 JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<JSObject*> obj, 1821 JS::Handle<jsid> id, JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc, 1822 bool& cacheOnHolder) { 1823 MOZ_ASSERT(desc.isNothing()); 1824 cacheOnHolder = false; 1825 1826 DOMObjectType type; 1827 const NativePropertyHooks* nativePropertyHooks = 1828 GetNativePropertyHooks(cx, obj, type); 1829 ResolveOwnProperty resolveOwnProperty = 1830 nativePropertyHooks->mIndexedOrNamedNativeProperties 1831 ? nativePropertyHooks->mIndexedOrNamedNativeProperties 1832 ->mResolveOwnProperty 1833 : nullptr; 1834 1835 if (type == eNamedPropertiesObject) { 1836 MOZ_ASSERT(!resolveOwnProperty, 1837 "Shouldn't have any Xray-visible properties"); 1838 return true; 1839 } 1840 1841 const NativePropertiesHolder& nativePropertiesHolder = 1842 nativePropertyHooks->mNativeProperties; 1843 1844 if (!InitPropertyInfos(cx, nativePropertiesHolder)) { 1845 return false; 1846 } 1847 1848 const NativeProperties* nativeProperties = nullptr; 1849 const PropertyInfo* found = nullptr; 1850 1851 if ((nativeProperties = nativePropertiesHolder.regular)) { 1852 found = XrayFindOwnPropertyInfo(cx, type, id, nativeProperties); 1853 } 1854 if (!found && (nativeProperties = nativePropertiesHolder.chromeOnly) && 1855 xpc::AccessCheck::isChrome(JS::GetCompartment(wrapper))) { 1856 found = XrayFindOwnPropertyInfo(cx, type, id, nativeProperties); 1857 } 1858 1859 if (IsInstance(type)) { 1860 // Check for unforgeable properties first to prevent names provided by 1861 // resolveOwnProperty callback from shadowing them. 1862 if (found && (found->type == eUnforgeableMethod || 1863 found->type == eUnforgeableAttribute)) { 1864 if (!XrayResolveProperty(cx, wrapper, obj, id, desc, cacheOnHolder, type, 1865 nativeProperties, *found)) { 1866 return false; 1867 } 1868 1869 if (desc.isSome()) { 1870 return true; 1871 } 1872 } 1873 1874 if (resolveOwnProperty) { 1875 if (!resolveOwnProperty(cx, wrapper, obj, id, desc)) { 1876 return false; 1877 } 1878 1879 if (desc.isSome()) { 1880 // None of these should be cached on the holder, since they're dynamic. 1881 return true; 1882 } 1883 } 1884 1885 // For non-global instance Xrays there are no other properties, so return 1886 // here for them. 1887 if (type != eGlobalInstance) { 1888 return true; 1889 } 1890 } else if (type == eInterface) { 1891 if (id.get() == GetJSIDByIndex(cx, XPCJSContext::IDX_PROTOTYPE)) { 1892 return nativePropertyHooks->mPrototypeID == prototypes::id::_ID_Count || 1893 ResolvePrototypeOrConstructor( 1894 cx, wrapper, obj, nativePropertyHooks->mPrototypeID, 1895 JSPROP_PERMANENT | JSPROP_READONLY, desc, cacheOnHolder); 1896 } 1897 1898 if (id.get() == GetJSIDByIndex(cx, XPCJSContext::IDX_ISINSTANCE)) { 1899 if (IsInterfaceObject(obj) && 1900 InterfaceInfoFromObject(obj)->wantsInterfaceIsInstance) { 1901 cacheOnHolder = true; 1902 1903 JSObject* funObj = XrayCreateFunction( 1904 cx, wrapper, {InterfaceIsInstance, nullptr}, 1, id); 1905 if (!funObj) { 1906 return false; 1907 } 1908 1909 desc.set(Some(JS::PropertyDescriptor::Data( 1910 JS::ObjectValue(*funObj), {JS::PropertyAttribute::Configurable, 1911 JS::PropertyAttribute::Writable}))); 1912 return true; 1913 } 1914 } 1915 1916 if (id.get() == GetJSIDByIndex(cx, XPCJSContext::IDX_NAME)) { 1917 const char* name = IsInterfaceObject(obj) 1918 ? InterfaceInfoFromObject(obj)->mConstructorName 1919 : LegacyFactoryFunctionFromObject(obj)->mName; 1920 JSString* nameStr = JS_NewStringCopyZ(cx, name); 1921 if (!nameStr) { 1922 return false; 1923 } 1924 desc.set(Some(JS::PropertyDescriptor::Data( 1925 JS::StringValue(nameStr), {JS::PropertyAttribute::Configurable, 1926 JS::PropertyAttribute::Enumerable}))); 1927 return true; 1928 } 1929 1930 if (id.get() == GetJSIDByIndex(cx, XPCJSContext::IDX_LENGTH)) { 1931 uint8_t length = IsInterfaceObject(obj) 1932 ? InterfaceInfoFromObject(obj)->mConstructorArgs 1933 : LegacyFactoryFunctionFromObject(obj)->mNargs; 1934 desc.set(Some(JS::PropertyDescriptor::Data( 1935 JS::Int32Value(length), {JS::PropertyAttribute::Configurable, 1936 JS::PropertyAttribute::Enumerable}))); 1937 return true; 1938 } 1939 } else if (type == eNamespace) { 1940 if (id.isWellKnownSymbol(JS::SymbolCode::toStringTag)) { 1941 JS::Rooted<JSString*> nameStr( 1942 cx, JS_AtomizeString(cx, JS::GetClass(obj)->name)); 1943 if (!nameStr) { 1944 return false; 1945 } 1946 1947 desc.set(Some(JS::PropertyDescriptor::Data( 1948 JS::StringValue(nameStr), {JS::PropertyAttribute::Configurable}))); 1949 return true; 1950 } 1951 } else { 1952 MOZ_ASSERT(IsInterfacePrototype(type)); 1953 1954 if (id.get() == GetJSIDByIndex(cx, XPCJSContext::IDX_CONSTRUCTOR)) { 1955 return nativePropertyHooks->mConstructorID == 1956 constructors::id::_ID_Count || 1957 ResolvePrototypeOrConstructor(cx, wrapper, obj, 1958 nativePropertyHooks->mConstructorID, 1959 0, desc, cacheOnHolder); 1960 } 1961 1962 if (id.isWellKnownSymbol(JS::SymbolCode::toStringTag)) { 1963 const JSClass* objClass = JS::GetClass(obj); 1964 prototypes::ID prototypeID = 1965 DOMIfaceAndProtoJSClass::FromJSClass(objClass)->mPrototypeID; 1966 JS::Rooted<JSString*> nameStr( 1967 cx, JS_AtomizeString(cx, NamesOfInterfacesWithProtos(prototypeID))); 1968 if (!nameStr) { 1969 return false; 1970 } 1971 1972 desc.set(Some(JS::PropertyDescriptor::Data( 1973 JS::StringValue(nameStr), {JS::PropertyAttribute::Configurable}))); 1974 return true; 1975 } 1976 1977 // The properties for globals live on the instance, so return here as there 1978 // are no properties on their interface prototype object. 1979 if (type == eGlobalInterfacePrototype) { 1980 return true; 1981 } 1982 } 1983 1984 if (found && !XrayResolveProperty(cx, wrapper, obj, id, desc, cacheOnHolder, 1985 type, nativeProperties, *found)) { 1986 return false; 1987 } 1988 1989 return true; 1990 } 1991 1992 bool XrayDefineProperty(JSContext* cx, JS::Handle<JSObject*> wrapper, 1993 JS::Handle<JSObject*> obj, JS::Handle<jsid> id, 1994 JS::Handle<JS::PropertyDescriptor> desc, 1995 JS::ObjectOpResult& result, bool* done) { 1996 if (!js::IsProxy(obj)) return true; 1997 1998 const DOMProxyHandler* handler = GetDOMProxyHandler(obj); 1999 return handler->defineProperty(cx, wrapper, id, desc, result, done); 2000 } 2001 2002 template <typename SpecType> 2003 bool XrayAppendPropertyKeys(JSContext* cx, JS::Handle<JSObject*> obj, 2004 const Prefable<const SpecType>* pref, 2005 const PropertyInfo* infos, unsigned flags, 2006 JS::MutableHandleVector<jsid> props) { 2007 do { 2008 bool prefIsEnabled = pref->isEnabled(cx, obj); 2009 if (prefIsEnabled) { 2010 const SpecType* spec = pref->specs; 2011 do { 2012 const jsid id = infos++->Id(); 2013 if (((flags & JSITER_HIDDEN) || 2014 (spec->attributes() & JSPROP_ENUMERATE)) && 2015 ((flags & JSITER_SYMBOLS) || !id.isSymbol()) && !props.append(id)) { 2016 return false; 2017 } 2018 } while ((++spec)->name); 2019 } 2020 // Break if we have reached the end of pref. 2021 if (!(++pref)->specs) { 2022 break; 2023 } 2024 // Advance infos if the previous pref is disabled. The -1 is required 2025 // because there is an end-of-list terminator between pref->specs and 2026 // (pref - 1)->specs. 2027 if (!prefIsEnabled) { 2028 infos += pref->specs - (pref - 1)->specs - 1; 2029 } 2030 } while (1); 2031 2032 return true; 2033 } 2034 2035 template <> 2036 bool XrayAppendPropertyKeys<ConstantSpec>( 2037 JSContext* cx, JS::Handle<JSObject*> obj, 2038 const Prefable<const ConstantSpec>* pref, const PropertyInfo* infos, 2039 unsigned flags, JS::MutableHandleVector<jsid> props) { 2040 do { 2041 bool prefIsEnabled = pref->isEnabled(cx, obj); 2042 if (prefIsEnabled) { 2043 const ConstantSpec* spec = pref->specs; 2044 do { 2045 if (!props.append(infos++->Id())) { 2046 return false; 2047 } 2048 } while ((++spec)->name); 2049 } 2050 // Break if we have reached the end of pref. 2051 if (!(++pref)->specs) { 2052 break; 2053 } 2054 // Advance infos if the previous pref is disabled. The -1 is required 2055 // because there is an end-of-list terminator between pref->specs and 2056 // (pref - 1)->specs. 2057 if (!prefIsEnabled) { 2058 infos += pref->specs - (pref - 1)->specs - 1; 2059 } 2060 } while (1); 2061 2062 return true; 2063 } 2064 2065 #define ADD_KEYS_IF_DEFINED(FieldName) \ 2066 { \ 2067 if (nativeProperties->Has##FieldName##s() && \ 2068 !XrayAppendPropertyKeys(cx, obj, nativeProperties->FieldName##s(), \ 2069 nativeProperties->FieldName##PropertyInfos(), \ 2070 flags, props)) { \ 2071 return false; \ 2072 } \ 2073 } 2074 2075 bool XrayOwnPropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper, 2076 JS::Handle<JSObject*> obj, unsigned flags, 2077 JS::MutableHandleVector<jsid> props, 2078 DOMObjectType type, 2079 const NativeProperties* nativeProperties) { 2080 MOZ_ASSERT(type != eNamedPropertiesObject); 2081 2082 if (IsInstance(type)) { 2083 ADD_KEYS_IF_DEFINED(UnforgeableMethod); 2084 ADD_KEYS_IF_DEFINED(UnforgeableAttribute); 2085 if (type == eGlobalInstance) { 2086 ADD_KEYS_IF_DEFINED(Method); 2087 ADD_KEYS_IF_DEFINED(Attribute); 2088 } 2089 } else { 2090 MOZ_ASSERT(type != eGlobalInterfacePrototype); 2091 if (type == eInterface || type == eNamespace) { 2092 ADD_KEYS_IF_DEFINED(StaticMethod); 2093 ADD_KEYS_IF_DEFINED(StaticAttribute); 2094 } else { 2095 MOZ_ASSERT(type == eInterfacePrototype); 2096 ADD_KEYS_IF_DEFINED(Method); 2097 ADD_KEYS_IF_DEFINED(Attribute); 2098 } 2099 ADD_KEYS_IF_DEFINED(Constant); 2100 } 2101 2102 return true; 2103 } 2104 2105 #undef ADD_KEYS_IF_DEFINED 2106 2107 bool XrayOwnNativePropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper, 2108 const NativePropertyHooks* nativePropertyHooks, 2109 DOMObjectType type, JS::Handle<JSObject*> obj, 2110 unsigned flags, 2111 JS::MutableHandleVector<jsid> props) { 2112 MOZ_ASSERT(type != eNamedPropertiesObject); 2113 2114 if (type == eInterface && 2115 nativePropertyHooks->mPrototypeID != prototypes::id::_ID_Count && 2116 !AddStringToIDVector(cx, props, "prototype")) { 2117 return false; 2118 } 2119 2120 if (IsInterfacePrototype(type) && 2121 nativePropertyHooks->mConstructorID != constructors::id::_ID_Count && 2122 (flags & JSITER_HIDDEN) && 2123 !AddStringToIDVector(cx, props, "constructor")) { 2124 return false; 2125 } 2126 2127 const NativePropertiesHolder& nativeProperties = 2128 nativePropertyHooks->mNativeProperties; 2129 2130 if (!InitPropertyInfos(cx, nativeProperties)) { 2131 return false; 2132 } 2133 2134 if (nativeProperties.regular && 2135 !XrayOwnPropertyKeys(cx, wrapper, obj, flags, props, type, 2136 nativeProperties.regular)) { 2137 return false; 2138 } 2139 2140 if (nativeProperties.chromeOnly && 2141 xpc::AccessCheck::isChrome(JS::GetCompartment(wrapper)) && 2142 !XrayOwnPropertyKeys(cx, wrapper, obj, flags, props, type, 2143 nativeProperties.chromeOnly)) { 2144 return false; 2145 } 2146 2147 return true; 2148 } 2149 2150 bool XrayOwnPropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper, 2151 JS::Handle<JSObject*> obj, unsigned flags, 2152 JS::MutableHandleVector<jsid> props) { 2153 DOMObjectType type; 2154 const NativePropertyHooks* nativePropertyHooks = 2155 GetNativePropertyHooks(cx, obj, type); 2156 EnumerateOwnProperties enumerateOwnProperties = 2157 nativePropertyHooks->mIndexedOrNamedNativeProperties 2158 ? nativePropertyHooks->mIndexedOrNamedNativeProperties 2159 ->mEnumerateOwnProperties 2160 : nullptr; 2161 2162 if (type == eNamedPropertiesObject) { 2163 MOZ_ASSERT(!enumerateOwnProperties, 2164 "Shouldn't have any Xray-visible properties"); 2165 return true; 2166 } 2167 2168 if (IsInstance(type)) { 2169 // FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=1071189 2170 // Should do something about XBL properties too. 2171 if (enumerateOwnProperties && 2172 !enumerateOwnProperties(cx, wrapper, obj, props)) { 2173 return false; 2174 } 2175 } 2176 2177 return type == eGlobalInterfacePrototype || 2178 XrayOwnNativePropertyKeys(cx, wrapper, nativePropertyHooks, type, obj, 2179 flags, props); 2180 } 2181 2182 const JSClass* XrayGetExpandoClass(JSContext* cx, JS::Handle<JSObject*> obj) { 2183 DOMObjectType type; 2184 const NativePropertyHooks* nativePropertyHooks = 2185 GetNativePropertyHooks(cx, obj, type); 2186 if (!IsInstance(type)) { 2187 // Non-instances don't need any special expando classes. 2188 return &DefaultXrayExpandoObjectClass; 2189 } 2190 2191 return nativePropertyHooks->mXrayExpandoClass; 2192 } 2193 2194 bool XrayDeleteNamedProperty(JSContext* cx, JS::Handle<JSObject*> wrapper, 2195 JS::Handle<JSObject*> obj, JS::Handle<jsid> id, 2196 JS::ObjectOpResult& opresult) { 2197 DOMObjectType type; 2198 const NativePropertyHooks* nativePropertyHooks = 2199 GetNativePropertyHooks(cx, obj, type); 2200 if (!IsInstance(type) || 2201 !nativePropertyHooks->mIndexedOrNamedNativeProperties || 2202 !nativePropertyHooks->mIndexedOrNamedNativeProperties 2203 ->mDeleteNamedProperty) { 2204 return opresult.succeed(); 2205 } 2206 return nativePropertyHooks->mIndexedOrNamedNativeProperties 2207 ->mDeleteNamedProperty(cx, wrapper, obj, id, opresult); 2208 } 2209 2210 namespace binding_detail { 2211 2212 bool ResolveOwnProperty(JSContext* cx, JS::Handle<JSObject*> wrapper, 2213 JS::Handle<JSObject*> obj, JS::Handle<jsid> id, 2214 JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc) { 2215 return js::GetProxyHandler(obj)->getOwnPropertyDescriptor(cx, wrapper, id, 2216 desc); 2217 } 2218 2219 bool EnumerateOwnProperties(JSContext* cx, JS::Handle<JSObject*> wrapper, 2220 JS::Handle<JSObject*> obj, 2221 JS::MutableHandleVector<jsid> props) { 2222 return js::GetProxyHandler(obj)->ownPropertyKeys(cx, wrapper, props); 2223 } 2224 2225 } // namespace binding_detail 2226 2227 JSObject* GetCachedSlotStorageObjectSlow(JSContext* cx, 2228 JS::Handle<JSObject*> obj, 2229 bool* isXray) { 2230 if (!xpc::WrapperFactory::IsXrayWrapper(obj)) { 2231 JSObject* retval = 2232 js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false); 2233 MOZ_ASSERT(IsDOMObject(retval)); 2234 *isXray = false; 2235 return retval; 2236 } 2237 2238 *isXray = true; 2239 return xpc::EnsureXrayExpandoObject(cx, obj); 2240 } 2241 2242 DEFINE_XRAY_EXPANDO_CLASS(, DefaultXrayExpandoObjectClass, 0); 2243 2244 bool sEmptyNativePropertiesInited = true; 2245 NativePropertyHooks sEmptyNativePropertyHooks = { 2246 nullptr, 2247 {nullptr, nullptr, &sEmptyNativePropertiesInited}, 2248 prototypes::id::_ID_Count, 2249 constructors::id::_ID_Count, 2250 nullptr}; 2251 2252 bool GetPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy, 2253 JS::Handle<JS::Value> receiver, JS::Handle<jsid> id, 2254 bool* found, JS::MutableHandle<JS::Value> vp) { 2255 JS::Rooted<JSObject*> proto(cx); 2256 if (!js::GetObjectProto(cx, proxy, &proto)) { 2257 return false; 2258 } 2259 if (!proto) { 2260 *found = false; 2261 return true; 2262 } 2263 2264 if (!JS_HasPropertyById(cx, proto, id, found)) { 2265 return false; 2266 } 2267 2268 if (!*found) { 2269 return true; 2270 } 2271 2272 return JS_ForwardGetPropertyTo(cx, proto, id, receiver, vp); 2273 } 2274 2275 bool HasPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy, 2276 JS::Handle<jsid> id, bool* has) { 2277 JS::Rooted<JSObject*> proto(cx); 2278 if (!js::GetObjectProto(cx, proxy, &proto)) { 2279 return false; 2280 } 2281 if (!proto) { 2282 *has = false; 2283 return true; 2284 } 2285 2286 return JS_HasPropertyById(cx, proto, id, has); 2287 } 2288 2289 bool AppendNamedPropertyIds(JSContext* cx, JS::Handle<JSObject*> proxy, 2290 nsTArray<nsString>& names, 2291 bool shadowPrototypeProperties, 2292 JS::MutableHandleVector<jsid> props) { 2293 for (uint32_t i = 0; i < names.Length(); ++i) { 2294 JS::Rooted<JS::Value> v(cx); 2295 if (!xpc::NonVoidStringToJsval(cx, names[i], &v)) { 2296 return false; 2297 } 2298 2299 JS::Rooted<jsid> id(cx); 2300 if (!JS_ValueToId(cx, v, &id)) { 2301 return false; 2302 } 2303 2304 bool shouldAppend = shadowPrototypeProperties; 2305 if (!shouldAppend) { 2306 bool has; 2307 if (!HasPropertyOnPrototype(cx, proxy, id, &has)) { 2308 return false; 2309 } 2310 shouldAppend = !has; 2311 } 2312 2313 if (shouldAppend) { 2314 if (!props.append(id)) { 2315 return false; 2316 } 2317 } 2318 } 2319 2320 return true; 2321 } 2322 2323 bool DictionaryBase::ParseJSON(JSContext* aCx, const nsAString& aJSON, 2324 JS::MutableHandle<JS::Value> aVal) { 2325 if (aJSON.IsEmpty()) { 2326 return true; 2327 } 2328 return JS_ParseJSON(aCx, aJSON.BeginReading(), aJSON.Length(), aVal); 2329 } 2330 2331 bool DictionaryBase::StringifyToJSON(JSContext* aCx, JS::Handle<JSObject*> aObj, 2332 nsAString& aJSON) const { 2333 return JS::ToJSONMaybeSafely(aCx, aObj, AppendJSONToString, &aJSON); 2334 } 2335 2336 /* static */ 2337 bool DictionaryBase::AppendJSONToString(const char16_t* aJSONData, 2338 uint32_t aDataLength, void* aString) { 2339 nsAString* string = static_cast<nsAString*>(aString); 2340 string->Append(aJSONData, aDataLength); 2341 return true; 2342 } 2343 2344 void UpdateReflectorGlobal(JSContext* aCx, JS::Handle<JSObject*> aObjArg, 2345 ErrorResult& aError) { 2346 js::AssertSameCompartment(aCx, aObjArg); 2347 2348 aError.MightThrowJSException(); 2349 2350 // Check if we're anywhere near the stack limit before we reach the 2351 // transplanting code, since it has no good way to handle errors. This uses 2352 // the untrusted script limit, which is not strictly necessary since no 2353 // actual script should run. 2354 js::AutoCheckRecursionLimit recursion(aCx); 2355 if (!recursion.checkConservative(aCx)) { 2356 aError.StealExceptionFromJSContext(aCx); 2357 return; 2358 } 2359 2360 JS::Rooted<JSObject*> aObj(aCx, aObjArg); 2361 MOZ_ASSERT(IsDOMObject(aObj)); 2362 2363 const DOMJSClass* domClass = GetDOMClass(aObj); 2364 2365 JS::Rooted<JSObject*> oldGlobal(aCx, JS::GetNonCCWObjectGlobal(aObj)); 2366 MOZ_ASSERT(JS_IsGlobalObject(oldGlobal)); 2367 2368 JS::Rooted<JSObject*> newGlobal(aCx, 2369 domClass->mGetAssociatedGlobal(aCx, aObj)); 2370 MOZ_ASSERT(JS_IsGlobalObject(newGlobal)); 2371 2372 JSAutoRealm oldAr(aCx, oldGlobal); 2373 2374 if (oldGlobal == newGlobal) { 2375 return; 2376 } 2377 2378 nsISupports* native = UnwrapDOMObjectToISupports(aObj); 2379 if (!native) { 2380 return; 2381 } 2382 2383 bool isProxy = js::IsProxy(aObj); 2384 JS::Rooted<JSObject*> expandoObject(aCx); 2385 if (isProxy) { 2386 expandoObject = DOMProxyHandler::GetAndClearExpandoObject(aObj); 2387 } 2388 2389 JSAutoRealm newAr(aCx, newGlobal); 2390 2391 // First we clone the reflector. We get a copy of its properties and clone its 2392 // expando chain. 2393 2394 JS::Handle<JSObject*> proto = (domClass->mGetProto)(aCx); 2395 if (!proto) { 2396 aError.StealExceptionFromJSContext(aCx); 2397 return; 2398 } 2399 2400 JS::Rooted<JSObject*> newobj(aCx, JS_CloneObject(aCx, aObj, proto)); 2401 if (!newobj) { 2402 aError.StealExceptionFromJSContext(aCx); 2403 return; 2404 } 2405 2406 // Assert it's possible to create wrappers when |aObj| and |newobj| are in 2407 // different compartments. 2408 MOZ_ASSERT_IF(JS::GetCompartment(aObj) != JS::GetCompartment(newobj), 2409 js::AllowNewWrapper(JS::GetCompartment(aObj), newobj)); 2410 2411 JS::Rooted<JSObject*> propertyHolder(aCx); 2412 JS::Rooted<JSObject*> copyFrom(aCx, isProxy ? expandoObject : aObj); 2413 if (copyFrom) { 2414 propertyHolder = JS_NewObjectWithGivenProto(aCx, nullptr, nullptr); 2415 if (!propertyHolder) { 2416 aError.StealExceptionFromJSContext(aCx); 2417 return; 2418 } 2419 2420 if (!JS_CopyOwnPropertiesAndPrivateFields(aCx, propertyHolder, copyFrom)) { 2421 aError.StealExceptionFromJSContext(aCx); 2422 return; 2423 } 2424 } else { 2425 propertyHolder = nullptr; 2426 } 2427 2428 // We've set up |newobj|, so we make it own the native by setting its reserved 2429 // slot and nulling out the reserved slot of |obj|. Update the wrapper cache 2430 // to keep everything consistent in case GC moves newobj. 2431 // 2432 // NB: It's important to do this _after_ copying the properties to 2433 // propertyHolder. Otherwise, an object with |foo.x === foo| will 2434 // crash when JS_CopyOwnPropertiesAndPrivateFields tries to call wrap() on 2435 // foo.x. 2436 static_assert(DOM_OBJECT_SLOT == JS_OBJECT_WRAPPER_SLOT); 2437 JS::SetReservedSlot(newobj, DOM_OBJECT_SLOT, 2438 JS::GetReservedSlot(aObj, DOM_OBJECT_SLOT)); 2439 JS::SetReservedSlot(aObj, DOM_OBJECT_SLOT, JS::PrivateValue(nullptr)); 2440 nsWrapperCache* cache = nullptr; 2441 CallQueryInterface(native, &cache); 2442 cache->UpdateWrapperForNewGlobal(native, newobj); 2443 2444 aObj = xpc::TransplantObjectRetainingXrayExpandos(aCx, aObj, newobj); 2445 if (!aObj) { 2446 MOZ_CRASH(); 2447 } 2448 2449 // Update the wrapper cache again if transplanting didn't use newobj but 2450 // returned some other object. 2451 if (aObj != newobj) { 2452 MOZ_ASSERT(UnwrapDOMObjectToISupports(aObj) == native); 2453 cache->UpdateWrapperForNewGlobal(native, aObj); 2454 } 2455 2456 if (propertyHolder) { 2457 JS::Rooted<JSObject*> copyTo(aCx); 2458 if (isProxy) { 2459 copyTo = DOMProxyHandler::EnsureExpandoObject(aCx, aObj); 2460 } else { 2461 copyTo = aObj; 2462 } 2463 2464 if (!copyTo || 2465 !JS_CopyOwnPropertiesAndPrivateFields(aCx, copyTo, propertyHolder)) { 2466 MOZ_CRASH(); 2467 } 2468 } 2469 } 2470 2471 GlobalObject::GlobalObject(JSContext* aCx, JSObject* aObject) 2472 : mGlobalJSObject(aCx), mCx(aCx), mGlobalObject(nullptr) { 2473 MOZ_ASSERT(mCx); 2474 JS::Rooted<JSObject*> obj(aCx, aObject); 2475 if (js::IsWrapper(obj)) { 2476 // aCx correctly represents the current global here. 2477 obj = js::CheckedUnwrapDynamic(obj, aCx, /* stopAtWindowProxy = */ false); 2478 if (!obj) { 2479 // We should never end up here on a worker thread, since there shouldn't 2480 // be any security wrappers to worry about. 2481 if (!MOZ_LIKELY(NS_IsMainThread())) { 2482 MOZ_CRASH(); 2483 } 2484 2485 Throw(aCx, NS_ERROR_XPC_SECURITY_MANAGER_VETO); 2486 return; 2487 } 2488 } 2489 2490 mGlobalJSObject = JS::GetNonCCWObjectGlobal(obj); 2491 } 2492 2493 nsISupports* GlobalObject::GetAsSupports() const { 2494 if (mGlobalObject) { 2495 return mGlobalObject; 2496 } 2497 2498 MOZ_ASSERT(!js::IsWrapper(mGlobalJSObject)); 2499 2500 // Most of our globals are DOM objects. Try that first. Note that this 2501 // assumes that either the first nsISupports in the object is the canonical 2502 // one or that we don't care about the canonical nsISupports here. 2503 mGlobalObject = UnwrapDOMObjectToISupports(mGlobalJSObject); 2504 if (mGlobalObject) { 2505 return mGlobalObject; 2506 } 2507 2508 MOZ_ASSERT(NS_IsMainThread(), "All our worker globals are DOM objects"); 2509 2510 // Remove everything below here once all our global objects are using new 2511 // bindings. If that ever happens; it would need to include Sandbox and 2512 // SystemGlobal. 2513 2514 // See whether mGlobalJSObject is an XPCWrappedNative. This will redo the 2515 // IsWrapper bit above and the UnwrapDOMObjectToISupports in the case when 2516 // we're not actually an XPCWrappedNative, but this should be a rare-ish case 2517 // anyway. 2518 // 2519 // It's OK to use ReflectorToISupportsStatic, because we know we don't have a 2520 // cross-compartment wrapper. 2521 nsCOMPtr<nsISupports> supp = xpc::ReflectorToISupportsStatic(mGlobalJSObject); 2522 if (supp) { 2523 // See documentation for mGlobalJSObject for why this assignment is OK. 2524 mGlobalObject = supp; 2525 return mGlobalObject; 2526 } 2527 2528 // And now a final hack. Sandbox is not a reflector, but it does have an 2529 // nsIGlobalObject hanging out in its private slot. Handle that case here, 2530 // (though again, this will do the useless UnwrapDOMObjectToISupports if we 2531 // got here for something that is somehow not a DOM object, not an 2532 // XPCWrappedNative _and_ not a Sandbox). 2533 if (XPCConvert::GetISupportsFromJSObject(mGlobalJSObject, &mGlobalObject)) { 2534 return mGlobalObject; 2535 } 2536 2537 MOZ_ASSERT(!mGlobalObject); 2538 2539 Throw(mCx, NS_ERROR_XPC_BAD_CONVERT_JS); 2540 return nullptr; 2541 } 2542 2543 nsIPrincipal* GlobalObject::GetSubjectPrincipal() const { 2544 if (!NS_IsMainThread()) { 2545 return nullptr; 2546 } 2547 2548 JS::Realm* realm = js::GetContextRealm(mCx); 2549 MOZ_ASSERT(realm); 2550 JSPrincipals* principals = JS::GetRealmPrincipals(realm); 2551 return nsJSPrincipals::get(principals); 2552 } 2553 2554 CallerType GlobalObject::CallerType() const { 2555 return nsContentUtils::ThreadsafeIsSystemCaller(mCx) 2556 ? dom::CallerType::System 2557 : dom::CallerType::NonSystem; 2558 } 2559 2560 bool ReportLenientThisUnwrappingFailure(JSContext* cx, JSObject* obj) { 2561 JS::Rooted<JSObject*> rootedObj(cx, obj); 2562 GlobalObject global(cx, rootedObj); 2563 if (global.Failed()) { 2564 return false; 2565 } 2566 nsCOMPtr<nsPIDOMWindowInner> window = 2567 do_QueryInterface(global.GetAsSupports()); 2568 if (window && window->GetDoc()) { 2569 window->GetDoc()->WarnOnceAbout(DeprecatedOperations::eLenientThis); 2570 } 2571 return true; 2572 } 2573 2574 bool GetContentGlobalForJSImplementedObject(BindingCallContext& cx, 2575 JS::Handle<JSObject*> obj, 2576 nsIGlobalObject** globalObj) { 2577 // Be very careful to not get tricked here. 2578 MOZ_ASSERT(NS_IsMainThread()); 2579 if (!xpc::AccessCheck::isChrome(JS::GetCompartment(obj))) { 2580 MOZ_CRASH("Should have a chrome object here"); 2581 } 2582 2583 // Look up the content-side object. 2584 JS::Rooted<JS::Value> domImplVal(cx); 2585 if (!JS_GetProperty(cx, obj, "__DOM_IMPL__", &domImplVal)) { 2586 return false; 2587 } 2588 2589 if (!domImplVal.isObject()) { 2590 cx.ThrowErrorMessage<MSG_NOT_OBJECT>("Value"); 2591 return false; 2592 } 2593 2594 // Go ahead and get the global from it. GlobalObject will handle 2595 // doing unwrapping as needed. 2596 GlobalObject global(cx, &domImplVal.toObject()); 2597 if (global.Failed()) { 2598 return false; 2599 } 2600 2601 DebugOnly<nsresult> rv = 2602 CallQueryInterface(global.GetAsSupports(), globalObj); 2603 MOZ_ASSERT(NS_SUCCEEDED(rv)); 2604 MOZ_ASSERT(*globalObj); 2605 return true; 2606 } 2607 2608 void ConstructJSImplementation(const char* aContractId, 2609 nsIGlobalObject* aGlobal, 2610 JS::MutableHandle<JSObject*> aObject, 2611 ErrorResult& aRv) { 2612 MOZ_ASSERT(NS_IsMainThread()); 2613 2614 // Make sure to divorce ourselves from the calling JS while creating and 2615 // initializing the object, so exceptions from that will get reported 2616 // properly, since those are never exceptions that a spec wants to be thrown. 2617 { 2618 AutoNoJSAPI nojsapi; 2619 2620 nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal); 2621 if (!window) { 2622 aRv.ThrowInvalidStateError("Global is not a Window"); 2623 return; 2624 } 2625 if (!window->IsCurrentInnerWindow()) { 2626 aRv.ThrowInvalidStateError("Window no longer active"); 2627 return; 2628 } 2629 2630 // Get the XPCOM component containing the JS implementation. 2631 nsresult rv; 2632 nsCOMPtr<nsISupports> implISupports = do_CreateInstance(aContractId, &rv); 2633 if (!implISupports) { 2634 nsPrintfCString msg("Failed to get JS implementation for contract \"%s\"", 2635 aContractId); 2636 NS_WARNING(msg.get()); 2637 aRv.Throw(rv); 2638 return; 2639 } 2640 // Initialize the object, if it implements nsIDOMGlobalPropertyInitializer 2641 // and our global is a window. 2642 nsCOMPtr<nsIDOMGlobalPropertyInitializer> gpi = 2643 do_QueryInterface(implISupports); 2644 if (gpi) { 2645 JS::Rooted<JS::Value> initReturn(RootingCx()); 2646 rv = gpi->Init(window, &initReturn); 2647 if (NS_FAILED(rv)) { 2648 aRv.Throw(rv); 2649 return; 2650 } 2651 // With JS-implemented WebIDL, the return value of init() is not used to 2652 // determine if init() failed, so init() should only return undefined. Any 2653 // kind of permission or pref checking must happen by adding an attribute 2654 // to the WebIDL interface. 2655 if (!initReturn.isUndefined()) { 2656 MOZ_ASSERT(false, 2657 "The init() method for JS-implemented WebIDL should not " 2658 "return anything"); 2659 MOZ_CRASH(); 2660 } 2661 } 2662 // Extract the JS implementation from the XPCOM object. 2663 nsCOMPtr<nsIXPConnectWrappedJS> implWrapped = 2664 do_QueryInterface(implISupports, &rv); 2665 MOZ_ASSERT(implWrapped, "Failed to get wrapped JS from XPCOM component."); 2666 if (!implWrapped) { 2667 aRv.Throw(rv); 2668 return; 2669 } 2670 aObject.set(implWrapped->GetJSObject()); 2671 if (!aObject) { 2672 aRv.Throw(NS_ERROR_FAILURE); 2673 } 2674 } 2675 } 2676 2677 bool NormalizeUSVString(nsAString& aString) { 2678 return EnsureUTF16Validity(aString); 2679 } 2680 2681 bool NormalizeUSVString(binding_detail::FakeString<char16_t>& aString) { 2682 uint32_t upTo = Utf16ValidUpTo(aString); 2683 uint32_t len = aString.Length(); 2684 if (upTo == len) { 2685 return true; 2686 } 2687 // This is the part that's different from EnsureUTF16Validity with an 2688 // nsAString& argument, because we don't want to ensure mutability in our 2689 // BeginWriting() in the common case and nsAString's EnsureMutable is not 2690 // public. This is a little annoying; I wish we could just share the more or 2691 // less identical code! 2692 if (!aString.EnsureMutable()) { 2693 return false; 2694 } 2695 2696 char16_t* ptr = aString.BeginWriting(); 2697 auto span = Span(ptr, len); 2698 span[upTo] = 0xFFFD; 2699 EnsureUtf16ValiditySpan(span.From(upTo + 1)); 2700 return true; 2701 } 2702 2703 bool ConvertJSValueToByteString(BindingCallContext& cx, JS::Handle<JS::Value> v, 2704 bool nullable, const char* sourceDescription, 2705 nsACString& result) { 2706 JS::Rooted<JSString*> s(cx); 2707 if (v.isString()) { 2708 s = v.toString(); 2709 2710 size_t length = JS::GetStringLength(s); 2711 if (XPCStringConvert::MaybeAssignLatin1StringChars(s, length, result)) { 2712 return true; 2713 } 2714 } else { 2715 if (nullable && v.isNullOrUndefined()) { 2716 result.SetIsVoid(true); 2717 return true; 2718 } 2719 2720 s = JS::ToString(cx, v); 2721 if (!s) { 2722 return false; 2723 } 2724 } 2725 2726 // Conversion from Javascript string to ByteString is only valid if all 2727 // characters < 256. This is always the case for Latin1 strings. 2728 size_t length; 2729 if (!JS::StringHasLatin1Chars(s)) { 2730 // ThrowErrorMessage can GC, so we first scan the string for bad chars 2731 // and report the error outside the AutoCheckCannotGC scope. 2732 bool foundBadChar = false; 2733 size_t badCharIndex; 2734 char16_t badChar; 2735 { 2736 JS::AutoCheckCannotGC nogc; 2737 const char16_t* chars = 2738 JS_GetTwoByteStringCharsAndLength(cx, nogc, s, &length); 2739 if (!chars) { 2740 return false; 2741 } 2742 2743 for (size_t i = 0; i < length; i++) { 2744 if (chars[i] > 255) { 2745 badCharIndex = i; 2746 badChar = chars[i]; 2747 foundBadChar = true; 2748 break; 2749 } 2750 } 2751 } 2752 2753 if (foundBadChar) { 2754 MOZ_ASSERT(badCharIndex < length); 2755 MOZ_ASSERT(badChar > 255); 2756 // The largest unsigned 64 bit number (18,446,744,073,709,551,615) has 2757 // 20 digits, plus one more for the null terminator. 2758 char index[21]; 2759 static_assert(sizeof(size_t) <= 8, "index array too small"); 2760 SprintfLiteral(index, "%zu", badCharIndex); 2761 // A char16_t is 16 bits long. The biggest unsigned 16 bit 2762 // number (65,535) has 5 digits, plus one more for the null 2763 // terminator. 2764 char badCharArray[6]; 2765 static_assert(sizeof(char16_t) <= 2, "badCharArray too small"); 2766 SprintfLiteral(badCharArray, "%d", badChar); 2767 cx.ThrowErrorMessage<MSG_INVALID_BYTESTRING>(sourceDescription, index, 2768 badCharArray); 2769 return false; 2770 } 2771 } else { 2772 length = JS::GetStringLength(s); 2773 } 2774 2775 static_assert(JS::MaxStringLength < UINT32_MAX, 2776 "length+1 shouldn't overflow"); 2777 2778 if (!result.SetLength(length, fallible)) { 2779 return false; 2780 } 2781 2782 if (!JS_EncodeStringToBuffer(cx, s, result.BeginWriting(), length)) { 2783 return false; 2784 } 2785 2786 return true; 2787 } 2788 2789 void FinalizeGlobal(JS::GCContext* aGcx, JSObject* aObj) { 2790 MOZ_ASSERT(JS::GetClass(aObj)->flags & JSCLASS_DOM_GLOBAL); 2791 mozilla::dom::DestroyProtoAndIfaceCache(aObj); 2792 } 2793 2794 bool ResolveGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj, 2795 JS::Handle<jsid> aId, bool* aResolvedp) { 2796 MOZ_ASSERT(JS_IsGlobalObject(aObj), 2797 "Should have a global here, since we plan to resolve standard " 2798 "classes!"); 2799 2800 return JS_ResolveStandardClass(aCx, aObj, aId, aResolvedp); 2801 } 2802 2803 bool MayResolveGlobal(const JSAtomState& aNames, jsid aId, 2804 JSObject* aMaybeObj) { 2805 return JS_MayResolveStandardClass(aNames, aId, aMaybeObj); 2806 } 2807 2808 bool EnumerateGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj, 2809 JS::MutableHandleVector<jsid> aProperties, 2810 bool aEnumerableOnly) { 2811 MOZ_ASSERT(JS_IsGlobalObject(aObj), 2812 "Should have a global here, since we plan to enumerate standard " 2813 "classes!"); 2814 2815 return JS_NewEnumerateStandardClasses(aCx, aObj, aProperties, 2816 aEnumerableOnly); 2817 } 2818 2819 bool IsGlobalInExposureSet(JSContext* aCx, JSObject* aGlobal, 2820 uint32_t aGlobalSet) { 2821 MOZ_ASSERT(aGlobalSet, "Why did we get called?"); 2822 MOZ_ASSERT((aGlobalSet & 2823 ~(GlobalNames::Window | GlobalNames::DedicatedWorkerGlobalScope | 2824 GlobalNames::SharedWorkerGlobalScope | 2825 GlobalNames::ServiceWorkerGlobalScope | 2826 GlobalNames::WorkerDebuggerGlobalScope | 2827 GlobalNames::AudioWorkletGlobalScope | 2828 GlobalNames::PaintWorkletGlobalScope | 2829 GlobalNames::ShadowRealmGlobalScope)) == 0, 2830 "Unknown global type"); 2831 2832 const char* name = JS::GetClass(aGlobal)->name; 2833 2834 if ((aGlobalSet & GlobalNames::Window) && 2835 (!strcmp(name, "Window") || !strcmp(name, "SystemGlobal"))) { 2836 return true; 2837 } 2838 2839 if ((aGlobalSet & GlobalNames::DedicatedWorkerGlobalScope) && 2840 !strcmp(name, "DedicatedWorkerGlobalScope")) { 2841 return true; 2842 } 2843 2844 if ((aGlobalSet & GlobalNames::SharedWorkerGlobalScope) && 2845 !strcmp(name, "SharedWorkerGlobalScope")) { 2846 return true; 2847 } 2848 2849 if ((aGlobalSet & GlobalNames::ServiceWorkerGlobalScope) && 2850 !strcmp(name, "ServiceWorkerGlobalScope")) { 2851 return true; 2852 } 2853 2854 if ((aGlobalSet & GlobalNames::WorkerDebuggerGlobalScope) && 2855 !strcmp(name, "WorkerDebuggerGlobalScopex")) { 2856 return true; 2857 } 2858 2859 if ((aGlobalSet & GlobalNames::AudioWorkletGlobalScope) && 2860 !strcmp(name, "AudioWorkletGlobalScope")) { 2861 return true; 2862 } 2863 2864 if ((aGlobalSet & GlobalNames::PaintWorkletGlobalScope) && 2865 !strcmp(name, "PaintWorkletGlobalScope")) { 2866 return true; 2867 } 2868 2869 if ((aGlobalSet & GlobalNames::ShadowRealmGlobalScope) && 2870 !strcmp(name, "ShadowRealmGlobalScope")) { 2871 return true; 2872 } 2873 2874 return false; 2875 } 2876 2877 namespace binding_detail { 2878 2879 /** 2880 * A ThisPolicy struct needs to provide the following methods: 2881 * 2882 * HasValidThisValue: Takes a CallArgs and returns a boolean indicating whether 2883 * the thisv() is valid in the sense of being the right type 2884 * of Value. It does not check whether it's the right sort 2885 * of object if the Value is a JSObject*. 2886 * 2887 * ExtractThisObject: Takes a CallArgs for which HasValidThisValue was true and 2888 * returns the JSObject* to use for getting |this|. 2889 * 2890 * MaybeUnwrapThisObject: If our |this| is a JSObject* that this policy wants to 2891 * allow unchecked access to for this 2892 * getter/setter/method, unwrap it. Otherwise just 2893 * return the given object. 2894 * 2895 * UnwrapThisObject: Takes a MutableHandle for a JSObject which contains the 2896 * this object (which the caller probably got from 2897 * MaybeUnwrapThisObject). It will try to get the right native 2898 * out of aObj. In some cases there are 2 possible types for 2899 * the native (which is why aSelf is a reference to a void*). 2900 * The ThisPolicy user should use the this JSObject* to 2901 * determine what C++ class aSelf contains. aObj is used to 2902 * keep the reflector object alive while self is being used, 2903 * so its value before and after the UnwrapThisObject call 2904 * could be different (if aObj was wrapped). The return value 2905 * is an nsresult, which will signal if an error occurred. 2906 * 2907 * This is passed a JSContext for dynamic unwrapping purposes, 2908 * but should not throw exceptions on that JSContext. 2909 * 2910 * HandleInvalidThis: If the |this| is not valid (wrong type of value, wrong 2911 * object, etc), decide what to do about it. Returns a 2912 * boolean to return from the JSNative (false for failure, 2913 * true for succcess). 2914 */ 2915 struct NormalThisPolicy { 2916 // This needs to be inlined because it's called on no-exceptions fast-paths. 2917 static MOZ_ALWAYS_INLINE bool HasValidThisValue(const JS::CallArgs& aArgs) { 2918 // Per WebIDL spec, all getters/setters/methods allow null/undefined "this" 2919 // and coerce it to the global. Then the "is this the right interface?" 2920 // check fails if the interface involved is not one that the global 2921 // implements. 2922 // 2923 // As an optimization, we skip doing the null/undefined stuff if we know our 2924 // interface is not implemented by the global. 2925 return aArgs.thisv().isObject(); 2926 } 2927 2928 static MOZ_ALWAYS_INLINE JSObject* ExtractThisObject( 2929 const JS::CallArgs& aArgs) { 2930 return &aArgs.thisv().toObject(); 2931 } 2932 2933 static MOZ_ALWAYS_INLINE JSObject* MaybeUnwrapThisObject(JSObject* aObj) { 2934 return aObj; 2935 } 2936 2937 static MOZ_ALWAYS_INLINE nsresult UnwrapThisObject( 2938 JS::MutableHandle<JSObject*> aObj, JSContext* aCx, void*& aSelf, 2939 prototypes::ID aProtoID, uint32_t aProtoDepth) { 2940 binding_detail::MutableObjectHandleWrapper wrapper(aObj); 2941 return binding_detail::UnwrapObjectInternal<void, true>( 2942 wrapper, aSelf, aProtoID, aProtoDepth, aCx); 2943 } 2944 2945 static bool HandleInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs, 2946 bool aSecurityError, prototypes::ID aProtoId) { 2947 return ThrowInvalidThis(aCx, aArgs, aSecurityError, aProtoId); 2948 } 2949 }; 2950 2951 struct MaybeGlobalThisPolicy : public NormalThisPolicy { 2952 static MOZ_ALWAYS_INLINE bool HasValidThisValue(const JS::CallArgs& aArgs) { 2953 // Here we have to allow null/undefined. 2954 return aArgs.thisv().isObject() || aArgs.thisv().isNullOrUndefined(); 2955 } 2956 2957 static MOZ_ALWAYS_INLINE JSObject* ExtractThisObject( 2958 const JS::CallArgs& aArgs) { 2959 return aArgs.thisv().isObject() 2960 ? &aArgs.thisv().toObject() 2961 : JS::GetNonCCWObjectGlobal(&aArgs.callee()); 2962 } 2963 2964 // We want the MaybeUnwrapThisObject of NormalThisPolicy. 2965 2966 // We want the HandleInvalidThis of NormalThisPolicy. 2967 }; 2968 2969 // Shared LenientThis behavior for our two different LenientThis policies. 2970 struct LenientThisPolicyMixin { 2971 static bool HandleInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs, 2972 bool aSecurityError, prototypes::ID aProtoId) { 2973 if (aSecurityError) { 2974 return NormalThisPolicy::HandleInvalidThis(aCx, aArgs, aSecurityError, 2975 aProtoId); 2976 } 2977 2978 MOZ_ASSERT(!JS_IsExceptionPending(aCx)); 2979 if (!ReportLenientThisUnwrappingFailure(aCx, &aArgs.callee())) { 2980 return false; 2981 } 2982 aArgs.rval().set(JS::UndefinedValue()); 2983 return true; 2984 } 2985 }; 2986 2987 // There are some LenientThis things on globals, so we inherit from 2988 // MaybeGlobalThisPolicy. 2989 struct LenientThisPolicy : public MaybeGlobalThisPolicy, 2990 public LenientThisPolicyMixin { 2991 // We want the HasValidThisValue of MaybeGlobalThisPolicy. 2992 2993 // We want the ExtractThisObject of MaybeGlobalThisPolicy. 2994 2995 // We want the MaybeUnwrapThisObject of MaybeGlobalThisPolicy. 2996 2997 // We want HandleInvalidThis from LenientThisPolicyMixin 2998 using LenientThisPolicyMixin::HandleInvalidThis; 2999 }; 3000 3001 // There are some cross-origin things on globals, so we inherit from 3002 // MaybeGlobalThisPolicy. 3003 struct CrossOriginThisPolicy : public MaybeGlobalThisPolicy { 3004 // We want the HasValidThisValue of MaybeGlobalThisPolicy. 3005 3006 // We want the ExtractThisObject of MaybeGlobalThisPolicy. 3007 3008 static MOZ_ALWAYS_INLINE JSObject* MaybeUnwrapThisObject(JSObject* aObj) { 3009 if (xpc::WrapperFactory::IsCrossOriginWrapper(aObj)) { 3010 return js::UncheckedUnwrap(aObj); 3011 } 3012 3013 // Else just return aObj; our UnwrapThisObject call will try to 3014 // CheckedUnwrap it, and either succeed or get a security error as needed. 3015 return aObj; 3016 } 3017 3018 // After calling UnwrapThisObject aSelf can contain one of 2 types, depending 3019 // on whether aObj is a proxy with a RemoteObjectProxy handler or a (maybe 3020 // wrapped) normal WebIDL reflector. The generated binding code relies on this 3021 // and uses IsRemoteObjectProxy to determine what type aSelf points to. 3022 static MOZ_ALWAYS_INLINE nsresult UnwrapThisObject( 3023 JS::MutableHandle<JSObject*> aObj, JSContext* aCx, void*& aSelf, 3024 prototypes::ID aProtoID, uint32_t aProtoDepth) { 3025 binding_detail::MutableObjectHandleWrapper wrapper(aObj); 3026 // We need to pass false here, because if aObj doesn't have a DOMJSClass 3027 // it might be a remote proxy object, and we don't want to throw in that 3028 // case (even though unwrapping would fail). 3029 nsresult rv = binding_detail::UnwrapObjectInternal<void, false>( 3030 wrapper, aSelf, aProtoID, aProtoDepth, nullptr); 3031 if (NS_SUCCEEDED(rv)) { 3032 return rv; 3033 } 3034 3035 if (js::IsWrapper(wrapper)) { 3036 // We want CheckedUnwrapDynamic here: aCx represents the Realm we are in 3037 // right now, so we want to check whether that Realm should be able to 3038 // access the object. And this object can definitely be a WindowProxy, so 3039 // we need he dynamic check. 3040 JSObject* unwrappedObj = js::CheckedUnwrapDynamic( 3041 wrapper, aCx, /* stopAtWindowProxy = */ false); 3042 if (!unwrappedObj) { 3043 return NS_ERROR_XPC_SECURITY_MANAGER_VETO; 3044 } 3045 3046 // At this point we want to keep "unwrappedObj" alive, because we don't 3047 // hold a strong reference in "aSelf". 3048 wrapper = unwrappedObj; 3049 3050 return binding_detail::UnwrapObjectInternal<void, false>( 3051 wrapper, aSelf, aProtoID, aProtoDepth, nullptr); 3052 } 3053 3054 if (!IsRemoteObjectProxy(wrapper, aProtoID)) { 3055 return NS_ERROR_XPC_BAD_CONVERT_JS; 3056 } 3057 aSelf = RemoteObjectProxyBase::GetNative(wrapper); 3058 return NS_OK; 3059 } 3060 3061 // We want the HandleInvalidThis of MaybeGlobalThisPolicy. 3062 }; 3063 3064 // Some objects that can be cross-origin objects are globals, so we inherit 3065 // from MaybeGlobalThisPolicy. 3066 struct MaybeCrossOriginObjectThisPolicy : public MaybeGlobalThisPolicy { 3067 // We want the HasValidThisValue of MaybeGlobalThisPolicy. 3068 3069 // We want the ExtractThisObject of MaybeGlobalThisPolicy. 3070 3071 // We want the MaybeUnwrapThisObject of MaybeGlobalThisPolicy 3072 3073 static MOZ_ALWAYS_INLINE nsresult UnwrapThisObject( 3074 JS::MutableHandle<JSObject*> aObj, JSContext* aCx, void*& aSelf, 3075 prototypes::ID aProtoID, uint32_t aProtoDepth) { 3076 // There are two cases at this point: either aObj is a cross-compartment 3077 // wrapper (CCW) or it's not. If it is, we don't need to do anything 3078 // special compared to MaybeGlobalThisPolicy: the CCW will do the relevant 3079 // security checks. Which is good, because if we tried to do the 3080 // cross-origin object check _before_ unwrapping it would always come back 3081 // as "same-origin" and if we tried to do it after unwrapping it would be 3082 // completely wrong: the checks rely on the two sides of the comparison 3083 // being symmetric (can access each other or cannot access each other), but 3084 // if we have a CCW we could have an Xray, which is asymmetric. And then 3085 // we'd think we should deny access, whereas we should actually allow 3086 // access. 3087 // 3088 // If we do _not_ have a CCW here, then we need to check whether it's a 3089 // cross-origin-accessible object, and if it is check whether it's 3090 // same-origin-domain with our current callee. 3091 if (!js::IsCrossCompartmentWrapper(aObj) && 3092 xpc::IsCrossOriginAccessibleObject(aObj) && 3093 !MaybeCrossOriginObjectMixins::IsPlatformObjectSameOrigin(aCx, aObj)) { 3094 return NS_ERROR_XPC_SECURITY_MANAGER_VETO; 3095 } 3096 3097 return MaybeGlobalThisPolicy::UnwrapThisObject(aObj, aCx, aSelf, aProtoID, 3098 aProtoDepth); 3099 } 3100 3101 // We want the HandleInvalidThis of MaybeGlobalThisPolicy. 3102 }; 3103 3104 // And in some cases we are dealing with a maybe-cross-origin object _and_ need 3105 // [LenientThis] behavior. 3106 struct MaybeCrossOriginObjectLenientThisPolicy 3107 : public MaybeCrossOriginObjectThisPolicy, 3108 public LenientThisPolicyMixin { 3109 // We want to get all of our behavior from 3110 // MaybeCrossOriginObjectLenientThisPolicy, except for HandleInvalidThis, 3111 // which should come from LenientThisPolicyMixin. 3112 using LenientThisPolicyMixin::HandleInvalidThis; 3113 }; 3114 3115 /** 3116 * An ExceptionPolicy struct provides a single HandleException method which is 3117 * used to handle an exception, if any. The method is given the current 3118 * success/failure boolean so it can decide whether there is in fact an 3119 * exception involved. 3120 */ 3121 struct ThrowExceptions { 3122 // This needs to be inlined because it's called even on no-exceptions 3123 // fast-paths. 3124 static MOZ_ALWAYS_INLINE bool HandleException(JSContext* aCx, 3125 JS::CallArgs& aArgs, 3126 const JSJitInfo* aInfo, 3127 bool aOK) { 3128 return aOK; 3129 } 3130 }; 3131 3132 struct ConvertExceptionsToPromises { 3133 // This needs to be inlined because it's called even on no-exceptions 3134 // fast-paths. 3135 static MOZ_ALWAYS_INLINE bool HandleException(JSContext* aCx, 3136 JS::CallArgs& aArgs, 3137 const JSJitInfo* aInfo, 3138 bool aOK) { 3139 // Promise-returning getters/methods always return objects. 3140 MOZ_ASSERT(aInfo->returnType() == JSVAL_TYPE_OBJECT); 3141 3142 if (aOK) { 3143 return true; 3144 } 3145 3146 return ConvertExceptionToPromise(aCx, aArgs.rval()); 3147 } 3148 }; 3149 3150 template <typename ThisPolicy, typename ExceptionPolicy> 3151 bool GenericGetter(JSContext* cx, unsigned argc, JS::Value* vp) { 3152 JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 3153 const JSJitInfo* info = FUNCTION_VALUE_TO_JITINFO(args.calleev()); 3154 prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID); 3155 if (!ThisPolicy::HasValidThisValue(args)) { 3156 bool ok = ThisPolicy::HandleInvalidThis(cx, args, false, protoID); 3157 return ExceptionPolicy::HandleException(cx, args, info, ok); 3158 } 3159 JS::Rooted<JSObject*> obj(cx, ThisPolicy::ExtractThisObject(args)); 3160 3161 // NOTE: we want to leave obj in its initial compartment, so don't want to 3162 // pass it to UnwrapObjectInternal. Also, the thing we pass to 3163 // UnwrapObjectInternal may be affected by our ThisPolicy. 3164 JS::Rooted<JSObject*> rootSelf(cx, ThisPolicy::MaybeUnwrapThisObject(obj)); 3165 void* self; 3166 { 3167 nsresult rv = 3168 ThisPolicy::UnwrapThisObject(&rootSelf, cx, self, protoID, info->depth); 3169 if (NS_FAILED(rv)) { 3170 bool ok = ThisPolicy::HandleInvalidThis( 3171 cx, args, rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO, protoID); 3172 return ExceptionPolicy::HandleException(cx, args, info, ok); 3173 } 3174 } 3175 3176 MOZ_ASSERT(info->type() == JSJitInfo::Getter); 3177 JSJitGetterOp getter = info->getter; 3178 bool ok = getter(cx, obj, self, JSJitGetterCallArgs(args)); 3179 #ifdef DEBUG 3180 if (ok) { 3181 AssertReturnTypeMatchesJitinfo(info, args.rval()); 3182 } 3183 #endif 3184 return ExceptionPolicy::HandleException(cx, args, info, ok); 3185 } 3186 3187 // Force instantiation of the specializations of GenericGetter we need here. 3188 template bool GenericGetter<NormalThisPolicy, ThrowExceptions>(JSContext* cx, 3189 unsigned argc, 3190 JS::Value* vp); 3191 template bool GenericGetter<NormalThisPolicy, ConvertExceptionsToPromises>( 3192 JSContext* cx, unsigned argc, JS::Value* vp); 3193 template bool GenericGetter<MaybeGlobalThisPolicy, ThrowExceptions>( 3194 JSContext* cx, unsigned argc, JS::Value* vp); 3195 template bool GenericGetter<MaybeGlobalThisPolicy, ConvertExceptionsToPromises>( 3196 JSContext* cx, unsigned argc, JS::Value* vp); 3197 template bool GenericGetter<LenientThisPolicy, ThrowExceptions>(JSContext* cx, 3198 unsigned argc, 3199 JS::Value* vp); 3200 // There aren't any [LenientThis] Promise-returning getters, so don't 3201 // bother instantiating that specialization. 3202 template bool GenericGetter<CrossOriginThisPolicy, ThrowExceptions>( 3203 JSContext* cx, unsigned argc, JS::Value* vp); 3204 // There aren't any cross-origin Promise-returning getters, so don't 3205 // bother instantiating that specialization. 3206 template bool GenericGetter<MaybeCrossOriginObjectThisPolicy, ThrowExceptions>( 3207 JSContext* cx, unsigned argc, JS::Value* vp); 3208 // There aren't any maybe-cross-origin-object Promise-returning getters, so 3209 // don't bother instantiating that specialization. 3210 template bool GenericGetter<MaybeCrossOriginObjectLenientThisPolicy, 3211 ThrowExceptions>(JSContext* cx, unsigned argc, 3212 JS::Value* vp); 3213 // There aren't any maybe-cross-origin-object Promise-returning lenient-this 3214 // getters, so don't bother instantiating that specialization. 3215 3216 template <typename ThisPolicy> 3217 bool GenericSetter(JSContext* cx, unsigned argc, JS::Value* vp) { 3218 JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 3219 const JSJitInfo* info = FUNCTION_VALUE_TO_JITINFO(args.calleev()); 3220 prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID); 3221 if (!ThisPolicy::HasValidThisValue(args)) { 3222 return ThisPolicy::HandleInvalidThis(cx, args, false, protoID); 3223 } 3224 JS::Rooted<JSObject*> obj(cx, ThisPolicy::ExtractThisObject(args)); 3225 3226 // NOTE: we want to leave obj in its initial compartment, so don't want to 3227 // pass it to UnwrapObject. Also the thing we pass to UnwrapObjectInternal 3228 // may be affected by our ThisPolicy. 3229 JS::Rooted<JSObject*> rootSelf(cx, ThisPolicy::MaybeUnwrapThisObject(obj)); 3230 void* self; 3231 { 3232 nsresult rv = 3233 ThisPolicy::UnwrapThisObject(&rootSelf, cx, self, protoID, info->depth); 3234 if (NS_FAILED(rv)) { 3235 return ThisPolicy::HandleInvalidThis( 3236 cx, args, rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO, protoID); 3237 } 3238 } 3239 MOZ_ASSERT(info->type() == JSJitInfo::Setter); 3240 JSJitSetterOp setter = info->setter; 3241 3242 // https://webidl.spec.whatwg.org/#dfn-attribute-setter 3243 // 3244 // Step 4.1. Let |V| be <emu-val>undefined</emu-val>. 3245 // Step 4.2. If any arguments were passed, then set |V| to the value of the 3246 // first argument passed. 3247 if (args.length() == 0) { 3248 JS::Rooted<JS::Value> undef(cx); 3249 if (!setter(cx, obj, self, JSJitSetterCallArgs(&undef))) { 3250 return false; 3251 } 3252 } else { 3253 if (!setter(cx, obj, self, JSJitSetterCallArgs(args))) { 3254 return false; 3255 } 3256 } 3257 args.rval().setUndefined(); 3258 #ifdef DEBUG 3259 AssertReturnTypeMatchesJitinfo(info, args.rval()); 3260 #endif 3261 return true; 3262 } 3263 3264 // Force instantiation of the specializations of GenericSetter we need here. 3265 template bool GenericSetter<NormalThisPolicy>(JSContext* cx, unsigned argc, 3266 JS::Value* vp); 3267 template bool GenericSetter<MaybeGlobalThisPolicy>(JSContext* cx, unsigned argc, 3268 JS::Value* vp); 3269 template bool GenericSetter<LenientThisPolicy>(JSContext* cx, unsigned argc, 3270 JS::Value* vp); 3271 template bool GenericSetter<CrossOriginThisPolicy>(JSContext* cx, unsigned argc, 3272 JS::Value* vp); 3273 template bool GenericSetter<MaybeCrossOriginObjectThisPolicy>(JSContext* cx, 3274 unsigned argc, 3275 JS::Value* vp); 3276 template bool GenericSetter<MaybeCrossOriginObjectLenientThisPolicy>( 3277 JSContext* cx, unsigned argc, JS::Value* vp); 3278 3279 template <typename ThisPolicy, typename ExceptionPolicy> 3280 bool GenericMethod(JSContext* cx, unsigned argc, JS::Value* vp) { 3281 JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 3282 const JSJitInfo* info = FUNCTION_VALUE_TO_JITINFO(args.calleev()); 3283 prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID); 3284 if (!ThisPolicy::HasValidThisValue(args)) { 3285 bool ok = ThisPolicy::HandleInvalidThis(cx, args, false, protoID); 3286 return ExceptionPolicy::HandleException(cx, args, info, ok); 3287 } 3288 JS::Rooted<JSObject*> obj(cx, ThisPolicy::ExtractThisObject(args)); 3289 3290 // NOTE: we want to leave obj in its initial compartment, so don't want to 3291 // pass it to UnwrapObjectInternal. Also, the thing we pass to 3292 // UnwrapObjectInternal may be affected by our ThisPolicy. 3293 JS::Rooted<JSObject*> rootSelf(cx, ThisPolicy::MaybeUnwrapThisObject(obj)); 3294 void* self; 3295 { 3296 nsresult rv = 3297 ThisPolicy::UnwrapThisObject(&rootSelf, cx, self, protoID, info->depth); 3298 if (NS_FAILED(rv)) { 3299 bool ok = ThisPolicy::HandleInvalidThis( 3300 cx, args, rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO, protoID); 3301 return ExceptionPolicy::HandleException(cx, args, info, ok); 3302 } 3303 } 3304 MOZ_ASSERT(info->type() == JSJitInfo::Method); 3305 JSJitMethodOp method = info->method; 3306 bool ok = method(cx, obj, self, JSJitMethodCallArgs(args)); 3307 #ifdef DEBUG 3308 if (ok) { 3309 AssertReturnTypeMatchesJitinfo(info, args.rval()); 3310 } 3311 #endif 3312 return ExceptionPolicy::HandleException(cx, args, info, ok); 3313 } 3314 3315 // Force instantiation of the specializations of GenericMethod we need here. 3316 template bool GenericMethod<NormalThisPolicy, ThrowExceptions>(JSContext* cx, 3317 unsigned argc, 3318 JS::Value* vp); 3319 template bool GenericMethod<NormalThisPolicy, ConvertExceptionsToPromises>( 3320 JSContext* cx, unsigned argc, JS::Value* vp); 3321 template bool GenericMethod<MaybeGlobalThisPolicy, ThrowExceptions>( 3322 JSContext* cx, unsigned argc, JS::Value* vp); 3323 template bool GenericMethod<MaybeGlobalThisPolicy, ConvertExceptionsToPromises>( 3324 JSContext* cx, unsigned argc, JS::Value* vp); 3325 template bool GenericMethod<CrossOriginThisPolicy, ThrowExceptions>( 3326 JSContext* cx, unsigned argc, JS::Value* vp); 3327 // There aren't any cross-origin Promise-returning methods, so don't 3328 // bother instantiating that specialization. 3329 template bool GenericMethod<MaybeCrossOriginObjectThisPolicy, ThrowExceptions>( 3330 JSContext* cx, unsigned argc, JS::Value* vp); 3331 template bool GenericMethod<MaybeCrossOriginObjectThisPolicy, 3332 ConvertExceptionsToPromises>(JSContext* cx, 3333 unsigned argc, 3334 JS::Value* vp); 3335 3336 } // namespace binding_detail 3337 3338 bool StaticMethodPromiseWrapper(JSContext* cx, unsigned argc, JS::Value* vp) { 3339 JS::CallArgs args = JS::CallArgsFromVp(argc, vp); 3340 3341 const JSJitInfo* info = FUNCTION_VALUE_TO_JITINFO(args.calleev()); 3342 MOZ_ASSERT(info); 3343 MOZ_ASSERT(info->type() == JSJitInfo::StaticMethod); 3344 3345 bool ok = info->staticMethod(cx, argc, vp); 3346 if (ok) { 3347 return true; 3348 } 3349 3350 return ConvertExceptionToPromise(cx, args.rval()); 3351 } 3352 3353 bool ConvertExceptionToPromise(JSContext* cx, 3354 JS::MutableHandle<JS::Value> rval) { 3355 JS::Rooted<JS::Value> exn(cx); 3356 if (!JS_GetPendingException(cx, &exn)) { 3357 // This is very important: if there is no pending exception here but we're 3358 // ending up in this code, that means the callee threw an uncatchable 3359 // exception. Just propagate that out as-is. 3360 return false; 3361 } 3362 3363 JS_ClearPendingException(cx); 3364 3365 JSObject* promise = JS::CallOriginalPromiseReject(cx, exn); 3366 if (!promise) { 3367 // We just give up. Put the exception back. 3368 JS_SetPendingException(cx, exn); 3369 return false; 3370 } 3371 3372 rval.setObject(*promise); 3373 return true; 3374 } 3375 3376 /* static */ 3377 void CreateGlobalOptionsWithXPConnect::TraceGlobal(JSTracer* aTrc, 3378 JSObject* aObj) { 3379 xpc::TraceXPCGlobal(aTrc, aObj); 3380 } 3381 3382 /* static */ 3383 bool CreateGlobalOptionsWithXPConnect::PostCreateGlobal( 3384 JSContext* aCx, JS::Handle<JSObject*> aGlobal) { 3385 JSPrincipals* principals = 3386 JS::GetRealmPrincipals(js::GetNonCCWObjectRealm(aGlobal)); 3387 nsIPrincipal* principal = nsJSPrincipals::get(principals); 3388 3389 SiteIdentifier site; 3390 nsresult rv = BasePrincipal::Cast(principal)->GetSiteIdentifier(site); 3391 NS_ENSURE_SUCCESS(rv, false); 3392 3393 xpc::RealmPrivate::Init(aGlobal, site); 3394 return true; 3395 } 3396 3397 uint64_t GetWindowID(void* aGlobal) { return 0; } 3398 3399 uint64_t GetWindowID(nsGlobalWindowInner* aGlobal) { 3400 return aGlobal->WindowID(); 3401 } 3402 3403 uint64_t GetWindowID(DedicatedWorkerGlobalScope* aGlobal) { 3404 return aGlobal->WindowID(); 3405 } 3406 3407 #ifdef DEBUG 3408 void AssertReturnTypeMatchesJitinfo(const JSJitInfo* aJitInfo, 3409 JS::Handle<JS::Value> aValue) { 3410 switch (aJitInfo->returnType()) { 3411 case JSVAL_TYPE_UNKNOWN: 3412 // Any value is good. 3413 break; 3414 case JSVAL_TYPE_DOUBLE: 3415 // The value could actually be an int32 value as well. 3416 MOZ_ASSERT(aValue.isNumber()); 3417 break; 3418 case JSVAL_TYPE_INT32: 3419 MOZ_ASSERT(aValue.isInt32()); 3420 break; 3421 case JSVAL_TYPE_UNDEFINED: 3422 MOZ_ASSERT(aValue.isUndefined()); 3423 break; 3424 case JSVAL_TYPE_BOOLEAN: 3425 MOZ_ASSERT(aValue.isBoolean()); 3426 break; 3427 case JSVAL_TYPE_STRING: 3428 MOZ_ASSERT(aValue.isString()); 3429 break; 3430 case JSVAL_TYPE_NULL: 3431 MOZ_ASSERT(aValue.isNull()); 3432 break; 3433 case JSVAL_TYPE_OBJECT: 3434 MOZ_ASSERT(aValue.isObject()); 3435 break; 3436 default: 3437 // Someone messed up their jitinfo type. 3438 MOZ_ASSERT(false, "Unexpected JSValueType stored in jitinfo"); 3439 break; 3440 } 3441 } 3442 #endif 3443 3444 bool CallerSubsumes(JSObject* aObject) { 3445 // Remote object proxies are not CCWs, so unwrapping them does not get you 3446 // their "real" principal, but we want to treat them like cross-origin objects 3447 // when considering them as WebIDL arguments, for consistency. 3448 if (IsRemoteObjectProxy(aObject)) { 3449 return false; 3450 } 3451 nsIPrincipal* objPrin = 3452 nsContentUtils::ObjectPrincipal(js::UncheckedUnwrap(aObject)); 3453 return nsContentUtils::SubjectPrincipal()->Subsumes(objPrin); 3454 } 3455 3456 nsresult UnwrapArgImpl(JSContext* cx, JS::Handle<JSObject*> src, 3457 const nsIID& iid, void** ppArg) { 3458 if (!NS_IsMainThread()) { 3459 return NS_ERROR_NOT_AVAILABLE; 3460 } 3461 3462 // The JSContext represents the "who is unwrapping" realm, so we want to use 3463 // it for ReflectorToISupportsDynamic here. 3464 nsCOMPtr<nsISupports> iface = xpc::ReflectorToISupportsDynamic(src, cx); 3465 if (iface) { 3466 if (NS_FAILED(iface->QueryInterface(iid, ppArg))) { 3467 return NS_ERROR_XPC_BAD_CONVERT_JS; 3468 } 3469 3470 return NS_OK; 3471 } 3472 3473 // Only allow XPCWrappedJS stuff in system code. Ideally we would remove this 3474 // even there, but that involves converting some things to WebIDL callback 3475 // interfaces and making some other things builtinclass... 3476 if (!nsContentUtils::IsSystemCaller(cx)) { 3477 return NS_ERROR_XPC_BAD_CONVERT_JS; 3478 } 3479 3480 RefPtr<nsXPCWrappedJS> wrappedJS; 3481 nsresult rv = 3482 nsXPCWrappedJS::GetNewOrUsed(cx, src, iid, getter_AddRefs(wrappedJS)); 3483 if (NS_FAILED(rv) || !wrappedJS) { 3484 return rv; 3485 } 3486 3487 // We need to go through the QueryInterface logic to make this return 3488 // the right thing for the various 'special' interfaces; e.g. 3489 // nsIPropertyBag. We must use AggregatedQueryInterface in cases where 3490 // there is an outer to avoid nasty recursion. 3491 return wrappedJS->QueryInterface(iid, ppArg); 3492 } 3493 3494 nsresult UnwrapWindowProxyArg(JSContext* cx, JS::Handle<JSObject*> src, 3495 WindowProxyHolder& ppArg) { 3496 if (IsRemoteObjectProxy(src, prototypes::id::Window)) { 3497 ppArg = 3498 static_cast<BrowsingContext*>(RemoteObjectProxyBase::GetNative(src)); 3499 return NS_OK; 3500 } 3501 3502 nsCOMPtr<nsPIDOMWindowInner> inner; 3503 nsresult rv = UnwrapArg<nsPIDOMWindowInner>(cx, src, getter_AddRefs(inner)); 3504 NS_ENSURE_SUCCESS(rv, rv); 3505 3506 nsCOMPtr<nsPIDOMWindowOuter> outer = inner->GetOuterWindow(); 3507 RefPtr<BrowsingContext> bc = outer ? outer->GetBrowsingContext() : nullptr; 3508 ppArg = std::move(bc); 3509 return NS_OK; 3510 } 3511 3512 template <auto Method, typename... Args> 3513 static bool GetBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj, 3514 size_t aSlotIndex, 3515 JS::MutableHandle<JSObject*> aBackingObj, 3516 bool* aBackingObjCreated, Args... aArgs) { 3517 JS::Rooted<JSObject*> reflector(aCx); 3518 reflector = IsDOMObject(aObj) 3519 ? aObj 3520 : js::UncheckedUnwrap(aObj, 3521 /* stopAtWindowProxy = */ false); 3522 3523 // Retrieve the backing object from the reserved slot on the maplike/setlike 3524 // object. If it doesn't exist yet, create it. 3525 JS::Rooted<JS::Value> slotValue(aCx); 3526 slotValue = JS::GetReservedSlot(reflector, aSlotIndex); 3527 if (slotValue.isUndefined()) { 3528 // Since backing object access can happen in non-originating realms, 3529 // make sure to create the backing object in reflector realm. 3530 { 3531 JSAutoRealm ar(aCx, reflector); 3532 JS::Rooted<JSObject*> newBackingObj(aCx); 3533 newBackingObj.set(Method(aCx, aArgs...)); 3534 if (NS_WARN_IF(!newBackingObj)) { 3535 return false; 3536 } 3537 JS::SetReservedSlot(reflector, aSlotIndex, 3538 JS::ObjectValue(*newBackingObj)); 3539 } 3540 slotValue = JS::GetReservedSlot(reflector, aSlotIndex); 3541 *aBackingObjCreated = true; 3542 } else { 3543 *aBackingObjCreated = false; 3544 } 3545 if (!MaybeWrapNonDOMObjectValue(aCx, &slotValue)) { 3546 return false; 3547 } 3548 aBackingObj.set(&slotValue.toObject()); 3549 return true; 3550 } 3551 3552 bool GetMaplikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj, 3553 size_t aSlotIndex, 3554 JS::MutableHandle<JSObject*> aBackingObj, 3555 bool* aBackingObjCreated) { 3556 return GetBackingObject<JS::NewMapObject>(aCx, aObj, aSlotIndex, aBackingObj, 3557 aBackingObjCreated); 3558 } 3559 3560 bool GetSetlikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj, 3561 size_t aSlotIndex, 3562 JS::MutableHandle<JSObject*> aBackingObj, 3563 bool* aBackingObjCreated) { 3564 return GetBackingObject<JS::NewSetObject>(aCx, aObj, aSlotIndex, aBackingObj, 3565 aBackingObjCreated); 3566 } 3567 3568 static inline JSObject* NewObservableArrayProxyObject( 3569 JSContext* aCx, const ObservableArrayProxyHandler* aHandler, void* aOwner) { 3570 JS::Rooted<JSObject*> target(aCx, JS::NewArrayObject(aCx, 0)); 3571 if (NS_WARN_IF(!target)) { 3572 return nullptr; 3573 } 3574 3575 JS::Rooted<JS::Value> targetValue(aCx, JS::ObjectValue(*target)); 3576 JS::Rooted<JSObject*> proxy( 3577 aCx, js::NewProxyObject(aCx, aHandler, targetValue, nullptr)); 3578 if (!proxy) { 3579 return nullptr; 3580 } 3581 js::SetProxyReservedSlot(proxy, OBSERVABLE_ARRAY_DOM_INTERFACE_SLOT, 3582 JS::PrivateValue(aOwner)); 3583 return proxy; 3584 } 3585 3586 bool GetObservableArrayBackingObject( 3587 JSContext* aCx, JS::Handle<JSObject*> aObj, size_t aSlotIndex, 3588 JS::MutableHandle<JSObject*> aBackingObj, bool* aBackingObjCreated, 3589 const ObservableArrayProxyHandler* aHandler, void* aOwner) { 3590 return GetBackingObject<NewObservableArrayProxyObject>( 3591 aCx, aObj, aSlotIndex, aBackingObj, aBackingObjCreated, aHandler, aOwner); 3592 } 3593 3594 bool ForEachHandler(JSContext* aCx, unsigned aArgc, JS::Value* aVp) { 3595 JS::CallArgs args = CallArgsFromVp(aArgc, aVp); 3596 // Unpack callback and object from slots 3597 JS::Rooted<JS::Value> callbackFn( 3598 aCx, 3599 js::GetFunctionNativeReserved(&args.callee(), FOREACH_CALLBACK_SLOT)); 3600 JS::Rooted<JS::Value> maplikeOrSetlikeObj( 3601 aCx, js::GetFunctionNativeReserved(&args.callee(), 3602 FOREACH_MAPLIKEORSETLIKEOBJ_SLOT)); 3603 MOZ_ASSERT(aArgc == 3); 3604 JS::RootedVector<JS::Value> newArgs(aCx); 3605 // Arguments are passed in as value, key, object. Keep value and key, replace 3606 // object with the maplike/setlike object. 3607 if (!newArgs.append(args.get(0))) { 3608 return false; 3609 } 3610 if (!newArgs.append(args.get(1))) { 3611 return false; 3612 } 3613 if (!newArgs.append(maplikeOrSetlikeObj)) { 3614 return false; 3615 } 3616 JS::Rooted<JS::Value> rval(aCx, JS::UndefinedValue()); 3617 // Now actually call the user specified callback 3618 return JS::Call(aCx, args.thisv(), callbackFn, newArgs, &rval); 3619 } 3620 3621 static inline prototypes::ID GetProtoIdForNewtarget( 3622 JS::Handle<JSObject*> aNewTarget) { 3623 if (IsDOMConstructor(aNewTarget)) { 3624 return GetNativePropertyHooksFromJSNative(aNewTarget)->mPrototypeID; 3625 } 3626 3627 return prototypes::id::_ID_Count; 3628 } 3629 3630 bool GetDesiredProto(JSContext* aCx, const JS::CallArgs& aCallArgs, 3631 prototypes::id::ID aProtoId, 3632 CreateInterfaceObjectsMethod aCreator, 3633 JS::MutableHandle<JSObject*> aDesiredProto) { 3634 // This basically implements 3635 // https://heycam.github.io/webidl/#internally-create-a-new-object-implementing-the-interface 3636 // step 3. 3637 MOZ_ASSERT(aCallArgs.isConstructing(), "How did we end up here?"); 3638 3639 // The desired prototype depends on the actual constructor that was invoked, 3640 // which is passed to us as the newTarget in the callargs. We want to do 3641 // something akin to the ES6 specification's GetProtototypeFromConstructor (so 3642 // get .prototype on the newTarget, with a fallback to some sort of default). 3643 3644 // First, a fast path for the case when the the constructor is in fact one of 3645 // our DOM constructors. This is safe because on those the "constructor" 3646 // property is non-configurable and non-writable, so we don't have to do the 3647 // slow JS_GetProperty call. 3648 JS::Rooted<JSObject*> newTarget(aCx, &aCallArgs.newTarget().toObject()); 3649 MOZ_ASSERT(JS::IsCallable(newTarget)); 3650 JS::Rooted<JSObject*> originalNewTarget(aCx, newTarget); 3651 // See whether we have a known DOM constructor here, such that we can take a 3652 // fast path. 3653 prototypes::ID protoID = GetProtoIdForNewtarget(newTarget); 3654 if (protoID == prototypes::id::_ID_Count) { 3655 // We might still have a cross-compartment wrapper for a known DOM 3656 // constructor. CheckedUnwrapStatic is fine here, because we're looking for 3657 // DOM constructors and those can't be cross-origin objects. 3658 newTarget = js::CheckedUnwrapStatic(newTarget); 3659 if (newTarget && newTarget != originalNewTarget) { 3660 protoID = GetProtoIdForNewtarget(newTarget); 3661 } 3662 } 3663 3664 if (protoID != prototypes::id::_ID_Count) { 3665 ProtoAndIfaceCache& protoAndIfaceCache = 3666 *GetProtoAndIfaceCache(JS::GetNonCCWObjectGlobal(newTarget)); 3667 aDesiredProto.set(protoAndIfaceCache.EntrySlotMustExist(protoID)); 3668 if (newTarget != originalNewTarget) { 3669 return JS_WrapObject(aCx, aDesiredProto); 3670 } 3671 return true; 3672 } 3673 3674 // Slow path. This basically duplicates the ES6 spec's 3675 // GetPrototypeFromConstructor except that instead of taking a string naming 3676 // the fallback prototype we determine the fallback based on the proto id we 3677 // were handed. 3678 // 3679 // Note that it's very important to do this property get on originalNewTarget, 3680 // not our unwrapped newTarget, since we want to get Xray behavior here as 3681 // needed. 3682 // XXXbz for speed purposes, using a preinterned id here sure would be nice. 3683 // We can't use GetJSIDByIndex, because that only works on the main thread, 3684 // not workers. 3685 JS::Rooted<JS::Value> protoVal(aCx); 3686 if (!JS_GetProperty(aCx, originalNewTarget, "prototype", &protoVal)) { 3687 return false; 3688 } 3689 3690 if (protoVal.isObject()) { 3691 aDesiredProto.set(&protoVal.toObject()); 3692 return true; 3693 } 3694 3695 // Fall back to getting the proto for our given proto id in the realm that 3696 // GetFunctionRealm(newTarget) returns. 3697 JS::Rooted<JS::Realm*> realm(aCx, JS::GetFunctionRealm(aCx, newTarget)); 3698 if (!realm) { 3699 return false; 3700 } 3701 3702 { 3703 // JS::GetRealmGlobalOrNull should not be returning null here, because we 3704 // have live objects in the Realm. 3705 JSAutoRealm ar(aCx, JS::GetRealmGlobalOrNull(realm)); 3706 aDesiredProto.set(GetPerInterfaceObjectHandle( 3707 aCx, aProtoId, aCreator, DefineInterfaceProperty::CheckExposure)); 3708 if (!aDesiredProto) { 3709 return false; 3710 } 3711 } 3712 3713 return MaybeWrapObject(aCx, aDesiredProto); 3714 } 3715 3716 namespace { 3717 3718 class MOZ_RAII AutoConstructionDepth final { 3719 public: 3720 MOZ_IMPLICIT AutoConstructionDepth(CustomElementDefinition* aDefinition) 3721 : mDefinition(aDefinition) { 3722 MOZ_ASSERT(mDefinition->mConstructionStack.IsEmpty()); 3723 3724 mDefinition->mConstructionDepth++; 3725 // If the mConstructionDepth isn't matched with the length of mPrefixStack, 3726 // this means the constructor is called directly from JS, i.e. 3727 // 'new CustomElementConstructor()', we have to push a dummy prefix into 3728 // stack. 3729 if (mDefinition->mConstructionDepth > mDefinition->mPrefixStack.Length()) { 3730 mDidPush = true; 3731 mDefinition->mPrefixStack.AppendElement(nullptr); 3732 } 3733 3734 MOZ_ASSERT(mDefinition->mConstructionDepth == 3735 mDefinition->mPrefixStack.Length()); 3736 } 3737 3738 ~AutoConstructionDepth() { 3739 MOZ_ASSERT(mDefinition->mConstructionDepth > 0); 3740 MOZ_ASSERT(mDefinition->mConstructionDepth == 3741 mDefinition->mPrefixStack.Length()); 3742 3743 if (mDidPush) { 3744 MOZ_ASSERT(mDefinition->mPrefixStack.LastElement() == nullptr); 3745 mDefinition->mPrefixStack.RemoveLastElement(); 3746 } 3747 mDefinition->mConstructionDepth--; 3748 } 3749 3750 private: 3751 CustomElementDefinition* mDefinition; 3752 bool mDidPush = false; 3753 }; 3754 3755 } // anonymous namespace 3756 3757 // https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor 3758 namespace binding_detail { 3759 bool HTMLConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp, 3760 constructors::id::ID aConstructorId, 3761 prototypes::id::ID aProtoId, 3762 CreateInterfaceObjectsMethod aCreator) { 3763 JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp); 3764 3765 // Per spec, this is technically part of step 3, but doing the check 3766 // directly lets us provide a better error message. And then in 3767 // step 2 we can work with newTarget in a simpler way because we 3768 // know it's an object. 3769 if (!args.isConstructing()) { 3770 return ThrowConstructorWithoutNew(aCx, 3771 NamesOfInterfacesWithProtos(aProtoId)); 3772 } 3773 3774 JS::Rooted<JSObject*> callee(aCx, &args.callee()); 3775 // 'callee' is not a function here; it's either an Xray for our interface 3776 // object or the interface object itself. So caling XrayAwareCalleeGlobal on 3777 // it is not safe. But since in the Xray case it's a wrapper for our 3778 // interface object, we can just construct our GlobalObject from it and end 3779 // up with the right thing. 3780 GlobalObject global(aCx, callee); 3781 if (global.Failed()) { 3782 return false; 3783 } 3784 3785 // Now we start the [HTMLConstructor] algorithm steps from 3786 // https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor 3787 3788 ErrorResult rv; 3789 auto scopeExit = 3790 MakeScopeExit([&]() { (void)rv.MaybeSetPendingException(aCx); }); 3791 3792 // Step 1. 3793 nsCOMPtr<nsPIDOMWindowInner> window = 3794 do_QueryInterface(global.GetAsSupports()); 3795 if (!window) { 3796 // This means we ended up with an HTML Element interface object defined in 3797 // a non-Window scope. That's ... pretty unexpected. 3798 rv.Throw(NS_ERROR_UNEXPECTED); 3799 return false; 3800 } 3801 RefPtr<mozilla::dom::CustomElementRegistry> registry( 3802 window->CustomElements()); 3803 3804 // Technically, per spec, a window always has a document. In Gecko, a 3805 // sufficiently torn-down window might not, so check for that case. We're 3806 // going to need a document to create an element. 3807 Document* doc = window->GetExtantDoc(); 3808 if (!doc) { 3809 rv.Throw(NS_ERROR_UNEXPECTED); 3810 return false; 3811 } 3812 3813 // Step 2. 3814 3815 // The newTarget might be a cross-compartment wrapper. Get the underlying 3816 // object so we can do the spec's object-identity checks. If we ever stop 3817 // unwrapping here, carefully audit uses of newTarget below! 3818 // 3819 // Note that the ES spec enforces that newTarget is always a constructor (in 3820 // the sense of having a [[Construct]]), so it's not a cross-origin object and 3821 // we can use CheckedUnwrapStatic. 3822 JS::Rooted<JSObject*> newTarget( 3823 aCx, js::CheckedUnwrapStatic(&args.newTarget().toObject())); 3824 if (!newTarget) { 3825 rv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>(); 3826 return false; 3827 } 3828 3829 // Enter the compartment of our underlying newTarget object, so we end 3830 // up comparing to the constructor object for our interface from that global. 3831 // XXXbz This is not what the spec says to do, and it's not super-clear to me 3832 // at this point why we're doing it. Why not just compare |newTarget| and 3833 // |callee| if the intent is just to prevent registration of HTML interface 3834 // objects as constructors? Of course it's not clear that the spec check 3835 // makes sense to start with: https://github.com/whatwg/html/issues/3575 3836 { 3837 JSAutoRealm ar(aCx, newTarget); 3838 JS::Handle<JSObject*> constructor = GetPerInterfaceObjectHandle( 3839 aCx, aConstructorId, aCreator, DefineInterfaceProperty::CheckExposure); 3840 if (!constructor) { 3841 return false; 3842 } 3843 if (newTarget == constructor) { 3844 rv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>(); 3845 return false; 3846 } 3847 } 3848 3849 // Step 3. 3850 CustomElementDefinition* definition = 3851 registry->LookupCustomElementDefinition(aCx, newTarget); 3852 if (!definition) { 3853 rv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>(); 3854 return false; 3855 } 3856 3857 // Steps 4, 5, 6 do some sanity checks on our callee. We add to those a 3858 // determination of what sort of element we're planning to construct. 3859 // Technically, this should happen (implicitly) in step 8, but this 3860 // determination is side-effect-free, so it's OK. 3861 int32_t ns = definition->mNamespaceID; 3862 3863 constructorGetterCallback cb = nullptr; 3864 if (ns == kNameSpaceID_XUL) { 3865 if (definition->mLocalName == nsGkAtoms::description || 3866 definition->mLocalName == nsGkAtoms::label) { 3867 cb = XULTextElement_Binding::GetConstructorObjectHandle; 3868 } else if (definition->mLocalName == nsGkAtoms::resizer) { 3869 cb = XULResizerElement_Binding::GetConstructorObjectHandle; 3870 } else if (definition->mLocalName == nsGkAtoms::menupopup || 3871 definition->mLocalName == nsGkAtoms::panel || 3872 definition->mLocalName == nsGkAtoms::tooltip) { 3873 cb = XULPopupElement_Binding::GetConstructorObjectHandle; 3874 } else if (definition->mLocalName == nsGkAtoms::iframe || 3875 definition->mLocalName == nsGkAtoms::browser || 3876 definition->mLocalName == nsGkAtoms::editor) { 3877 cb = XULFrameElement_Binding::GetConstructorObjectHandle; 3878 } else if (definition->mLocalName == nsGkAtoms::menu || 3879 definition->mLocalName == nsGkAtoms::menulist) { 3880 cb = XULMenuElement_Binding::GetConstructorObjectHandle; 3881 } else if (definition->mLocalName == nsGkAtoms::tree) { 3882 cb = XULTreeElement_Binding::GetConstructorObjectHandle; 3883 } else { 3884 cb = XULElement_Binding::GetConstructorObjectHandle; 3885 } 3886 } 3887 3888 int32_t tag = eHTMLTag_userdefined; 3889 if (!definition->IsCustomBuiltIn()) { 3890 // Step 4. 3891 // If the definition is for an autonomous custom element, the active 3892 // function should be HTMLElement or extend from XULElement. 3893 if (!cb) { 3894 cb = HTMLElement_Binding::GetConstructorObjectHandle; 3895 } 3896 3897 // We want to get the constructor from our global's realm, not the 3898 // caller realm. 3899 JSAutoRealm ar(aCx, global.Get()); 3900 JS::Rooted<JSObject*> constructor(aCx, cb(aCx)); 3901 3902 // CheckedUnwrapStatic is OK here, since our callee is callable, hence not a 3903 // cross-origin object. 3904 if (constructor != js::CheckedUnwrapStatic(callee)) { 3905 rv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>(); 3906 return false; 3907 } 3908 } else { 3909 if (ns == kNameSpaceID_XHTML) { 3910 // Step 5. 3911 // If the definition is for a customized built-in element, the localName 3912 // should be one of the ones defined in the specification for this 3913 // interface. 3914 tag = nsHTMLTags::CaseSensitiveAtomTagToId(definition->mLocalName); 3915 if (tag == eHTMLTag_userdefined) { 3916 rv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>(); 3917 return false; 3918 } 3919 3920 MOZ_ASSERT(tag <= NS_HTML_TAG_MAX, "tag is out of bounds"); 3921 3922 // If the definition is for a customized built-in element, the active 3923 // function should be the localname's element interface. 3924 cb = sConstructorGetterCallback[tag]; 3925 } 3926 3927 if (!cb) { 3928 rv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>(); 3929 return false; 3930 } 3931 3932 // We want to get the constructor from our global's realm, not the 3933 // caller realm. 3934 JSAutoRealm ar(aCx, global.Get()); 3935 JS::Rooted<JSObject*> constructor(aCx, cb(aCx)); 3936 if (!constructor) { 3937 return false; 3938 } 3939 3940 // CheckedUnwrapStatic is OK here, since our callee is callable, hence not a 3941 // cross-origin object. 3942 if (constructor != js::CheckedUnwrapStatic(callee)) { 3943 rv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>(); 3944 return false; 3945 } 3946 } 3947 3948 // Steps 7 and 8. 3949 JS::Rooted<JSObject*> desiredProto(aCx); 3950 if (!GetDesiredProto(aCx, args, aProtoId, aCreator, &desiredProto)) { 3951 return false; 3952 } 3953 3954 MOZ_ASSERT(desiredProto, "How could we not have a prototype by now?"); 3955 3956 // We need to do some work to actually return an Element, so we do step 8 on 3957 // one branch and steps 9-12 on another branch, then common up the "return 3958 // element" work. 3959 RefPtr<Element> element; 3960 nsTArray<RefPtr<Element>>& constructionStack = definition->mConstructionStack; 3961 if (constructionStack.IsEmpty()) { 3962 // Step 8. 3963 // Now we go to construct an element. We want to do this in global's 3964 // realm, not caller realm (the normal constructor behavior), 3965 // just in case those elements create JS things. 3966 JSAutoRealm ar(aCx, global.Get()); 3967 AutoConstructionDepth acd(definition); 3968 3969 RefPtr<NodeInfo> nodeInfo = doc->NodeInfoManager()->GetNodeInfo( 3970 definition->mLocalName, definition->mPrefixStack.LastElement(), ns, 3971 nsINode::ELEMENT_NODE); 3972 MOZ_ASSERT(nodeInfo); 3973 3974 if (ns == kNameSpaceID_XUL) { 3975 element = nsXULElement::Construct(nodeInfo.forget()); 3976 3977 } else { 3978 if (tag == eHTMLTag_userdefined) { 3979 // Autonomous custom element. 3980 element = NS_NewHTMLElement(nodeInfo.forget()); 3981 } else { 3982 // Customized built-in element. 3983 element = CreateHTMLElement(tag, nodeInfo.forget(), NOT_FROM_PARSER); 3984 } 3985 } 3986 3987 element->SetCustomElementData(MakeUnique<CustomElementData>( 3988 definition->mType, CustomElementData::State::eCustom)); 3989 3990 element->SetCustomElementDefinition(definition); 3991 } else { 3992 // Step 9. 3993 element = constructionStack.LastElement(); 3994 3995 // Step 10. 3996 if (element == ALREADY_CONSTRUCTED_MARKER) { 3997 rv.ThrowTypeError( 3998 "Cannot instantiate a custom element inside its own constructor " 3999 "during upgrades"); 4000 return false; 4001 } 4002 4003 // Step 11. 4004 // Do prototype swizzling for upgrading a custom element here, for cases 4005 // when we have a reflector already. If we don't have one yet, we will 4006 // create it with the right proto (by calling GetOrCreateDOMReflector with 4007 // that proto), and will preserve it by means of the proto != canonicalProto 4008 // check). 4009 JS::Rooted<JSObject*> reflector(aCx, element->GetWrapper()); 4010 if (reflector) { 4011 // reflector might be in different realm. 4012 JSAutoRealm ar(aCx, reflector); 4013 JS::Rooted<JSObject*> givenProto(aCx, desiredProto); 4014 if (!JS_WrapObject(aCx, &givenProto) || 4015 !JS_SetPrototype(aCx, reflector, givenProto)) { 4016 return false; 4017 } 4018 PreserveWrapper(element.get()); 4019 } 4020 4021 // Step 12. 4022 constructionStack.LastElement() = ALREADY_CONSTRUCTED_MARKER; 4023 } 4024 4025 // Tail end of step 8 and step 13: returning the element. We want to do this 4026 // part in the global's realm, though in practice it won't matter much 4027 // because Element always knows which realm it should be created in. 4028 JSAutoRealm ar(aCx, global.Get()); 4029 if (!js::IsObjectInContextCompartment(desiredProto, aCx) && 4030 !JS_WrapObject(aCx, &desiredProto)) { 4031 return false; 4032 } 4033 4034 return GetOrCreateDOMReflector(aCx, element, args.rval(), desiredProto); 4035 } 4036 } // namespace binding_detail 4037 4038 #ifdef DEBUG 4039 namespace binding_detail { 4040 void AssertReflectorHasGivenProto(JSContext* aCx, JSObject* aReflector, 4041 JS::Handle<JSObject*> aGivenProto) { 4042 if (!aGivenProto) { 4043 // Nothing to assert here 4044 return; 4045 } 4046 4047 JS::Rooted<JSObject*> reflector(aCx, aReflector); 4048 JSAutoRealm ar(aCx, reflector); 4049 JS::Rooted<JSObject*> reflectorProto(aCx); 4050 bool ok = JS_GetPrototype(aCx, reflector, &reflectorProto); 4051 MOZ_ASSERT(ok); 4052 // aGivenProto may not be in the right realm here, so we 4053 // have to wrap it to compare. 4054 JS::Rooted<JSObject*> givenProto(aCx, aGivenProto); 4055 ok = JS_WrapObject(aCx, &givenProto); 4056 MOZ_ASSERT(ok); 4057 MOZ_ASSERT(givenProto == reflectorProto, 4058 "How are we supposed to change the proto now?"); 4059 } 4060 } // namespace binding_detail 4061 #endif // DEBUG 4062 4063 void SetUseCounter(JSObject* aObject, UseCounter aUseCounter) { 4064 nsGlobalWindowInner* win = 4065 xpc::WindowGlobalOrNull(js::UncheckedUnwrap(aObject)); 4066 if (win && win->GetDocument()) { 4067 win->GetDocument()->SetUseCounter(aUseCounter); 4068 } 4069 } 4070 4071 void SetUseCounter(UseCounterWorker aUseCounter) { 4072 // If this is called from Worklet thread, workerPrivate will be null. 4073 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 4074 if (workerPrivate) { 4075 workerPrivate->SetUseCounter(aUseCounter); 4076 } 4077 } 4078 4079 namespace { 4080 4081 #define DEPRECATED_OPERATION(_op) #_op, 4082 static const char* kDeprecatedOperations[] = { 4083 #include "nsDeprecatedOperationList.h" 4084 nullptr}; 4085 #undef DEPRECATED_OPERATION 4086 4087 void ReportDeprecation(nsIGlobalObject* aGlobal, Document* aDoc, nsIURI* aURI, 4088 DeprecatedOperations aOperation, 4089 const nsACString& aFileName, 4090 const Nullable<uint32_t>& aLineNumber, 4091 const Nullable<uint32_t>& aColumnNumber) { 4092 MOZ_ASSERT(aURI); 4093 4094 // If the URI has the data scheme, report that instead of the spec, 4095 // as the spec may be arbitrarily long and we would like to avoid 4096 // copying it. 4097 nsAutoCString specOrScheme; 4098 nsresult rv = nsContentUtils::AnonymizeURI(aURI, specOrScheme); 4099 if (NS_WARN_IF(NS_FAILED(rv))) { 4100 return; 4101 } 4102 4103 nsAutoString type; 4104 type.AssignASCII(kDeprecatedOperations[static_cast<size_t>(aOperation)]); 4105 4106 nsAutoCString key; 4107 key.AssignASCII(kDeprecatedOperations[static_cast<size_t>(aOperation)]); 4108 key.AppendASCII("Warning"); 4109 4110 nsAutoString msg; 4111 rv = nsContentUtils::GetMaybeLocalizedString(nsContentUtils::eDOM_PROPERTIES, 4112 key.get(), aDoc, msg); 4113 if (NS_WARN_IF(NS_FAILED(rv))) { 4114 return; 4115 } 4116 4117 RefPtr<DeprecationReportBody> body = 4118 new DeprecationReportBody(aGlobal, type, nullptr /* date */, msg, 4119 aFileName, aLineNumber, aColumnNumber); 4120 4121 ReportingUtils::Report(aGlobal, nsGkAtoms::deprecation, u"default"_ns, 4122 NS_ConvertUTF8toUTF16(specOrScheme), body); 4123 } 4124 4125 // This runnable is used to write a deprecation message from a worker to the 4126 // console running on the main-thread. 4127 class DeprecationWarningRunnable final 4128 : public WorkerProxyToMainThreadRunnable { 4129 const DeprecatedOperations mOperation; 4130 4131 public: 4132 explicit DeprecationWarningRunnable(DeprecatedOperations aOperation) 4133 : mOperation(aOperation) {} 4134 4135 private: 4136 void RunOnMainThread(WorkerPrivate* aWorkerPrivate) override { 4137 MOZ_ASSERT(NS_IsMainThread()); 4138 MOZ_ASSERT(aWorkerPrivate); 4139 4140 nsPIDOMWindowInner* window = aWorkerPrivate->GetAncestorWindow(); 4141 if (window && window->GetExtantDoc()) { 4142 window->GetExtantDoc()->WarnOnceAbout(mOperation); 4143 } 4144 } 4145 4146 void RunBackOnWorkerThreadForCleanup(WorkerPrivate* aWorkerPrivate) override { 4147 } 4148 }; 4149 4150 void MaybeShowDeprecationWarning(const GlobalObject& aGlobal, 4151 DeprecatedOperations aOperation) { 4152 if (NS_IsMainThread()) { 4153 nsCOMPtr<nsPIDOMWindowInner> window = 4154 do_QueryInterface(aGlobal.GetAsSupports()); 4155 if (window && window->GetExtantDoc()) { 4156 window->GetExtantDoc()->WarnOnceAbout(aOperation); 4157 } 4158 return; 4159 } 4160 4161 WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aGlobal.Context()); 4162 if (!workerPrivate) { 4163 return; 4164 } 4165 4166 RefPtr<DeprecationWarningRunnable> runnable = 4167 new DeprecationWarningRunnable(aOperation); 4168 runnable->Dispatch(workerPrivate); 4169 } 4170 4171 void MaybeReportDeprecation(const GlobalObject& aGlobal, 4172 DeprecatedOperations aOperation) { 4173 nsCOMPtr<nsIURI> uri; 4174 nsCOMPtr<Document> doc; 4175 if (NS_IsMainThread()) { 4176 nsCOMPtr<nsPIDOMWindowInner> window = 4177 do_QueryInterface(aGlobal.GetAsSupports()); 4178 if (!window || !window->GetExtantDoc()) { 4179 return; 4180 } 4181 4182 doc = window->GetExtantDoc(); 4183 uri = doc->GetDocumentURI(); 4184 } else { 4185 WorkerPrivate* workerPrivate = 4186 GetWorkerPrivateFromContext(aGlobal.Context()); 4187 if (!workerPrivate) { 4188 return; 4189 } 4190 4191 uri = workerPrivate->GetResolvedScriptURI(); 4192 } 4193 4194 if (NS_WARN_IF(!uri)) { 4195 return; 4196 } 4197 4198 auto location = JSCallingLocation::Get(aGlobal.Context()); 4199 Nullable<uint32_t> lineNumber; 4200 Nullable<uint32_t> columnNumber; 4201 if (location) { 4202 lineNumber.SetValue(location.mLine); 4203 columnNumber.SetValue(location.mColumn); 4204 } 4205 4206 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); 4207 MOZ_ASSERT(global); 4208 4209 ReportDeprecation(global, doc, uri, aOperation, location.FileName(), 4210 lineNumber, columnNumber); 4211 } 4212 4213 } // anonymous namespace 4214 4215 void DeprecationWarning(JSContext* aCx, JSObject* aObject, 4216 DeprecatedOperations aOperation) { 4217 GlobalObject global(aCx, aObject); 4218 if (global.Failed()) { 4219 NS_ERROR("Could not create global for DeprecationWarning"); 4220 return; 4221 } 4222 4223 DeprecationWarning(global, aOperation); 4224 } 4225 4226 void DeprecationWarning(const GlobalObject& aGlobal, 4227 DeprecatedOperations aOperation) { 4228 MaybeShowDeprecationWarning(aGlobal, aOperation); 4229 MaybeReportDeprecation(aGlobal, aOperation); 4230 } 4231 4232 namespace binding_detail { 4233 JSObject* UnprivilegedJunkScopeOrWorkerGlobal(const fallible_t&) { 4234 if (NS_IsMainThread()) { 4235 return xpc::UnprivilegedJunkScope(fallible); 4236 } 4237 4238 return GetCurrentThreadWorkerGlobal(); 4239 } 4240 } // namespace binding_detail 4241 4242 JS::Handle<JSObject*> GetPerInterfaceObjectHandle( 4243 JSContext* aCx, size_t aSlotId, CreateInterfaceObjectsMethod aCreator, 4244 DefineInterfaceProperty aDefineOnGlobal) { 4245 /* Make sure our global is sane. Hopefully we can remove this sometime */ 4246 JSObject* global = JS::CurrentGlobalOrNull(aCx); 4247 if (!(JS::GetClass(global)->flags & JSCLASS_DOM_GLOBAL)) { 4248 return nullptr; 4249 } 4250 4251 /* Check to see whether the interface objects are already installed */ 4252 ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(global); 4253 if (!protoAndIfaceCache.HasEntryInSlot(aSlotId)) { 4254 JS::Rooted<JSObject*> rootedGlobal(aCx, global); 4255 aCreator(aCx, rootedGlobal, protoAndIfaceCache, aDefineOnGlobal); 4256 } 4257 4258 /* 4259 * The object might _still_ be null, but that's OK. 4260 * 4261 * Calling fromMarkedLocation() is safe because protoAndIfaceCache is 4262 * traced by TraceProtoAndIfaceCache() and its contents are never 4263 * changed after they have been set. 4264 * 4265 * Calling address() avoids the read barrier that does gray unmarking, but 4266 * it's not possible for the object to be gray here. 4267 */ 4268 4269 const JS::Heap<JSObject*>& entrySlot = 4270 protoAndIfaceCache.EntrySlotMustExist(aSlotId); 4271 JS::AssertObjectIsNotGray(entrySlot); 4272 return JS::Handle<JSObject*>::fromMarkedLocation(entrySlot.unsafeAddress()); 4273 } 4274 4275 namespace binding_detail { 4276 bool IsGetterEnabled(JSContext* aCx, JS::Handle<JSObject*> aObj, 4277 JSJitGetterOp aGetter, 4278 const Prefable<const JSPropertySpec>* aAttributes) { 4279 MOZ_ASSERT(aAttributes); 4280 MOZ_ASSERT(aAttributes->specs); 4281 do { 4282 if (aAttributes->isEnabled(aCx, aObj)) { 4283 const JSPropertySpec* specs = aAttributes->specs; 4284 do { 4285 if (!specs->isAccessor() || specs->isSelfHosted()) { 4286 // It won't have a JSJitGetterOp. 4287 continue; 4288 } 4289 const JSJitInfo* info = specs->u.accessors.getter.native.info; 4290 if (!info) { 4291 continue; 4292 } 4293 MOZ_ASSERT(info->type() == JSJitInfo::OpType::Getter); 4294 if (info->getter == aGetter) { 4295 return true; 4296 } 4297 } while ((++specs)->name); 4298 } 4299 } while ((++aAttributes)->specs); 4300 4301 // Didn't find it. 4302 return false; 4303 } 4304 4305 already_AddRefed<Promise> CreateRejectedPromiseFromThrownException( 4306 JSContext* aCx, ErrorResult& aError) { 4307 if (!JS_IsExceptionPending(aCx)) { 4308 // If there is no pending exception here but we're ending up in this code, 4309 // that means the callee threw an uncatchable exception. Just propagate that 4310 // out as-is. Promise::RejectWithExceptionFromContext also checks this, but 4311 // we want to bail out here before trying to get the globals. 4312 aError.ThrowUncatchableException(); 4313 return nullptr; 4314 } 4315 4316 GlobalObject promiseGlobal(aCx, GetEntryGlobal()->GetGlobalJSObject()); 4317 if (promiseGlobal.Failed()) { 4318 aError.StealExceptionFromJSContext(aCx); 4319 return nullptr; 4320 } 4321 4322 nsCOMPtr<nsIGlobalObject> global = 4323 do_QueryInterface(promiseGlobal.GetAsSupports()); 4324 if (!global) { 4325 aError.Throw(NS_ERROR_UNEXPECTED); 4326 return nullptr; 4327 } 4328 4329 return Promise::RejectWithExceptionFromContext(global, aCx, aError); 4330 } 4331 4332 /* static */ 4333 void ReflectedHTMLAttributeSlotsBase::ForEachXrayReflectedHTMLAttributeSlots( 4334 JS::RootingContext* aCx, JSObject* aObject, size_t aSlotIndex, 4335 size_t aArrayIndex, void (*aFunc)(void* aSlots, size_t aArrayIndex)) { 4336 xpc::ForEachXrayExpandoObject( 4337 aCx, aObject, [aSlotIndex, aFunc, aArrayIndex](JSObject* aExpandObject) { 4338 MOZ_ASSERT(JSCLASS_RESERVED_SLOTS(JS::GetClass(aExpandObject)) > 4339 aSlotIndex); 4340 MOZ_ASSERT(aSlotIndex >= DOM_EXPANDO_RESERVED_SLOTS); 4341 JS::Value array = JS::GetReservedSlot(aExpandObject, aSlotIndex); 4342 if (!array.isUndefined()) { 4343 aFunc(array.toPrivate(), aArrayIndex); 4344 } 4345 }); 4346 } 4347 4348 /* static */ 4349 void ReflectedHTMLAttributeSlotsBase::XrayExpandoObjectFinalize( 4350 JS::GCContext* aCx, JSObject* aObject) { 4351 xpc::ExpandoObjectFinalize(aCx, aObject); 4352 } 4353 4354 void ClearXrayExpandoSlots(JS::RootingContext* aCx, JSObject* aObject, 4355 size_t aSlotIndex) { 4356 xpc::ForEachXrayExpandoObject( 4357 aCx, aObject, [aSlotIndex](JSObject* aExpandObject) { 4358 MOZ_ASSERT(JSCLASS_RESERVED_SLOTS(JS::GetClass(aExpandObject)) > 4359 aSlotIndex); 4360 MOZ_ASSERT(aSlotIndex >= DOM_EXPANDO_RESERVED_SLOTS); 4361 JS::SetReservedSlot(aExpandObject, aSlotIndex, JS::UndefinedValue()); 4362 }); 4363 } 4364 4365 } // namespace binding_detail 4366 4367 static_assert(UnderlyingValue(DOM_EXPANDO_RESERVED_SLOTS) == 4368 UnderlyingValue(xpc::JSSLOT_EXPANDO_COUNT)); 4369 4370 } // namespace dom 4371 } // namespace mozilla