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/FloatingPoint.h"
15 #include "mozilla/Preferences.h"
16 #include "mozilla/Unused.h"
17 #include "mozilla/UseCounter.h"
18 
19 #include "AccessCheck.h"
20 #include "jsfriendapi.h"
21 #include "nsContentCreatorFunctions.h"
22 #include "nsContentUtils.h"
23 #include "nsGlobalWindow.h"
24 #include "nsHTMLTags.h"
25 #include "nsIDocShell.h"
26 #include "nsIDOMGlobalPropertyInitializer.h"
27 #include "nsINode.h"
28 #include "nsIPermissionManager.h"
29 #include "nsIPrincipal.h"
30 #include "nsIXPConnect.h"
31 #include "nsUTF8Utils.h"
32 #include "WorkerPrivate.h"
33 #include "WorkerRunnable.h"
34 #include "WrapperFactory.h"
35 #include "xpcprivate.h"
36 #include "XrayWrapper.h"
37 #include "nsPrintfCString.h"
38 #include "mozilla/Sprintf.h"
39 #include "nsGlobalWindow.h"
40 
41 #include "mozilla/dom/ScriptSettings.h"
42 #include "mozilla/dom/CustomElementRegistry.h"
43 #include "mozilla/dom/DOMException.h"
44 #include "mozilla/dom/ElementBinding.h"
45 #include "mozilla/dom/HTMLObjectElement.h"
46 #include "mozilla/dom/HTMLObjectElementBinding.h"
47 #include "mozilla/dom/HTMLEmbedElement.h"
48 #include "mozilla/dom/HTMLElementBinding.h"
49 #include "mozilla/dom/HTMLEmbedElementBinding.h"
50 #include "mozilla/dom/XULElementBinding.h"
51 #include "mozilla/dom/Promise.h"
52 #include "mozilla/dom/ResolveSystemBinding.h"
53 #include "mozilla/dom/WebIDLGlobalNameHash.h"
54 #include "mozilla/dom/WorkerPrivate.h"
55 #include "mozilla/dom/WorkerScope.h"
56 #include "mozilla/dom/XrayExpandoClass.h"
57 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
58 #include "nsDOMClassInfo.h"
59 #include "ipc/ErrorIPCUtils.h"
60 #include "mozilla/UseCounter.h"
61 #include "mozilla/dom/DocGroup.h"
62 #include "nsXULElement.h"
63 
64 namespace mozilla {
65 namespace dom {
66 
67 // Forward declare GetConstructorObject methods.
68 #define HTML_TAG(_tag, _classname, _interfacename) \
69   namespace HTML##_interfacename##ElementBinding { \
70     JSObject* GetConstructorObject(JSContext*);    \
71   }
72 #define HTML_OTHER(_tag)
73 #include "nsHTMLTagList.h"
74 #undef HTML_TAG
75 #undef HTML_OTHER
76 
77 typedef JSObject* (*constructorGetterCallback)(JSContext*);
78 
79 // Mapping of html tag and GetConstructorObject methods.
80 #define HTML_TAG(_tag, _classname, _interfacename) \
81   HTML##_interfacename##ElementBinding::GetConstructorObject,
82 #define HTML_OTHER(_tag) nullptr,
83 // We use eHTMLTag_foo (where foo is the tag) which is defined in nsHTMLTags.h
84 // to index into this array.
85 static const constructorGetterCallback sConstructorGetterCallback[] = {
86     HTMLUnknownElementBinding::GetConstructorObject,
87 #include "nsHTMLTagList.h"
88 #undef HTML_TAG
89 #undef HTML_OTHER
90 };
91 
92 const JSErrorFormatString ErrorFormatString[] = {
93 #define MSG_DEF(_name, _argc, _exn, _str) {#_name, _str, _argc, _exn},
94 #include "mozilla/dom/Errors.msg"
95 #undef MSG_DEF
96 };
97 
98 #define MSG_DEF(_name, _argc, _exn, _str)      \
99   static_assert(                               \
100       _argc < JS::MaxNumErrorArguments, #_name \
101       " must only have as many error arguments as the JS engine can support");
102 #include "mozilla/dom/Errors.msg"
103 #undef MSG_DEF
104 
GetErrorMessage(void * aUserRef,const unsigned aErrorNumber)105 const JSErrorFormatString* GetErrorMessage(void* aUserRef,
106                                            const unsigned aErrorNumber) {
107   MOZ_ASSERT(aErrorNumber < ArrayLength(ErrorFormatString));
108   return &ErrorFormatString[aErrorNumber];
109 }
110 
GetErrorArgCount(const ErrNum aErrorNumber)111 uint16_t GetErrorArgCount(const ErrNum aErrorNumber) {
112   return GetErrorMessage(nullptr, aErrorNumber)->argCount;
113 }
114 
ThrowErrorMessage(JSContext * aCx,const unsigned aErrorNumber,...)115 void binding_detail::ThrowErrorMessage(JSContext* aCx,
116                                        const unsigned aErrorNumber, ...) {
117   va_list ap;
118   va_start(ap, aErrorNumber);
119   JS_ReportErrorNumberUTF8VA(aCx, GetErrorMessage, nullptr, aErrorNumber, ap);
120   va_end(ap);
121 }
122 
ThrowInvalidThis(JSContext * aCx,const JS::CallArgs & aArgs,bool aSecurityError,const char * aInterfaceName)123 bool ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
124                       bool aSecurityError, const char* aInterfaceName) {
125   NS_ConvertASCIItoUTF16 ifaceName(aInterfaceName);
126   // This should only be called for DOM methods/getters/setters, which
127   // are JSNative-backed functions, so we can assume that
128   // JS_ValueToFunction and JS_GetFunctionDisplayId will both return
129   // non-null and that JS_GetStringCharsZ returns non-null.
130   JS::Rooted<JSFunction*> func(aCx, JS_ValueToFunction(aCx, aArgs.calleev()));
131   MOZ_ASSERT(func);
132   JS::Rooted<JSString*> funcName(aCx, JS_GetFunctionDisplayId(func));
133   MOZ_ASSERT(funcName);
134   nsAutoJSString funcNameStr;
135   if (!funcNameStr.init(aCx, funcName)) {
136     return false;
137   }
138   const ErrNum errorNumber = aSecurityError
139                                  ? MSG_METHOD_THIS_UNWRAPPING_DENIED
140                                  : MSG_METHOD_THIS_DOES_NOT_IMPLEMENT_INTERFACE;
141   MOZ_RELEASE_ASSERT(GetErrorArgCount(errorNumber) <= 2);
142   JS_ReportErrorNumberUC(aCx, GetErrorMessage, nullptr,
143                          static_cast<unsigned>(errorNumber), funcNameStr.get(),
144                          ifaceName.get());
145   return false;
146 }
147 
ThrowInvalidThis(JSContext * aCx,const JS::CallArgs & aArgs,bool aSecurityError,prototypes::ID aProtoId)148 bool ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
149                       bool aSecurityError, prototypes::ID aProtoId) {
150   return ThrowInvalidThis(aCx, aArgs, aSecurityError,
151                           NamesOfInterfacesWithProtos(aProtoId));
152 }
153 
ThrowNoSetterArg(JSContext * aCx,prototypes::ID aProtoId)154 bool ThrowNoSetterArg(JSContext* aCx, prototypes::ID aProtoId) {
155   nsPrintfCString errorMessage("%s attribute setter",
156                                NamesOfInterfacesWithProtos(aProtoId));
157   return ThrowErrorMessage(aCx, MSG_MISSING_ARGUMENTS, errorMessage.get());
158 }
159 
160 }  // namespace dom
161 
162 namespace binding_danger {
163 
164 template <typename CleanupPolicy>
165 struct TErrorResult<CleanupPolicy>::Message {
Messagemozilla::binding_danger::TErrorResult::Message166   Message() { MOZ_COUNT_CTOR(TErrorResult::Message); }
~Messagemozilla::binding_danger::TErrorResult::Message167   ~Message() { MOZ_COUNT_DTOR(TErrorResult::Message); }
168 
169   nsTArray<nsString> mArgs;
170   dom::ErrNum mErrorNumber;
171 
HasCorrectNumberOfArgumentsmozilla::binding_danger::TErrorResult::Message172   bool HasCorrectNumberOfArguments() {
173     return GetErrorArgCount(mErrorNumber) == mArgs.Length();
174   }
175 };
176 
177 template <typename CleanupPolicy>
CreateErrorMessageHelper(const dom::ErrNum errorNumber,nsresult errorType)178 nsTArray<nsString>& TErrorResult<CleanupPolicy>::CreateErrorMessageHelper(
179     const dom::ErrNum errorNumber, nsresult errorType) {
180   AssertInOwningThread();
181   mResult = errorType;
182 
183   mMessage = new Message();
184   mMessage->mErrorNumber = errorNumber;
185   return mMessage->mArgs;
186 }
187 
188 template <typename CleanupPolicy>
SerializeMessage(IPC::Message * aMsg) const189 void TErrorResult<CleanupPolicy>::SerializeMessage(IPC::Message* aMsg) const {
190   using namespace IPC;
191   AssertInOwningThread();
192   MOZ_ASSERT(mUnionState == HasMessage);
193   MOZ_ASSERT(mMessage);
194   WriteParam(aMsg, mMessage->mArgs);
195   WriteParam(aMsg, mMessage->mErrorNumber);
196 }
197 
198 template <typename CleanupPolicy>
DeserializeMessage(const IPC::Message * aMsg,PickleIterator * aIter)199 bool TErrorResult<CleanupPolicy>::DeserializeMessage(const IPC::Message* aMsg,
200                                                      PickleIterator* aIter) {
201   using namespace IPC;
202   AssertInOwningThread();
203   nsAutoPtr<Message> readMessage(new Message());
204   if (!ReadParam(aMsg, aIter, &readMessage->mArgs) ||
205       !ReadParam(aMsg, aIter, &readMessage->mErrorNumber)) {
206     return false;
207   }
208   if (!readMessage->HasCorrectNumberOfArguments()) {
209     return false;
210   }
211 
212   MOZ_ASSERT(mUnionState == HasNothing);
213   mMessage = readMessage.forget();
214 #ifdef DEBUG
215   mUnionState = HasMessage;
216 #endif  // DEBUG
217   return true;
218 }
219 
220 template <typename CleanupPolicy>
SetPendingExceptionWithMessage(JSContext * aCx)221 void TErrorResult<CleanupPolicy>::SetPendingExceptionWithMessage(
222     JSContext* aCx) {
223   AssertInOwningThread();
224   MOZ_ASSERT(mMessage,
225              "SetPendingExceptionWithMessage() can be called only once");
226   MOZ_ASSERT(mUnionState == HasMessage);
227 
228   Message* message = mMessage;
229   MOZ_RELEASE_ASSERT(message->HasCorrectNumberOfArguments());
230   const uint32_t argCount = message->mArgs.Length();
231   const char16_t* args[JS::MaxNumErrorArguments + 1];
232   for (uint32_t i = 0; i < argCount; ++i) {
233     args[i] = message->mArgs.ElementAt(i).get();
234   }
235   args[argCount] = nullptr;
236 
237   JS_ReportErrorNumberUCArray(aCx, dom::GetErrorMessage, nullptr,
238                               static_cast<unsigned>(message->mErrorNumber),
239                               argCount > 0 ? args : nullptr);
240 
241   ClearMessage();
242   mResult = NS_OK;
243 }
244 
245 template <typename CleanupPolicy>
ClearMessage()246 void TErrorResult<CleanupPolicy>::ClearMessage() {
247   AssertInOwningThread();
248   MOZ_ASSERT(IsErrorWithMessage());
249   delete mMessage;
250   mMessage = nullptr;
251 #ifdef DEBUG
252   mUnionState = HasNothing;
253 #endif  // DEBUG
254 }
255 
256 template <typename CleanupPolicy>
ThrowJSException(JSContext * cx,JS::Handle<JS::Value> exn)257 void TErrorResult<CleanupPolicy>::ThrowJSException(JSContext* cx,
258                                                    JS::Handle<JS::Value> exn) {
259   AssertInOwningThread();
260   MOZ_ASSERT(mMightHaveUnreportedJSException,
261              "Why didn't you tell us you planned to throw a JS exception?");
262 
263   ClearUnionData();
264 
265   // Make sure mJSException is initialized _before_ we try to root it.  But
266   // don't set it to exn yet, because we don't want to do that until after we
267   // root.
268   mJSException.asValueRef().setUndefined();
269   if (!js::AddRawValueRoot(cx, &mJSException.asValueRef(),
270                            "TErrorResult::mJSException")) {
271     // Don't use NS_ERROR_INTERNAL_ERRORRESULT_JS_EXCEPTION, because that
272     // indicates we have in fact rooted mJSException.
273     mResult = NS_ERROR_OUT_OF_MEMORY;
274   } else {
275     mJSException = exn;
276     mResult = NS_ERROR_INTERNAL_ERRORRESULT_JS_EXCEPTION;
277 #ifdef DEBUG
278     mUnionState = HasJSException;
279 #endif  // DEBUG
280   }
281 }
282 
283 template <typename CleanupPolicy>
SetPendingJSException(JSContext * cx)284 void TErrorResult<CleanupPolicy>::SetPendingJSException(JSContext* cx) {
285   AssertInOwningThread();
286   MOZ_ASSERT(!mMightHaveUnreportedJSException,
287              "Why didn't you tell us you planned to handle JS exceptions?");
288   MOZ_ASSERT(mUnionState == HasJSException);
289 
290   JS::Rooted<JS::Value> exception(cx, mJSException);
291   if (JS_WrapValue(cx, &exception)) {
292     JS_SetPendingException(cx, exception);
293   }
294   mJSException = exception;
295   // If JS_WrapValue failed, not much we can do about it...  No matter
296   // what, go ahead and unroot mJSException.
297   js::RemoveRawValueRoot(cx, &mJSException.asValueRef());
298 
299   mResult = NS_OK;
300 #ifdef DEBUG
301   mUnionState = HasNothing;
302 #endif  // DEBUG
303 }
304 
305 template <typename CleanupPolicy>
306 struct TErrorResult<CleanupPolicy>::DOMExceptionInfo {
DOMExceptionInfomozilla::binding_danger::TErrorResult::DOMExceptionInfo307   DOMExceptionInfo(nsresult rv, const nsACString& message)
308       : mMessage(message), mRv(rv) {}
309 
310   nsCString mMessage;
311   nsresult mRv;
312 };
313 
314 template <typename CleanupPolicy>
SerializeDOMExceptionInfo(IPC::Message * aMsg) const315 void TErrorResult<CleanupPolicy>::SerializeDOMExceptionInfo(
316     IPC::Message* aMsg) const {
317   using namespace IPC;
318   AssertInOwningThread();
319   MOZ_ASSERT(mDOMExceptionInfo);
320   MOZ_ASSERT(mUnionState == HasDOMExceptionInfo);
321   WriteParam(aMsg, mDOMExceptionInfo->mMessage);
322   WriteParam(aMsg, mDOMExceptionInfo->mRv);
323 }
324 
325 template <typename CleanupPolicy>
DeserializeDOMExceptionInfo(const IPC::Message * aMsg,PickleIterator * aIter)326 bool TErrorResult<CleanupPolicy>::DeserializeDOMExceptionInfo(
327     const IPC::Message* aMsg, PickleIterator* aIter) {
328   using namespace IPC;
329   AssertInOwningThread();
330   nsCString message;
331   nsresult rv;
332   if (!ReadParam(aMsg, aIter, &message) || !ReadParam(aMsg, aIter, &rv)) {
333     return false;
334   }
335 
336   MOZ_ASSERT(mUnionState == HasNothing);
337   MOZ_ASSERT(IsDOMException());
338   mDOMExceptionInfo = new DOMExceptionInfo(rv, message);
339 #ifdef DEBUG
340   mUnionState = HasDOMExceptionInfo;
341 #endif  // DEBUG
342   return true;
343 }
344 
345 template <typename CleanupPolicy>
ThrowDOMException(nsresult rv,const nsACString & message)346 void TErrorResult<CleanupPolicy>::ThrowDOMException(nsresult rv,
347                                                     const nsACString& message) {
348   AssertInOwningThread();
349   ClearUnionData();
350 
351   mResult = NS_ERROR_INTERNAL_ERRORRESULT_DOMEXCEPTION;
352   mDOMExceptionInfo = new DOMExceptionInfo(rv, message);
353 #ifdef DEBUG
354   mUnionState = HasDOMExceptionInfo;
355 #endif
356 }
357 
358 template <typename CleanupPolicy>
SetPendingDOMException(JSContext * cx)359 void TErrorResult<CleanupPolicy>::SetPendingDOMException(JSContext* cx) {
360   AssertInOwningThread();
361   MOZ_ASSERT(mDOMExceptionInfo,
362              "SetPendingDOMException() can be called only once");
363   MOZ_ASSERT(mUnionState == HasDOMExceptionInfo);
364 
365   dom::Throw(cx, mDOMExceptionInfo->mRv, mDOMExceptionInfo->mMessage);
366 
367   ClearDOMExceptionInfo();
368   mResult = NS_OK;
369 }
370 
371 template <typename CleanupPolicy>
ClearDOMExceptionInfo()372 void TErrorResult<CleanupPolicy>::ClearDOMExceptionInfo() {
373   AssertInOwningThread();
374   MOZ_ASSERT(IsDOMException());
375   MOZ_ASSERT(mUnionState == HasDOMExceptionInfo || !mDOMExceptionInfo);
376   delete mDOMExceptionInfo;
377   mDOMExceptionInfo = nullptr;
378 #ifdef DEBUG
379   mUnionState = HasNothing;
380 #endif  // DEBUG
381 }
382 
383 template <typename CleanupPolicy>
ClearUnionData()384 void TErrorResult<CleanupPolicy>::ClearUnionData() {
385   AssertInOwningThread();
386   if (IsJSException()) {
387     JSContext* cx = dom::danger::GetJSContext();
388     MOZ_ASSERT(cx);
389     mJSException.asValueRef().setUndefined();
390     js::RemoveRawValueRoot(cx, &mJSException.asValueRef());
391 #ifdef DEBUG
392     mUnionState = HasNothing;
393 #endif  // DEBUG
394   } else if (IsErrorWithMessage()) {
395     ClearMessage();
396   } else if (IsDOMException()) {
397     ClearDOMExceptionInfo();
398   }
399 }
400 
401 template <typename CleanupPolicy>
SetPendingGenericErrorException(JSContext * cx)402 void TErrorResult<CleanupPolicy>::SetPendingGenericErrorException(
403     JSContext* cx) {
404   AssertInOwningThread();
405   MOZ_ASSERT(!IsErrorWithMessage());
406   MOZ_ASSERT(!IsJSException());
407   MOZ_ASSERT(!IsDOMException());
408   dom::Throw(cx, ErrorCode());
409   mResult = NS_OK;
410 }
411 
412 template <typename CleanupPolicy>
operator =(TErrorResult<CleanupPolicy> && aRHS)413 TErrorResult<CleanupPolicy>& TErrorResult<CleanupPolicy>::operator=(
414     TErrorResult<CleanupPolicy>&& aRHS) {
415   AssertInOwningThread();
416   aRHS.AssertInOwningThread();
417   // Clear out any union members we may have right now, before we
418   // start writing to it.
419   ClearUnionData();
420 
421 #ifdef DEBUG
422   mMightHaveUnreportedJSException = aRHS.mMightHaveUnreportedJSException;
423   aRHS.mMightHaveUnreportedJSException = false;
424 #endif
425   if (aRHS.IsErrorWithMessage()) {
426     mMessage = aRHS.mMessage;
427     aRHS.mMessage = nullptr;
428   } else if (aRHS.IsJSException()) {
429     JSContext* cx = dom::danger::GetJSContext();
430     MOZ_ASSERT(cx);
431     mJSException.asValueRef().setUndefined();
432     if (!js::AddRawValueRoot(cx, &mJSException.asValueRef(),
433                              "TErrorResult::mJSException")) {
434       MOZ_CRASH("Could not root mJSException, we're about to OOM");
435     }
436     mJSException = aRHS.mJSException;
437     aRHS.mJSException.asValueRef().setUndefined();
438     js::RemoveRawValueRoot(cx, &aRHS.mJSException.asValueRef());
439   } else if (aRHS.IsDOMException()) {
440     mDOMExceptionInfo = aRHS.mDOMExceptionInfo;
441     aRHS.mDOMExceptionInfo = nullptr;
442   } else {
443     // Null out the union on both sides for hygiene purposes.
444     mMessage = aRHS.mMessage = nullptr;
445   }
446 
447 #ifdef DEBUG
448   mUnionState = aRHS.mUnionState;
449   aRHS.mUnionState = HasNothing;
450 #endif  // DEBUG
451 
452   // Note: It's important to do this last, since this affects the condition
453   // checks above!
454   mResult = aRHS.mResult;
455   aRHS.mResult = NS_OK;
456   return *this;
457 }
458 
459 template <typename CleanupPolicy>
CloneTo(TErrorResult & aRv) const460 void TErrorResult<CleanupPolicy>::CloneTo(TErrorResult& aRv) const {
461   AssertInOwningThread();
462   aRv.AssertInOwningThread();
463 
464   aRv.ClearUnionData();
465   aRv.mResult = mResult;
466 #ifdef DEBUG
467   aRv.mMightHaveUnreportedJSException = mMightHaveUnreportedJSException;
468 #endif
469 
470   if (IsErrorWithMessage()) {
471 #ifdef DEBUG
472     aRv.mUnionState = HasMessage;
473 #endif
474     aRv.mMessage = new Message();
475     aRv.mMessage->mArgs = mMessage->mArgs;
476     aRv.mMessage->mErrorNumber = mMessage->mErrorNumber;
477   } else if (IsDOMException()) {
478 #ifdef DEBUG
479     aRv.mUnionState = HasDOMExceptionInfo;
480 #endif
481     aRv.mDOMExceptionInfo = new DOMExceptionInfo(mDOMExceptionInfo->mRv,
482                                                  mDOMExceptionInfo->mMessage);
483   } else if (IsJSException()) {
484 #ifdef DEBUG
485     aRv.mUnionState = HasJSException;
486 #endif
487     JSContext* cx = dom::danger::GetJSContext();
488     JS::Rooted<JS::Value> exception(cx, mJSException.asValueRef());
489     aRv.ThrowJSException(cx, exception);
490   }
491 }
492 
493 template <typename CleanupPolicy>
SuppressException()494 void TErrorResult<CleanupPolicy>::SuppressException() {
495   AssertInOwningThread();
496   WouldReportJSException();
497   ClearUnionData();
498   // We don't use AssignErrorCode, because we want to override existing error
499   // states, which AssignErrorCode is not allowed to do.
500   mResult = NS_OK;
501 }
502 
503 template <typename CleanupPolicy>
SetPendingException(JSContext * cx)504 void TErrorResult<CleanupPolicy>::SetPendingException(JSContext* cx) {
505   AssertInOwningThread();
506   if (IsUncatchableException()) {
507     // Nuke any existing exception on cx, to make sure we're uncatchable.
508     JS_ClearPendingException(cx);
509     // Don't do any reporting.  Just return, to create an
510     // uncatchable exception.
511     mResult = NS_OK;
512     return;
513   }
514   if (IsJSContextException()) {
515     // Whatever we need to throw is on the JSContext already.
516     MOZ_ASSERT(JS_IsExceptionPending(cx));
517     mResult = NS_OK;
518     return;
519   }
520   if (IsErrorWithMessage()) {
521     SetPendingExceptionWithMessage(cx);
522     return;
523   }
524   if (IsJSException()) {
525     SetPendingJSException(cx);
526     return;
527   }
528   if (IsDOMException()) {
529     SetPendingDOMException(cx);
530     return;
531   }
532   SetPendingGenericErrorException(cx);
533 }
534 
535 template <typename CleanupPolicy>
StealExceptionFromJSContext(JSContext * cx)536 void TErrorResult<CleanupPolicy>::StealExceptionFromJSContext(JSContext* cx) {
537   AssertInOwningThread();
538   MOZ_ASSERT(mMightHaveUnreportedJSException,
539              "Why didn't you tell us you planned to throw a JS exception?");
540 
541   JS::Rooted<JS::Value> exn(cx);
542   if (!JS_GetPendingException(cx, &exn)) {
543     ThrowUncatchableException();
544     return;
545   }
546 
547   ThrowJSException(cx, exn);
548   JS_ClearPendingException(cx);
549 }
550 
551 template <typename CleanupPolicy>
NoteJSContextException(JSContext * aCx)552 void TErrorResult<CleanupPolicy>::NoteJSContextException(JSContext* aCx) {
553   AssertInOwningThread();
554   if (JS_IsExceptionPending(aCx)) {
555     mResult = NS_ERROR_INTERNAL_ERRORRESULT_EXCEPTION_ON_JSCONTEXT;
556   } else {
557     mResult = NS_ERROR_UNCATCHABLE_EXCEPTION;
558   }
559 }
560 
561 template class TErrorResult<JustAssertCleanupPolicy>;
562 template class TErrorResult<AssertAndSuppressCleanupPolicy>;
563 template class TErrorResult<JustSuppressCleanupPolicy>;
564 
565 }  // namespace binding_danger
566 
567 namespace dom {
568 
DefineConstants(JSContext * cx,JS::Handle<JSObject * > obj,const ConstantSpec * cs)569 bool DefineConstants(JSContext* cx, JS::Handle<JSObject*> obj,
570                      const ConstantSpec* cs) {
571   JS::Rooted<JS::Value> value(cx);
572   for (; cs->name; ++cs) {
573     value = cs->value;
574     bool ok = JS_DefineProperty(
575         cx, obj, cs->name, value,
576         JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
577     if (!ok) {
578       return false;
579     }
580   }
581   return true;
582 }
583 
Define(JSContext * cx,JS::Handle<JSObject * > obj,const JSFunctionSpec * spec)584 static inline bool Define(JSContext* cx, JS::Handle<JSObject*> obj,
585                           const JSFunctionSpec* spec) {
586   return JS_DefineFunctions(cx, obj, spec);
587 }
Define(JSContext * cx,JS::Handle<JSObject * > obj,const JSPropertySpec * spec)588 static inline bool Define(JSContext* cx, JS::Handle<JSObject*> obj,
589                           const JSPropertySpec* spec) {
590   return JS_DefineProperties(cx, obj, spec);
591 }
Define(JSContext * cx,JS::Handle<JSObject * > obj,const ConstantSpec * spec)592 static inline bool Define(JSContext* cx, JS::Handle<JSObject*> obj,
593                           const ConstantSpec* spec) {
594   return DefineConstants(cx, obj, spec);
595 }
596 
597 template <typename T>
DefinePrefable(JSContext * cx,JS::Handle<JSObject * > obj,const Prefable<T> * props)598 bool DefinePrefable(JSContext* cx, JS::Handle<JSObject*> obj,
599                     const Prefable<T>* props) {
600   MOZ_ASSERT(props);
601   MOZ_ASSERT(props->specs);
602   do {
603     // Define if enabled
604     if (props->isEnabled(cx, obj)) {
605       if (!Define(cx, obj, props->specs)) {
606         return false;
607       }
608     }
609   } while ((++props)->specs);
610   return true;
611 }
612 
DefineUnforgeableMethods(JSContext * cx,JS::Handle<JSObject * > obj,const Prefable<const JSFunctionSpec> * props)613 bool DefineUnforgeableMethods(JSContext* cx, JS::Handle<JSObject*> obj,
614                               const Prefable<const JSFunctionSpec>* props) {
615   return DefinePrefable(cx, obj, props);
616 }
617 
DefineUnforgeableAttributes(JSContext * cx,JS::Handle<JSObject * > obj,const Prefable<const JSPropertySpec> * props)618 bool DefineUnforgeableAttributes(JSContext* cx, JS::Handle<JSObject*> obj,
619                                  const Prefable<const JSPropertySpec>* props) {
620   return DefinePrefable(cx, obj, props);
621 }
622 
623 // We should use JSFunction objects for interface objects, but we need a custom
624 // hasInstance hook because we have new interface objects on prototype chains of
625 // old (XPConnect-based) bindings. We also need Xrays and arbitrary numbers of
626 // reserved slots (e.g. for named constructors).  So we define a custom
627 // funToString ObjectOps member for interface objects.
InterfaceObjectToString(JSContext * aCx,JS::Handle<JSObject * > aObject,bool)628 JSString* InterfaceObjectToString(JSContext* aCx, JS::Handle<JSObject*> aObject,
629                                   bool /* isToSource */) {
630   const js::Class* clasp = js::GetObjectClass(aObject);
631   MOZ_ASSERT(IsDOMIfaceAndProtoClass(clasp));
632 
633   const DOMIfaceAndProtoJSClass* ifaceAndProtoJSClass =
634       DOMIfaceAndProtoJSClass::FromJSClass(clasp);
635   return JS_NewStringCopyZ(aCx, ifaceAndProtoJSClass->mToString);
636 }
637 
Constructor(JSContext * cx,unsigned argc,JS::Value * vp)638 bool Constructor(JSContext* cx, unsigned argc, JS::Value* vp) {
639   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
640   const JS::Value& v = js::GetFunctionNativeReserved(
641       &args.callee(), CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT);
642   const JSNativeHolder* nativeHolder =
643       static_cast<const JSNativeHolder*>(v.toPrivate());
644   return (nativeHolder->mNative)(cx, argc, vp);
645 }
646 
CreateConstructor(JSContext * cx,JS::Handle<JSObject * > global,const char * name,const JSNativeHolder * nativeHolder,unsigned ctorNargs)647 static JSObject* CreateConstructor(JSContext* cx, JS::Handle<JSObject*> global,
648                                    const char* name,
649                                    const JSNativeHolder* nativeHolder,
650                                    unsigned ctorNargs) {
651   JSFunction* fun = js::NewFunctionWithReserved(cx, Constructor, ctorNargs,
652                                                 JSFUN_CONSTRUCTOR, name);
653   if (!fun) {
654     return nullptr;
655   }
656 
657   JSObject* constructor = JS_GetFunctionObject(fun);
658   js::SetFunctionNativeReserved(
659       constructor, CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT,
660       js::PrivateValue(const_cast<JSNativeHolder*>(nativeHolder)));
661   return constructor;
662 }
663 
DefineConstructor(JSContext * cx,JS::Handle<JSObject * > global,const char * name,JS::Handle<JSObject * > constructor)664 static bool DefineConstructor(JSContext* cx, JS::Handle<JSObject*> global,
665                               const char* name,
666                               JS::Handle<JSObject*> constructor) {
667   bool alreadyDefined;
668   if (!JS_AlreadyHasOwnProperty(cx, global, name, &alreadyDefined)) {
669     return false;
670   }
671 
672   // This is Enumerable: False per spec.
673   return alreadyDefined ||
674          JS_DefineProperty(cx, global, name, constructor, JSPROP_RESOLVING);
675 }
676 
CreateInterfaceObject(JSContext * cx,JS::Handle<JSObject * > global,JS::Handle<JSObject * > constructorProto,const js::Class * constructorClass,unsigned ctorNargs,const NamedConstructor * namedConstructors,JS::Handle<JSObject * > proto,const NativeProperties * properties,const NativeProperties * chromeOnlyProperties,const char * name,bool defineOnGlobal)677 static JSObject* CreateInterfaceObject(
678     JSContext* cx, JS::Handle<JSObject*> global,
679     JS::Handle<JSObject*> constructorProto, const js::Class* constructorClass,
680     unsigned ctorNargs, const NamedConstructor* namedConstructors,
681     JS::Handle<JSObject*> proto, const NativeProperties* properties,
682     const NativeProperties* chromeOnlyProperties, const char* name,
683     bool defineOnGlobal) {
684   JS::Rooted<JSObject*> constructor(cx);
685   MOZ_ASSERT(constructorProto);
686   MOZ_ASSERT(constructorClass);
687   constructor = JS_NewObjectWithGivenProto(cx, Jsvalify(constructorClass),
688                                            constructorProto);
689   if (!constructor) {
690     return nullptr;
691   }
692 
693   if (!JS_DefineProperty(cx, constructor, "length", ctorNargs,
694                          JSPROP_READONLY)) {
695     return nullptr;
696   }
697 
698   // Might as well intern, since we're going to need an atomized
699   // version of name anyway when we stick our constructor on the
700   // global.
701   JS::Rooted<JSString*> nameStr(cx, JS_AtomizeAndPinString(cx, name));
702   if (!nameStr) {
703     return nullptr;
704   }
705 
706   if (!JS_DefineProperty(cx, constructor, "name", nameStr, JSPROP_READONLY)) {
707     return nullptr;
708   }
709 
710   if (DOMIfaceAndProtoJSClass::FromJSClass(constructorClass)
711           ->wantsInterfaceHasInstance) {
712     JS::Rooted<jsid> hasInstanceId(cx, SYMBOL_TO_JSID(JS::GetWellKnownSymbol(
713                                            cx, JS::SymbolCode::hasInstance)));
714     if (!JS_DefineFunctionById(
715             cx, constructor, hasInstanceId, InterfaceHasInstance, 1,
716             // Flags match those of Function[Symbol.hasInstance]
717             JSPROP_READONLY | JSPROP_PERMANENT)) {
718       return nullptr;
719     }
720   }
721 
722   if (properties) {
723     if (properties->HasStaticMethods() &&
724         !DefinePrefable(cx, constructor, properties->StaticMethods())) {
725       return nullptr;
726     }
727 
728     if (properties->HasStaticAttributes() &&
729         !DefinePrefable(cx, constructor, properties->StaticAttributes())) {
730       return nullptr;
731     }
732 
733     if (properties->HasConstants() &&
734         !DefinePrefable(cx, constructor, properties->Constants())) {
735       return nullptr;
736     }
737   }
738 
739   if (chromeOnlyProperties) {
740     if (chromeOnlyProperties->HasStaticMethods() &&
741         !DefinePrefable(cx, constructor,
742                         chromeOnlyProperties->StaticMethods())) {
743       return nullptr;
744     }
745 
746     if (chromeOnlyProperties->HasStaticAttributes() &&
747         !DefinePrefable(cx, constructor,
748                         chromeOnlyProperties->StaticAttributes())) {
749       return nullptr;
750     }
751 
752     if (chromeOnlyProperties->HasConstants() &&
753         !DefinePrefable(cx, constructor, chromeOnlyProperties->Constants())) {
754       return nullptr;
755     }
756   }
757 
758   if (proto && !JS_LinkConstructorAndPrototype(cx, constructor, proto)) {
759     return nullptr;
760   }
761 
762   if (defineOnGlobal && !DefineConstructor(cx, global, name, constructor)) {
763     return nullptr;
764   }
765 
766   if (namedConstructors) {
767     int namedConstructorSlot = DOM_INTERFACE_SLOTS_BASE;
768     while (namedConstructors->mName) {
769       JS::Rooted<JSObject*> namedConstructor(
770           cx, CreateConstructor(cx, global, namedConstructors->mName,
771                                 &namedConstructors->mHolder,
772                                 namedConstructors->mNargs));
773       if (!namedConstructor ||
774           !JS_DefineProperty(cx, namedConstructor, "prototype", proto,
775                              JSPROP_PERMANENT | JSPROP_READONLY) ||
776           (defineOnGlobal &&
777            !DefineConstructor(cx, global, namedConstructors->mName,
778                               namedConstructor))) {
779         return nullptr;
780       }
781       js::SetReservedSlot(constructor, namedConstructorSlot++,
782                           JS::ObjectValue(*namedConstructor));
783       ++namedConstructors;
784     }
785   }
786 
787   return constructor;
788 }
789 
CreateInterfacePrototypeObject(JSContext * cx,JS::Handle<JSObject * > global,JS::Handle<JSObject * > parentProto,const js::Class * protoClass,const NativeProperties * properties,const NativeProperties * chromeOnlyProperties,const char * const * unscopableNames,bool isGlobal)790 static JSObject* CreateInterfacePrototypeObject(
791     JSContext* cx, JS::Handle<JSObject*> global,
792     JS::Handle<JSObject*> parentProto, const js::Class* protoClass,
793     const NativeProperties* properties,
794     const NativeProperties* chromeOnlyProperties,
795     const char* const* unscopableNames, bool isGlobal) {
796   JS::Rooted<JSObject*> ourProto(
797       cx, JS_NewObjectWithUniqueType(cx, Jsvalify(protoClass), parentProto));
798   if (!ourProto ||
799       // We don't try to define properties on the global's prototype; those
800       // properties go on the global itself.
801       (!isGlobal &&
802        !DefineProperties(cx, ourProto, properties, chromeOnlyProperties))) {
803     return nullptr;
804   }
805 
806   if (unscopableNames) {
807     JS::Rooted<JSObject*> unscopableObj(
808         cx, JS_NewObjectWithGivenProto(cx, nullptr, nullptr));
809     if (!unscopableObj) {
810       return nullptr;
811     }
812 
813     for (; *unscopableNames; ++unscopableNames) {
814       if (!JS_DefineProperty(cx, unscopableObj, *unscopableNames,
815                              JS::TrueHandleValue, JSPROP_ENUMERATE)) {
816         return nullptr;
817       }
818     }
819 
820     JS::Rooted<jsid> unscopableId(cx, SYMBOL_TO_JSID(JS::GetWellKnownSymbol(
821                                           cx, JS::SymbolCode::unscopables)));
822     // Readonly and non-enumerable to match Array.prototype.
823     if (!JS_DefinePropertyById(cx, ourProto, unscopableId, unscopableObj,
824                                JSPROP_READONLY)) {
825       return nullptr;
826     }
827   }
828 
829   return ourProto;
830 }
831 
DefineProperties(JSContext * cx,JS::Handle<JSObject * > obj,const NativeProperties * properties,const NativeProperties * chromeOnlyProperties)832 bool DefineProperties(JSContext* cx, JS::Handle<JSObject*> obj,
833                       const NativeProperties* properties,
834                       const NativeProperties* chromeOnlyProperties) {
835   if (properties) {
836     if (properties->HasMethods() &&
837         !DefinePrefable(cx, obj, properties->Methods())) {
838       return false;
839     }
840 
841     if (properties->HasAttributes() &&
842         !DefinePrefable(cx, obj, properties->Attributes())) {
843       return false;
844     }
845 
846     if (properties->HasConstants() &&
847         !DefinePrefable(cx, obj, properties->Constants())) {
848       return false;
849     }
850   }
851 
852   if (chromeOnlyProperties) {
853     if (chromeOnlyProperties->HasMethods() &&
854         !DefinePrefable(cx, obj, chromeOnlyProperties->Methods())) {
855       return false;
856     }
857 
858     if (chromeOnlyProperties->HasAttributes() &&
859         !DefinePrefable(cx, obj, chromeOnlyProperties->Attributes())) {
860       return false;
861     }
862 
863     if (chromeOnlyProperties->HasConstants() &&
864         !DefinePrefable(cx, obj, chromeOnlyProperties->Constants())) {
865       return false;
866     }
867   }
868 
869   return true;
870 }
871 
CreateInterfaceObjects(JSContext * cx,JS::Handle<JSObject * > global,JS::Handle<JSObject * > protoProto,const js::Class * protoClass,JS::Heap<JSObject * > * protoCache,JS::Handle<JSObject * > constructorProto,const js::Class * constructorClass,unsigned ctorNargs,const NamedConstructor * namedConstructors,JS::Heap<JSObject * > * constructorCache,const NativeProperties * properties,const NativeProperties * chromeOnlyProperties,const char * name,bool defineOnGlobal,const char * const * unscopableNames,bool isGlobal)872 void CreateInterfaceObjects(
873     JSContext* cx, JS::Handle<JSObject*> global,
874     JS::Handle<JSObject*> protoProto, const js::Class* protoClass,
875     JS::Heap<JSObject*>* protoCache, JS::Handle<JSObject*> constructorProto,
876     const js::Class* constructorClass, unsigned ctorNargs,
877     const NamedConstructor* namedConstructors,
878     JS::Heap<JSObject*>* constructorCache, const NativeProperties* properties,
879     const NativeProperties* chromeOnlyProperties, const char* name,
880     bool defineOnGlobal, const char* const* unscopableNames, bool isGlobal) {
881   MOZ_ASSERT(protoClass || constructorClass, "Need at least one class!");
882   MOZ_ASSERT(
883       !((properties &&
884          (properties->HasMethods() || properties->HasAttributes())) ||
885         (chromeOnlyProperties && (chromeOnlyProperties->HasMethods() ||
886                                   chromeOnlyProperties->HasAttributes()))) ||
887           protoClass,
888       "Methods or properties but no protoClass!");
889   MOZ_ASSERT(!((properties && (properties->HasStaticMethods() ||
890                                properties->HasStaticAttributes())) ||
891                (chromeOnlyProperties &&
892                 (chromeOnlyProperties->HasStaticMethods() ||
893                  chromeOnlyProperties->HasStaticAttributes()))) ||
894                  constructorClass,
895              "Static methods but no constructorClass!");
896   MOZ_ASSERT(bool(name) == bool(constructorClass),
897              "Must have name precisely when we have an interface object");
898   MOZ_ASSERT(!protoClass == !protoCache,
899              "If, and only if, there is an interface prototype object we need "
900              "to cache it");
901   MOZ_ASSERT(bool(constructorClass) == bool(constructorCache),
902              "If, and only if, there is an interface object we need to cache "
903              "it");
904   MOZ_ASSERT(constructorProto || !constructorClass,
905              "Must have a constructor proto if we plan to create a constructor "
906              "object");
907 
908   JS::Rooted<JSObject*> proto(cx);
909   if (protoClass) {
910     proto = CreateInterfacePrototypeObject(cx, global, protoProto, protoClass,
911                                            properties, chromeOnlyProperties,
912                                            unscopableNames, isGlobal);
913     if (!proto) {
914       return;
915     }
916 
917     *protoCache = proto;
918   } else {
919     MOZ_ASSERT(!proto);
920   }
921 
922   JSObject* interface;
923   if (constructorClass) {
924     interface =
925         CreateInterfaceObject(cx, global, constructorProto, constructorClass,
926                               ctorNargs, namedConstructors, proto, properties,
927                               chromeOnlyProperties, name, defineOnGlobal);
928     if (!interface) {
929       if (protoCache) {
930         // If we fail we need to make sure to clear the value of protoCache we
931         // set above.
932         *protoCache = nullptr;
933       }
934       return;
935     }
936     *constructorCache = interface;
937   }
938 }
939 
NativeInterface2JSObjectAndThrowIfFailed(JSContext * aCx,JS::Handle<JSObject * > aScope,JS::MutableHandle<JS::Value> aRetval,xpcObjectHelper & aHelper,const nsIID * aIID,bool aAllowNativeWrapper)940 bool NativeInterface2JSObjectAndThrowIfFailed(
941     JSContext* aCx, JS::Handle<JSObject*> aScope,
942     JS::MutableHandle<JS::Value> aRetval, xpcObjectHelper& aHelper,
943     const nsIID* aIID, bool aAllowNativeWrapper) {
944   js::AssertSameCompartment(aCx, aScope);
945   nsresult rv;
946   // Inline some logic from XPCConvert::NativeInterfaceToJSObject that we need
947   // on all threads.
948   nsWrapperCache* cache = aHelper.GetWrapperCache();
949 
950   if (cache && cache->IsDOMBinding()) {
951     JS::Rooted<JSObject*> obj(aCx, cache->GetWrapper());
952     if (!obj) {
953       obj = cache->WrapObject(aCx, nullptr);
954     }
955 
956     if (obj && aAllowNativeWrapper && !JS_WrapObject(aCx, &obj)) {
957       return false;
958     }
959 
960     if (obj) {
961       aRetval.setObject(*obj);
962       return true;
963     }
964   }
965 
966   MOZ_ASSERT(NS_IsMainThread());
967 
968   if (!XPCConvert::NativeInterface2JSObject(aRetval, aHelper, aIID,
969                                             aAllowNativeWrapper, &rv)) {
970     // I can't tell if NativeInterface2JSObject throws JS exceptions
971     // or not.  This is a sloppy stab at the right semantics; the
972     // method really ought to be fixed to behave consistently.
973     if (!JS_IsExceptionPending(aCx)) {
974       Throw(aCx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED);
975     }
976     return false;
977   }
978   return true;
979 }
980 
TryPreserveWrapper(JSObject * obj)981 bool TryPreserveWrapper(JSObject* obj) {
982   MOZ_ASSERT(IsDOMObject(obj));
983 
984   if (nsISupports* native = UnwrapDOMObjectToISupports(obj)) {
985     nsWrapperCache* cache = nullptr;
986     CallQueryInterface(native, &cache);
987     if (cache) {
988       cache->PreserveWrapper(native);
989     }
990     return true;
991   }
992 
993   // If this DOMClass is not cycle collected, then it isn't wrappercached,
994   // so it does not need to be preserved. If it is cycle collected, then
995   // we can't tell if it is wrappercached or not, so we just return false.
996   const DOMJSClass* domClass = GetDOMClass(obj);
997   return domClass && !domClass->mParticipant;
998 }
999 
1000 // Can only be called with a DOM JSClass.
InstanceClassHasProtoAtDepth(const js::Class * clasp,uint32_t protoID,uint32_t depth)1001 bool InstanceClassHasProtoAtDepth(const js::Class* clasp, uint32_t protoID,
1002                                   uint32_t depth) {
1003   const DOMJSClass* domClass = DOMJSClass::FromJSClass(clasp);
1004   return static_cast<uint32_t>(domClass->mInterfaceChain[depth]) == protoID;
1005 }
1006 
1007 // Only set allowNativeWrapper to false if you really know you need it, if in
1008 // 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)1009 bool XPCOMObjectToJsval(JSContext* cx, JS::Handle<JSObject*> scope,
1010                         xpcObjectHelper& helper, const nsIID* iid,
1011                         bool allowNativeWrapper,
1012                         JS::MutableHandle<JS::Value> rval) {
1013   if (!NativeInterface2JSObjectAndThrowIfFailed(cx, scope, rval, helper, iid,
1014                                                 allowNativeWrapper)) {
1015     return false;
1016   }
1017 
1018 #ifdef DEBUG
1019   JSObject* jsobj = rval.toObjectOrNull();
1020   if (jsobj && js::GetGlobalForObjectCrossCompartment(jsobj) == jsobj) {
1021     NS_ASSERTION(js::GetObjectClass(jsobj)->flags & JSCLASS_IS_GLOBAL,
1022                  "Why did we recreate this wrapper?");
1023   }
1024 #endif
1025 
1026   return true;
1027 }
1028 
VariantToJsval(JSContext * aCx,nsIVariant * aVariant,JS::MutableHandle<JS::Value> aRetval)1029 bool VariantToJsval(JSContext* aCx, nsIVariant* aVariant,
1030                     JS::MutableHandle<JS::Value> aRetval) {
1031   nsresult rv;
1032   if (!XPCVariant::VariantDataToJS(aVariant, &rv, aRetval)) {
1033     // Does it throw?  Who knows
1034     if (!JS_IsExceptionPending(aCx)) {
1035       Throw(aCx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED);
1036     }
1037     return false;
1038   }
1039 
1040   return true;
1041 }
1042 
CompareIdsAtIndices(const void * aElement1,const void * aElement2,void * aClosure)1043 static int CompareIdsAtIndices(const void* aElement1, const void* aElement2,
1044                                void* aClosure) {
1045   const uint16_t index1 = *static_cast<const uint16_t*>(aElement1);
1046   const uint16_t index2 = *static_cast<const uint16_t*>(aElement2);
1047   const PropertyInfo* infos = static_cast<PropertyInfo*>(aClosure);
1048 
1049   MOZ_ASSERT(JSID_BITS(infos[index1].id) != JSID_BITS(infos[index2].id));
1050 
1051   return JSID_BITS(infos[index1].id) < JSID_BITS(infos[index2].id) ? -1 : 1;
1052 }
1053 
1054 template <typename SpecT>
InitIdsInternal(JSContext * cx,const Prefable<SpecT> * pref,PropertyInfo * infos,PropertyType type)1055 static bool InitIdsInternal(JSContext* cx, const Prefable<SpecT>* pref,
1056                             PropertyInfo* infos, PropertyType type) {
1057   MOZ_ASSERT(pref);
1058   MOZ_ASSERT(pref->specs);
1059 
1060   // Index of the Prefable that contains the id for the current PropertyInfo.
1061   uint32_t prefIndex = 0;
1062 
1063   do {
1064     // We ignore whether the set of ids is enabled and just intern all the IDs,
1065     // because this is only done once per application runtime.
1066     const SpecT* spec = pref->specs;
1067     // Index of the property/function/constant spec for our current PropertyInfo
1068     // in the "specs" array of the relevant Prefable.
1069     uint32_t specIndex = 0;
1070     do {
1071       if (!JS::PropertySpecNameToPermanentId(cx, spec->name, &infos->id)) {
1072         return false;
1073       }
1074       infos->type = type;
1075       infos->prefIndex = prefIndex;
1076       infos->specIndex = specIndex++;
1077       ++infos;
1078     } while ((++spec)->name);
1079     ++prefIndex;
1080   } while ((++pref)->specs);
1081 
1082   return true;
1083 }
1084 
1085 #define INIT_IDS_IF_DEFINED(TypeName)                                 \
1086   {                                                                   \
1087     if (nativeProperties->Has##TypeName##s() &&                       \
1088         !InitIdsInternal(cx, nativeProperties->TypeName##s(),         \
1089                          nativeProperties->TypeName##PropertyInfos(), \
1090                          e##TypeName)) {                              \
1091       return false;                                                   \
1092     }                                                                 \
1093   }
1094 
InitIds(JSContext * cx,const NativeProperties * nativeProperties)1095 bool InitIds(JSContext* cx, const NativeProperties* nativeProperties) {
1096   INIT_IDS_IF_DEFINED(StaticMethod);
1097   INIT_IDS_IF_DEFINED(StaticAttribute);
1098   INIT_IDS_IF_DEFINED(Method);
1099   INIT_IDS_IF_DEFINED(Attribute);
1100   INIT_IDS_IF_DEFINED(UnforgeableMethod);
1101   INIT_IDS_IF_DEFINED(UnforgeableAttribute);
1102   INIT_IDS_IF_DEFINED(Constant);
1103 
1104   // Initialize and sort the index array.
1105   uint16_t* indices = nativeProperties->sortedPropertyIndices;
1106   for (unsigned int i = 0; i < nativeProperties->propertyInfoCount; ++i) {
1107     indices[i] = i;
1108   }
1109   // CompareIdsAtIndices() doesn't actually modify the PropertyInfo array, so
1110   // the const_cast here is OK in spite of the signature of NS_QuickSort().
1111   NS_QuickSort(indices, nativeProperties->propertyInfoCount, sizeof(uint16_t),
1112                CompareIdsAtIndices,
1113                const_cast<PropertyInfo*>(nativeProperties->PropertyInfos()));
1114 
1115   return true;
1116 }
1117 
1118 #undef INIT_IDS_IF_DEFINED
1119 
QueryInterface(JSContext * cx,unsigned argc,JS::Value * vp)1120 bool QueryInterface(JSContext* cx, unsigned argc, JS::Value* vp) {
1121   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
1122   JS::Rooted<JS::Value> thisv(cx, JS_THIS(cx, vp));
1123   if (thisv.isNull()) return false;
1124 
1125   // Get the object. It might be a security wrapper, in which case we do a
1126   // checked unwrap.
1127   JS::Rooted<JSObject*> origObj(cx, &thisv.toObject());
1128   JS::Rooted<JSObject*> obj(cx,
1129                             js::CheckedUnwrap(origObj,
1130                                               /* stopAtWindowProxy = */ false));
1131   if (!obj) {
1132     JS_ReportErrorASCII(cx, "Permission denied to access object");
1133     return false;
1134   }
1135 
1136   // Switch this to UnwrapDOMObjectToISupports once our global objects are
1137   // using new bindings.
1138   nsCOMPtr<nsISupports> native;
1139   UnwrapArg<nsISupports>(cx, obj, getter_AddRefs(native));
1140   if (!native) {
1141     return Throw(cx, NS_ERROR_FAILURE);
1142   }
1143 
1144   if (argc < 1) {
1145     return Throw(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);
1146   }
1147 
1148   if (!args[0].isObject()) {
1149     return Throw(cx, NS_ERROR_XPC_BAD_CONVERT_JS);
1150   }
1151 
1152   nsCOMPtr<nsIJSID> iid;
1153   obj = &args[0].toObject();
1154   if (NS_FAILED(UnwrapArg<nsIJSID>(cx, obj, getter_AddRefs(iid)))) {
1155     return Throw(cx, NS_ERROR_XPC_BAD_CONVERT_JS);
1156   }
1157   MOZ_ASSERT(iid);
1158 
1159   if (iid->GetID()->Equals(NS_GET_IID(nsIClassInfo))) {
1160     nsresult rv;
1161     nsCOMPtr<nsIClassInfo> ci = do_QueryInterface(native, &rv);
1162     if (NS_FAILED(rv)) {
1163       return Throw(cx, rv);
1164     }
1165 
1166     return WrapObject(cx, ci, &NS_GET_IID(nsIClassInfo), args.rval());
1167   }
1168 
1169   nsCOMPtr<nsISupports> unused;
1170   nsresult rv = native->QueryInterface(*iid->GetID(), getter_AddRefs(unused));
1171   if (NS_FAILED(rv)) {
1172     return Throw(cx, rv);
1173   }
1174 
1175   *vp = thisv;
1176   return true;
1177 }
1178 
GetInterfaceImpl(JSContext * aCx,nsIInterfaceRequestor * aRequestor,nsWrapperCache * aCache,nsIJSID * aIID,JS::MutableHandle<JS::Value> aRetval,ErrorResult & aError)1179 void GetInterfaceImpl(JSContext* aCx, nsIInterfaceRequestor* aRequestor,
1180                       nsWrapperCache* aCache, nsIJSID* aIID,
1181                       JS::MutableHandle<JS::Value> aRetval,
1182                       ErrorResult& aError) {
1183   const nsID* iid = aIID->GetID();
1184 
1185   RefPtr<nsISupports> result;
1186   aError = aRequestor->GetInterface(*iid, getter_AddRefs(result));
1187   if (aError.Failed()) {
1188     return;
1189   }
1190 
1191   if (!WrapObject(aCx, result, iid, aRetval)) {
1192     aError.Throw(NS_ERROR_FAILURE);
1193   }
1194 }
1195 
ThrowingConstructor(JSContext * cx,unsigned argc,JS::Value * vp)1196 bool ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp) {
1197   return ThrowErrorMessage(cx, MSG_ILLEGAL_CONSTRUCTOR);
1198 }
1199 
ThrowConstructorWithoutNew(JSContext * cx,const char * name)1200 bool ThrowConstructorWithoutNew(JSContext* cx, const char* name) {
1201   return ThrowErrorMessage(cx, MSG_CONSTRUCTOR_WITHOUT_NEW, name);
1202 }
1203 
GetNativePropertyHooksFromConstructorFunction(JS::Handle<JSObject * > obj)1204 inline const NativePropertyHooks* GetNativePropertyHooksFromConstructorFunction(
1205     JS::Handle<JSObject*> obj) {
1206   MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor));
1207   const JS::Value& v = js::GetFunctionNativeReserved(
1208       obj, CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT);
1209   const JSNativeHolder* nativeHolder =
1210       static_cast<const JSNativeHolder*>(v.toPrivate());
1211   return nativeHolder->mPropertyHooks;
1212 }
1213 
GetNativePropertyHooks(JSContext * cx,JS::Handle<JSObject * > obj,DOMObjectType & type)1214 inline const NativePropertyHooks* GetNativePropertyHooks(
1215     JSContext* cx, JS::Handle<JSObject*> obj, DOMObjectType& type) {
1216   const js::Class* clasp = js::GetObjectClass(obj);
1217 
1218   const DOMJSClass* domClass = GetDOMClass(clasp);
1219   if (domClass) {
1220     bool isGlobal = (clasp->flags & JSCLASS_DOM_GLOBAL) != 0;
1221     type = isGlobal ? eGlobalInstance : eInstance;
1222     return domClass->mNativeHooks;
1223   }
1224 
1225   if (JS_ObjectIsFunction(cx, obj)) {
1226     type = eInterface;
1227     return GetNativePropertyHooksFromConstructorFunction(obj);
1228   }
1229 
1230   MOZ_ASSERT(IsDOMIfaceAndProtoClass(js::GetObjectClass(obj)));
1231   const DOMIfaceAndProtoJSClass* ifaceAndProtoJSClass =
1232       DOMIfaceAndProtoJSClass::FromJSClass(js::GetObjectClass(obj));
1233   type = ifaceAndProtoJSClass->mType;
1234   return ifaceAndProtoJSClass->mNativeHooks;
1235 }
1236 
XrayCreateFunction(JSContext * cx,JS::Handle<JSObject * > wrapper,JSNativeWrapper native,unsigned nargs,JS::Handle<jsid> id)1237 static JSObject* XrayCreateFunction(JSContext* cx,
1238                                     JS::Handle<JSObject*> wrapper,
1239                                     JSNativeWrapper native, unsigned nargs,
1240                                     JS::Handle<jsid> id) {
1241   JSFunction* fun;
1242   if (JSID_IS_STRING(id)) {
1243     fun = js::NewFunctionByIdWithReserved(cx, native.op, nargs, 0, id);
1244   } else {
1245     // Can't pass this id (probably a symbol) to NewFunctionByIdWithReserved;
1246     // just use an empty name for lack of anything better.
1247     fun = js::NewFunctionWithReserved(cx, native.op, nargs, 0, nullptr);
1248   }
1249 
1250   if (!fun) {
1251     return nullptr;
1252   }
1253 
1254   SET_JITINFO(fun, native.info);
1255   JSObject* obj = JS_GetFunctionObject(fun);
1256   js::SetFunctionNativeReserved(obj, XRAY_DOM_FUNCTION_PARENT_WRAPPER_SLOT,
1257                                 JS::ObjectValue(*wrapper));
1258 #ifdef DEBUG
1259   js::SetFunctionNativeReserved(obj, XRAY_DOM_FUNCTION_NATIVE_SLOT_FOR_SELF,
1260                                 JS::ObjectValue(*obj));
1261 #endif
1262   return obj;
1263 }
1264 
1265 struct IdToIndexComparator {
1266   // The id we're searching for.
1267   const jsid& mId;
1268   // The list of ids we're searching in.
1269   const PropertyInfo* mInfos;
1270 
IdToIndexComparatormozilla::dom::IdToIndexComparator1271   explicit IdToIndexComparator(const jsid& aId, const PropertyInfo* aInfos)
1272       : mId(aId), mInfos(aInfos) {}
operator ()mozilla::dom::IdToIndexComparator1273   int operator()(const uint16_t aIndex) const {
1274     if (JSID_BITS(mId) == JSID_BITS(mInfos[aIndex].id)) {
1275       return 0;
1276     }
1277     return JSID_BITS(mId) < JSID_BITS(mInfos[aIndex].id) ? -1 : 1;
1278   }
1279 };
1280 
XrayFindOwnPropertyInfo(JSContext * cx,JS::Handle<jsid> id,const NativeProperties * nativeProperties)1281 static const PropertyInfo* XrayFindOwnPropertyInfo(
1282     JSContext* cx, JS::Handle<jsid> id,
1283     const NativeProperties* nativeProperties) {
1284   if (MOZ_UNLIKELY(nativeProperties->iteratorAliasMethodIndex >= 0) &&
1285       id == SYMBOL_TO_JSID(
1286                 JS::GetWellKnownSymbol(cx, JS::SymbolCode::iterator))) {
1287     return nativeProperties->MethodPropertyInfos() +
1288            nativeProperties->iteratorAliasMethodIndex;
1289   }
1290 
1291   size_t idx;
1292   const uint16_t* sortedPropertyIndices =
1293       nativeProperties->sortedPropertyIndices;
1294   const PropertyInfo* propertyInfos = nativeProperties->PropertyInfos();
1295 
1296   if (BinarySearchIf(sortedPropertyIndices, 0,
1297                      nativeProperties->propertyInfoCount,
1298                      IdToIndexComparator(id, propertyInfos), &idx)) {
1299     return propertyInfos + sortedPropertyIndices[idx];
1300   }
1301 
1302   return nullptr;
1303 }
1304 
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<JS::PropertyDescriptor> desc,bool & cacheOnHolder)1305 static bool XrayResolveAttribute(JSContext* cx, JS::Handle<JSObject*> wrapper,
1306                                  JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
1307                                  const Prefable<const JSPropertySpec>& pref,
1308                                  const JSPropertySpec& attrSpec,
1309                                  JS::MutableHandle<JS::PropertyDescriptor> desc,
1310                                  bool& cacheOnHolder) {
1311   if (!pref.isEnabled(cx, obj)) {
1312     return true;
1313   }
1314 
1315   cacheOnHolder = true;
1316 
1317   // Because of centralization, we need to make sure we fault in the JitInfos as
1318   // well. At present, until the JSAPI changes, the easiest way to do this is
1319   // wrap them up as functions ourselves.
1320   desc.setAttributes(attrSpec.flags);
1321   // They all have getters, so we can just make it.
1322   JS::Rooted<JSObject*> funobj(
1323       cx,
1324       XrayCreateFunction(cx, wrapper, attrSpec.accessors.getter.native, 0, id));
1325   if (!funobj) return false;
1326   desc.setGetterObject(funobj);
1327   desc.attributesRef() |= JSPROP_GETTER;
1328   if (attrSpec.accessors.setter.native.op) {
1329     // We have a setter! Make it.
1330     funobj = XrayCreateFunction(cx, wrapper, attrSpec.accessors.setter.native,
1331                                 1, id);
1332     if (!funobj) return false;
1333     desc.setSetterObject(funobj);
1334     desc.attributesRef() |= JSPROP_SETTER;
1335   } else {
1336     desc.setSetter(nullptr);
1337   }
1338   desc.object().set(wrapper);
1339   desc.value().setUndefined();
1340 
1341   return true;
1342 }
1343 
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<JS::PropertyDescriptor> desc,bool & cacheOnHolder)1344 static bool XrayResolveMethod(JSContext* cx, JS::Handle<JSObject*> wrapper,
1345                               JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
1346                               const Prefable<const JSFunctionSpec>& pref,
1347                               const JSFunctionSpec& methodSpec,
1348                               JS::MutableHandle<JS::PropertyDescriptor> desc,
1349                               bool& cacheOnHolder) {
1350   if (!pref.isEnabled(cx, obj)) {
1351     return true;
1352   }
1353 
1354   cacheOnHolder = true;
1355 
1356   JSObject* funobj;
1357   if (methodSpec.selfHostedName) {
1358     JSFunction* fun = JS::GetSelfHostedFunction(cx, methodSpec.selfHostedName,
1359                                                 id, methodSpec.nargs);
1360     if (!fun) {
1361       return false;
1362     }
1363     MOZ_ASSERT(!methodSpec.call.op,
1364                "Bad FunctionSpec declaration: non-null native");
1365     MOZ_ASSERT(!methodSpec.call.info,
1366                "Bad FunctionSpec declaration: non-null jitinfo");
1367     funobj = JS_GetFunctionObject(fun);
1368   } else {
1369     funobj =
1370         XrayCreateFunction(cx, wrapper, methodSpec.call, methodSpec.nargs, id);
1371     if (!funobj) {
1372       return false;
1373     }
1374   }
1375   desc.value().setObject(*funobj);
1376   desc.setAttributes(methodSpec.flags);
1377   desc.object().set(wrapper);
1378   desc.setSetter(nullptr);
1379   desc.setGetter(nullptr);
1380 
1381   return true;
1382 }
1383 
XrayResolveConstant(JSContext * cx,JS::Handle<JSObject * > wrapper,JS::Handle<JSObject * > obj,JS::Handle<jsid>,const Prefable<const ConstantSpec> & pref,const ConstantSpec & constantSpec,JS::MutableHandle<JS::PropertyDescriptor> desc,bool & cacheOnHolder)1384 static bool XrayResolveConstant(JSContext* cx, JS::Handle<JSObject*> wrapper,
1385                                 JS::Handle<JSObject*> obj, JS::Handle<jsid>,
1386                                 const Prefable<const ConstantSpec>& pref,
1387                                 const ConstantSpec& constantSpec,
1388                                 JS::MutableHandle<JS::PropertyDescriptor> desc,
1389                                 bool& cacheOnHolder) {
1390   if (!pref.isEnabled(cx, obj)) {
1391     return true;
1392   }
1393 
1394   cacheOnHolder = true;
1395 
1396   desc.setAttributes(JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
1397   desc.object().set(wrapper);
1398   desc.value().set(constantSpec.value);
1399 
1400   return true;
1401 }
1402 
1403 #define RESOLVE_CASE(PropType, SpecType, Resolver)                            \
1404   case e##PropType: {                                                         \
1405     MOZ_ASSERT(nativeProperties->Has##PropType##s());                         \
1406     const Prefable<const SpecType>& pref =                                    \
1407         nativeProperties->PropType##s()[propertyInfo.prefIndex];              \
1408     return Resolver(cx, wrapper, obj, id, pref,                               \
1409                     pref.specs[propertyInfo.specIndex], desc, cacheOnHolder); \
1410   }
1411 
XrayResolveProperty(JSContext * cx,JS::Handle<JSObject * > wrapper,JS::Handle<JSObject * > obj,JS::Handle<jsid> id,JS::MutableHandle<JS::PropertyDescriptor> desc,bool & cacheOnHolder,DOMObjectType type,const NativeProperties * nativeProperties,const PropertyInfo & propertyInfo)1412 static bool XrayResolveProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
1413                                 JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
1414                                 JS::MutableHandle<JS::PropertyDescriptor> desc,
1415                                 bool& cacheOnHolder, DOMObjectType type,
1416                                 const NativeProperties* nativeProperties,
1417                                 const PropertyInfo& propertyInfo) {
1418   MOZ_ASSERT(type != eGlobalInterfacePrototype);
1419 
1420   // Make sure we resolve for matched object type.
1421   switch (propertyInfo.type) {
1422     case eStaticMethod:
1423     case eStaticAttribute:
1424       if (type != eInterface) {
1425         return true;
1426       }
1427       break;
1428     case eMethod:
1429     case eAttribute:
1430       if (type != eGlobalInstance && type != eInterfacePrototype) {
1431         return true;
1432       }
1433       break;
1434     case eUnforgeableMethod:
1435     case eUnforgeableAttribute:
1436       if (!IsInstance(type)) {
1437         return true;
1438       }
1439       break;
1440     case eConstant:
1441       if (IsInstance(type)) {
1442         return true;
1443       }
1444       break;
1445   }
1446 
1447   switch (propertyInfo.type) {
1448     RESOLVE_CASE(StaticMethod, JSFunctionSpec, XrayResolveMethod)
1449     RESOLVE_CASE(StaticAttribute, JSPropertySpec, XrayResolveAttribute)
1450     RESOLVE_CASE(Method, JSFunctionSpec, XrayResolveMethod)
1451     RESOLVE_CASE(Attribute, JSPropertySpec, XrayResolveAttribute)
1452     RESOLVE_CASE(UnforgeableMethod, JSFunctionSpec, XrayResolveMethod)
1453     RESOLVE_CASE(UnforgeableAttribute, JSPropertySpec, XrayResolveAttribute)
1454     RESOLVE_CASE(Constant, ConstantSpec, XrayResolveConstant)
1455   }
1456 
1457   return true;
1458 }
1459 
1460 #undef RESOLVE_CASE
1461 
ResolvePrototypeOrConstructor(JSContext * cx,JS::Handle<JSObject * > wrapper,JS::Handle<JSObject * > obj,size_t protoAndIfaceCacheIndex,unsigned attrs,JS::MutableHandle<JS::PropertyDescriptor> desc,bool & cacheOnHolder)1462 static bool ResolvePrototypeOrConstructor(
1463     JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<JSObject*> obj,
1464     size_t protoAndIfaceCacheIndex, unsigned attrs,
1465     JS::MutableHandle<JS::PropertyDescriptor> desc, bool& cacheOnHolder) {
1466   JS::Rooted<JSObject*> global(cx, js::GetGlobalForObjectCrossCompartment(obj));
1467   {
1468     JSAutoCompartment ac(cx, global);
1469     ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(global);
1470     // This function is called when resolving the "constructor" and "prototype"
1471     // properties of Xrays for DOM prototypes and constructors respectively.
1472     // This means the relevant Xray exists, which means its _target_ exists.
1473     // And that means we managed to successfullly create the prototype or
1474     // constructor, respectively, and hence must have managed to create the
1475     // thing it's pointing to as well.  So our entry slot must exist.
1476     JSObject* protoOrIface =
1477         protoAndIfaceCache.EntrySlotMustExist(protoAndIfaceCacheIndex);
1478     MOZ_RELEASE_ASSERT(protoOrIface, "How can this object not exist?");
1479 
1480     cacheOnHolder = true;
1481 
1482     desc.object().set(wrapper);
1483     desc.setAttributes(attrs);
1484     desc.setGetter(nullptr);
1485     desc.setSetter(nullptr);
1486     desc.value().set(JS::ObjectValue(*protoOrIface));
1487   }
1488   return JS_WrapPropertyDescriptor(cx, desc);
1489 }
1490 
1491 #ifdef DEBUG
1492 
DEBUG_CheckXBLCallable(JSContext * cx,JSObject * obj)1493 static void DEBUG_CheckXBLCallable(JSContext* cx, JSObject* obj) {
1494   // In general, we shouldn't have cross-compartment wrappers here, because
1495   // we should be running in an XBL scope, and the content prototype should
1496   // contain wrappers to functions defined in the XBL scope. But if the node
1497   // has been adopted into another compartment, those prototypes will now point
1498   // to a different XBL scope (which is ok).
1499   MOZ_ASSERT_IF(js::IsCrossCompartmentWrapper(obj),
1500                 xpc::IsInContentXBLScope(js::UncheckedUnwrap(obj)));
1501   MOZ_ASSERT(JS::IsCallable(obj));
1502 }
1503 
DEBUG_CheckXBLLookup(JSContext * cx,JS::PropertyDescriptor * desc)1504 static void DEBUG_CheckXBLLookup(JSContext* cx, JS::PropertyDescriptor* desc) {
1505   if (!desc->obj) return;
1506   if (!desc->value.isUndefined()) {
1507     MOZ_ASSERT(desc->value.isObject());
1508     DEBUG_CheckXBLCallable(cx, &desc->value.toObject());
1509   }
1510   if (desc->getter) {
1511     MOZ_ASSERT(desc->attrs & JSPROP_GETTER);
1512     DEBUG_CheckXBLCallable(cx, JS_FUNC_TO_DATA_PTR(JSObject*, desc->getter));
1513   }
1514   if (desc->setter) {
1515     MOZ_ASSERT(desc->attrs & JSPROP_SETTER);
1516     DEBUG_CheckXBLCallable(cx, JS_FUNC_TO_DATA_PTR(JSObject*, desc->setter));
1517   }
1518 }
1519 #else
1520 #define DEBUG_CheckXBLLookup(a, b) \
1521   {}
1522 #endif
1523 
XrayResolveOwnProperty(JSContext * cx,JS::Handle<JSObject * > wrapper,JS::Handle<JSObject * > obj,JS::Handle<jsid> id,JS::MutableHandle<JS::PropertyDescriptor> desc,bool & cacheOnHolder)1524 /* static */ bool XrayResolveOwnProperty(
1525     JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<JSObject*> obj,
1526     JS::Handle<jsid> id, JS::MutableHandle<JS::PropertyDescriptor> desc,
1527     bool& cacheOnHolder) {
1528   cacheOnHolder = false;
1529 
1530   DOMObjectType type;
1531   const NativePropertyHooks* nativePropertyHooks =
1532       GetNativePropertyHooks(cx, obj, type);
1533   ResolveOwnProperty resolveOwnProperty =
1534       nativePropertyHooks->mResolveOwnProperty;
1535 
1536   if (type == eNamedPropertiesObject) {
1537     MOZ_ASSERT(!resolveOwnProperty,
1538                "Shouldn't have any Xray-visible properties");
1539     return true;
1540   }
1541 
1542   const NativePropertiesHolder& nativePropertiesHolder =
1543       nativePropertyHooks->mNativeProperties;
1544   const NativeProperties* nativeProperties = nullptr;
1545   const PropertyInfo* found = nullptr;
1546 
1547   if ((nativeProperties = nativePropertiesHolder.regular)) {
1548     found = XrayFindOwnPropertyInfo(cx, id, nativeProperties);
1549   }
1550   if (!found && (nativeProperties = nativePropertiesHolder.chromeOnly) &&
1551       xpc::AccessCheck::isChrome(js::GetObjectCompartment(wrapper))) {
1552     found = XrayFindOwnPropertyInfo(cx, id, nativeProperties);
1553   }
1554 
1555   if (IsInstance(type)) {
1556     // Check for unforgeable properties first to prevent names provided by
1557     // resolveOwnProperty callback from shadowing them.
1558     if (found && (found->type == eUnforgeableMethod ||
1559                   found->type == eUnforgeableAttribute)) {
1560       if (!XrayResolveProperty(cx, wrapper, obj, id, desc, cacheOnHolder, type,
1561                                nativeProperties, *found)) {
1562         return false;
1563       }
1564 
1565       if (desc.object()) {
1566         return true;
1567       }
1568     }
1569 
1570     if (resolveOwnProperty) {
1571       if (!resolveOwnProperty(cx, wrapper, obj, id, desc)) {
1572         return false;
1573       }
1574 
1575       if (desc.object()) {
1576         // None of these should be cached on the holder, since they're dynamic.
1577         return true;
1578       }
1579     }
1580 
1581     // If we're a special scope for in-content XBL, our script expects to see
1582     // the bound XBL methods and attributes when accessing content. However,
1583     // these members are implemented in content via custom-spliced prototypes,
1584     // and thus aren't visible through Xray wrappers unless we handle them
1585     // explicitly. So we check if we're running in such a scope, and if so,
1586     // whether the wrappee is a bound element. If it is, we do a lookup via
1587     // specialized XBL machinery.
1588     //
1589     // While we have to do some sketchy walking through content land, we should
1590     // be protected by read-only/non-configurable properties, and any functions
1591     // we end up with should _always_ be living in our own scope (the XBL
1592     // scope). Make sure to assert that.
1593     JS::Rooted<JSObject*> maybeElement(cx, obj);
1594     Element* element;
1595     if (xpc::IsInContentXBLScope(wrapper) &&
1596         NS_SUCCEEDED(UNWRAP_OBJECT(Element, &maybeElement, element))) {
1597       if (!nsContentUtils::LookupBindingMember(cx, element, id, desc)) {
1598         return false;
1599       }
1600 
1601       DEBUG_CheckXBLLookup(cx, desc.address());
1602 
1603       if (desc.object()) {
1604         // XBL properties shouldn't be cached on the holder, as they might be
1605         // shadowed by own properties returned from mResolveOwnProperty.
1606         desc.object().set(wrapper);
1607 
1608         return true;
1609       }
1610     }
1611 
1612     // For non-global instance Xrays there are no other properties, so return
1613     // here for them.
1614     if (type != eGlobalInstance) {
1615       return true;
1616     }
1617   } else if (type == eInterface) {
1618     if (id == GetJSIDByIndex(cx, XPCJSContext::IDX_PROTOTYPE)) {
1619       return nativePropertyHooks->mPrototypeID == prototypes::id::_ID_Count ||
1620              ResolvePrototypeOrConstructor(
1621                  cx, wrapper, obj, nativePropertyHooks->mPrototypeID,
1622                  JSPROP_PERMANENT | JSPROP_READONLY, desc, cacheOnHolder);
1623     }
1624 
1625     if (id == SYMBOL_TO_JSID(
1626                   JS::GetWellKnownSymbol(cx, JS::SymbolCode::hasInstance)) &&
1627         DOMIfaceAndProtoJSClass::FromJSClass(js::GetObjectClass(obj))
1628             ->wantsInterfaceHasInstance) {
1629       cacheOnHolder = true;
1630       JSNativeWrapper interfaceHasInstanceWrapper = {InterfaceHasInstance,
1631                                                      nullptr};
1632       JSObject* funObj =
1633           XrayCreateFunction(cx, wrapper, interfaceHasInstanceWrapper, 1, id);
1634       if (!funObj) {
1635         return false;
1636       }
1637 
1638       desc.value().setObject(*funObj);
1639       desc.setAttributes(JSPROP_READONLY | JSPROP_PERMANENT);
1640       desc.object().set(wrapper);
1641       desc.setSetter(nullptr);
1642       desc.setGetter(nullptr);
1643       return true;
1644     }
1645   } else {
1646     MOZ_ASSERT(IsInterfacePrototype(type));
1647 
1648     if (id == GetJSIDByIndex(cx, XPCJSContext::IDX_CONSTRUCTOR)) {
1649       return nativePropertyHooks->mConstructorID ==
1650                  constructors::id::_ID_Count ||
1651              ResolvePrototypeOrConstructor(cx, wrapper, obj,
1652                                            nativePropertyHooks->mConstructorID,
1653                                            0, desc, cacheOnHolder);
1654     }
1655 
1656     // The properties for globals live on the instance, so return here as there
1657     // are no properties on their interface prototype object.
1658     if (type == eGlobalInterfacePrototype) {
1659       return true;
1660     }
1661   }
1662 
1663   if (found && !XrayResolveProperty(cx, wrapper, obj, id, desc, cacheOnHolder,
1664                                     type, nativeProperties, *found)) {
1665     return false;
1666   }
1667 
1668   return true;
1669 }
1670 
XrayDefineProperty(JSContext * cx,JS::Handle<JSObject * > wrapper,JS::Handle<JSObject * > obj,JS::Handle<jsid> id,JS::Handle<JS::PropertyDescriptor> desc,JS::ObjectOpResult & result,bool * defined)1671 bool XrayDefineProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
1672                         JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
1673                         JS::Handle<JS::PropertyDescriptor> desc,
1674                         JS::ObjectOpResult& result, bool* defined) {
1675   if (!js::IsProxy(obj)) return true;
1676 
1677   const DOMProxyHandler* handler = GetDOMProxyHandler(obj);
1678   return handler->defineProperty(cx, wrapper, id, desc, result, defined);
1679 }
1680 
1681 template <typename SpecType>
XrayAppendPropertyKeys(JSContext * cx,JS::Handle<JSObject * > obj,const Prefable<const SpecType> * pref,const PropertyInfo * infos,unsigned flags,JS::AutoIdVector & props)1682 bool XrayAppendPropertyKeys(JSContext* cx, JS::Handle<JSObject*> obj,
1683                             const Prefable<const SpecType>* pref,
1684                             const PropertyInfo* infos, unsigned flags,
1685                             JS::AutoIdVector& props) {
1686   do {
1687     bool prefIsEnabled = pref->isEnabled(cx, obj);
1688     if (prefIsEnabled) {
1689       const SpecType* spec = pref->specs;
1690       do {
1691         const jsid& id = infos++->id;
1692         if (((flags & JSITER_HIDDEN) || (spec->flags & JSPROP_ENUMERATE)) &&
1693             ((flags & JSITER_SYMBOLS) || !JSID_IS_SYMBOL(id)) &&
1694             !props.append(id)) {
1695           return false;
1696         }
1697       } while ((++spec)->name);
1698     }
1699     // Break if we have reached the end of pref.
1700     if (!(++pref)->specs) {
1701       break;
1702     }
1703     // Advance infos if the previous pref is disabled. The -1 is required
1704     // because there is an end-of-list terminator between pref->specs and
1705     // (pref - 1)->specs.
1706     if (!prefIsEnabled) {
1707       infos += pref->specs - (pref - 1)->specs - 1;
1708     }
1709   } while (1);
1710 
1711   return true;
1712 }
1713 
1714 template <>
XrayAppendPropertyKeys(JSContext * cx,JS::Handle<JSObject * > obj,const Prefable<const ConstantSpec> * pref,const PropertyInfo * infos,unsigned flags,JS::AutoIdVector & props)1715 bool XrayAppendPropertyKeys<ConstantSpec>(
1716     JSContext* cx, JS::Handle<JSObject*> obj,
1717     const Prefable<const ConstantSpec>* pref, const PropertyInfo* infos,
1718     unsigned flags, JS::AutoIdVector& props) {
1719   do {
1720     bool prefIsEnabled = pref->isEnabled(cx, obj);
1721     if (prefIsEnabled) {
1722       const ConstantSpec* spec = pref->specs;
1723       do {
1724         if (!props.append(infos++->id)) {
1725           return false;
1726         }
1727       } while ((++spec)->name);
1728     }
1729     // Break if we have reached the end of pref.
1730     if (!(++pref)->specs) {
1731       break;
1732     }
1733     // Advance infos if the previous pref is disabled. The -1 is required
1734     // because there is an end-of-list terminator between pref->specs and
1735     // (pref - 1)->specs.
1736     if (!prefIsEnabled) {
1737       infos += pref->specs - (pref - 1)->specs - 1;
1738     }
1739   } while (1);
1740 
1741   return true;
1742 }
1743 
1744 #define ADD_KEYS_IF_DEFINED(FieldName)                                        \
1745   {                                                                           \
1746     if (nativeProperties->Has##FieldName##s() &&                              \
1747         !XrayAppendPropertyKeys(cx, obj, nativeProperties->FieldName##s(),    \
1748                                 nativeProperties->FieldName##PropertyInfos(), \
1749                                 flags, props)) {                              \
1750       return false;                                                           \
1751     }                                                                         \
1752   }
1753 
XrayOwnPropertyKeys(JSContext * cx,JS::Handle<JSObject * > wrapper,JS::Handle<JSObject * > obj,unsigned flags,JS::AutoIdVector & props,DOMObjectType type,const NativeProperties * nativeProperties)1754 bool XrayOwnPropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
1755                          JS::Handle<JSObject*> obj, unsigned flags,
1756                          JS::AutoIdVector& props, DOMObjectType type,
1757                          const NativeProperties* nativeProperties) {
1758   MOZ_ASSERT(type != eNamedPropertiesObject);
1759 
1760   if (IsInstance(type)) {
1761     ADD_KEYS_IF_DEFINED(UnforgeableMethod);
1762     ADD_KEYS_IF_DEFINED(UnforgeableAttribute);
1763     if (type == eGlobalInstance) {
1764       ADD_KEYS_IF_DEFINED(Method);
1765       ADD_KEYS_IF_DEFINED(Attribute);
1766     }
1767   } else {
1768     MOZ_ASSERT(type != eGlobalInterfacePrototype);
1769     if (type == eInterface) {
1770       ADD_KEYS_IF_DEFINED(StaticMethod);
1771       ADD_KEYS_IF_DEFINED(StaticAttribute);
1772     } else {
1773       MOZ_ASSERT(type == eInterfacePrototype);
1774       ADD_KEYS_IF_DEFINED(Method);
1775       ADD_KEYS_IF_DEFINED(Attribute);
1776     }
1777     ADD_KEYS_IF_DEFINED(Constant);
1778   }
1779 
1780   return true;
1781 }
1782 
1783 #undef ADD_KEYS_IF_DEFINED
1784 
XrayOwnNativePropertyKeys(JSContext * cx,JS::Handle<JSObject * > wrapper,const NativePropertyHooks * nativePropertyHooks,DOMObjectType type,JS::Handle<JSObject * > obj,unsigned flags,JS::AutoIdVector & props)1785 bool XrayOwnNativePropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
1786                                const NativePropertyHooks* nativePropertyHooks,
1787                                DOMObjectType type, JS::Handle<JSObject*> obj,
1788                                unsigned flags, JS::AutoIdVector& props) {
1789   MOZ_ASSERT(type != eNamedPropertiesObject);
1790 
1791   if (type == eInterface &&
1792       nativePropertyHooks->mPrototypeID != prototypes::id::_ID_Count &&
1793       !AddStringToIDVector(cx, props, "prototype")) {
1794     return false;
1795   }
1796 
1797   if (IsInterfacePrototype(type) &&
1798       nativePropertyHooks->mConstructorID != constructors::id::_ID_Count &&
1799       (flags & JSITER_HIDDEN) &&
1800       !AddStringToIDVector(cx, props, "constructor")) {
1801     return false;
1802   }
1803 
1804   const NativePropertiesHolder& nativeProperties =
1805       nativePropertyHooks->mNativeProperties;
1806 
1807   if (nativeProperties.regular &&
1808       !XrayOwnPropertyKeys(cx, wrapper, obj, flags, props, type,
1809                            nativeProperties.regular)) {
1810     return false;
1811   }
1812 
1813   if (nativeProperties.chromeOnly &&
1814       xpc::AccessCheck::isChrome(js::GetObjectCompartment(wrapper)) &&
1815       !XrayOwnPropertyKeys(cx, wrapper, obj, flags, props, type,
1816                            nativeProperties.chromeOnly)) {
1817     return false;
1818   }
1819 
1820   return true;
1821 }
1822 
XrayOwnPropertyKeys(JSContext * cx,JS::Handle<JSObject * > wrapper,JS::Handle<JSObject * > obj,unsigned flags,JS::AutoIdVector & props)1823 bool XrayOwnPropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
1824                          JS::Handle<JSObject*> obj, unsigned flags,
1825                          JS::AutoIdVector& props) {
1826   DOMObjectType type;
1827   const NativePropertyHooks* nativePropertyHooks =
1828       GetNativePropertyHooks(cx, obj, type);
1829   EnumerateOwnProperties enumerateOwnProperties =
1830       nativePropertyHooks->mEnumerateOwnProperties;
1831 
1832   if (type == eNamedPropertiesObject) {
1833     MOZ_ASSERT(!enumerateOwnProperties,
1834                "Shouldn't have any Xray-visible properties");
1835     return true;
1836   }
1837 
1838   if (IsInstance(type)) {
1839     // FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=1071189
1840     //       Should do something about XBL properties too.
1841     if (enumerateOwnProperties &&
1842         !enumerateOwnProperties(cx, wrapper, obj, props)) {
1843       return false;
1844     }
1845   }
1846 
1847   return type == eGlobalInterfacePrototype ||
1848          XrayOwnNativePropertyKeys(cx, wrapper, nativePropertyHooks, type, obj,
1849                                    flags, props);
1850 }
1851 
XrayGetExpandoClass(JSContext * cx,JS::Handle<JSObject * > obj)1852 const JSClass* XrayGetExpandoClass(JSContext* cx, JS::Handle<JSObject*> obj) {
1853   DOMObjectType type;
1854   const NativePropertyHooks* nativePropertyHooks =
1855       GetNativePropertyHooks(cx, obj, type);
1856   if (!IsInstance(type)) {
1857     // Non-instances don't need any special expando classes.
1858     return &DefaultXrayExpandoObjectClass;
1859   }
1860 
1861   return nativePropertyHooks->mXrayExpandoClass;
1862 }
1863 
XrayDeleteNamedProperty(JSContext * cx,JS::Handle<JSObject * > wrapper,JS::Handle<JSObject * > obj,JS::Handle<jsid> id,JS::ObjectOpResult & opresult)1864 bool XrayDeleteNamedProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
1865                              JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
1866                              JS::ObjectOpResult& opresult) {
1867   DOMObjectType type;
1868   const NativePropertyHooks* nativePropertyHooks =
1869       GetNativePropertyHooks(cx, obj, type);
1870   if (!IsInstance(type) || !nativePropertyHooks->mDeleteNamedProperty) {
1871     return opresult.succeed();
1872   }
1873   return nativePropertyHooks->mDeleteNamedProperty(cx, wrapper, obj, id,
1874                                                    opresult);
1875 }
1876 
GetCachedSlotStorageObjectSlow(JSContext * cx,JS::Handle<JSObject * > obj,bool * isXray)1877 JSObject* GetCachedSlotStorageObjectSlow(JSContext* cx,
1878                                          JS::Handle<JSObject*> obj,
1879                                          bool* isXray) {
1880   if (!xpc::WrapperFactory::IsXrayWrapper(obj)) {
1881     JSObject* retval =
1882         js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false);
1883     MOZ_ASSERT(IsDOMObject(retval));
1884     *isXray = false;
1885     return retval;
1886   }
1887 
1888   *isXray = true;
1889   return xpc::EnsureXrayExpandoObject(cx, obj);
1890   ;
1891 }
1892 
1893 DEFINE_XRAY_EXPANDO_CLASS(, DefaultXrayExpandoObjectClass, 0);
1894 
1895 NativePropertyHooks sEmptyNativePropertyHooks = {nullptr,
1896                                                  nullptr,
1897                                                  nullptr,
1898                                                  {nullptr, nullptr},
1899                                                  prototypes::id::_ID_Count,
1900                                                  constructors::id::_ID_Count,
1901                                                  nullptr};
1902 
1903 const js::ClassOps sBoringInterfaceObjectClassClassOps = {
1904     nullptr,             /* addProperty */
1905     nullptr,             /* delProperty */
1906     nullptr,             /* enumerate */
1907     nullptr,             /* newEnumerate */
1908     nullptr,             /* resolve */
1909     nullptr,             /* mayResolve */
1910     nullptr,             /* finalize */
1911     ThrowingConstructor, /* call */
1912     nullptr,             /* hasInstance */
1913     ThrowingConstructor, /* construct */
1914     nullptr,             /* trace */
1915 };
1916 
1917 const js::ObjectOps sInterfaceObjectClassObjectOps = {
1918     nullptr,                 /* lookupProperty */
1919     nullptr,                 /* defineProperty */
1920     nullptr,                 /* hasProperty */
1921     nullptr,                 /* getProperty */
1922     nullptr,                 /* setProperty */
1923     nullptr,                 /* getOwnPropertyDescriptor */
1924     nullptr,                 /* deleteProperty */
1925     nullptr,                 /* getElements */
1926     InterfaceObjectToString, /* funToString */
1927 };
1928 
GetPropertyOnPrototype(JSContext * cx,JS::Handle<JSObject * > proxy,JS::Handle<JS::Value> receiver,JS::Handle<jsid> id,bool * found,JS::MutableHandle<JS::Value> vp)1929 bool GetPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
1930                             JS::Handle<JS::Value> receiver, JS::Handle<jsid> id,
1931                             bool* found, JS::MutableHandle<JS::Value> vp) {
1932   JS::Rooted<JSObject*> proto(cx);
1933   if (!js::GetObjectProto(cx, proxy, &proto)) {
1934     return false;
1935   }
1936   if (!proto) {
1937     *found = false;
1938     return true;
1939   }
1940 
1941   if (!JS_HasPropertyById(cx, proto, id, found)) {
1942     return false;
1943   }
1944 
1945   if (!*found) {
1946     return true;
1947   }
1948 
1949   return JS_ForwardGetPropertyTo(cx, proto, id, receiver, vp);
1950 }
1951 
HasPropertyOnPrototype(JSContext * cx,JS::Handle<JSObject * > proxy,JS::Handle<jsid> id,bool * has)1952 bool HasPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
1953                             JS::Handle<jsid> id, bool* has) {
1954   JS::Rooted<JSObject*> proto(cx);
1955   if (!js::GetObjectProto(cx, proxy, &proto)) {
1956     return false;
1957   }
1958   if (!proto) {
1959     *has = false;
1960     return true;
1961   }
1962 
1963   return JS_HasPropertyById(cx, proto, id, has);
1964 }
1965 
AppendNamedPropertyIds(JSContext * cx,JS::Handle<JSObject * > proxy,nsTArray<nsString> & names,bool shadowPrototypeProperties,JS::AutoIdVector & props)1966 bool AppendNamedPropertyIds(JSContext* cx, JS::Handle<JSObject*> proxy,
1967                             nsTArray<nsString>& names,
1968                             bool shadowPrototypeProperties,
1969                             JS::AutoIdVector& props) {
1970   for (uint32_t i = 0; i < names.Length(); ++i) {
1971     JS::Rooted<JS::Value> v(cx);
1972     if (!xpc::NonVoidStringToJsval(cx, names[i], &v)) {
1973       return false;
1974     }
1975 
1976     JS::Rooted<jsid> id(cx);
1977     if (!JS_ValueToId(cx, v, &id)) {
1978       return false;
1979     }
1980 
1981     bool shouldAppend = shadowPrototypeProperties;
1982     if (!shouldAppend) {
1983       bool has;
1984       if (!HasPropertyOnPrototype(cx, proxy, id, &has)) {
1985         return false;
1986       }
1987       shouldAppend = !has;
1988     }
1989 
1990     if (shouldAppend) {
1991       if (!props.append(id)) {
1992         return false;
1993       }
1994     }
1995   }
1996 
1997   return true;
1998 }
1999 
ParseJSON(JSContext * aCx,const nsAString & aJSON,JS::MutableHandle<JS::Value> aVal)2000 bool DictionaryBase::ParseJSON(JSContext* aCx, const nsAString& aJSON,
2001                                JS::MutableHandle<JS::Value> aVal) {
2002   if (aJSON.IsEmpty()) {
2003     return true;
2004   }
2005   return JS_ParseJSON(aCx, PromiseFlatString(aJSON).get(), aJSON.Length(),
2006                       aVal);
2007 }
2008 
StringifyToJSON(JSContext * aCx,JS::Handle<JSObject * > aObj,nsAString & aJSON) const2009 bool DictionaryBase::StringifyToJSON(JSContext* aCx, JS::Handle<JSObject*> aObj,
2010                                      nsAString& aJSON) const {
2011   return JS::ToJSONMaybeSafely(aCx, aObj, AppendJSONToString, &aJSON);
2012 }
2013 
2014 /* static */
AppendJSONToString(const char16_t * aJSONData,uint32_t aDataLength,void * aString)2015 bool DictionaryBase::AppendJSONToString(const char16_t* aJSONData,
2016                                         uint32_t aDataLength, void* aString) {
2017   nsAString* string = static_cast<nsAString*>(aString);
2018   string->Append(aJSONData, aDataLength);
2019   return true;
2020 }
2021 
ReparentWrapper(JSContext * aCx,JS::Handle<JSObject * > aObjArg,ErrorResult & aError)2022 void ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg,
2023                      ErrorResult& aError) {
2024   js::AssertSameCompartment(aCx, aObjArg);
2025 
2026   aError.MightThrowJSException();
2027 
2028   // Check if we're anywhere near the stack limit before we reach the
2029   // transplanting code, since it has no good way to handle errors. This uses
2030   // the untrusted script limit, which is not strictly necessary since no
2031   // actual script should run.
2032   if (!js::CheckRecursionLimitConservative(aCx)) {
2033     aError.StealExceptionFromJSContext(aCx);
2034     return;
2035   }
2036 
2037   JS::Rooted<JSObject*> aObj(aCx, aObjArg);
2038   const DOMJSClass* domClass = GetDOMClass(aObj);
2039 
2040   // DOM things are always parented to globals.
2041   JS::Rooted<JSObject*> oldParent(aCx,
2042                                   js::GetGlobalForObjectCrossCompartment(aObj));
2043   MOZ_ASSERT(js::GetGlobalForObjectCrossCompartment(oldParent) == oldParent);
2044 
2045   JS::Rooted<JSObject*> newParent(aCx,
2046                                   domClass->mGetAssociatedGlobal(aCx, aObj));
2047   MOZ_ASSERT(JS_IsGlobalObject(newParent));
2048 
2049   JSAutoCompartment oldAc(aCx, oldParent);
2050 
2051   JSCompartment* oldCompartment = js::GetObjectCompartment(oldParent);
2052   JSCompartment* newCompartment = js::GetObjectCompartment(newParent);
2053   if (oldCompartment == newCompartment) {
2054     MOZ_ASSERT(oldParent == newParent);
2055     return;
2056   }
2057 
2058   nsISupports* native = UnwrapDOMObjectToISupports(aObj);
2059   if (!native) {
2060     return;
2061   }
2062 
2063   bool isProxy = js::IsProxy(aObj);
2064   JS::Rooted<JSObject*> expandoObject(aCx);
2065   if (isProxy) {
2066     expandoObject = DOMProxyHandler::GetAndClearExpandoObject(aObj);
2067   }
2068 
2069   JSAutoCompartment newAc(aCx, newParent);
2070 
2071   // First we clone the reflector. We get a copy of its properties and clone its
2072   // expando chain.
2073 
2074   JS::Handle<JSObject*> proto = (domClass->mGetProto)(aCx);
2075   if (!proto) {
2076     aError.StealExceptionFromJSContext(aCx);
2077     return;
2078   }
2079 
2080   JS::Rooted<JSObject*> newobj(aCx, JS_CloneObject(aCx, aObj, proto));
2081   if (!newobj) {
2082     aError.StealExceptionFromJSContext(aCx);
2083     return;
2084   }
2085 
2086   JS::Rooted<JSObject*> propertyHolder(aCx);
2087   JS::Rooted<JSObject*> copyFrom(aCx, isProxy ? expandoObject : aObj);
2088   if (copyFrom) {
2089     propertyHolder = JS_NewObjectWithGivenProto(aCx, nullptr, nullptr);
2090     if (!propertyHolder) {
2091       aError.StealExceptionFromJSContext(aCx);
2092       return;
2093     }
2094 
2095     if (!JS_CopyPropertiesFrom(aCx, propertyHolder, copyFrom)) {
2096       aError.StealExceptionFromJSContext(aCx);
2097       return;
2098     }
2099   } else {
2100     propertyHolder = nullptr;
2101   }
2102 
2103   // We've set up |newobj|, so we make it own the native by setting its reserved
2104   // slot and nulling out the reserved slot of |obj|.
2105   //
2106   // NB: It's important to do this _after_ copying the properties to
2107   // propertyHolder. Otherwise, an object with |foo.x === foo| will
2108   // crash when JS_CopyPropertiesFrom tries to call wrap() on foo.x.
2109   js::SetReservedSlot(newobj, DOM_OBJECT_SLOT,
2110                       js::GetReservedSlot(aObj, DOM_OBJECT_SLOT));
2111   js::SetReservedSlot(aObj, DOM_OBJECT_SLOT, JS::PrivateValue(nullptr));
2112 
2113   aObj = xpc::TransplantObjectRetainingXrayExpandos(aCx, aObj, newobj);
2114   if (!aObj) {
2115     MOZ_CRASH();
2116   }
2117 
2118   nsWrapperCache* cache = nullptr;
2119   CallQueryInterface(native, &cache);
2120   bool preserving = cache->PreservingWrapper();
2121   cache->SetPreservingWrapper(false);
2122   cache->SetWrapper(aObj);
2123   cache->SetPreservingWrapper(preserving);
2124 
2125   if (propertyHolder) {
2126     JS::Rooted<JSObject*> copyTo(aCx);
2127     if (isProxy) {
2128       copyTo = DOMProxyHandler::EnsureExpandoObject(aCx, aObj);
2129     } else {
2130       copyTo = aObj;
2131     }
2132 
2133     if (!copyTo || !JS_CopyPropertiesFrom(aCx, copyTo, propertyHolder)) {
2134       MOZ_CRASH();
2135     }
2136   }
2137 
2138   JS::Rooted<JSObject*> maybeObjLC(aCx, aObj);
2139   nsObjectLoadingContent* htmlobject;
2140   nsresult rv = UNWRAP_OBJECT(HTMLObjectElement, &maybeObjLC, htmlobject);
2141   if (NS_FAILED(rv)) {
2142     rv = UNWRAP_OBJECT(HTMLEmbedElement, &maybeObjLC, htmlobject);
2143     if (NS_FAILED(rv)) {
2144       htmlobject = nullptr;
2145     }
2146   }
2147   if (htmlobject) {
2148     htmlobject->SetupProtoChain(aCx, aObj);
2149   }
2150 }
2151 
GlobalObject(JSContext * aCx,JSObject * aObject)2152 GlobalObject::GlobalObject(JSContext* aCx, JSObject* aObject)
2153     : mGlobalJSObject(aCx), mCx(aCx), mGlobalObject(nullptr) {
2154   MOZ_ASSERT(mCx);
2155   JS::Rooted<JSObject*> obj(aCx, aObject);
2156   if (js::IsWrapper(obj)) {
2157     obj = js::CheckedUnwrap(obj, /* stopAtWindowProxy = */ false);
2158     if (!obj) {
2159       // We should never end up here on a worker thread, since there shouldn't
2160       // be any security wrappers to worry about.
2161       if (!MOZ_LIKELY(NS_IsMainThread())) {
2162         MOZ_CRASH();
2163       }
2164 
2165       Throw(aCx, NS_ERROR_XPC_SECURITY_MANAGER_VETO);
2166       return;
2167     }
2168   }
2169 
2170   mGlobalJSObject = js::GetGlobalForObjectCrossCompartment(obj);
2171 }
2172 
GetAsSupports() const2173 nsISupports* GlobalObject::GetAsSupports() const {
2174   if (mGlobalObject) {
2175     return mGlobalObject;
2176   }
2177 
2178   MOZ_ASSERT(!js::IsWrapper(mGlobalJSObject));
2179 
2180   // Most of our globals are DOM objects.  Try that first.  Note that this
2181   // assumes that either the first nsISupports in the object is the canonical
2182   // one or that we don't care about the canonical nsISupports here.
2183   mGlobalObject = UnwrapDOMObjectToISupports(mGlobalJSObject);
2184   if (mGlobalObject) {
2185     return mGlobalObject;
2186   }
2187 
2188   MOZ_ASSERT(NS_IsMainThread(), "All our worker globals are DOM objects");
2189 
2190   // Remove everything below here once all our global objects are using new
2191   // bindings.  If that ever happens; it would need to include Sandbox and
2192   // BackstagePass.
2193 
2194   // See whether mGlobalJSObject is an XPCWrappedNative.  This will redo the
2195   // IsWrapper bit above and the UnwrapDOMObjectToISupports in the case when
2196   // we're not actually an XPCWrappedNative, but this should be a rare-ish case
2197   // anyway.
2198   nsCOMPtr<nsISupports> supp = xpc::UnwrapReflectorToISupports(mGlobalJSObject);
2199   if (supp) {
2200     // See documentation for mGlobalJSObject for why this assignment is OK.
2201     mGlobalObject = supp;
2202     return mGlobalObject;
2203   }
2204 
2205   // And now a final hack.  Sandbox is not a reflector, but it does have an
2206   // nsIGlobalObject hanging out in its private slot.  Handle that case here,
2207   // (though again, this will do the useless UnwrapDOMObjectToISupports if we
2208   // got here for something that is somehow not a DOM object, not an
2209   // XPCWrappedNative _and_ not a Sandbox).
2210   if (XPCConvert::GetISupportsFromJSObject(mGlobalJSObject, &mGlobalObject)) {
2211     return mGlobalObject;
2212   }
2213 
2214   MOZ_ASSERT(!mGlobalObject);
2215 
2216   Throw(mCx, NS_ERROR_XPC_BAD_CONVERT_JS);
2217   return nullptr;
2218 }
2219 
GetSubjectPrincipal() const2220 nsIPrincipal* GlobalObject::GetSubjectPrincipal() const {
2221   if (!NS_IsMainThread()) {
2222     return nullptr;
2223   }
2224 
2225   JSCompartment* compartment = js::GetContextCompartment(mCx);
2226   MOZ_ASSERT(compartment);
2227   JSPrincipals* principals = JS_GetCompartmentPrincipals(compartment);
2228   return nsJSPrincipals::get(principals);
2229 }
2230 
CallerType() const2231 CallerType GlobalObject::CallerType() const {
2232   return nsContentUtils::ThreadsafeIsSystemCaller(mCx)
2233              ? dom::CallerType::System
2234              : dom::CallerType::NonSystem;
2235 }
2236 
CallOrdinaryHasInstance(JSContext * cx,JS::CallArgs & args)2237 static bool CallOrdinaryHasInstance(JSContext* cx, JS::CallArgs& args) {
2238   JS::Rooted<JSObject*> thisObj(cx, &args.thisv().toObject());
2239   bool isInstance;
2240   if (!JS::OrdinaryHasInstance(cx, thisObj, args.get(0), &isInstance)) {
2241     return false;
2242   }
2243   args.rval().setBoolean(isInstance);
2244   return true;
2245 }
2246 
InterfaceHasInstance(JSContext * cx,unsigned argc,JS::Value * vp)2247 bool InterfaceHasInstance(JSContext* cx, unsigned argc, JS::Value* vp) {
2248   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
2249   // If the thing we were passed is not an object, return false like
2250   // OrdinaryHasInstance does.
2251   if (!args.get(0).isObject()) {
2252     args.rval().setBoolean(false);
2253     return true;
2254   }
2255 
2256   // If "this" is not an object, likewise return false (again, like
2257   // OrdinaryHasInstance).
2258   if (!args.thisv().isObject()) {
2259     args.rval().setBoolean(false);
2260     return true;
2261   }
2262 
2263   // If "this" doesn't have a DOMIfaceAndProtoJSClass, it's not a DOM
2264   // constructor, so just fall back to OrdinaryHasInstance.  But note that we
2265   // should CheckedUnwrap here, because otherwise we won't get the right
2266   // answers.
2267   JS::Rooted<JSObject*> thisObj(cx,
2268                                 js::CheckedUnwrap(&args.thisv().toObject()));
2269   if (!thisObj) {
2270     // Just fall back on the normal thing, in case it still happens to work.
2271     return CallOrdinaryHasInstance(cx, args);
2272   }
2273 
2274   const js::Class* thisClass = js::GetObjectClass(thisObj);
2275 
2276   if (!IsDOMIfaceAndProtoClass(thisClass)) {
2277     return CallOrdinaryHasInstance(cx, args);
2278   }
2279 
2280   const DOMIfaceAndProtoJSClass* clasp =
2281       DOMIfaceAndProtoJSClass::FromJSClass(thisClass);
2282 
2283   // If "this" isn't a DOM constructor or is a constructor for an interface
2284   // without a prototype, just fall back to OrdinaryHasInstance.
2285   if (clasp->mType != eInterface ||
2286       clasp->mPrototypeID == prototypes::id::_ID_Count) {
2287     return CallOrdinaryHasInstance(cx, args);
2288   }
2289 
2290   JS::Rooted<JSObject*> instance(cx, &args[0].toObject());
2291   const DOMJSClass* domClass = GetDOMClass(
2292       js::UncheckedUnwrap(instance, /* stopAtWindowProxy = */ false));
2293 
2294   if (domClass &&
2295       domClass->mInterfaceChain[clasp->mDepth] == clasp->mPrototypeID) {
2296     args.rval().setBoolean(true);
2297     return true;
2298   }
2299 
2300   if (jsipc::IsWrappedCPOW(instance)) {
2301     bool boolp = false;
2302     if (!jsipc::DOMInstanceOf(cx, js::UncheckedUnwrap(instance),
2303                               clasp->mPrototypeID, clasp->mDepth, &boolp)) {
2304       return false;
2305     }
2306     args.rval().setBoolean(boolp);
2307     return true;
2308   }
2309 
2310   return CallOrdinaryHasInstance(cx, args);
2311 }
2312 
InterfaceHasInstance(JSContext * cx,int prototypeID,int depth,JS::Handle<JSObject * > instance,bool * bp)2313 bool InterfaceHasInstance(JSContext* cx, int prototypeID, int depth,
2314                           JS::Handle<JSObject*> instance, bool* bp) {
2315   const DOMJSClass* domClass = GetDOMClass(js::UncheckedUnwrap(instance));
2316 
2317   MOZ_ASSERT(!domClass || prototypeID != prototypes::id::_ID_Count,
2318              "Why do we have a hasInstance hook if we don't have a prototype "
2319              "ID?");
2320 
2321   *bp = (domClass && domClass->mInterfaceChain[depth] == prototypeID);
2322   return true;
2323 }
2324 
InterfaceIsInstance(JSContext * cx,unsigned argc,JS::Value * vp,prototypes::ID prototypeID,int depth)2325 bool InterfaceIsInstance(JSContext* cx, unsigned argc, JS::Value* vp,
2326                          prototypes::ID prototypeID, int depth) {
2327   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
2328   if (MOZ_UNLIKELY(args.length() < 1)) {
2329     nsPrintfCString message("%s.isInstance",
2330                             NamesOfInterfacesWithProtos(prototypeID));
2331     return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, message.get());
2332   }
2333 
2334   if (!args[0].isObject()) {
2335     nsPrintfCString message("Argument 1 of %s.isInstance",
2336                             NamesOfInterfacesWithProtos(prototypeID));
2337     return ThrowErrorMessage(cx, MSG_NOT_OBJECT, message.get());
2338   }
2339 
2340   JS::Rooted<JSObject*> instance(cx, &args[0].toObject());
2341 
2342   const DOMJSClass* domClass = GetDOMClass(
2343       js::UncheckedUnwrap(instance, /* stopAtWindowProxy = */ false));
2344 
2345   if (domClass && domClass->mInterfaceChain[depth] == prototypeID) {
2346     args.rval().setBoolean(true);
2347     return true;
2348   }
2349 
2350   args.rval().setBoolean(false);
2351   return true;
2352 }
2353 
ReportLenientThisUnwrappingFailure(JSContext * cx,JSObject * obj)2354 bool ReportLenientThisUnwrappingFailure(JSContext* cx, JSObject* obj) {
2355   JS::Rooted<JSObject*> rootedObj(cx, obj);
2356   GlobalObject global(cx, rootedObj);
2357   if (global.Failed()) {
2358     return false;
2359   }
2360   nsCOMPtr<nsPIDOMWindowInner> window =
2361       do_QueryInterface(global.GetAsSupports());
2362   if (window && window->GetDoc()) {
2363     window->GetDoc()->WarnOnceAbout(nsIDocument::eLenientThis);
2364   }
2365   return true;
2366 }
2367 
GetContentGlobalForJSImplementedObject(JSContext * cx,JS::Handle<JSObject * > obj,nsIGlobalObject ** globalObj)2368 bool GetContentGlobalForJSImplementedObject(JSContext* cx,
2369                                             JS::Handle<JSObject*> obj,
2370                                             nsIGlobalObject** globalObj) {
2371   // Be very careful to not get tricked here.
2372   MOZ_ASSERT(NS_IsMainThread());
2373   if (!xpc::AccessCheck::isChrome(js::GetObjectCompartment(obj))) {
2374     MOZ_CRASH("Should have a chrome object here");
2375   }
2376 
2377   // Look up the content-side object.
2378   JS::Rooted<JS::Value> domImplVal(cx);
2379   if (!JS_GetProperty(cx, obj, "__DOM_IMPL__", &domImplVal)) {
2380     return false;
2381   }
2382 
2383   if (!domImplVal.isObject()) {
2384     ThrowErrorMessage(cx, MSG_NOT_OBJECT, "Value");
2385     return false;
2386   }
2387 
2388   // Go ahead and get the global from it.  GlobalObject will handle
2389   // doing unwrapping as needed.
2390   GlobalObject global(cx, &domImplVal.toObject());
2391   if (global.Failed()) {
2392     return false;
2393   }
2394 
2395   DebugOnly<nsresult> rv =
2396       CallQueryInterface(global.GetAsSupports(), globalObj);
2397   MOZ_ASSERT(NS_SUCCEEDED(rv));
2398   MOZ_ASSERT(*globalObj);
2399   return true;
2400 }
2401 
ConstructJSImplementation(const char * aContractId,const GlobalObject & aGlobal,JS::MutableHandle<JSObject * > aObject,ErrorResult & aRv)2402 already_AddRefed<nsIGlobalObject> ConstructJSImplementation(
2403     const char* aContractId, const GlobalObject& aGlobal,
2404     JS::MutableHandle<JSObject*> aObject, ErrorResult& aRv) {
2405   // Get the global object to use as a parent and for initialization.
2406   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
2407   if (!global) {
2408     aRv.Throw(NS_ERROR_FAILURE);
2409     return nullptr;
2410   }
2411 
2412   ConstructJSImplementation(aContractId, global, aObject, aRv);
2413 
2414   if (aRv.Failed()) {
2415     return nullptr;
2416   }
2417   return global.forget();
2418 }
2419 
ConstructJSImplementation(const char * aContractId,nsIGlobalObject * aGlobal,JS::MutableHandle<JSObject * > aObject,ErrorResult & aRv)2420 void ConstructJSImplementation(const char* aContractId,
2421                                nsIGlobalObject* aGlobal,
2422                                JS::MutableHandle<JSObject*> aObject,
2423                                ErrorResult& aRv) {
2424   MOZ_ASSERT(NS_IsMainThread());
2425 
2426   // Make sure to divorce ourselves from the calling JS while creating and
2427   // initializing the object, so exceptions from that will get reported
2428   // properly, since those are never exceptions that a spec wants to be thrown.
2429   {
2430     AutoNoJSAPI nojsapi;
2431 
2432     // Get the XPCOM component containing the JS implementation.
2433     nsresult rv;
2434     nsCOMPtr<nsISupports> implISupports = do_CreateInstance(aContractId, &rv);
2435     if (!implISupports) {
2436       nsPrintfCString msg("Failed to get JS implementation for contract \"%s\"",
2437                           aContractId);
2438       NS_WARNING(msg.get());
2439       aRv.Throw(rv);
2440       return;
2441     }
2442     // Initialize the object, if it implements nsIDOMGlobalPropertyInitializer
2443     // and our global is a window.
2444     nsCOMPtr<nsIDOMGlobalPropertyInitializer> gpi =
2445         do_QueryInterface(implISupports);
2446     nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal);
2447     if (gpi) {
2448       JS::Rooted<JS::Value> initReturn(RootingCx());
2449       rv = gpi->Init(window, &initReturn);
2450       if (NS_FAILED(rv)) {
2451         aRv.Throw(rv);
2452         return;
2453       }
2454       // With JS-implemented WebIDL, the return value of init() is not used to
2455       // determine if init() failed, so init() should only return undefined. Any
2456       // kind of permission or pref checking must happen by adding an attribute
2457       // to the WebIDL interface.
2458       if (!initReturn.isUndefined()) {
2459         MOZ_ASSERT(false,
2460                    "The init() method for JS-implemented WebIDL should not "
2461                    "return anything");
2462         MOZ_CRASH();
2463       }
2464     }
2465     // Extract the JS implementation from the XPCOM object.
2466     nsCOMPtr<nsIXPConnectWrappedJS> implWrapped =
2467         do_QueryInterface(implISupports, &rv);
2468     MOZ_ASSERT(implWrapped, "Failed to get wrapped JS from XPCOM component.");
2469     if (!implWrapped) {
2470       aRv.Throw(rv);
2471       return;
2472     }
2473     aObject.set(implWrapped->GetJSObject());
2474     if (!aObject) {
2475       aRv.Throw(NS_ERROR_FAILURE);
2476     }
2477   }
2478 }
2479 
NonVoidByteStringToJsval(JSContext * cx,const nsACString & str,JS::MutableHandle<JS::Value> rval)2480 bool NonVoidByteStringToJsval(JSContext* cx, const nsACString& str,
2481                               JS::MutableHandle<JS::Value> rval) {
2482   // ByteStrings are not UTF-8 encoded.
2483   JSString* jsStr = JS_NewStringCopyN(cx, str.Data(), str.Length());
2484 
2485   if (!jsStr) return false;
2486 
2487   rval.setString(jsStr);
2488   return true;
2489 }
2490 
2491 template <typename T>
NormalizeUSVStringInternal(T & aString)2492 static void NormalizeUSVStringInternal(T& aString) {
2493   char16_t* start = aString.BeginWriting();
2494   // Must use const here because we can't pass char** to UTF16CharEnumerator as
2495   // it expects const char**.  Unclear why this is illegal...
2496   const char16_t* nextChar = start;
2497   const char16_t* end = aString.Data() + aString.Length();
2498   while (nextChar < end) {
2499     uint32_t enumerated = UTF16CharEnumerator::NextChar(&nextChar, end);
2500     if (enumerated == UCS2_REPLACEMENT_CHAR) {
2501       int32_t lastCharIndex = (nextChar - start) - 1;
2502       start[lastCharIndex] = static_cast<char16_t>(enumerated);
2503     }
2504   }
2505 }
2506 
NormalizeUSVString(nsAString & aString)2507 void NormalizeUSVString(nsAString& aString) {
2508   NormalizeUSVStringInternal(aString);
2509 }
2510 
NormalizeUSVString(binding_detail::FakeString & aString)2511 void NormalizeUSVString(binding_detail::FakeString& aString) {
2512   NormalizeUSVStringInternal(aString);
2513 }
2514 
ConvertJSValueToByteString(JSContext * cx,JS::Handle<JS::Value> v,bool nullable,nsACString & result)2515 bool ConvertJSValueToByteString(JSContext* cx, JS::Handle<JS::Value> v,
2516                                 bool nullable, nsACString& result) {
2517   JS::Rooted<JSString*> s(cx);
2518   if (v.isString()) {
2519     s = v.toString();
2520   } else {
2521     if (nullable && v.isNullOrUndefined()) {
2522       result.SetIsVoid(true);
2523       return true;
2524     }
2525 
2526     s = JS::ToString(cx, v);
2527     if (!s) {
2528       return false;
2529     }
2530   }
2531 
2532   // Conversion from Javascript string to ByteString is only valid if all
2533   // characters < 256. This is always the case for Latin1 strings.
2534   size_t length;
2535   if (!js::StringHasLatin1Chars(s)) {
2536     // ThrowErrorMessage can GC, so we first scan the string for bad chars
2537     // and report the error outside the AutoCheckCannotGC scope.
2538     bool foundBadChar = false;
2539     size_t badCharIndex;
2540     char16_t badChar;
2541     {
2542       JS::AutoCheckCannotGC nogc;
2543       const char16_t* chars =
2544           JS_GetTwoByteStringCharsAndLength(cx, nogc, s, &length);
2545       if (!chars) {
2546         return false;
2547       }
2548 
2549       for (size_t i = 0; i < length; i++) {
2550         if (chars[i] > 255) {
2551           badCharIndex = i;
2552           badChar = chars[i];
2553           foundBadChar = true;
2554           break;
2555         }
2556       }
2557     }
2558 
2559     if (foundBadChar) {
2560       MOZ_ASSERT(badCharIndex < length);
2561       MOZ_ASSERT(badChar > 255);
2562       // The largest unsigned 64 bit number (18,446,744,073,709,551,615) has
2563       // 20 digits, plus one more for the null terminator.
2564       char index[21];
2565       static_assert(sizeof(size_t) <= 8, "index array too small");
2566       SprintfLiteral(index, "%zu", badCharIndex);
2567       // A char16_t is 16 bits long.  The biggest unsigned 16 bit
2568       // number (65,535) has 5 digits, plus one more for the null
2569       // terminator.
2570       char badCharArray[6];
2571       static_assert(sizeof(char16_t) <= 2, "badCharArray too small");
2572       SprintfLiteral(badCharArray, "%d", badChar);
2573       ThrowErrorMessage(cx, MSG_INVALID_BYTESTRING, index, badCharArray);
2574       return false;
2575     }
2576   } else {
2577     length = js::GetStringLength(s);
2578   }
2579 
2580   static_assert(js::MaxStringLength < UINT32_MAX,
2581                 "length+1 shouldn't overflow");
2582 
2583   if (!result.SetLength(length, fallible)) {
2584     return false;
2585   }
2586 
2587   JS_EncodeStringToBuffer(cx, s, result.BeginWriting(), length);
2588 
2589   return true;
2590 }
2591 
FinalizeGlobal(JSFreeOp * aFreeOp,JSObject * aObj)2592 void FinalizeGlobal(JSFreeOp* aFreeOp, JSObject* aObj) {
2593   MOZ_ASSERT(js::GetObjectClass(aObj)->flags & JSCLASS_DOM_GLOBAL);
2594   mozilla::dom::DestroyProtoAndIfaceCache(aObj);
2595 }
2596 
ResolveGlobal(JSContext * aCx,JS::Handle<JSObject * > aObj,JS::Handle<jsid> aId,bool * aResolvedp)2597 bool ResolveGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj,
2598                    JS::Handle<jsid> aId, bool* aResolvedp) {
2599   MOZ_ASSERT(JS_IsGlobalObject(aObj),
2600              "Should have a global here, since we plan to resolve standard "
2601              "classes!");
2602 
2603   return JS_ResolveStandardClass(aCx, aObj, aId, aResolvedp);
2604 }
2605 
MayResolveGlobal(const JSAtomState & aNames,jsid aId,JSObject * aMaybeObj)2606 bool MayResolveGlobal(const JSAtomState& aNames, jsid aId,
2607                       JSObject* aMaybeObj) {
2608   return JS_MayResolveStandardClass(aNames, aId, aMaybeObj);
2609 }
2610 
EnumerateGlobal(JSContext * aCx,JS::HandleObject aObj,JS::AutoIdVector & aProperties,bool aEnumerableOnly)2611 bool EnumerateGlobal(JSContext* aCx, JS::HandleObject aObj,
2612                      JS::AutoIdVector& aProperties, bool aEnumerableOnly) {
2613   MOZ_ASSERT(JS_IsGlobalObject(aObj),
2614              "Should have a global here, since we plan to enumerate standard "
2615              "classes!");
2616 
2617   return JS_NewEnumerateStandardClasses(aCx, aObj, aProperties,
2618                                         aEnumerableOnly);
2619 }
2620 
IsNonExposedGlobal(JSContext * aCx,JSObject * aGlobal,uint32_t aNonExposedGlobals)2621 bool IsNonExposedGlobal(JSContext* aCx, JSObject* aGlobal,
2622                         uint32_t aNonExposedGlobals) {
2623   MOZ_ASSERT(aNonExposedGlobals, "Why did we get called?");
2624   MOZ_ASSERT(
2625       (aNonExposedGlobals & ~(GlobalNames::Window | GlobalNames::BackstagePass |
2626                               GlobalNames::DedicatedWorkerGlobalScope |
2627                               GlobalNames::SharedWorkerGlobalScope |
2628                               GlobalNames::ServiceWorkerGlobalScope |
2629                               GlobalNames::WorkerDebuggerGlobalScope |
2630                               GlobalNames::WorkletGlobalScope)) == 0,
2631       "Unknown non-exposed global type");
2632 
2633   const char* name = js::GetObjectClass(aGlobal)->name;
2634 
2635   if ((aNonExposedGlobals & GlobalNames::Window) && !strcmp(name, "Window")) {
2636     return true;
2637   }
2638 
2639   if ((aNonExposedGlobals & GlobalNames::BackstagePass) &&
2640       !strcmp(name, "BackstagePass")) {
2641     return true;
2642   }
2643 
2644   if ((aNonExposedGlobals & GlobalNames::DedicatedWorkerGlobalScope) &&
2645       !strcmp(name, "DedicatedWorkerGlobalScope")) {
2646     return true;
2647   }
2648 
2649   if ((aNonExposedGlobals & GlobalNames::SharedWorkerGlobalScope) &&
2650       !strcmp(name, "SharedWorkerGlobalScope")) {
2651     return true;
2652   }
2653 
2654   if ((aNonExposedGlobals & GlobalNames::ServiceWorkerGlobalScope) &&
2655       !strcmp(name, "ServiceWorkerGlobalScope")) {
2656     return true;
2657   }
2658 
2659   if ((aNonExposedGlobals & GlobalNames::WorkerDebuggerGlobalScope) &&
2660       !strcmp(name, "WorkerDebuggerGlobalScopex")) {
2661     return true;
2662   }
2663 
2664   if ((aNonExposedGlobals & GlobalNames::WorkletGlobalScope) &&
2665       !strcmp(name, "WorkletGlobalScope")) {
2666     return true;
2667   }
2668 
2669   return false;
2670 }
2671 
GenericBindingGetter(JSContext * cx,unsigned argc,JS::Value * vp)2672 bool GenericBindingGetter(JSContext* cx, unsigned argc, JS::Value* vp) {
2673   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
2674   const JSJitInfo* info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
2675   prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID);
2676   if (!args.thisv().isObject()) {
2677     return ThrowInvalidThis(cx, args, false, protoID);
2678   }
2679   JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
2680 
2681   // NOTE: we want to leave obj in its initial compartment, so don't want to
2682   // pass it to UnwrapObject.
2683   JS::Rooted<JSObject*> rootSelf(cx, obj);
2684   void* self;
2685   {
2686     binding_detail::MutableObjectHandleWrapper wrapper(&rootSelf);
2687     nsresult rv = binding_detail::UnwrapObjectInternal<void, true>(
2688         wrapper, self, protoID, info->depth);
2689     if (NS_FAILED(rv)) {
2690       return ThrowInvalidThis(
2691           cx, args, rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO, protoID);
2692     }
2693   }
2694 
2695   MOZ_ASSERT(info->type() == JSJitInfo::Getter);
2696   JSJitGetterOp getter = info->getter;
2697   bool ok = getter(cx, obj, self, JSJitGetterCallArgs(args));
2698 #ifdef DEBUG
2699   if (ok) {
2700     AssertReturnTypeMatchesJitinfo(info, args.rval());
2701   }
2702 #endif
2703   return ok;
2704 }
2705 
GenericPromiseReturningBindingGetter(JSContext * cx,unsigned argc,JS::Value * vp)2706 bool GenericPromiseReturningBindingGetter(JSContext* cx, unsigned argc,
2707                                           JS::Value* vp) {
2708   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
2709 
2710   // We could invoke GenericBindingGetter here, but that involves an
2711   // extra call.  Manually inline it instead.
2712   const JSJitInfo* info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
2713   prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID);
2714   if (!args.thisv().isObject()) {
2715     ThrowInvalidThis(cx, args, false, protoID);
2716     return ConvertExceptionToPromise(cx, args.rval());
2717   }
2718   JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
2719 
2720   // NOTE: we want to leave obj in its initial compartment, so don't want to
2721   // pass it to UnwrapObject.
2722   JS::Rooted<JSObject*> rootSelf(cx, obj);
2723   void* self;
2724   {
2725     binding_detail::MutableObjectHandleWrapper wrapper(&rootSelf);
2726     nsresult rv = binding_detail::UnwrapObjectInternal<void, true>(
2727         wrapper, self, protoID, info->depth);
2728     if (NS_FAILED(rv)) {
2729       ThrowInvalidThis(cx, args, rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO,
2730                        protoID);
2731       return ConvertExceptionToPromise(cx, args.rval());
2732     }
2733   }
2734   MOZ_ASSERT(info->type() == JSJitInfo::Getter);
2735   JSJitGetterOp getter = info->getter;
2736   bool ok = getter(cx, obj, self, JSJitGetterCallArgs(args));
2737   if (ok) {
2738 #ifdef DEBUG
2739     AssertReturnTypeMatchesJitinfo(info, args.rval());
2740 #endif
2741     return true;
2742   }
2743 
2744   // Promise-returning getters always return objects
2745   MOZ_ASSERT(info->returnType() == JSVAL_TYPE_OBJECT);
2746   return ConvertExceptionToPromise(cx, args.rval());
2747 }
2748 
GenericBindingSetter(JSContext * cx,unsigned argc,JS::Value * vp)2749 bool GenericBindingSetter(JSContext* cx, unsigned argc, JS::Value* vp) {
2750   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
2751   const JSJitInfo* info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
2752   prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID);
2753   if (!args.thisv().isObject()) {
2754     return ThrowInvalidThis(cx, args, false, protoID);
2755   }
2756   JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
2757 
2758   // NOTE: we want to leave obj in its initial compartment, so don't want to
2759   // pass it to UnwrapObject.
2760   JS::Rooted<JSObject*> rootSelf(cx, obj);
2761   void* self;
2762   {
2763     binding_detail::MutableObjectHandleWrapper wrapper(&rootSelf);
2764     nsresult rv = binding_detail::UnwrapObjectInternal<void, true>(
2765         wrapper, self, protoID, info->depth);
2766     if (NS_FAILED(rv)) {
2767       return ThrowInvalidThis(
2768           cx, args, rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO, protoID);
2769     }
2770   }
2771   if (args.length() == 0) {
2772     return ThrowNoSetterArg(cx, protoID);
2773   }
2774   MOZ_ASSERT(info->type() == JSJitInfo::Setter);
2775   JSJitSetterOp setter = info->setter;
2776   if (!setter(cx, obj, self, JSJitSetterCallArgs(args))) {
2777     return false;
2778   }
2779   args.rval().setUndefined();
2780 #ifdef DEBUG
2781   AssertReturnTypeMatchesJitinfo(info, args.rval());
2782 #endif
2783   return true;
2784 }
2785 
GenericBindingMethod(JSContext * cx,unsigned argc,JS::Value * vp)2786 bool GenericBindingMethod(JSContext* cx, unsigned argc, JS::Value* vp) {
2787   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
2788   const JSJitInfo* info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
2789   prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID);
2790   if (!args.thisv().isObject()) {
2791     return ThrowInvalidThis(cx, args, false, protoID);
2792   }
2793   JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
2794 
2795   // NOTE: we want to leave obj in its initial compartment, so don't want to
2796   // pass it to UnwrapObject.
2797   JS::Rooted<JSObject*> rootSelf(cx, obj);
2798   void* self;
2799   {
2800     binding_detail::MutableObjectHandleWrapper wrapper(&rootSelf);
2801     nsresult rv = binding_detail::UnwrapObjectInternal<void, true>(
2802         wrapper, self, protoID, info->depth);
2803     if (NS_FAILED(rv)) {
2804       return ThrowInvalidThis(
2805           cx, args, rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO, protoID);
2806     }
2807   }
2808   MOZ_ASSERT(info->type() == JSJitInfo::Method);
2809   JSJitMethodOp method = info->method;
2810   bool ok = method(cx, obj, self, JSJitMethodCallArgs(args));
2811 #ifdef DEBUG
2812   if (ok) {
2813     AssertReturnTypeMatchesJitinfo(info, args.rval());
2814   }
2815 #endif
2816   return ok;
2817 }
2818 
GenericPromiseReturningBindingMethod(JSContext * cx,unsigned argc,JS::Value * vp)2819 bool GenericPromiseReturningBindingMethod(JSContext* cx, unsigned argc,
2820                                           JS::Value* vp) {
2821   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
2822 
2823   // We could invoke GenericBindingMethod here, but that involves an
2824   // extra call.  Manually inline it instead.
2825   const JSJitInfo* info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
2826   prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID);
2827   if (!args.thisv().isObject()) {
2828     ThrowInvalidThis(cx, args, false, protoID);
2829     return ConvertExceptionToPromise(cx, args.rval());
2830   }
2831   JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
2832 
2833   // NOTE: we want to leave obj in its initial compartment, so don't want to
2834   // pass it to UnwrapObject.
2835   JS::Rooted<JSObject*> rootSelf(cx, obj);
2836   void* self;
2837   {
2838     binding_detail::MutableObjectHandleWrapper wrapper(&rootSelf);
2839     nsresult rv = binding_detail::UnwrapObjectInternal<void, true>(
2840         wrapper, self, protoID, info->depth);
2841     if (NS_FAILED(rv)) {
2842       ThrowInvalidThis(cx, args, rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO,
2843                        protoID);
2844       return ConvertExceptionToPromise(cx, args.rval());
2845     }
2846   }
2847   MOZ_ASSERT(info->type() == JSJitInfo::Method);
2848   JSJitMethodOp method = info->method;
2849   bool ok = method(cx, obj, self, JSJitMethodCallArgs(args));
2850   if (ok) {
2851 #ifdef DEBUG
2852     AssertReturnTypeMatchesJitinfo(info, args.rval());
2853 #endif
2854     return true;
2855   }
2856 
2857   // Promise-returning methods always return objects
2858   MOZ_ASSERT(info->returnType() == JSVAL_TYPE_OBJECT);
2859   return ConvertExceptionToPromise(cx, args.rval());
2860 }
2861 
StaticMethodPromiseWrapper(JSContext * cx,unsigned argc,JS::Value * vp)2862 bool StaticMethodPromiseWrapper(JSContext* cx, unsigned argc, JS::Value* vp) {
2863   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
2864 
2865   const JSJitInfo* info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
2866   MOZ_ASSERT(info);
2867   MOZ_ASSERT(info->type() == JSJitInfo::StaticMethod);
2868 
2869   bool ok = info->staticMethod(cx, argc, vp);
2870   if (ok) {
2871     return true;
2872   }
2873 
2874   return ConvertExceptionToPromise(cx, args.rval());
2875 }
2876 
ConvertExceptionToPromise(JSContext * cx,JS::MutableHandle<JS::Value> rval)2877 bool ConvertExceptionToPromise(JSContext* cx,
2878                                JS::MutableHandle<JS::Value> rval) {
2879   JS::Rooted<JS::Value> exn(cx);
2880   if (!JS_GetPendingException(cx, &exn)) {
2881     // This is very important: if there is no pending exception here but we're
2882     // ending up in this code, that means the callee threw an uncatchable
2883     // exception.  Just propagate that out as-is.
2884     return false;
2885   }
2886 
2887   JS_ClearPendingException(cx);
2888 
2889   JSObject* promise = JS::CallOriginalPromiseReject(cx, exn);
2890   if (!promise) {
2891     // We just give up.  Put the exception back.
2892     JS_SetPendingException(cx, exn);
2893     return false;
2894   }
2895 
2896   rval.setObject(*promise);
2897   return true;
2898 }
2899 
2900 /* static */
TraceGlobal(JSTracer * aTrc,JSObject * aObj)2901 void CreateGlobalOptions<nsGlobalWindowInner>::TraceGlobal(JSTracer* aTrc,
2902                                                            JSObject* aObj) {
2903   xpc::TraceXPCGlobal(aTrc, aObj);
2904 }
2905 
2906 static bool sRegisteredDOMNames = false;
2907 
RegisterDOMNames()2908 nsresult RegisterDOMNames() {
2909   if (sRegisteredDOMNames) {
2910     return NS_OK;
2911   }
2912 
2913   // Register new DOM bindings
2914   WebIDLGlobalNameHash::Init();
2915 
2916   nsresult rv = nsDOMClassInfo::Init();
2917   if (NS_FAILED(rv)) {
2918     NS_ERROR("Could not initialize nsDOMClassInfo");
2919     return rv;
2920   }
2921 
2922   sRegisteredDOMNames = true;
2923 
2924   return NS_OK;
2925 }
2926 
2927 /* static */
PostCreateGlobal(JSContext * aCx,JS::Handle<JSObject * > aGlobal)2928 bool CreateGlobalOptions<nsGlobalWindowInner>::PostCreateGlobal(
2929     JSContext* aCx, JS::Handle<JSObject*> aGlobal) {
2930   nsresult rv = RegisterDOMNames();
2931   if (NS_FAILED(rv)) {
2932     return Throw(aCx, rv);
2933   }
2934 
2935   // Invoking the XPCWrappedNativeScope constructor automatically hooks it
2936   // up to the compartment of aGlobal.
2937   (void)new XPCWrappedNativeScope(aCx, aGlobal);
2938   return true;
2939 }
2940 
2941 #ifdef DEBUG
AssertReturnTypeMatchesJitinfo(const JSJitInfo * aJitInfo,JS::Handle<JS::Value> aValue)2942 void AssertReturnTypeMatchesJitinfo(const JSJitInfo* aJitInfo,
2943                                     JS::Handle<JS::Value> aValue) {
2944   switch (aJitInfo->returnType()) {
2945     case JSVAL_TYPE_UNKNOWN:
2946       // Any value is good.
2947       break;
2948     case JSVAL_TYPE_DOUBLE:
2949       // The value could actually be an int32 value as well.
2950       MOZ_ASSERT(aValue.isNumber());
2951       break;
2952     case JSVAL_TYPE_INT32:
2953       MOZ_ASSERT(aValue.isInt32());
2954       break;
2955     case JSVAL_TYPE_UNDEFINED:
2956       MOZ_ASSERT(aValue.isUndefined());
2957       break;
2958     case JSVAL_TYPE_BOOLEAN:
2959       MOZ_ASSERT(aValue.isBoolean());
2960       break;
2961     case JSVAL_TYPE_STRING:
2962       MOZ_ASSERT(aValue.isString());
2963       break;
2964     case JSVAL_TYPE_NULL:
2965       MOZ_ASSERT(aValue.isNull());
2966       break;
2967     case JSVAL_TYPE_OBJECT:
2968       MOZ_ASSERT(aValue.isObject());
2969       break;
2970     default:
2971       // Someone messed up their jitinfo type.
2972       MOZ_ASSERT(false, "Unexpected JSValueType stored in jitinfo");
2973       break;
2974   }
2975 }
2976 #endif
2977 
CallerSubsumes(JSObject * aObject)2978 bool CallerSubsumes(JSObject* aObject) {
2979   nsIPrincipal* objPrin =
2980       nsContentUtils::ObjectPrincipal(js::UncheckedUnwrap(aObject));
2981   return nsContentUtils::SubjectPrincipal()->Subsumes(objPrin);
2982 }
2983 
UnwrapArgImpl(JSContext * cx,JS::Handle<JSObject * > src,const nsIID & iid,void ** ppArg)2984 nsresult UnwrapArgImpl(JSContext* cx, JS::Handle<JSObject*> src,
2985                        const nsIID& iid, void** ppArg) {
2986   if (!NS_IsMainThread()) {
2987     return NS_ERROR_NOT_AVAILABLE;
2988   }
2989 
2990   nsCOMPtr<nsISupports> iface = xpc::UnwrapReflectorToISupports(src);
2991   if (iface) {
2992     if (NS_FAILED(iface->QueryInterface(iid, ppArg))) {
2993       return NS_ERROR_XPC_BAD_CONVERT_JS;
2994     }
2995 
2996     return NS_OK;
2997   }
2998 
2999   // Only allow XPCWrappedJS stuff in system code.  Ideally we would remove this
3000   // even there, but that involves converting some things to WebIDL callback
3001   // interfaces and making some other things builtinclass...
3002   if (!nsContentUtils::IsSystemCaller(cx)) {
3003     return NS_ERROR_XPC_BAD_CONVERT_JS;
3004   }
3005 
3006   RefPtr<nsXPCWrappedJS> wrappedJS;
3007   nsresult rv =
3008       nsXPCWrappedJS::GetNewOrUsed(src, iid, getter_AddRefs(wrappedJS));
3009   if (NS_FAILED(rv) || !wrappedJS) {
3010     return rv;
3011   }
3012 
3013   // We need to go through the QueryInterface logic to make this return
3014   // the right thing for the various 'special' interfaces; e.g.
3015   // nsIPropertyBag. We must use AggregatedQueryInterface in cases where
3016   // there is an outer to avoid nasty recursion.
3017   return wrappedJS->QueryInterface(iid, ppArg);
3018 }
3019 
UnwrapXPConnectImpl(JSContext * cx,JS::MutableHandle<JS::Value> src,const nsIID & iid,void ** ppArg)3020 nsresult UnwrapXPConnectImpl(JSContext* cx, JS::MutableHandle<JS::Value> src,
3021                              const nsIID& iid, void** ppArg) {
3022   if (!NS_IsMainThread()) {
3023     return NS_ERROR_NOT_AVAILABLE;
3024   }
3025 
3026   MOZ_ASSERT(src.isObject());
3027   // Unwrap ourselves, because we're going to want access to the unwrapped
3028   // object.
3029   JS::Rooted<JSObject*> obj(cx,
3030                             js::CheckedUnwrap(&src.toObject(),
3031                                               /* stopAtWindowProxy = */ false));
3032   if (!obj) {
3033     return NS_ERROR_NOT_AVAILABLE;
3034   }
3035 
3036   nsCOMPtr<nsISupports> iface = xpc::UnwrapReflectorToISupports(obj);
3037   if (!iface) {
3038     return NS_ERROR_XPC_BAD_CONVERT_JS;
3039   }
3040 
3041   if (NS_FAILED(iface->QueryInterface(iid, ppArg))) {
3042     return NS_ERROR_XPC_BAD_CONVERT_JS;
3043   }
3044 
3045   // Now update our source to keep rooting our object.
3046   src.setObject(*obj);
3047   return NS_OK;
3048 }
3049 
UnwrapWindowProxyImpl(JSContext * cx,JS::Handle<JSObject * > src,nsPIDOMWindowOuter ** ppArg)3050 nsresult UnwrapWindowProxyImpl(JSContext* cx, JS::Handle<JSObject*> src,
3051                                nsPIDOMWindowOuter** ppArg) {
3052   nsCOMPtr<nsPIDOMWindowInner> inner;
3053   nsresult rv = UnwrapArg<nsPIDOMWindowInner>(cx, src, getter_AddRefs(inner));
3054   NS_ENSURE_SUCCESS(rv, rv);
3055 
3056   nsCOMPtr<nsPIDOMWindowOuter> outer = inner->GetOuterWindow();
3057   outer.forget(ppArg);
3058   return NS_OK;
3059 }
3060 
SystemGlobalResolve(JSContext * cx,JS::Handle<JSObject * > obj,JS::Handle<jsid> id,bool * resolvedp)3061 bool SystemGlobalResolve(JSContext* cx, JS::Handle<JSObject*> obj,
3062                          JS::Handle<jsid> id, bool* resolvedp) {
3063   if (!ResolveGlobal(cx, obj, id, resolvedp)) {
3064     return false;
3065   }
3066 
3067   if (*resolvedp) {
3068     return true;
3069   }
3070 
3071   return ResolveSystemBinding(cx, obj, id, resolvedp);
3072 }
3073 
SystemGlobalEnumerate(JSContext * cx,JS::Handle<JSObject * > obj)3074 bool SystemGlobalEnumerate(JSContext* cx, JS::Handle<JSObject*> obj) {
3075   bool ignored = false;
3076   return JS_EnumerateStandardClasses(cx, obj) &&
3077          ResolveSystemBinding(cx, obj, JSID_VOIDHANDLE, &ignored);
3078 }
3079 
3080 template <decltype(JS::NewMapObject) Method>
GetMaplikeSetlikeBackingObject(JSContext * aCx,JS::Handle<JSObject * > aObj,size_t aSlotIndex,JS::MutableHandle<JSObject * > aBackingObj,bool * aBackingObjCreated)3081 bool GetMaplikeSetlikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
3082                                     size_t aSlotIndex,
3083                                     JS::MutableHandle<JSObject*> aBackingObj,
3084                                     bool* aBackingObjCreated) {
3085   JS::Rooted<JSObject*> reflector(aCx);
3086   reflector = IsDOMObject(aObj)
3087                   ? aObj
3088                   : js::UncheckedUnwrap(aObj,
3089                                         /* stopAtWindowProxy = */ false);
3090 
3091   // Retrieve the backing object from the reserved slot on the maplike/setlike
3092   // object. If it doesn't exist yet, create it.
3093   JS::Rooted<JS::Value> slotValue(aCx);
3094   slotValue = js::GetReservedSlot(reflector, aSlotIndex);
3095   if (slotValue.isUndefined()) {
3096     // Since backing object access can happen in non-originating compartments,
3097     // make sure to create the backing object in reflector compartment.
3098     {
3099       JSAutoCompartment ac(aCx, reflector);
3100       JS::Rooted<JSObject*> newBackingObj(aCx);
3101       newBackingObj.set(Method(aCx));
3102       if (NS_WARN_IF(!newBackingObj)) {
3103         return false;
3104       }
3105       js::SetReservedSlot(reflector, aSlotIndex,
3106                           JS::ObjectValue(*newBackingObj));
3107     }
3108     slotValue = js::GetReservedSlot(reflector, aSlotIndex);
3109     *aBackingObjCreated = true;
3110   } else {
3111     *aBackingObjCreated = false;
3112   }
3113   if (!MaybeWrapNonDOMObjectValue(aCx, &slotValue)) {
3114     return false;
3115   }
3116   aBackingObj.set(&slotValue.toObject());
3117   return true;
3118 }
3119 
GetMaplikeBackingObject(JSContext * aCx,JS::Handle<JSObject * > aObj,size_t aSlotIndex,JS::MutableHandle<JSObject * > aBackingObj,bool * aBackingObjCreated)3120 bool GetMaplikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
3121                              size_t aSlotIndex,
3122                              JS::MutableHandle<JSObject*> aBackingObj,
3123                              bool* aBackingObjCreated) {
3124   return GetMaplikeSetlikeBackingObject<JS::NewMapObject>(
3125       aCx, aObj, aSlotIndex, aBackingObj, aBackingObjCreated);
3126 }
3127 
GetSetlikeBackingObject(JSContext * aCx,JS::Handle<JSObject * > aObj,size_t aSlotIndex,JS::MutableHandle<JSObject * > aBackingObj,bool * aBackingObjCreated)3128 bool GetSetlikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
3129                              size_t aSlotIndex,
3130                              JS::MutableHandle<JSObject*> aBackingObj,
3131                              bool* aBackingObjCreated) {
3132   return GetMaplikeSetlikeBackingObject<JS::NewSetObject>(
3133       aCx, aObj, aSlotIndex, aBackingObj, aBackingObjCreated);
3134 }
3135 
ForEachHandler(JSContext * aCx,unsigned aArgc,JS::Value * aVp)3136 bool ForEachHandler(JSContext* aCx, unsigned aArgc, JS::Value* aVp) {
3137   JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
3138   // Unpack callback and object from slots
3139   JS::Rooted<JS::Value> callbackFn(
3140       aCx,
3141       js::GetFunctionNativeReserved(&args.callee(), FOREACH_CALLBACK_SLOT));
3142   JS::Rooted<JS::Value> maplikeOrSetlikeObj(
3143       aCx, js::GetFunctionNativeReserved(&args.callee(),
3144                                          FOREACH_MAPLIKEORSETLIKEOBJ_SLOT));
3145   MOZ_ASSERT(aArgc == 3);
3146   JS::AutoValueVector newArgs(aCx);
3147   // Arguments are passed in as value, key, object. Keep value and key, replace
3148   // object with the maplike/setlike object.
3149   if (!newArgs.append(args.get(0))) {
3150     return false;
3151   }
3152   if (!newArgs.append(args.get(1))) {
3153     return false;
3154   }
3155   if (!newArgs.append(maplikeOrSetlikeObj)) {
3156     return false;
3157   }
3158   JS::Rooted<JS::Value> rval(aCx, JS::UndefinedValue());
3159   // Now actually call the user specified callback
3160   return JS::Call(aCx, args.thisv(), callbackFn, newArgs, &rval);
3161 }
3162 
GetProtoIdForNewtarget(JS::Handle<JSObject * > aNewTarget)3163 static inline prototypes::ID GetProtoIdForNewtarget(
3164     JS::Handle<JSObject*> aNewTarget) {
3165   const js::Class* newTargetClass = js::GetObjectClass(aNewTarget);
3166   if (IsDOMIfaceAndProtoClass(newTargetClass)) {
3167     const DOMIfaceAndProtoJSClass* newTargetIfaceClass =
3168         DOMIfaceAndProtoJSClass::FromJSClass(newTargetClass);
3169     if (newTargetIfaceClass->mType == eInterface) {
3170       return newTargetIfaceClass->mPrototypeID;
3171     }
3172   } else if (JS_IsNativeFunction(aNewTarget, Constructor)) {
3173     return GetNativePropertyHooksFromConstructorFunction(aNewTarget)
3174         ->mPrototypeID;
3175   }
3176 
3177   return prototypes::id::_ID_Count;
3178 }
3179 
GetDesiredProto(JSContext * aCx,const JS::CallArgs & aCallArgs,JS::MutableHandle<JSObject * > aDesiredProto)3180 bool GetDesiredProto(JSContext* aCx, const JS::CallArgs& aCallArgs,
3181                      JS::MutableHandle<JSObject*> aDesiredProto) {
3182   if (!aCallArgs.isConstructing()) {
3183     aDesiredProto.set(nullptr);
3184     return true;
3185   }
3186 
3187   // The desired prototype depends on the actual constructor that was invoked,
3188   // which is passed to us as the newTarget in the callargs.  We want to do
3189   // something akin to the ES6 specification's GetProtototypeFromConstructor (so
3190   // get .prototype on the newTarget, with a fallback to some sort of default).
3191 
3192   // First, a fast path for the case when the the constructor is in fact one of
3193   // our DOM constructors.  This is safe because on those the "constructor"
3194   // property is non-configurable and non-writable, so we don't have to do the
3195   // slow JS_GetProperty call.
3196   JS::Rooted<JSObject*> newTarget(aCx, &aCallArgs.newTarget().toObject());
3197   JS::Rooted<JSObject*> originalNewTarget(aCx, newTarget);
3198   // See whether we have a known DOM constructor here, such that we can take a
3199   // fast path.
3200   prototypes::ID protoID = GetProtoIdForNewtarget(newTarget);
3201   if (protoID == prototypes::id::_ID_Count) {
3202     // We might still have a cross-compartment wrapper for a known DOM
3203     // constructor.
3204     newTarget = js::CheckedUnwrap(newTarget);
3205     if (newTarget && newTarget != originalNewTarget) {
3206       protoID = GetProtoIdForNewtarget(newTarget);
3207     }
3208   }
3209 
3210   if (protoID != prototypes::id::_ID_Count) {
3211     ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(
3212         js::GetGlobalForObjectCrossCompartment(newTarget));
3213     aDesiredProto.set(protoAndIfaceCache.EntrySlotMustExist(protoID));
3214     if (newTarget != originalNewTarget) {
3215       return JS_WrapObject(aCx, aDesiredProto);
3216     }
3217     return true;
3218   }
3219 
3220   // Slow path.  This basically duplicates the ES6 spec's
3221   // GetPrototypeFromConstructor except that instead of taking a string naming
3222   // the fallback prototype we just fall back to using null and assume that our
3223   // caller will then pick the right default.  The actual defaulting behavior
3224   // here still needs to be defined in the Web IDL specification.
3225   //
3226   // Note that it's very important to do this property get on originalNewTarget,
3227   // not our unwrapped newTarget, since we want to get Xray behavior here as
3228   // needed.
3229   // XXXbz for speed purposes, using a preinterned id here sure would be nice.
3230   JS::Rooted<JS::Value> protoVal(aCx);
3231   if (!JS_GetProperty(aCx, originalNewTarget, "prototype", &protoVal)) {
3232     return false;
3233   }
3234 
3235   if (!protoVal.isObject()) {
3236     aDesiredProto.set(nullptr);
3237     return true;
3238   }
3239 
3240   aDesiredProto.set(&protoVal.toObject());
3241   return true;
3242 }
3243 
3244 // https://html.spec.whatwg.org/multipage/dom.html#htmlconstructor
CreateXULOrHTMLElement(const GlobalObject & aGlobal,const JS::CallArgs & aCallArgs,JS::Handle<JSObject * > aGivenProto,ErrorResult & aRv)3245 already_AddRefed<Element> CreateXULOrHTMLElement(
3246     const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs,
3247     JS::Handle<JSObject*> aGivenProto, ErrorResult& aRv) {
3248   // Step 1.
3249   nsCOMPtr<nsPIDOMWindowInner> window =
3250       do_QueryInterface(aGlobal.GetAsSupports());
3251   if (!window) {
3252     aRv.Throw(NS_ERROR_UNEXPECTED);
3253     return nullptr;
3254   }
3255 
3256   nsIDocument* doc = window->GetExtantDoc();
3257   if (!doc) {
3258     aRv.Throw(NS_ERROR_UNEXPECTED);
3259     return nullptr;
3260   }
3261 
3262   int32_t ns = doc->GetDefaultNamespaceID();
3263   if (ns != kNameSpaceID_XUL) {
3264     ns = kNameSpaceID_XHTML;
3265   }
3266 
3267   RefPtr<mozilla::dom::CustomElementRegistry> registry(
3268       window->CustomElements());
3269   if (!registry) {
3270     aRv.Throw(NS_ERROR_UNEXPECTED);
3271     return nullptr;
3272   }
3273 
3274   // Step 2 is in the code output by CGClassConstructor.
3275   // Step 3.
3276   JSContext* cx = aGlobal.Context();
3277   JS::Rooted<JSObject*> newTarget(cx, &aCallArgs.newTarget().toObject());
3278   CustomElementDefinition* definition =
3279       registry->LookupCustomElementDefinition(cx, newTarget);
3280   if (!definition) {
3281     aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>();
3282     return nullptr;
3283   }
3284 
3285   // The callee might be an Xray. Unwrap it to get actual callee.
3286   JS::Rooted<JSObject*> callee(cx, js::CheckedUnwrap(&aCallArgs.callee()));
3287   if (!callee) {
3288     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
3289     return nullptr;
3290   }
3291 
3292   // And the actual callee might be in different compartment, so enter its
3293   // compartment before getting the standard constructor object to compare to,
3294   // so we get it from the same global as callee itself.
3295   JSAutoCompartment ac(cx, callee);
3296   int32_t tag = eHTMLTag_userdefined;
3297   if (!definition->IsCustomBuiltIn()) {
3298     // Step 4.
3299     // If the definition is for an autonomous custom element, the active
3300     // function should be HTMLElement or XULElement
3301     JS::Rooted<JSObject*> constructor(cx);
3302     if (ns == kNameSpaceID_XUL) {
3303       constructor = XULElementBinding::GetConstructorObject(cx);
3304     } else {
3305       constructor = HTMLElementBinding::GetConstructorObject(cx);
3306     }
3307 
3308     if (!constructor) {
3309       aRv.NoteJSContextException(cx);
3310       return nullptr;
3311     }
3312 
3313     if (callee != constructor) {
3314       aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>();
3315       return nullptr;
3316     }
3317   } else {
3318     // Step 5.
3319     // If the definition is for a customized built-in element, the localName
3320     // should be defined in the specification.
3321 
3322     // Customized built-in elements are not supported for XUL yet.
3323     if (ns == kNameSpaceID_XUL) {
3324       aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
3325       return nullptr;
3326     }
3327 
3328     tag = nsHTMLTags::CaseSensitiveAtomTagToId(definition->mLocalName);
3329     if (tag == eHTMLTag_userdefined) {
3330       aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>();
3331       return nullptr;
3332     }
3333 
3334     MOZ_ASSERT(tag <= NS_HTML_TAG_MAX, "tag is out of bounds");
3335 
3336     // If the definition is for a customized built-in element, the active
3337     // function should be the localname's element interface.
3338     constructorGetterCallback cb = sConstructorGetterCallback[tag];
3339     if (!cb) {
3340       aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>();
3341       return nullptr;
3342     }
3343 
3344     JS::Rooted<JSObject*> constructor(cx, cb(cx));
3345     if (!constructor) {
3346       aRv.NoteJSContextException(cx);
3347       return nullptr;
3348     }
3349 
3350     if (callee != constructor) {
3351       aRv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>();
3352       return nullptr;
3353     }
3354   }
3355 
3356   RefPtr<mozilla::dom::NodeInfo> nodeInfo = doc->NodeInfoManager()->GetNodeInfo(
3357       definition->mLocalName, nullptr, ns, nsINode::ELEMENT_NODE);
3358   if (!nodeInfo) {
3359     aRv.Throw(NS_ERROR_UNEXPECTED);
3360     return nullptr;
3361   }
3362 
3363   // Step 6 and Step 7 are in the code output by CGClassConstructor.
3364   // Step 8.
3365   nsTArray<RefPtr<Element>>& constructionStack = definition->mConstructionStack;
3366   if (constructionStack.IsEmpty()) {
3367     RefPtr<Element> newElement;
3368     if (ns == kNameSpaceID_XUL) {
3369       newElement = new nsXULElement(nodeInfo.forget());
3370     } else {
3371       if (tag == eHTMLTag_userdefined) {
3372         // Autonomous custom element.
3373         newElement = NS_NewHTMLElement(nodeInfo.forget());
3374       } else {
3375         // Customized built-in element.
3376         newElement = CreateHTMLElement(tag, nodeInfo.forget(), NOT_FROM_PARSER);
3377       }
3378     }
3379 
3380     newElement->SetCustomElementData(new CustomElementData(
3381         definition->mType, CustomElementData::State::eCustom));
3382 
3383     newElement->SetCustomElementDefinition(definition);
3384 
3385     return newElement.forget();
3386   }
3387 
3388   // Step 9.
3389   RefPtr<Element>& element = constructionStack.LastElement();
3390 
3391   // Step 10.
3392   if (element == ALEADY_CONSTRUCTED_MARKER) {
3393     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
3394     return nullptr;
3395   }
3396 
3397   // Step 11.
3398   // Do prototype swizzling for upgrading a custom element here, for cases when
3399   // we have a reflector already.  If we don't have one yet, our caller will
3400   // create it with the right proto (by calling DoGetOrCreateDOMReflector with
3401   // that proto).
3402   JS::Rooted<JSObject*> reflector(cx, element->GetWrapper());
3403   if (reflector) {
3404     // reflector might be in different compartment.
3405     JSAutoCompartment ac(cx, reflector);
3406     JS::Rooted<JSObject*> givenProto(cx, aGivenProto);
3407     if (!JS_WrapObject(cx, &givenProto) ||
3408         !JS_SetPrototype(cx, reflector, givenProto)) {
3409       aRv.NoteJSContextException(cx);
3410       return nullptr;
3411     }
3412   }
3413 
3414   // Step 12 and Step 13.
3415   return element.forget();
3416 }
3417 
3418 #ifdef DEBUG
3419 namespace binding_detail {
AssertReflectorHasGivenProto(JSContext * aCx,JSObject * aReflector,JS::Handle<JSObject * > aGivenProto)3420 void AssertReflectorHasGivenProto(JSContext* aCx, JSObject* aReflector,
3421                                   JS::Handle<JSObject*> aGivenProto) {
3422   if (!aGivenProto) {
3423     // Nothing to assert here
3424     return;
3425   }
3426 
3427   JS::Rooted<JSObject*> reflector(aCx, aReflector);
3428   JSAutoCompartment ac(aCx, reflector);
3429   JS::Rooted<JSObject*> reflectorProto(aCx);
3430   bool ok = JS_GetPrototype(aCx, reflector, &reflectorProto);
3431   MOZ_ASSERT(ok);
3432   // aGivenProto may not be in the right compartment here, so we
3433   // have to wrap it to compare.
3434   JS::Rooted<JSObject*> givenProto(aCx, aGivenProto);
3435   ok = JS_WrapObject(aCx, &givenProto);
3436   MOZ_ASSERT(ok);
3437   MOZ_ASSERT(givenProto == reflectorProto,
3438              "How are we supposed to change the proto now?");
3439 }
3440 }  // namespace binding_detail
3441 #endif  // DEBUG
3442 
SetDocumentAndPageUseCounter(JSObject * aObject,UseCounter aUseCounter)3443 void SetDocumentAndPageUseCounter(JSObject* aObject, UseCounter aUseCounter) {
3444   nsGlobalWindowInner* win =
3445       xpc::WindowGlobalOrNull(js::UncheckedUnwrap(aObject));
3446   if (win && win->GetDocument()) {
3447     win->GetDocument()->SetDocumentAndPageUseCounter(aUseCounter);
3448   }
3449 }
3450 
3451 namespace {
3452 
3453 // This runnable is used to write a deprecation message from a worker to the
3454 // console running on the main-thread.
3455 class DeprecationWarningRunnable final
3456     : public WorkerProxyToMainThreadRunnable {
3457   nsIDocument::DeprecatedOperations mOperation;
3458 
3459  public:
DeprecationWarningRunnable(WorkerPrivate * aWorkerPrivate,nsIDocument::DeprecatedOperations aOperation)3460   DeprecationWarningRunnable(WorkerPrivate* aWorkerPrivate,
3461                              nsIDocument::DeprecatedOperations aOperation)
3462       : WorkerProxyToMainThreadRunnable(aWorkerPrivate),
3463         mOperation(aOperation) {
3464     MOZ_ASSERT(aWorkerPrivate);
3465     aWorkerPrivate->AssertIsOnWorkerThread();
3466   }
3467 
3468  private:
RunOnMainThread()3469   void RunOnMainThread() override {
3470     MOZ_ASSERT(NS_IsMainThread());
3471 
3472     // Walk up to our containing page
3473     WorkerPrivate* wp = mWorkerPrivate;
3474     while (wp->GetParent()) {
3475       wp = wp->GetParent();
3476     }
3477 
3478     nsPIDOMWindowInner* window = wp->GetWindow();
3479     if (window && window->GetExtantDoc()) {
3480       window->GetExtantDoc()->WarnOnceAbout(mOperation);
3481     }
3482   }
3483 
RunBackOnWorkerThreadForCleanup()3484   void RunBackOnWorkerThreadForCleanup() override {}
3485 };
3486 
3487 }  // anonymous namespace
3488 
DeprecationWarning(JSContext * aCx,JSObject * aObject,nsIDocument::DeprecatedOperations aOperation)3489 void DeprecationWarning(JSContext* aCx, JSObject* aObject,
3490                         nsIDocument::DeprecatedOperations aOperation) {
3491   GlobalObject global(aCx, aObject);
3492   if (global.Failed()) {
3493     NS_ERROR("Could not create global for DeprecationWarning");
3494     return;
3495   }
3496 
3497   DeprecationWarning(global, aOperation);
3498 }
3499 
DeprecationWarning(const GlobalObject & aGlobal,nsIDocument::DeprecatedOperations aOperation)3500 void DeprecationWarning(const GlobalObject& aGlobal,
3501                         nsIDocument::DeprecatedOperations aOperation) {
3502   if (NS_IsMainThread()) {
3503     nsCOMPtr<nsPIDOMWindowInner> window =
3504         do_QueryInterface(aGlobal.GetAsSupports());
3505     if (window && window->GetExtantDoc()) {
3506       window->GetExtantDoc()->WarnOnceAbout(aOperation);
3507     }
3508 
3509     return;
3510   }
3511 
3512   WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aGlobal.Context());
3513   if (!workerPrivate) {
3514     return;
3515   }
3516 
3517   RefPtr<DeprecationWarningRunnable> runnable =
3518       new DeprecationWarningRunnable(workerPrivate, aOperation);
3519   runnable->Dispatch();
3520 }
3521 
3522 namespace binding_detail {
UnprivilegedJunkScopeOrWorkerGlobal()3523 JSObject* UnprivilegedJunkScopeOrWorkerGlobal() {
3524   if (NS_IsMainThread()) {
3525     return xpc::UnprivilegedJunkScope();
3526   }
3527 
3528   return GetCurrentThreadWorkerGlobal();
3529 }
3530 }  // namespace binding_detail
3531 
GetPerInterfaceObjectHandle(JSContext * aCx,size_t aSlotId,CreateInterfaceObjectsMethod aCreator,bool aDefineOnGlobal)3532 JS::Handle<JSObject*> GetPerInterfaceObjectHandle(
3533     JSContext* aCx, size_t aSlotId, CreateInterfaceObjectsMethod aCreator,
3534     bool aDefineOnGlobal) {
3535   /* Make sure our global is sane.  Hopefully we can remove this sometime */
3536   JSObject* global = JS::CurrentGlobalOrNull(aCx);
3537   if (!(js::GetObjectClass(global)->flags & JSCLASS_DOM_GLOBAL)) {
3538     return nullptr;
3539   }
3540 
3541   /* Check to see whether the interface objects are already installed */
3542   ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(global);
3543   if (!protoAndIfaceCache.HasEntryInSlot(aSlotId)) {
3544     JS::Rooted<JSObject*> rootedGlobal(aCx, global);
3545     aCreator(aCx, rootedGlobal, protoAndIfaceCache, aDefineOnGlobal);
3546   }
3547 
3548   /*
3549    * The object might _still_ be null, but that's OK.
3550    *
3551    * Calling fromMarkedLocation() is safe because protoAndIfaceCache is
3552    * traced by TraceProtoAndIfaceCache() and its contents are never
3553    * changed after they have been set.
3554    *
3555    * Calling address() avoids the read barrier that does gray unmarking, but
3556    * it's not possible for the object to be gray here.
3557    */
3558 
3559   const JS::Heap<JSObject*>& entrySlot =
3560       protoAndIfaceCache.EntrySlotMustExist(aSlotId);
3561   MOZ_ASSERT(JS::ObjectIsNotGray(entrySlot));
3562   return JS::Handle<JSObject*>::fromMarkedLocation(entrySlot.address());
3563 }
3564 
3565 }  // namespace dom
3566 }  // namespace mozilla
3567