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