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