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