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