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