Bumping manifests a=b2g-bump
[gecko.git] / dom / bindings / BindingUtils.cpp
blob60ab62c27f414aa1e7761df415ef5bf2cebd39a0
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
2 /* vim: set ts=2 sw=2 et tw=79: */
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/. */
7 #include "BindingUtils.h"
9 #include <algorithm>
10 #include <stdarg.h>
12 #include "JavaScriptParent.h"
14 #include "mozilla/DebugOnly.h"
15 #include "mozilla/FloatingPoint.h"
16 #include "mozilla/Assertions.h"
17 #include "mozilla/Preferences.h"
19 #include "AccessCheck.h"
20 #include "jsfriendapi.h"
21 #include "js/OldDebugAPI.h"
22 #include "nsContentUtils.h"
23 #include "nsGlobalWindow.h"
24 #include "nsIDOMGlobalPropertyInitializer.h"
25 #include "nsIPermissionManager.h"
26 #include "nsIPrincipal.h"
27 #include "nsIXPConnect.h"
28 #include "nsUTF8Utils.h"
29 #include "WrapperFactory.h"
30 #include "xpcprivate.h"
31 #include "XPCQuickStubs.h"
32 #include "XrayWrapper.h"
33 #include "nsPrintfCString.h"
34 #include "prprf.h"
36 #include "mozilla/dom/ScriptSettings.h"
37 #include "mozilla/dom/DOMError.h"
38 #include "mozilla/dom/DOMErrorBinding.h"
39 #include "mozilla/dom/HTMLObjectElement.h"
40 #include "mozilla/dom/HTMLObjectElementBinding.h"
41 #include "mozilla/dom/HTMLSharedObjectElement.h"
42 #include "mozilla/dom/HTMLEmbedElementBinding.h"
43 #include "mozilla/dom/HTMLAppletElementBinding.h"
44 #include "mozilla/dom/Promise.h"
45 #include "WorkerPrivate.h"
46 #include "nsDOMClassInfo.h"
48 namespace mozilla {
49 namespace dom {
51 JSErrorFormatString ErrorFormatString[] = {
52 #define MSG_DEF(_name, _argc, _str) \
53 { _str, _argc, JSEXN_TYPEERR },
54 #include "mozilla/dom/Errors.msg"
55 #undef MSG_DEF
58 const JSErrorFormatString*
59 GetErrorMessage(void* aUserRef, const unsigned aErrorNumber)
61 MOZ_ASSERT(aErrorNumber < ArrayLength(ErrorFormatString));
62 return &ErrorFormatString[aErrorNumber];
65 bool
66 ThrowErrorMessage(JSContext* aCx, const ErrNum aErrorNumber, ...)
68 va_list ap;
69 va_start(ap, aErrorNumber);
70 JS_ReportErrorNumberVA(aCx, GetErrorMessage, nullptr,
71 static_cast<const unsigned>(aErrorNumber), ap);
72 va_end(ap);
73 return false;
76 bool
77 ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
78 const ErrNum aErrorNumber,
79 const char* aInterfaceName)
81 NS_ConvertASCIItoUTF16 ifaceName(aInterfaceName);
82 // This should only be called for DOM methods/getters/setters, which
83 // are JSNative-backed functions, so we can assume that
84 // JS_ValueToFunction and JS_GetFunctionDisplayId will both return
85 // non-null and that JS_GetStringCharsZ returns non-null.
86 JS::Rooted<JSFunction*> func(aCx, JS_ValueToFunction(aCx, aArgs.calleev()));
87 MOZ_ASSERT(func);
88 JS::Rooted<JSString*> funcName(aCx, JS_GetFunctionDisplayId(func));
89 MOZ_ASSERT(funcName);
90 nsAutoJSString funcNameStr;
91 if (!funcNameStr.init(aCx, funcName)) {
92 return false;
94 JS_ReportErrorNumberUC(aCx, GetErrorMessage, nullptr,
95 static_cast<const unsigned>(aErrorNumber),
96 funcNameStr.get(), ifaceName.get());
97 return false;
100 bool
101 ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
102 const ErrNum aErrorNumber,
103 prototypes::ID aProtoId)
105 return ThrowInvalidThis(aCx, aArgs, aErrorNumber,
106 NamesOfInterfacesWithProtos(aProtoId));
109 bool
110 ThrowNoSetterArg(JSContext* aCx, prototypes::ID aProtoId)
112 nsPrintfCString errorMessage("%s attribute setter",
113 NamesOfInterfacesWithProtos(aProtoId));
114 return ThrowErrorMessage(aCx, MSG_MISSING_ARGUMENTS, errorMessage.get());
117 } // namespace dom
119 struct ErrorResult::Message {
120 nsTArray<nsString> mArgs;
121 dom::ErrNum mErrorNumber;
124 void
125 ErrorResult::ThrowTypeError(const dom::ErrNum errorNumber, ...)
127 va_list ap;
128 va_start(ap, errorNumber);
129 if (IsJSException()) {
130 // We have rooted our mJSException, and we don't have the info
131 // needed to unroot here, so just bail.
132 va_end(ap);
133 MOZ_ASSERT(false,
134 "Ignoring ThrowTypeError call because we have a JS exception");
135 return;
137 if (IsTypeError()) {
138 delete mMessage;
140 mResult = NS_ERROR_TYPE_ERR;
141 Message* message = new Message();
142 message->mErrorNumber = errorNumber;
143 uint16_t argCount = dom::GetErrorMessage(nullptr, errorNumber)->argCount;
144 MOZ_ASSERT(argCount <= 10);
145 argCount = std::min<uint16_t>(argCount, 10);
146 while (argCount--) {
147 message->mArgs.AppendElement(*va_arg(ap, nsString*));
149 mMessage = message;
150 va_end(ap);
153 void
154 ErrorResult::ReportTypeError(JSContext* aCx)
156 MOZ_ASSERT(mMessage, "ReportTypeError() can be called only once");
158 Message* message = mMessage;
159 const uint32_t argCount = message->mArgs.Length();
160 const jschar* args[11];
161 for (uint32_t i = 0; i < argCount; ++i) {
162 args[i] = message->mArgs.ElementAt(i).get();
164 args[argCount] = nullptr;
166 JS_ReportErrorNumberUCArray(aCx, dom::GetErrorMessage, nullptr,
167 static_cast<const unsigned>(message->mErrorNumber),
168 argCount > 0 ? args : nullptr);
170 ClearMessage();
173 void
174 ErrorResult::ClearMessage()
176 if (IsTypeError()) {
177 delete mMessage;
178 mMessage = nullptr;
182 void
183 ErrorResult::ThrowJSException(JSContext* cx, JS::Handle<JS::Value> exn)
185 MOZ_ASSERT(mMightHaveUnreportedJSException,
186 "Why didn't you tell us you planned to throw a JS exception?");
188 if (IsTypeError()) {
189 delete mMessage;
192 // Make sure mJSException is initialized _before_ we try to root it. But
193 // don't set it to exn yet, because we don't want to do that until after we
194 // root.
195 mJSException = JS::UndefinedValue();
196 if (!js::AddRawValueRoot(cx, &mJSException, "ErrorResult::mJSException")) {
197 // Don't use NS_ERROR_DOM_JS_EXCEPTION, because that indicates we have
198 // in fact rooted mJSException.
199 mResult = NS_ERROR_OUT_OF_MEMORY;
200 } else {
201 mJSException = exn;
202 mResult = NS_ERROR_DOM_JS_EXCEPTION;
206 void
207 ErrorResult::ReportJSException(JSContext* cx)
209 MOZ_ASSERT(!mMightHaveUnreportedJSException,
210 "Why didn't you tell us you planned to handle JS exceptions?");
212 JS::Rooted<JS::Value> exception(cx, mJSException);
213 if (JS_WrapValue(cx, &exception)) {
214 JS_SetPendingException(cx, exception);
216 mJSException = exception;
217 // If JS_WrapValue failed, not much we can do about it... No matter
218 // what, go ahead and unroot mJSException.
219 js::RemoveRawValueRoot(cx, &mJSException);
222 void
223 ErrorResult::ReportJSExceptionFromJSImplementation(JSContext* aCx)
225 MOZ_ASSERT(!mMightHaveUnreportedJSException,
226 "Why didn't you tell us you planned to handle JS exceptions?");
228 dom::DOMError* domError;
229 nsresult rv = UNWRAP_OBJECT(DOMError, &mJSException.toObject(), domError);
230 if (NS_FAILED(rv)) {
231 // Unwrapping really shouldn't fail here, if mExceptionHandling is set to
232 // eRethrowContentExceptions then the CallSetup destructor only stores an
233 // exception if it unwraps to DOMError. If we reach this then either
234 // mExceptionHandling wasn't set to eRethrowContentExceptions and we
235 // shouldn't be calling ReportJSExceptionFromJSImplementation or something
236 // went really wrong.
237 NS_RUNTIMEABORT("We stored a non-DOMError exception!");
240 nsString message;
241 domError->GetMessage(message);
243 JS_ReportError(aCx, "%hs", message.get());
244 js::RemoveRawValueRoot(aCx, &mJSException);
246 // We no longer have a useful exception but we do want to signal that an error
247 // occured.
248 mResult = NS_ERROR_FAILURE;
251 void
252 ErrorResult::StealJSException(JSContext* cx,
253 JS::MutableHandle<JS::Value> value)
255 MOZ_ASSERT(!mMightHaveUnreportedJSException,
256 "Must call WouldReportJSException unconditionally in all codepaths that might call StealJSException");
257 MOZ_ASSERT(IsJSException(), "No exception to steal");
259 value.set(mJSException);
260 js::RemoveRawValueRoot(cx, &mJSException);
261 mResult = NS_OK;
264 void
265 ErrorResult::ReportNotEnoughArgsError(JSContext* cx,
266 const char* ifaceName,
267 const char* memberName)
269 MOZ_ASSERT(ErrorCode() == NS_ERROR_XPC_NOT_ENOUGH_ARGS);
271 nsPrintfCString errorMessage("%s.%s", ifaceName, memberName);
272 ThrowErrorMessage(cx, dom::MSG_MISSING_ARGUMENTS, errorMessage.get());
275 namespace dom {
277 bool
278 DefineConstants(JSContext* cx, JS::Handle<JSObject*> obj,
279 const ConstantSpec* cs)
281 JS::Rooted<JS::Value> value(cx);
282 for (; cs->name; ++cs) {
283 value = cs->value;
284 bool ok =
285 JS_DefineProperty(cx, obj, cs->name, value,
286 JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
287 if (!ok) {
288 return false;
291 return true;
294 static inline bool
295 Define(JSContext* cx, JS::Handle<JSObject*> obj, const JSFunctionSpec* spec) {
296 return JS_DefineFunctions(cx, obj, spec);
298 static inline bool
299 Define(JSContext* cx, JS::Handle<JSObject*> obj, const JSPropertySpec* spec) {
300 return JS_DefineProperties(cx, obj, spec);
302 static inline bool
303 Define(JSContext* cx, JS::Handle<JSObject*> obj, const ConstantSpec* spec) {
304 return DefineConstants(cx, obj, spec);
307 template<typename T>
308 bool
309 DefinePrefable(JSContext* cx, JS::Handle<JSObject*> obj,
310 const Prefable<T>* props)
312 MOZ_ASSERT(props);
313 MOZ_ASSERT(props->specs);
314 do {
315 // Define if enabled
316 if (props->isEnabled(cx, obj)) {
317 if (!Define(cx, obj, props->specs)) {
318 return false;
321 } while ((++props)->specs);
322 return true;
325 bool
326 DefineUnforgeableMethods(JSContext* cx, JS::Handle<JSObject*> obj,
327 const Prefable<const JSFunctionSpec>* props)
329 return DefinePrefable(cx, obj, props);
332 bool
333 DefineUnforgeableAttributes(JSContext* cx, JS::Handle<JSObject*> obj,
334 const Prefable<const JSPropertySpec>* props)
336 return DefinePrefable(cx, obj, props);
340 // We should use JSFunction objects for interface objects, but we need a custom
341 // hasInstance hook because we have new interface objects on prototype chains of
342 // old (XPConnect-based) bindings. Because Function.prototype.toString throws if
343 // passed a non-Function object we also need to provide our own toString method
344 // for interface objects.
346 enum {
347 TOSTRING_CLASS_RESERVED_SLOT = 0,
348 TOSTRING_NAME_RESERVED_SLOT = 1
351 static bool
352 InterfaceObjectToString(JSContext* cx, unsigned argc, JS::Value *vp)
354 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
355 JS::Rooted<JSObject*> callee(cx, &args.callee());
357 if (!args.thisv().isObject()) {
358 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
359 JSMSG_CANT_CONVERT_TO, "null", "object");
360 return false;
363 JS::Value v = js::GetFunctionNativeReserved(callee,
364 TOSTRING_CLASS_RESERVED_SLOT);
365 const JSClass* clasp = static_cast<const JSClass*>(v.toPrivate());
367 v = js::GetFunctionNativeReserved(callee, TOSTRING_NAME_RESERVED_SLOT);
368 JSString* jsname = v.toString();
370 nsAutoJSString name;
371 if (!name.init(cx, jsname)) {
372 return false;
375 if (js::GetObjectJSClass(&args.thisv().toObject()) != clasp) {
376 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
377 JSMSG_INCOMPATIBLE_PROTO,
378 NS_ConvertUTF16toUTF8(name).get(), "toString",
379 "object");
380 return false;
383 nsString str;
384 str.AppendLiteral("function ");
385 str.Append(name);
386 str.AppendLiteral("() {");
387 str.Append('\n');
388 str.AppendLiteral(" [native code]");
389 str.Append('\n');
390 str.Append('}');
392 return xpc::NonVoidStringToJsval(cx, str, args.rval());
395 bool
396 Constructor(JSContext* cx, unsigned argc, JS::Value* vp)
398 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
399 const JS::Value& v =
400 js::GetFunctionNativeReserved(&args.callee(),
401 CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT);
402 const JSNativeHolder* nativeHolder =
403 static_cast<const JSNativeHolder*>(v.toPrivate());
404 return (nativeHolder->mNative)(cx, argc, vp);
407 static JSObject*
408 CreateConstructor(JSContext* cx, JS::Handle<JSObject*> global, const char* name,
409 const JSNativeHolder* nativeHolder, unsigned ctorNargs)
411 JSFunction* fun = js::NewFunctionWithReserved(cx, Constructor, ctorNargs,
412 JSFUN_CONSTRUCTOR, global,
413 name);
414 if (!fun) {
415 return nullptr;
418 JSObject* constructor = JS_GetFunctionObject(fun);
419 js::SetFunctionNativeReserved(constructor,
420 CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT,
421 js::PrivateValue(const_cast<JSNativeHolder*>(nativeHolder)));
422 return constructor;
425 static bool
426 DefineConstructor(JSContext* cx, JS::Handle<JSObject*> global, const char* name,
427 JS::Handle<JSObject*> constructor)
429 bool alreadyDefined;
430 if (!JS_AlreadyHasOwnProperty(cx, global, name, &alreadyDefined)) {
431 return false;
434 // This is Enumerable: False per spec.
435 return alreadyDefined ||
436 JS_DefineProperty(cx, global, name, constructor, 0);
439 static JSObject*
440 CreateInterfaceObject(JSContext* cx, JS::Handle<JSObject*> global,
441 JS::Handle<JSObject*> constructorProto,
442 const JSClass* constructorClass,
443 const JSNativeHolder* constructorNative,
444 unsigned ctorNargs, const NamedConstructor* namedConstructors,
445 JS::Handle<JSObject*> proto,
446 const NativeProperties* properties,
447 const NativeProperties* chromeOnlyProperties,
448 const char* name, bool defineOnGlobal)
450 JS::Rooted<JSObject*> constructor(cx);
451 if (constructorClass) {
452 MOZ_ASSERT(constructorProto);
453 constructor = JS_NewObject(cx, constructorClass, constructorProto, global);
454 } else {
455 MOZ_ASSERT(constructorNative);
456 MOZ_ASSERT(constructorProto == JS_GetFunctionPrototype(cx, global));
457 constructor = CreateConstructor(cx, global, name, constructorNative,
458 ctorNargs);
460 if (!constructor) {
461 return nullptr;
464 if (constructorClass) {
465 // Have to shadow Function.prototype.toString, since that throws
466 // on things that are not js::FunctionClass.
467 JS::Rooted<JSFunction*> toString(cx,
468 js::DefineFunctionWithReserved(cx, constructor,
469 "toString",
470 InterfaceObjectToString,
471 0, 0));
472 if (!toString) {
473 return nullptr;
476 JSString *str = ::JS_InternString(cx, name);
477 if (!str) {
478 return nullptr;
480 JSObject* toStringObj = JS_GetFunctionObject(toString);
481 js::SetFunctionNativeReserved(toStringObj, TOSTRING_CLASS_RESERVED_SLOT,
482 PRIVATE_TO_JSVAL(const_cast<JSClass *>(constructorClass)));
484 js::SetFunctionNativeReserved(toStringObj, TOSTRING_NAME_RESERVED_SLOT,
485 STRING_TO_JSVAL(str));
487 if (!JS_DefineProperty(cx, constructor, "length", ctorNargs,
488 JSPROP_READONLY | JSPROP_PERMANENT)) {
489 return nullptr;
493 if (properties) {
494 if (properties->staticMethods &&
495 !DefinePrefable(cx, constructor, properties->staticMethods)) {
496 return nullptr;
499 if (properties->staticAttributes &&
500 !DefinePrefable(cx, constructor, properties->staticAttributes)) {
501 return nullptr;
504 if (properties->constants &&
505 !DefinePrefable(cx, constructor, properties->constants)) {
506 return nullptr;
510 if (chromeOnlyProperties) {
511 if (chromeOnlyProperties->staticMethods &&
512 !DefinePrefable(cx, constructor, chromeOnlyProperties->staticMethods)) {
513 return nullptr;
516 if (chromeOnlyProperties->staticAttributes &&
517 !DefinePrefable(cx, constructor,
518 chromeOnlyProperties->staticAttributes)) {
519 return nullptr;
522 if (chromeOnlyProperties->constants &&
523 !DefinePrefable(cx, constructor, chromeOnlyProperties->constants)) {
524 return nullptr;
528 if (proto && !JS_LinkConstructorAndPrototype(cx, constructor, proto)) {
529 return nullptr;
532 if (defineOnGlobal && !DefineConstructor(cx, global, name, constructor)) {
533 return nullptr;
536 if (namedConstructors) {
537 int namedConstructorSlot = DOM_INTERFACE_SLOTS_BASE;
538 while (namedConstructors->mName) {
539 JS::Rooted<JSObject*> namedConstructor(cx,
540 CreateConstructor(cx, global, namedConstructors->mName,
541 &namedConstructors->mHolder,
542 namedConstructors->mNargs));
543 if (!namedConstructor ||
544 !JS_DefineProperty(cx, namedConstructor, "prototype",
545 proto, JSPROP_PERMANENT | JSPROP_READONLY,
546 JS_PropertyStub, JS_StrictPropertyStub) ||
547 (defineOnGlobal &&
548 !DefineConstructor(cx, global, namedConstructors->mName,
549 namedConstructor))) {
550 return nullptr;
552 js::SetReservedSlot(constructor, namedConstructorSlot++,
553 JS::ObjectValue(*namedConstructor));
554 ++namedConstructors;
558 return constructor;
561 bool
562 DefineWebIDLBindingUnforgeablePropertiesOnXPCObject(JSContext* cx,
563 JS::Handle<JSObject*> obj,
564 const NativeProperties* properties)
566 if (properties->unforgeableAttributes &&
567 !DefinePrefable(cx, obj, properties->unforgeableAttributes)) {
568 return false;
571 return true;
574 bool
575 DefineWebIDLBindingPropertiesOnXPCObject(JSContext* cx,
576 JS::Handle<JSObject*> obj,
577 const NativeProperties* properties)
579 if (properties->methods &&
580 !DefinePrefable(cx, obj, properties->methods)) {
581 return false;
584 if (properties->attributes &&
585 !DefinePrefable(cx, obj, properties->attributes)) {
586 return false;
589 return true;
592 static JSObject*
593 CreateInterfacePrototypeObject(JSContext* cx, JS::Handle<JSObject*> global,
594 JS::Handle<JSObject*> parentProto,
595 const JSClass* protoClass,
596 const NativeProperties* properties,
597 const NativeProperties* chromeOnlyProperties)
599 JS::Rooted<JSObject*> ourProto(cx,
600 JS_NewObjectWithUniqueType(cx, protoClass, parentProto, global));
601 if (!ourProto ||
602 !DefineProperties(cx, ourProto, properties, chromeOnlyProperties)) {
603 return nullptr;
606 return ourProto;
609 bool
610 DefineProperties(JSContext* cx, JS::Handle<JSObject*> obj,
611 const NativeProperties* properties,
612 const NativeProperties* chromeOnlyProperties)
614 if (properties) {
615 if (properties->methods &&
616 !DefinePrefable(cx, obj, properties->methods)) {
617 return false;
620 if (properties->attributes &&
621 !DefinePrefable(cx, obj, properties->attributes)) {
622 return false;
625 if (properties->constants &&
626 !DefinePrefable(cx, obj, properties->constants)) {
627 return false;
631 if (chromeOnlyProperties) {
632 if (chromeOnlyProperties->methods &&
633 !DefinePrefable(cx, obj, chromeOnlyProperties->methods)) {
634 return false;
637 if (chromeOnlyProperties->attributes &&
638 !DefinePrefable(cx, obj, chromeOnlyProperties->attributes)) {
639 return false;
642 if (chromeOnlyProperties->constants &&
643 !DefinePrefable(cx, obj, chromeOnlyProperties->constants)) {
644 return false;
648 return true;
651 void
652 CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global,
653 JS::Handle<JSObject*> protoProto,
654 const JSClass* protoClass, JS::Heap<JSObject*>* protoCache,
655 JS::Handle<JSObject*> constructorProto,
656 const JSClass* constructorClass, const JSNativeHolder* constructor,
657 unsigned ctorNargs, const NamedConstructor* namedConstructors,
658 JS::Heap<JSObject*>* constructorCache,
659 const NativeProperties* properties,
660 const NativeProperties* chromeOnlyProperties,
661 const char* name, bool defineOnGlobal)
663 MOZ_ASSERT(protoClass || constructorClass || constructor,
664 "Need at least one class or a constructor!");
665 MOZ_ASSERT(!((properties &&
666 (properties->methods || properties->attributes)) ||
667 (chromeOnlyProperties &&
668 (chromeOnlyProperties->methods ||
669 chromeOnlyProperties->attributes))) || protoClass,
670 "Methods or properties but no protoClass!");
671 MOZ_ASSERT(!((properties &&
672 (properties->staticMethods || properties->staticAttributes)) ||
673 (chromeOnlyProperties &&
674 (chromeOnlyProperties->staticMethods ||
675 chromeOnlyProperties->staticAttributes))) ||
676 constructorClass || constructor,
677 "Static methods but no constructorClass or constructor!");
678 MOZ_ASSERT(bool(name) == bool(constructorClass || constructor),
679 "Must have name precisely when we have an interface object");
680 MOZ_ASSERT(!constructorClass || !constructor);
681 MOZ_ASSERT(!protoClass == !protoCache,
682 "If, and only if, there is an interface prototype object we need "
683 "to cache it");
684 MOZ_ASSERT(!(constructorClass || constructor) == !constructorCache,
685 "If, and only if, there is an interface object we need to cache "
686 "it");
688 JS::Rooted<JSObject*> proto(cx);
689 if (protoClass) {
690 proto =
691 CreateInterfacePrototypeObject(cx, global, protoProto, protoClass,
692 properties, chromeOnlyProperties);
693 if (!proto) {
694 return;
697 *protoCache = proto;
699 else {
700 MOZ_ASSERT(!proto);
703 JSObject* interface;
704 if (constructorClass || constructor) {
705 interface = CreateInterfaceObject(cx, global, constructorProto,
706 constructorClass, constructor,
707 ctorNargs, namedConstructors, proto,
708 properties, chromeOnlyProperties, name,
709 defineOnGlobal);
710 if (!interface) {
711 if (protoCache) {
712 // If we fail we need to make sure to clear the value of protoCache we
713 // set above.
714 *protoCache = nullptr;
716 return;
718 *constructorCache = interface;
722 bool
723 NativeInterface2JSObjectAndThrowIfFailed(JSContext* aCx,
724 JS::Handle<JSObject*> aScope,
725 JS::MutableHandle<JS::Value> aRetval,
726 xpcObjectHelper& aHelper,
727 const nsIID* aIID,
728 bool aAllowNativeWrapper)
730 js::AssertSameCompartment(aCx, aScope);
731 nsresult rv;
732 // Inline some logic from XPCConvert::NativeInterfaceToJSObject that we need
733 // on all threads.
734 nsWrapperCache *cache = aHelper.GetWrapperCache();
736 if (cache && cache->IsDOMBinding()) {
737 JS::Rooted<JSObject*> obj(aCx, cache->GetWrapper());
738 if (!obj) {
739 obj = cache->WrapObject(aCx);
742 if (obj && aAllowNativeWrapper && !JS_WrapObject(aCx, &obj)) {
743 return false;
746 if (obj) {
747 aRetval.setObject(*obj);
748 return true;
752 MOZ_ASSERT(NS_IsMainThread());
754 if (!XPCConvert::NativeInterface2JSObject(aRetval, nullptr, aHelper, aIID,
755 nullptr, aAllowNativeWrapper, &rv)) {
756 // I can't tell if NativeInterface2JSObject throws JS exceptions
757 // or not. This is a sloppy stab at the right semantics; the
758 // method really ought to be fixed to behave consistently.
759 if (!JS_IsExceptionPending(aCx)) {
760 Throw(aCx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED);
762 return false;
764 return true;
767 bool
768 TryPreserveWrapper(JSObject* obj)
770 MOZ_ASSERT(IsDOMObject(obj));
772 if (nsISupports* native = UnwrapDOMObjectToISupports(obj)) {
773 nsWrapperCache* cache = nullptr;
774 CallQueryInterface(native, &cache);
775 if (cache) {
776 cache->PreserveWrapper(native);
778 return true;
781 // If this DOMClass is not cycle collected, then it isn't wrappercached,
782 // so it does not need to be preserved. If it is cycle collected, then
783 // we can't tell if it is wrappercached or not, so we just return false.
784 const DOMJSClass* domClass = GetDOMClass(obj);
785 return domClass && !domClass->mParticipant;
788 // Can only be called with a DOM JSClass.
789 bool
790 InstanceClassHasProtoAtDepth(const js::Class* clasp,
791 uint32_t protoID, uint32_t depth)
793 const DOMJSClass* domClass = DOMJSClass::FromJSClass(clasp);
794 return static_cast<uint32_t>(domClass->mInterfaceChain[depth]) == protoID;
797 // Only set allowNativeWrapper to false if you really know you need it, if in
798 // doubt use true. Setting it to false disables security wrappers.
799 bool
800 XPCOMObjectToJsval(JSContext* cx, JS::Handle<JSObject*> scope,
801 xpcObjectHelper& helper, const nsIID* iid,
802 bool allowNativeWrapper, JS::MutableHandle<JS::Value> rval)
804 if (!NativeInterface2JSObjectAndThrowIfFailed(cx, scope, rval, helper, iid,
805 allowNativeWrapper)) {
806 return false;
809 #ifdef DEBUG
810 JSObject* jsobj = rval.toObjectOrNull();
811 if (jsobj && !js::GetObjectParent(jsobj))
812 NS_ASSERTION(js::GetObjectClass(jsobj)->flags & JSCLASS_IS_GLOBAL,
813 "Why did we recreate this wrapper?");
814 #endif
816 return true;
819 bool
820 VariantToJsval(JSContext* aCx, nsIVariant* aVariant,
821 JS::MutableHandle<JS::Value> aRetval)
823 nsresult rv;
824 if (!XPCVariant::VariantDataToJS(aVariant, &rv, aRetval)) {
825 // Does it throw? Who knows
826 if (!JS_IsExceptionPending(aCx)) {
827 Throw(aCx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED);
829 return false;
832 return true;
835 bool
836 QueryInterface(JSContext* cx, unsigned argc, JS::Value* vp)
838 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
839 JS::Rooted<JS::Value> thisv(cx, JS_THIS(cx, vp));
840 if (thisv.isNull())
841 return false;
843 // Get the object. It might be a security wrapper, in which case we do a checked
844 // unwrap.
845 JS::Rooted<JSObject*> origObj(cx, &thisv.toObject());
846 JSObject* obj = js::CheckedUnwrap(origObj, /* stopAtOuter = */ false);
847 if (!obj) {
848 JS_ReportError(cx, "Permission denied to access object");
849 return false;
852 // Switch this to UnwrapDOMObjectToISupports once our global objects are
853 // using new bindings.
854 JS::Rooted<JS::Value> val(cx, JS::ObjectValue(*obj));
855 nsISupports* native = nullptr;
856 nsCOMPtr<nsISupports> nativeRef;
857 xpc_qsUnwrapArg<nsISupports>(cx, val, &native,
858 static_cast<nsISupports**>(getter_AddRefs(nativeRef)),
859 &val);
860 if (!native) {
861 return Throw(cx, NS_ERROR_FAILURE);
864 if (argc < 1) {
865 return Throw(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);
868 if (!args[0].isObject()) {
869 return Throw(cx, NS_ERROR_XPC_BAD_CONVERT_JS);
872 nsIJSID* iid;
873 SelfRef iidRef;
874 if (NS_FAILED(xpc_qsUnwrapArg<nsIJSID>(cx, args[0], &iid, &iidRef.ptr,
875 args[0]))) {
876 return Throw(cx, NS_ERROR_XPC_BAD_CONVERT_JS);
878 MOZ_ASSERT(iid);
880 if (iid->GetID()->Equals(NS_GET_IID(nsIClassInfo))) {
881 nsresult rv;
882 nsCOMPtr<nsIClassInfo> ci = do_QueryInterface(native, &rv);
883 if (NS_FAILED(rv)) {
884 return Throw(cx, rv);
887 return WrapObject(cx, ci, &NS_GET_IID(nsIClassInfo), args.rval());
890 nsCOMPtr<nsISupports> unused;
891 nsresult rv = native->QueryInterface(*iid->GetID(), getter_AddRefs(unused));
892 if (NS_FAILED(rv)) {
893 return Throw(cx, rv);
896 *vp = thisv;
897 return true;
900 void
901 GetInterfaceImpl(JSContext* aCx, nsIInterfaceRequestor* aRequestor,
902 nsWrapperCache* aCache, nsIJSID* aIID,
903 JS::MutableHandle<JS::Value> aRetval, ErrorResult& aError)
905 const nsID* iid = aIID->GetID();
907 nsRefPtr<nsISupports> result;
908 aError = aRequestor->GetInterface(*iid, getter_AddRefs(result));
909 if (aError.Failed()) {
910 return;
913 if (!WrapObject(aCx, result, iid, aRetval)) {
914 aError.Throw(NS_ERROR_FAILURE);
918 bool
919 UnforgeableValueOf(JSContext* cx, unsigned argc, JS::Value* vp)
921 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
922 args.rval().set(args.thisv());
923 return true;
926 bool
927 ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp)
929 return ThrowErrorMessage(cx, MSG_ILLEGAL_CONSTRUCTOR);
932 bool
933 ThrowConstructorWithoutNew(JSContext* cx, const char* name)
935 return ThrowErrorMessage(cx, MSG_CONSTRUCTOR_WITHOUT_NEW, name);
938 inline const NativePropertyHooks*
939 GetNativePropertyHooks(JSContext *cx, JS::Handle<JSObject*> obj,
940 DOMObjectType& type)
942 const DOMJSClass* domClass = GetDOMClass(obj);
943 if (domClass) {
944 type = eInstance;
945 return domClass->mNativeHooks;
948 if (JS_ObjectIsFunction(cx, obj)) {
949 MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor));
950 type = eInterface;
951 const JS::Value& v =
952 js::GetFunctionNativeReserved(obj,
953 CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT);
954 const JSNativeHolder* nativeHolder =
955 static_cast<const JSNativeHolder*>(v.toPrivate());
956 return nativeHolder->mPropertyHooks;
959 MOZ_ASSERT(IsDOMIfaceAndProtoClass(js::GetObjectClass(obj)));
960 const DOMIfaceAndProtoJSClass* ifaceAndProtoJSClass =
961 DOMIfaceAndProtoJSClass::FromJSClass(js::GetObjectClass(obj));
962 type = ifaceAndProtoJSClass->mType;
963 return ifaceAndProtoJSClass->mNativeHooks;
966 // Try to resolve a property as an unforgeable property from the given
967 // NativeProperties, if it's there. nativeProperties is allowed to be null (in
968 // which case we of course won't resolve anything).
969 static bool
970 XrayResolveUnforgeableProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
971 JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
972 JS::MutableHandle<JSPropertyDescriptor> desc,
973 bool& cacheOnHolder,
974 const NativeProperties* nativeProperties);
976 static bool
977 XrayResolveNativeProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
978 const NativePropertyHooks* nativePropertyHooks,
979 DOMObjectType type, JS::Handle<JSObject*> obj,
980 JS::Handle<jsid> id,
981 JS::MutableHandle<JSPropertyDescriptor> desc,
982 bool& cacheOnHolder);
984 bool
985 XrayResolveOwnProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
986 JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
987 JS::MutableHandle<JSPropertyDescriptor> desc,
988 bool& cacheOnHolder)
990 cacheOnHolder = false;
992 DOMObjectType type;
993 const NativePropertyHooks *nativePropertyHooks =
994 GetNativePropertyHooks(cx, obj, type);
996 if (type != eInstance) {
997 // For prototype objects and interface objects, just return their
998 // normal set of properties.
999 return XrayResolveNativeProperty(cx, wrapper, nativePropertyHooks, type,
1000 obj, id, desc, cacheOnHolder);
1003 // Check for unforgeable properties before doing mResolveOwnProperty weirdness
1004 const NativePropertiesHolder& nativeProperties =
1005 nativePropertyHooks->mNativeProperties;
1006 if (!XrayResolveUnforgeableProperty(cx, wrapper, obj, id, desc, cacheOnHolder,
1007 nativeProperties.regular)) {
1008 return false;
1010 if (desc.object()) {
1011 return true;
1013 if (!XrayResolveUnforgeableProperty(cx, wrapper, obj, id, desc, cacheOnHolder,
1014 nativeProperties.chromeOnly)) {
1015 return false;
1017 if (desc.object()) {
1018 return true;
1021 return !nativePropertyHooks->mResolveOwnProperty ||
1022 nativePropertyHooks->mResolveOwnProperty(cx, wrapper, obj, id, desc);
1025 static bool
1026 XrayResolveAttribute(JSContext* cx, JS::Handle<JSObject*> wrapper,
1027 JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
1028 const Prefable<const JSPropertySpec>* attributes, jsid* attributeIds,
1029 const JSPropertySpec* attributeSpecs, JS::MutableHandle<JSPropertyDescriptor> desc,
1030 bool& cacheOnHolder)
1032 for (; attributes->specs; ++attributes) {
1033 if (attributes->isEnabled(cx, obj)) {
1034 // Set i to be the index into our full list of ids/specs that we're
1035 // looking at now.
1036 size_t i = attributes->specs - attributeSpecs;
1037 for ( ; attributeIds[i] != JSID_VOID; ++i) {
1038 if (id == attributeIds[i]) {
1039 cacheOnHolder = true;
1041 const JSPropertySpec& attrSpec = attributeSpecs[i];
1042 // Because of centralization, we need to make sure we fault in the
1043 // JitInfos as well. At present, until the JSAPI changes, the easiest
1044 // way to do this is wrap them up as functions ourselves.
1045 desc.setAttributes(attrSpec.flags & ~JSPROP_NATIVE_ACCESSORS);
1046 // They all have getters, so we can just make it.
1047 JS::Rooted<JSFunction*> fun(cx,
1048 JS_NewFunctionById(cx, (JSNative)attrSpec.getter.propertyOp.op,
1049 0, 0, wrapper, id));
1050 if (!fun)
1051 return false;
1052 SET_JITINFO(fun, attrSpec.getter.propertyOp.info);
1053 JSObject *funobj = JS_GetFunctionObject(fun);
1054 desc.setGetterObject(funobj);
1055 desc.attributesRef() |= JSPROP_GETTER;
1056 if (attrSpec.setter.propertyOp.op) {
1057 // We have a setter! Make it.
1058 fun = JS_NewFunctionById(cx, (JSNative)attrSpec.setter.propertyOp.op, 1, 0,
1059 wrapper, id);
1060 if (!fun)
1061 return false;
1062 SET_JITINFO(fun, attrSpec.setter.propertyOp.info);
1063 funobj = JS_GetFunctionObject(fun);
1064 desc.setSetterObject(funobj);
1065 desc.attributesRef() |= JSPROP_SETTER;
1066 } else {
1067 desc.setSetter(nullptr);
1069 desc.object().set(wrapper);
1070 return true;
1075 return true;
1078 static bool
1079 XrayResolveMethod(JSContext* cx, JS::Handle<JSObject*> wrapper,
1080 JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
1081 const Prefable<const JSFunctionSpec>* methods,
1082 jsid* methodIds,
1083 const JSFunctionSpec* methodSpecs,
1084 JS::MutableHandle<JSPropertyDescriptor> desc,
1085 bool& cacheOnHolder)
1087 const Prefable<const JSFunctionSpec>* method;
1088 for (method = methods; method->specs; ++method) {
1089 if (method->isEnabled(cx, obj)) {
1090 // Set i to be the index into our full list of ids/specs that we're
1091 // looking at now.
1092 size_t i = method->specs - methodSpecs;
1093 for ( ; methodIds[i] != JSID_VOID; ++i) {
1094 if (id == methodIds[i]) {
1095 cacheOnHolder = true;
1097 const JSFunctionSpec& methodSpec = methodSpecs[i];
1098 JSFunction *fun;
1099 if (methodSpec.selfHostedName) {
1100 fun = JS::GetSelfHostedFunction(cx, methodSpec.selfHostedName, id, methodSpec.nargs);
1101 if (!fun) {
1102 return false;
1104 MOZ_ASSERT(!methodSpec.call.op, "Bad FunctionSpec declaration: non-null native");
1105 MOZ_ASSERT(!methodSpec.call.info, "Bad FunctionSpec declaration: non-null jitinfo");
1106 } else {
1107 fun = JS_NewFunctionById(cx, methodSpec.call.op, methodSpec.nargs, 0, wrapper, id);
1108 if (!fun) {
1109 return false;
1111 SET_JITINFO(fun, methodSpec.call.info);
1113 JSObject *funobj = JS_GetFunctionObject(fun);
1114 desc.value().setObject(*funobj);
1115 desc.setAttributes(methodSpec.flags);
1116 desc.object().set(wrapper);
1117 desc.setSetter(nullptr);
1118 desc.setGetter(nullptr);
1119 return true;
1124 return true;
1127 /* static */ bool
1128 XrayResolveUnforgeableProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
1129 JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
1130 JS::MutableHandle<JSPropertyDescriptor> desc,
1131 bool& cacheOnHolder,
1132 const NativeProperties* nativeProperties)
1134 if (!nativeProperties) {
1135 return true;
1138 if (nativeProperties->unforgeableAttributes) {
1139 if (!XrayResolveAttribute(cx, wrapper, obj, id,
1140 nativeProperties->unforgeableAttributes,
1141 nativeProperties->unforgeableAttributeIds,
1142 nativeProperties->unforgeableAttributeSpecs,
1143 desc, cacheOnHolder)) {
1144 return false;
1147 if (desc.object()) {
1148 return true;
1152 if (nativeProperties->unforgeableMethods) {
1153 if (!XrayResolveMethod(cx, wrapper, obj, id,
1154 nativeProperties->unforgeableMethods,
1155 nativeProperties->unforgeableMethodIds,
1156 nativeProperties->unforgeableMethodSpecs,
1157 desc, cacheOnHolder)) {
1158 return false;
1161 if (desc.object()) {
1162 return true;
1166 return true;
1169 static bool
1170 XrayResolveProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
1171 JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
1172 JS::MutableHandle<JSPropertyDescriptor> desc,
1173 bool& cacheOnHolder, DOMObjectType type,
1174 const NativeProperties* nativeProperties)
1176 const Prefable<const JSFunctionSpec>* methods;
1177 jsid* methodIds;
1178 const JSFunctionSpec* methodSpecs;
1179 if (type == eInterface) {
1180 methods = nativeProperties->staticMethods;
1181 methodIds = nativeProperties->staticMethodIds;
1182 methodSpecs = nativeProperties->staticMethodSpecs;
1183 } else {
1184 methods = nativeProperties->methods;
1185 methodIds = nativeProperties->methodIds;
1186 methodSpecs = nativeProperties->methodSpecs;
1188 if (methods) {
1189 if (!XrayResolveMethod(cx, wrapper, obj, id, methods, methodIds,
1190 methodSpecs, desc, cacheOnHolder)) {
1191 return false;
1193 if (desc.object()) {
1194 return true;
1198 if (type == eInterface) {
1199 if (nativeProperties->staticAttributes) {
1200 if (!XrayResolveAttribute(cx, wrapper, obj, id,
1201 nativeProperties->staticAttributes,
1202 nativeProperties->staticAttributeIds,
1203 nativeProperties->staticAttributeSpecs, desc,
1204 cacheOnHolder)) {
1205 return false;
1207 if (desc.object()) {
1208 return true;
1211 } else {
1212 if (nativeProperties->attributes) {
1213 if (!XrayResolveAttribute(cx, wrapper, obj, id,
1214 nativeProperties->attributes,
1215 nativeProperties->attributeIds,
1216 nativeProperties->attributeSpecs, desc,
1217 cacheOnHolder)) {
1218 return false;
1220 if (desc.object()) {
1221 return true;
1226 if (nativeProperties->constants) {
1227 const Prefable<const ConstantSpec>* constant;
1228 for (constant = nativeProperties->constants; constant->specs; ++constant) {
1229 if (constant->isEnabled(cx, obj)) {
1230 // Set i to be the index into our full list of ids/specs that we're
1231 // looking at now.
1232 size_t i = constant->specs - nativeProperties->constantSpecs;
1233 for ( ; nativeProperties->constantIds[i] != JSID_VOID; ++i) {
1234 if (id == nativeProperties->constantIds[i]) {
1235 cacheOnHolder = true;
1237 desc.setAttributes(JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
1238 desc.object().set(wrapper);
1239 desc.value().set(nativeProperties->constantSpecs[i].value);
1240 return true;
1247 return true;
1250 static bool
1251 ResolvePrototypeOrConstructor(JSContext* cx, JS::Handle<JSObject*> wrapper,
1252 JS::Handle<JSObject*> obj,
1253 size_t protoAndIfaceCacheIndex, unsigned attrs,
1254 JS::MutableHandle<JSPropertyDescriptor> desc,
1255 bool cacheOnHolder)
1257 JS::Rooted<JSObject*> global(cx, js::GetGlobalForObjectCrossCompartment(obj));
1259 JSAutoCompartment ac(cx, global);
1260 ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(global);
1261 JSObject* protoOrIface =
1262 protoAndIfaceCache.EntrySlotIfExists(protoAndIfaceCacheIndex);
1263 if (!protoOrIface) {
1264 return false;
1267 cacheOnHolder = true;
1269 desc.object().set(wrapper);
1270 desc.setAttributes(attrs);
1271 desc.setGetter(JS_PropertyStub);
1272 desc.setSetter(JS_StrictPropertyStub);
1273 desc.value().set(JS::ObjectValue(*protoOrIface));
1275 return JS_WrapPropertyDescriptor(cx, desc);
1278 /* static */ bool
1279 XrayResolveNativeProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
1280 const NativePropertyHooks* nativePropertyHooks,
1281 DOMObjectType type, JS::Handle<JSObject*> obj,
1282 JS::Handle<jsid> id,
1283 JS::MutableHandle<JSPropertyDescriptor> desc,
1284 bool& cacheOnHolder)
1286 if (type == eInterface && IdEquals(id, "prototype")) {
1287 return nativePropertyHooks->mPrototypeID == prototypes::id::_ID_Count ||
1288 ResolvePrototypeOrConstructor(cx, wrapper, obj,
1289 nativePropertyHooks->mPrototypeID,
1290 JSPROP_PERMANENT | JSPROP_READONLY,
1291 desc, cacheOnHolder);
1294 if (type == eInterfacePrototype && IdEquals(id, "constructor")) {
1295 return nativePropertyHooks->mConstructorID == constructors::id::_ID_Count ||
1296 ResolvePrototypeOrConstructor(cx, wrapper, obj,
1297 nativePropertyHooks->mConstructorID,
1298 0, desc, cacheOnHolder);
1301 const NativePropertiesHolder& nativeProperties =
1302 nativePropertyHooks->mNativeProperties;
1304 if (nativeProperties.regular &&
1305 !XrayResolveProperty(cx, wrapper, obj, id, desc, cacheOnHolder, type,
1306 nativeProperties.regular)) {
1307 return false;
1310 if (!desc.object() &&
1311 nativeProperties.chromeOnly &&
1312 xpc::AccessCheck::isChrome(js::GetObjectCompartment(wrapper)) &&
1313 !XrayResolveProperty(cx, wrapper, obj, id, desc, cacheOnHolder, type,
1314 nativeProperties.chromeOnly)) {
1315 return false;
1318 return true;
1321 bool
1322 XrayResolveNativeProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
1323 JS::Handle<JSObject*> obj,
1324 JS::Handle<jsid> id, JS::MutableHandle<JSPropertyDescriptor> desc,
1325 bool& cacheOnHolder)
1327 cacheOnHolder = false;
1329 DOMObjectType type;
1330 const NativePropertyHooks* nativePropertyHooks =
1331 GetNativePropertyHooks(cx, obj, type);
1333 if (type == eInstance) {
1334 // Force the type to be eInterfacePrototype, since we need to walk the
1335 // prototype chain.
1336 type = eInterfacePrototype;
1339 if (type == eInterfacePrototype) {
1340 do {
1341 if (!XrayResolveNativeProperty(cx, wrapper, nativePropertyHooks, type,
1342 obj, id, desc, cacheOnHolder)) {
1343 return false;
1346 if (desc.object()) {
1347 return true;
1349 } while ((nativePropertyHooks = nativePropertyHooks->mProtoHooks));
1351 return true;
1354 return XrayResolveNativeProperty(cx, wrapper, nativePropertyHooks, type, obj,
1355 id, desc, cacheOnHolder);
1358 bool
1359 XrayDefineProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
1360 JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
1361 JS::MutableHandle<JSPropertyDescriptor> desc, bool* defined)
1363 if (!js::IsProxy(obj))
1364 return true;
1366 const DOMProxyHandler* handler = GetDOMProxyHandler(obj);
1367 return handler->defineProperty(cx, wrapper, id, desc, defined);
1370 template<typename SpecType>
1371 bool
1372 XrayEnumerateAttributesOrMethods(JSContext* cx, JS::Handle<JSObject*> wrapper,
1373 JS::Handle<JSObject*> obj,
1374 const Prefable<const SpecType>* list,
1375 jsid* ids, const SpecType* specList,
1376 unsigned flags, JS::AutoIdVector& props)
1378 for (; list->specs; ++list) {
1379 if (list->isEnabled(cx, obj)) {
1380 // Set i to be the index into our full list of ids/specs that we're
1381 // looking at now.
1382 size_t i = list->specs - specList;
1383 for ( ; ids[i] != JSID_VOID; ++i) {
1384 if (((flags & JSITER_HIDDEN) ||
1385 (specList[i].flags & JSPROP_ENUMERATE)) &&
1386 !props.append(ids[i])) {
1387 return false;
1392 return true;
1395 #define ENUMERATE_IF_DEFINED(fieldName) { \
1396 if (nativeProperties->fieldName##s && \
1397 !XrayEnumerateAttributesOrMethods(cx, wrapper, obj, \
1398 nativeProperties->fieldName##s, \
1399 nativeProperties->fieldName##Ids, \
1400 nativeProperties->fieldName##Specs, \
1401 flags, props)) { \
1402 return false; \
1407 bool
1408 XrayEnumerateProperties(JSContext* cx, JS::Handle<JSObject*> wrapper,
1409 JS::Handle<JSObject*> obj,
1410 unsigned flags, JS::AutoIdVector& props,
1411 DOMObjectType type,
1412 const NativeProperties* nativeProperties)
1414 if (type == eInstance) {
1415 ENUMERATE_IF_DEFINED(unforgeableMethod);
1416 ENUMERATE_IF_DEFINED(unforgeableAttribute);
1417 } else if (type == eInterface) {
1418 ENUMERATE_IF_DEFINED(staticMethod);
1419 ENUMERATE_IF_DEFINED(staticAttribute);
1420 } else {
1421 MOZ_ASSERT(type == eInterfacePrototype);
1422 ENUMERATE_IF_DEFINED(method);
1423 ENUMERATE_IF_DEFINED(attribute);
1426 if (nativeProperties->constants) {
1427 const Prefable<const ConstantSpec>* constant;
1428 for (constant = nativeProperties->constants; constant->specs; ++constant) {
1429 if (constant->isEnabled(cx, obj)) {
1430 // Set i to be the index into our full list of ids/specs that we're
1431 // looking at now.
1432 size_t i = constant->specs - nativeProperties->constantSpecs;
1433 for ( ; nativeProperties->constantIds[i] != JSID_VOID; ++i) {
1434 if (!props.append(nativeProperties->constantIds[i])) {
1435 return false;
1442 return true;
1445 #undef ENUMERATE_IF_DEFINED
1447 bool
1448 XrayEnumerateNativeProperties(JSContext* cx, JS::Handle<JSObject*> wrapper,
1449 const NativePropertyHooks* nativePropertyHooks,
1450 DOMObjectType type, JS::Handle<JSObject*> obj,
1451 unsigned flags, JS::AutoIdVector& props)
1453 if (type == eInterface &&
1454 nativePropertyHooks->mPrototypeID != prototypes::id::_ID_Count &&
1455 !AddStringToIDVector(cx, props, "prototype")) {
1456 return false;
1459 if (type == eInterfacePrototype &&
1460 nativePropertyHooks->mConstructorID != constructors::id::_ID_Count &&
1461 (flags & JSITER_HIDDEN) &&
1462 !AddStringToIDVector(cx, props, "constructor")) {
1463 return false;
1466 const NativePropertiesHolder& nativeProperties =
1467 nativePropertyHooks->mNativeProperties;
1469 if (nativeProperties.regular &&
1470 !XrayEnumerateProperties(cx, wrapper, obj, flags, props, type,
1471 nativeProperties.regular)) {
1472 return false;
1475 if (nativeProperties.chromeOnly &&
1476 xpc::AccessCheck::isChrome(js::GetObjectCompartment(wrapper)) &&
1477 !XrayEnumerateProperties(cx, wrapper, obj, flags, props, type,
1478 nativeProperties.chromeOnly)) {
1479 return false;
1482 return true;
1485 bool
1486 XrayEnumerateProperties(JSContext* cx, JS::Handle<JSObject*> wrapper,
1487 JS::Handle<JSObject*> obj,
1488 unsigned flags, JS::AutoIdVector& props)
1490 DOMObjectType type;
1491 const NativePropertyHooks* nativePropertyHooks =
1492 GetNativePropertyHooks(cx, obj, type);
1494 if (type == eInstance) {
1495 if (nativePropertyHooks->mEnumerateOwnProperties &&
1496 !nativePropertyHooks->mEnumerateOwnProperties(cx, wrapper, obj,
1497 props)) {
1498 return false;
1501 // Handle Unforgeable properties.
1502 if (!XrayEnumerateNativeProperties(cx, wrapper, nativePropertyHooks, type,
1503 obj, flags, props)) {
1504 return false;
1507 if (flags & JSITER_OWNONLY) {
1508 return true;
1511 // Force the type to be eInterfacePrototype, since we need to walk the
1512 // prototype chain.
1513 type = eInterfacePrototype;
1516 if (type == eInterfacePrototype) {
1517 do {
1518 if (!XrayEnumerateNativeProperties(cx, wrapper, nativePropertyHooks, type,
1519 obj, flags, props)) {
1520 return false;
1523 if (flags & JSITER_OWNONLY) {
1524 return true;
1526 } while ((nativePropertyHooks = nativePropertyHooks->mProtoHooks));
1528 return true;
1531 return XrayEnumerateNativeProperties(cx, wrapper, nativePropertyHooks, type,
1532 obj, flags, props);
1535 NativePropertyHooks sWorkerNativePropertyHooks = {
1536 nullptr,
1537 nullptr,
1539 nullptr,
1540 nullptr
1542 prototypes::id::_ID_Count,
1543 constructors::id::_ID_Count,
1544 nullptr
1547 bool
1548 GetPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
1549 JS::Handle<jsid> id, bool* found,
1550 JS::Value* vp)
1552 JS::Rooted<JSObject*> proto(cx);
1553 if (!js::GetObjectProto(cx, proxy, &proto)) {
1554 return false;
1556 if (!proto) {
1557 *found = false;
1558 return true;
1561 bool hasProp;
1562 if (!JS_HasPropertyById(cx, proto, id, &hasProp)) {
1563 return false;
1566 *found = hasProp;
1567 if (!hasProp || !vp) {
1568 return true;
1571 JS::Rooted<JS::Value> value(cx);
1572 if (!JS_ForwardGetPropertyTo(cx, proto, id, proxy, &value)) {
1573 return false;
1576 *vp = value;
1577 return true;
1580 bool
1581 HasPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
1582 JS::Handle<jsid> id)
1584 JS::Rooted<JSObject*> curr(cx, proxy);
1585 while (true) {
1586 JS::Rooted<JSObject*> proto(cx);
1587 if (!js::GetObjectProto(cx, curr, &proto)) {
1588 JS_ClearPendingException(cx);
1589 return true; // Fail safe.
1592 if (!proto) {
1593 return false;
1596 bool hasProp;
1597 if (!JS_HasPropertyById(cx, proto, id, &hasProp)) {
1598 JS_ClearPendingException(cx);
1599 return true; // Fail safe.
1602 if (hasProp) {
1603 return true;
1606 curr = proto;
1610 bool
1611 AppendNamedPropertyIds(JSContext* cx, JS::Handle<JSObject*> proxy,
1612 nsTArray<nsString>& names,
1613 bool shadowPrototypeProperties,
1614 JS::AutoIdVector& props)
1616 for (uint32_t i = 0; i < names.Length(); ++i) {
1617 JS::Rooted<JS::Value> v(cx);
1618 if (!xpc::NonVoidStringToJsval(cx, names[i], &v)) {
1619 return false;
1622 JS::Rooted<jsid> id(cx);
1623 if (!JS_ValueToId(cx, v, &id)) {
1624 return false;
1627 if (shadowPrototypeProperties || !HasPropertyOnPrototype(cx, proxy, id)) {
1628 if (!props.append(id)) {
1629 return false;
1634 return true;
1637 bool
1638 DictionaryBase::ParseJSON(JSContext* aCx,
1639 const nsAString& aJSON,
1640 JS::MutableHandle<JS::Value> aVal)
1642 if (aJSON.IsEmpty()) {
1643 return true;
1645 return JS_ParseJSON(aCx,
1646 static_cast<const jschar*>(PromiseFlatString(aJSON).get()),
1647 aJSON.Length(), aVal);
1650 bool
1651 DictionaryBase::StringifyToJSON(JSContext* aCx,
1652 JS::MutableHandle<JS::Value> aValue,
1653 nsAString& aJSON)
1655 return JS_Stringify(aCx, aValue, JS::NullPtr(), JS::NullHandleValue,
1656 AppendJSONToString, &aJSON);
1659 /* static */
1660 bool
1661 DictionaryBase::AppendJSONToString(const jschar* aJSONData,
1662 uint32_t aDataLength,
1663 void* aString)
1665 nsAString* string = static_cast<nsAString*>(aString);
1666 string->Append(static_cast<const char16_t*>(aJSONData),
1667 aDataLength);
1668 return true;
1673 static JSString*
1674 ConcatJSString(JSContext* cx, const char* pre, JS::Handle<JSString*> str, const char* post)
1676 if (!str) {
1677 return nullptr;
1680 JS::Rooted<JSString*> preString(cx, JS_NewStringCopyN(cx, pre, strlen(pre)));
1681 JS::Rooted<JSString*> postString(cx, JS_NewStringCopyN(cx, post, strlen(post)));
1682 if (!preString || !postString) {
1683 return nullptr;
1686 preString = JS_ConcatStrings(cx, preString, str);
1687 if (!preString) {
1688 return nullptr;
1691 return JS_ConcatStrings(cx, preString, postString);
1694 bool
1695 NativeToString(JSContext* cx, JS::Handle<JSObject*> wrapper,
1696 JS::Handle<JSObject*> obj,
1697 JS::MutableHandle<JS::Value> v)
1699 JS::Rooted<JSPropertyDescriptor> toStringDesc(cx);
1700 toStringDesc.object().set(nullptr);
1701 toStringDesc.setAttributes(0);
1702 toStringDesc.setGetter(nullptr);
1703 toStringDesc.setSetter(nullptr);
1704 toStringDesc.value().set(JS::UndefinedValue());
1705 JS::Rooted<jsid> id(cx,
1706 nsXPConnect::GetRuntimeInstance()->GetStringID(XPCJSRuntime::IDX_TO_STRING));
1707 bool unused;
1708 if (!XrayResolveNativeProperty(cx, wrapper, obj, id, &toStringDesc, unused)) {
1709 return false;
1712 JS::Rooted<JSString*> str(cx);
1714 JSAutoCompartment ac(cx, obj);
1715 if (toStringDesc.object()) {
1716 JS::Rooted<JS::Value> toString(cx, toStringDesc.value());
1717 if (!JS_WrapValue(cx, &toString)) {
1718 return false;
1720 MOZ_ASSERT(JS_ObjectIsCallable(cx, &toString.toObject()));
1721 JS::Rooted<JS::Value> toStringResult(cx);
1722 if (JS_CallFunctionValue(cx, obj, toString, JS::HandleValueArray::empty(),
1723 &toStringResult)) {
1724 str = toStringResult.toString();
1725 } else {
1726 str = nullptr;
1728 } else {
1729 const js::Class* clasp = js::GetObjectClass(obj);
1730 if (IsDOMClass(clasp)) {
1731 str = JS_NewStringCopyZ(cx, clasp->name);
1732 str = ConcatJSString(cx, "[object ", str, "]");
1733 } else if (IsDOMIfaceAndProtoClass(clasp)) {
1734 const DOMIfaceAndProtoJSClass* ifaceAndProtoJSClass =
1735 DOMIfaceAndProtoJSClass::FromJSClass(clasp);
1736 str = JS_NewStringCopyZ(cx, ifaceAndProtoJSClass->mToString);
1737 } else {
1738 MOZ_ASSERT(JS_IsNativeFunction(obj, Constructor));
1739 JS::Rooted<JSFunction*> fun(cx, JS_GetObjectFunction(obj));
1740 str = JS_DecompileFunction(cx, fun, 0);
1745 if (!str) {
1746 return false;
1749 v.setString(str);
1750 return JS_WrapValue(cx, v);
1753 // Dynamically ensure that two objects don't end up with the same reserved slot.
1754 class MOZ_STACK_CLASS AutoCloneDOMObjectSlotGuard
1756 public:
1757 AutoCloneDOMObjectSlotGuard(JSContext* aCx, JSObject* aOld, JSObject* aNew)
1758 : mOldReflector(aCx, aOld), mNewReflector(aCx, aNew)
1760 MOZ_ASSERT(js::GetReservedSlot(aOld, DOM_OBJECT_SLOT) ==
1761 js::GetReservedSlot(aNew, DOM_OBJECT_SLOT));
1764 ~AutoCloneDOMObjectSlotGuard()
1766 if (js::GetReservedSlot(mOldReflector, DOM_OBJECT_SLOT).toPrivate()) {
1767 js::SetReservedSlot(mNewReflector, DOM_OBJECT_SLOT,
1768 JS::PrivateValue(nullptr));
1772 private:
1773 JS::Rooted<JSObject*> mOldReflector;
1774 JS::Rooted<JSObject*> mNewReflector;
1777 nsresult
1778 ReparentWrapper(JSContext* aCx, JS::Handle<JSObject*> aObjArg)
1780 js::AssertSameCompartment(aCx, aObjArg);
1782 // Check if we're anywhere near the stack limit before we reach the
1783 // transplanting code, since it has no good way to handle errors. This uses
1784 // the untrusted script limit, which is not strictly necessary since no
1785 // actual script should run.
1786 JS_CHECK_RECURSION_CONSERVATIVE(aCx, return NS_ERROR_FAILURE);
1788 JS::Rooted<JSObject*> aObj(aCx, aObjArg);
1789 const DOMJSClass* domClass = GetDOMClass(aObj);
1791 JS::Rooted<JSObject*> oldParent(aCx, JS_GetParent(aObj));
1792 JS::Rooted<JSObject*> newParent(aCx, domClass->mGetParent(aCx, aObj));
1794 JSAutoCompartment oldAc(aCx, oldParent);
1796 JSCompartment* oldCompartment = js::GetObjectCompartment(oldParent);
1797 JSCompartment* newCompartment = js::GetObjectCompartment(newParent);
1798 if (oldCompartment == newCompartment) {
1799 if (!JS_SetParent(aCx, aObj, newParent)) {
1800 MOZ_CRASH();
1802 return NS_OK;
1805 nsISupports* native = UnwrapDOMObjectToISupports(aObj);
1806 if (!native) {
1807 return NS_OK;
1810 bool isProxy = js::IsProxy(aObj);
1811 JS::Rooted<JSObject*> expandoObject(aCx);
1812 if (isProxy) {
1813 expandoObject = DOMProxyHandler::GetAndClearExpandoObject(aObj);
1816 JSAutoCompartment newAc(aCx, newParent);
1818 // First we clone the reflector. We get a copy of its properties and clone its
1819 // expando chain. The only part that is dangerous here is that if we have to
1820 // return early we must avoid ending up with two reflectors pointing to the
1821 // same native. Other than that, the objects we create will just go away.
1823 JS::Rooted<JSObject*> global(aCx,
1824 js::GetGlobalForObjectCrossCompartment(newParent));
1825 JS::Handle<JSObject*> proto = (domClass->mGetProto)(aCx, global);
1826 if (!proto) {
1827 return NS_ERROR_FAILURE;
1830 JS::Rooted<JSObject*> newobj(aCx, JS_CloneObject(aCx, aObj, proto, newParent));
1831 if (!newobj) {
1832 return NS_ERROR_FAILURE;
1835 js::SetReservedSlot(newobj, DOM_OBJECT_SLOT,
1836 js::GetReservedSlot(aObj, DOM_OBJECT_SLOT));
1838 // At this point, both |aObj| and |newobj| point to the same native
1839 // which is bad, because one of them will end up being finalized with a
1840 // native it does not own. |cloneGuard| ensures that if we exit before
1841 // clearing |aObj|'s reserved slot the reserved slot of |newobj| will be
1842 // set to null. |aObj| will go away soon, because we swap it with
1843 // another object during the transplant and let that object die.
1844 JS::Rooted<JSObject*> propertyHolder(aCx);
1846 AutoCloneDOMObjectSlotGuard cloneGuard(aCx, aObj, newobj);
1848 JS::Rooted<JSObject*> copyFrom(aCx, isProxy ? expandoObject : aObj);
1849 if (copyFrom) {
1850 propertyHolder = JS_NewObjectWithGivenProto(aCx, nullptr, JS::NullPtr(),
1851 newParent);
1852 if (!propertyHolder) {
1853 return NS_ERROR_OUT_OF_MEMORY;
1856 if (!JS_CopyPropertiesFrom(aCx, propertyHolder, copyFrom)) {
1857 return NS_ERROR_FAILURE;
1859 } else {
1860 propertyHolder = nullptr;
1863 // Expandos from other compartments are attached to the target JS object.
1864 // Copy them over, and let the old ones die a natural death.
1865 if (!xpc::XrayUtils::CloneExpandoChain(aCx, newobj, aObj)) {
1866 return NS_ERROR_FAILURE;
1869 // We've set up |newobj|, so we make it own the native by nulling
1870 // out the reserved slot of |obj|.
1872 // NB: It's important to do this _after_ copying the properties to
1873 // propertyHolder. Otherwise, an object with |foo.x === foo| will
1874 // crash when JS_CopyPropertiesFrom tries to call wrap() on foo.x.
1875 js::SetReservedSlot(aObj, DOM_OBJECT_SLOT, JS::PrivateValue(nullptr));
1878 aObj = xpc::TransplantObject(aCx, aObj, newobj);
1879 if (!aObj) {
1880 MOZ_CRASH();
1883 nsWrapperCache* cache = nullptr;
1884 CallQueryInterface(native, &cache);
1885 bool preserving = cache->PreservingWrapper();
1886 cache->SetPreservingWrapper(false);
1887 cache->SetWrapper(aObj);
1888 cache->SetPreservingWrapper(preserving);
1890 if (propertyHolder) {
1891 JS::Rooted<JSObject*> copyTo(aCx);
1892 if (isProxy) {
1893 copyTo = DOMProxyHandler::EnsureExpandoObject(aCx, aObj);
1894 } else {
1895 copyTo = aObj;
1898 if (!copyTo || !JS_CopyPropertiesFrom(aCx, copyTo, propertyHolder)) {
1899 MOZ_CRASH();
1903 nsObjectLoadingContent* htmlobject;
1904 nsresult rv = UNWRAP_OBJECT(HTMLObjectElement, aObj, htmlobject);
1905 if (NS_FAILED(rv)) {
1906 rv = UnwrapObject<prototypes::id::HTMLEmbedElement,
1907 HTMLSharedObjectElement>(aObj, htmlobject);
1908 if (NS_FAILED(rv)) {
1909 rv = UnwrapObject<prototypes::id::HTMLAppletElement,
1910 HTMLSharedObjectElement>(aObj, htmlobject);
1911 if (NS_FAILED(rv)) {
1912 htmlobject = nullptr;
1916 if (htmlobject) {
1917 htmlobject->SetupProtoChain(aCx, aObj);
1920 // Now we can just fix up the parent and return the wrapper
1922 if (newParent && !JS_SetParent(aCx, aObj, newParent)) {
1923 MOZ_CRASH();
1926 return NS_OK;
1929 GlobalObject::GlobalObject(JSContext* aCx, JSObject* aObject)
1930 : mGlobalJSObject(aCx),
1931 mCx(aCx),
1932 mGlobalObject(nullptr)
1934 MOZ_ASSERT(mCx);
1935 JS::Rooted<JSObject*> obj(aCx, aObject);
1936 if (js::IsWrapper(obj)) {
1937 obj = js::CheckedUnwrap(obj, /* stopAtOuter = */ false);
1938 if (!obj) {
1939 // We should never end up here on a worker thread, since there shouldn't
1940 // be any security wrappers to worry about.
1941 if (!MOZ_LIKELY(NS_IsMainThread())) {
1942 MOZ_CRASH();
1945 Throw(aCx, NS_ERROR_XPC_SECURITY_MANAGER_VETO);
1946 return;
1950 mGlobalJSObject = js::GetGlobalForObjectCrossCompartment(obj);
1953 nsISupports*
1954 GlobalObject::GetAsSupports() const
1956 if (mGlobalObject) {
1957 return mGlobalObject;
1960 if (!NS_IsMainThread()) {
1961 mGlobalObject = UnwrapDOMObjectToISupports(mGlobalJSObject);
1962 return mGlobalObject;
1965 JS::Rooted<JS::Value> val(mCx, JS::ObjectValue(*mGlobalJSObject));
1967 // Switch this to UnwrapDOMObjectToISupports once our global objects are
1968 // using new bindings.
1969 nsresult rv = xpc_qsUnwrapArg<nsISupports>(mCx, val, &mGlobalObject,
1970 static_cast<nsISupports**>(getter_AddRefs(mGlobalObjectRef)),
1971 &val);
1972 if (NS_FAILED(rv)) {
1973 mGlobalObject = nullptr;
1974 Throw(mCx, NS_ERROR_XPC_BAD_CONVERT_JS);
1977 return mGlobalObject;
1980 bool
1981 InterfaceHasInstance(JSContext* cx, JS::Handle<JSObject*> obj,
1982 JS::Handle<JSObject*> instance,
1983 bool* bp)
1985 const DOMIfaceAndProtoJSClass* clasp =
1986 DOMIfaceAndProtoJSClass::FromJSClass(js::GetObjectClass(obj));
1988 const DOMJSClass* domClass = GetDOMClass(js::UncheckedUnwrap(instance, /* stopAtOuter = */ false));
1990 MOZ_ASSERT(!domClass || clasp->mPrototypeID != prototypes::id::_ID_Count,
1991 "Why do we have a hasInstance hook if we don't have a prototype "
1992 "ID?");
1994 if (domClass &&
1995 domClass->mInterfaceChain[clasp->mDepth] == clasp->mPrototypeID) {
1996 *bp = true;
1997 return true;
2000 if (jsipc::IsWrappedCPOW(instance)) {
2001 bool boolp = false;
2002 if (!jsipc::DOMInstanceOf(cx, js::CheckedUnwrap(instance), clasp->mPrototypeID,
2003 clasp->mDepth, &boolp)) {
2004 return false;
2006 *bp = boolp;
2007 return true;
2010 JS::Rooted<JS::Value> protov(cx);
2011 DebugOnly<bool> ok = JS_GetProperty(cx, obj, "prototype", &protov);
2012 MOZ_ASSERT(ok, "Someone messed with our prototype property?");
2014 JS::Rooted<JSObject*> interfacePrototype(cx, &protov.toObject());
2015 MOZ_ASSERT(IsDOMIfaceAndProtoClass(js::GetObjectClass(interfacePrototype)),
2016 "Someone messed with our prototype property?");
2018 JS::Rooted<JSObject*> proto(cx);
2019 if (!JS_GetPrototype(cx, instance, &proto)) {
2020 return false;
2023 while (proto) {
2024 if (proto == interfacePrototype) {
2025 *bp = true;
2026 return true;
2029 if (!JS_GetPrototype(cx, proto, &proto)) {
2030 return false;
2034 *bp = false;
2035 return true;
2038 bool
2039 InterfaceHasInstance(JSContext* cx, JS::Handle<JSObject*> obj, JS::MutableHandle<JS::Value> vp,
2040 bool* bp)
2042 if (!vp.isObject()) {
2043 *bp = false;
2044 return true;
2047 JS::Rooted<JSObject*> instanceObject(cx, &vp.toObject());
2048 return InterfaceHasInstance(cx, obj, instanceObject, bp);
2051 bool
2052 InterfaceHasInstance(JSContext* cx, int prototypeID, int depth,
2053 JS::Handle<JSObject*> instance,
2054 bool* bp)
2056 const DOMJSClass* domClass = GetDOMClass(js::UncheckedUnwrap(instance));
2058 MOZ_ASSERT(!domClass || prototypeID != prototypes::id::_ID_Count,
2059 "Why do we have a hasInstance hook if we don't have a prototype "
2060 "ID?");
2062 *bp = (domClass && domClass->mInterfaceChain[depth] == prototypeID);
2063 return true;
2066 bool
2067 ReportLenientThisUnwrappingFailure(JSContext* cx, JSObject* obj)
2069 JS::Rooted<JSObject*> rootedObj(cx, obj);
2070 GlobalObject global(cx, rootedObj);
2071 if (global.Failed()) {
2072 return false;
2074 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(global.GetAsSupports());
2075 if (window && window->GetDoc()) {
2076 window->GetDoc()->WarnOnceAbout(nsIDocument::eLenientThis);
2078 return true;
2081 bool
2082 GetWindowForJSImplementedObject(JSContext* cx, JS::Handle<JSObject*> obj,
2083 nsPIDOMWindow** window)
2085 // Be very careful to not get tricked here.
2086 MOZ_ASSERT(NS_IsMainThread());
2087 if (!xpc::AccessCheck::isChrome(js::GetObjectCompartment(obj))) {
2088 NS_RUNTIMEABORT("Should have a chrome object here");
2091 // Look up the content-side object.
2092 JS::Rooted<JS::Value> domImplVal(cx);
2093 if (!JS_GetProperty(cx, obj, "__DOM_IMPL__", &domImplVal)) {
2094 return false;
2097 if (!domImplVal.isObject()) {
2098 ThrowErrorMessage(cx, MSG_NOT_OBJECT, "Value");
2099 return false;
2102 // Go ahead and get the global from it. GlobalObject will handle
2103 // doing unwrapping as needed.
2104 GlobalObject global(cx, &domImplVal.toObject());
2105 if (global.Failed()) {
2106 return false;
2109 // It's OK if we have null here: that just means the content-side
2110 // object really wasn't associated with any window.
2111 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(global.GetAsSupports()));
2112 win.forget(window);
2113 return true;
2116 already_AddRefed<nsPIDOMWindow>
2117 ConstructJSImplementation(JSContext* aCx, const char* aContractId,
2118 const GlobalObject& aGlobal,
2119 JS::MutableHandle<JSObject*> aObject,
2120 ErrorResult& aRv)
2122 // Get the window to use as a parent and for initialization.
2123 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
2124 if (!window) {
2125 aRv.Throw(NS_ERROR_FAILURE);
2126 return nullptr;
2129 ConstructJSImplementation(aCx, aContractId, window, aObject, aRv);
2131 if (aRv.Failed()) {
2132 return nullptr;
2134 return window.forget();
2137 void
2138 ConstructJSImplementation(JSContext* aCx, const char* aContractId,
2139 nsPIDOMWindow* aWindow,
2140 JS::MutableHandle<JSObject*> aObject,
2141 ErrorResult& aRv)
2143 // Make sure to divorce ourselves from the calling JS while creating and
2144 // initializing the object, so exceptions from that will get reported
2145 // properly, since those are never exceptions that a spec wants to be thrown.
2147 AutoNoJSAPI nojsapi;
2149 // Get the XPCOM component containing the JS implementation.
2150 nsCOMPtr<nsISupports> implISupports = do_CreateInstance(aContractId);
2151 if (!implISupports) {
2152 NS_WARNING("Failed to get JS implementation for contract");
2153 aRv.Throw(NS_ERROR_FAILURE);
2154 return;
2156 // Initialize the object, if it implements nsIDOMGlobalPropertyInitializer.
2157 nsCOMPtr<nsIDOMGlobalPropertyInitializer> gpi =
2158 do_QueryInterface(implISupports);
2159 if (gpi) {
2160 JS::Rooted<JS::Value> initReturn(aCx);
2161 nsresult rv = gpi->Init(aWindow, &initReturn);
2162 if (NS_FAILED(rv)) {
2163 aRv.Throw(rv);
2164 return;
2166 // With JS-implemented WebIDL, the return value of init() is not used to determine
2167 // if init() failed, so init() should only return undefined. Any kind of permission
2168 // or pref checking must happen by adding an attribute to the WebIDL interface.
2169 if (!initReturn.isUndefined()) {
2170 MOZ_ASSERT(false, "The init() method for JS-implemented WebIDL should not return anything");
2171 MOZ_CRASH();
2174 // Extract the JS implementation from the XPCOM object.
2175 nsCOMPtr<nsIXPConnectWrappedJS> implWrapped =
2176 do_QueryInterface(implISupports);
2177 MOZ_ASSERT(implWrapped, "Failed to get wrapped JS from XPCOM component.");
2178 if (!implWrapped) {
2179 aRv.Throw(NS_ERROR_FAILURE);
2180 return;
2182 aObject.set(implWrapped->GetJSObject());
2183 if (!aObject) {
2184 aRv.Throw(NS_ERROR_FAILURE);
2189 bool
2190 NonVoidByteStringToJsval(JSContext *cx, const nsACString &str,
2191 JS::MutableHandle<JS::Value> rval)
2193 // ByteStrings are not UTF-8 encoded.
2194 JSString* jsStr = JS_NewStringCopyN(cx, str.Data(), str.Length());
2196 if (!jsStr)
2197 return false;
2199 rval.setString(jsStr);
2200 return true;
2204 template<typename T> static void
2205 NormalizeScalarValueStringInternal(JSContext* aCx, T& aString)
2207 char16_t* start = aString.BeginWriting();
2208 // Must use const here because we can't pass char** to UTF16CharEnumerator as
2209 // it expects const char**. Unclear why this is illegal...
2210 const char16_t* nextChar = start;
2211 const char16_t* end = aString.Data() + aString.Length();
2212 while (nextChar < end) {
2213 uint32_t enumerated = UTF16CharEnumerator::NextChar(&nextChar, end);
2214 if (enumerated == UCS2_REPLACEMENT_CHAR) {
2215 int32_t lastCharIndex = (nextChar - start) - 1;
2216 start[lastCharIndex] = static_cast<char16_t>(enumerated);
2221 void
2222 NormalizeScalarValueString(JSContext* aCx, nsAString& aString)
2224 NormalizeScalarValueStringInternal(aCx, aString);
2227 void
2228 NormalizeScalarValueString(JSContext* aCx, binding_detail::FakeString& aString)
2230 NormalizeScalarValueStringInternal(aCx, aString);
2233 bool
2234 ConvertJSValueToByteString(JSContext* cx, JS::Handle<JS::Value> v,
2235 bool nullable, nsACString& result)
2237 JS::Rooted<JSString*> s(cx);
2238 if (v.isString()) {
2239 s = v.toString();
2240 } else {
2242 if (nullable && v.isNullOrUndefined()) {
2243 result.SetIsVoid(true);
2244 return true;
2247 s = JS::ToString(cx, v);
2248 if (!s) {
2249 return false;
2253 // Conversion from Javascript string to ByteString is only valid if all
2254 // characters < 256. This is always the case for Latin1 strings.
2255 size_t length;
2256 if (!js::StringHasLatin1Chars(s)) {
2257 // ThrowErrorMessage can GC, so we first scan the string for bad chars
2258 // and report the error outside the AutoCheckCannotGC scope.
2259 bool foundBadChar = false;
2260 size_t badCharIndex;
2261 jschar badChar;
2263 JS::AutoCheckCannotGC nogc;
2264 const jschar* chars = JS_GetTwoByteStringCharsAndLength(cx, nogc, s, &length);
2265 if (!chars) {
2266 return false;
2269 for (size_t i = 0; i < length; i++) {
2270 if (chars[i] > 255) {
2271 badCharIndex = i;
2272 badChar = chars[i];
2273 foundBadChar = true;
2274 break;
2279 if (foundBadChar) {
2280 MOZ_ASSERT(badCharIndex < length);
2281 MOZ_ASSERT(badChar > 255);
2282 // The largest unsigned 64 bit number (18,446,744,073,709,551,615) has
2283 // 20 digits, plus one more for the null terminator.
2284 char index[21];
2285 static_assert(sizeof(size_t) <= 8, "index array too small");
2286 PR_snprintf(index, sizeof(index), "%d", badCharIndex);
2287 // A jschar is 16 bits long. The biggest unsigned 16 bit
2288 // number (65,535) has 5 digits, plus one more for the null
2289 // terminator.
2290 char badCharArray[6];
2291 static_assert(sizeof(jschar) <= 2, "badCharArray too small");
2292 PR_snprintf(badCharArray, sizeof(badCharArray), "%d", badChar);
2293 ThrowErrorMessage(cx, MSG_INVALID_BYTESTRING, index, badCharArray);
2294 return false;
2296 } else {
2297 length = js::GetStringLength(s);
2300 static_assert(js::MaxStringLength < UINT32_MAX,
2301 "length+1 shouldn't overflow");
2303 result.SetLength(length);
2304 JS_EncodeStringToBuffer(cx, s, result.BeginWriting(), length);
2306 return true;
2309 bool
2310 IsInPrivilegedApp(JSContext* aCx, JSObject* aObj)
2312 using mozilla::dom::workers::GetWorkerPrivateFromContext;
2313 if (!NS_IsMainThread()) {
2314 return GetWorkerPrivateFromContext(aCx)->IsInPrivilegedApp();
2317 nsIPrincipal* principal = nsContentUtils::ObjectPrincipal(aObj);
2318 uint16_t appStatus = principal->GetAppStatus();
2319 return (appStatus == nsIPrincipal::APP_STATUS_CERTIFIED ||
2320 appStatus == nsIPrincipal::APP_STATUS_PRIVILEGED) ||
2321 Preferences::GetBool("dom.ignore_webidl_scope_checks", false);
2324 bool
2325 IsInCertifiedApp(JSContext* aCx, JSObject* aObj)
2327 using mozilla::dom::workers::GetWorkerPrivateFromContext;
2328 if (!NS_IsMainThread()) {
2329 return GetWorkerPrivateFromContext(aCx)->IsInCertifiedApp();
2332 nsIPrincipal* principal = nsContentUtils::ObjectPrincipal(aObj);
2333 return principal->GetAppStatus() == nsIPrincipal::APP_STATUS_CERTIFIED ||
2334 Preferences::GetBool("dom.ignore_webidl_scope_checks", false);
2337 #ifdef DEBUG
2338 void
2339 VerifyTraceProtoAndIfaceCacheCalled(JSTracer *trc, void **thingp,
2340 JSGCTraceKind kind)
2342 // We don't do anything here, we only want to verify that
2343 // TraceProtoAndIfaceCache was called.
2345 #endif
2347 void
2348 FinalizeGlobal(JSFreeOp* aFreeOp, JSObject* aObj)
2350 MOZ_ASSERT(js::GetObjectClass(aObj)->flags & JSCLASS_DOM_GLOBAL);
2351 mozilla::dom::DestroyProtoAndIfaceCache(aObj);
2354 bool
2355 ResolveGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj,
2356 JS::Handle<jsid> aId, JS::MutableHandle<JSObject*> aObjp)
2358 bool resolved;
2359 if (!JS_ResolveStandardClass(aCx, aObj, aId, &resolved)) {
2360 return false;
2363 aObjp.set(resolved ? aObj.get() : nullptr);
2364 return true;
2367 bool
2368 EnumerateGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj)
2370 return JS_EnumerateStandardClasses(aCx, aObj);
2373 bool
2374 CheckPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissions[])
2376 JS::Rooted<JSObject*> rootedObj(aCx, aObj);
2377 nsPIDOMWindow* window = xpc::WindowGlobalOrNull(rootedObj);
2378 if (!window) {
2379 return false;
2382 nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
2383 NS_ENSURE_TRUE(permMgr, false);
2385 do {
2386 uint32_t permission = nsIPermissionManager::DENY_ACTION;
2387 permMgr->TestPermissionFromWindow(window, *aPermissions, &permission);
2388 if (permission == nsIPermissionManager::ALLOW_ACTION) {
2389 return true;
2391 } while (*(++aPermissions));
2392 return false;
2395 bool
2396 CheckSafetyInPrerendering(JSContext* aCx, JSObject* aObj)
2398 //TODO: Check if page is being prerendered.
2399 //Returning false for now.
2400 return false;
2403 bool
2404 GenericBindingGetter(JSContext* cx, unsigned argc, JS::Value* vp)
2406 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
2407 const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
2408 prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID);
2409 if (!args.thisv().isObject()) {
2410 return ThrowInvalidThis(cx, args,
2411 MSG_GETTER_THIS_DOES_NOT_IMPLEMENT_INTERFACE,
2412 protoID);
2414 JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
2416 void* self;
2418 nsresult rv = UnwrapObject<void>(obj, self, protoID, info->depth);
2419 if (NS_FAILED(rv)) {
2420 return ThrowInvalidThis(cx, args,
2421 GetInvalidThisErrorForGetter(rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO),
2422 protoID);
2426 MOZ_ASSERT(info->type() == JSJitInfo::Getter);
2427 JSJitGetterOp getter = info->getter;
2428 bool ok = getter(cx, obj, self, JSJitGetterCallArgs(args));
2429 #ifdef DEBUG
2430 if (ok) {
2431 AssertReturnTypeMatchesJitinfo(info, args.rval());
2433 #endif
2434 return ok;
2437 bool
2438 GenericBindingSetter(JSContext* cx, unsigned argc, JS::Value* vp)
2440 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
2441 const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
2442 prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID);
2443 if (!args.thisv().isObject()) {
2444 return ThrowInvalidThis(cx, args,
2445 MSG_SETTER_THIS_DOES_NOT_IMPLEMENT_INTERFACE,
2446 protoID);
2448 JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
2450 void* self;
2452 nsresult rv = UnwrapObject<void>(obj, self, protoID, info->depth);
2453 if (NS_FAILED(rv)) {
2454 return ThrowInvalidThis(cx, args,
2455 GetInvalidThisErrorForSetter(rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO),
2456 protoID);
2459 if (args.length() == 0) {
2460 return ThrowNoSetterArg(cx, protoID);
2462 MOZ_ASSERT(info->type() == JSJitInfo::Setter);
2463 JSJitSetterOp setter = info->setter;
2464 if (!setter(cx, obj, self, JSJitSetterCallArgs(args))) {
2465 return false;
2467 args.rval().setUndefined();
2468 #ifdef DEBUG
2469 AssertReturnTypeMatchesJitinfo(info, args.rval());
2470 #endif
2471 return true;
2474 bool
2475 GenericBindingMethod(JSContext* cx, unsigned argc, JS::Value* vp)
2477 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
2478 const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
2479 prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID);
2480 if (!args.thisv().isObject()) {
2481 return ThrowInvalidThis(cx, args,
2482 MSG_METHOD_THIS_DOES_NOT_IMPLEMENT_INTERFACE,
2483 protoID);
2485 JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
2487 void* self;
2489 nsresult rv = UnwrapObject<void>(obj, self, protoID, info->depth);
2490 if (NS_FAILED(rv)) {
2491 return ThrowInvalidThis(cx, args,
2492 GetInvalidThisErrorForMethod(rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO),
2493 protoID);
2496 MOZ_ASSERT(info->type() == JSJitInfo::Method);
2497 JSJitMethodOp method = info->method;
2498 bool ok = method(cx, obj, self, JSJitMethodCallArgs(args));
2499 #ifdef DEBUG
2500 if (ok) {
2501 AssertReturnTypeMatchesJitinfo(info, args.rval());
2503 #endif
2504 return ok;
2507 bool
2508 GenericPromiseReturningBindingMethod(JSContext* cx, unsigned argc, JS::Value* vp)
2510 // Make sure to save the callee before someone maybe messes with rval().
2511 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
2512 JS::Rooted<JSObject*> callee(cx, &args.callee());
2514 // We could invoke GenericBindingMethod here, but that involves an
2515 // extra call. Manually inline it instead.
2516 const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
2517 prototypes::ID protoID = static_cast<prototypes::ID>(info->protoID);
2518 if (!args.thisv().isObject()) {
2519 ThrowInvalidThis(cx, args,
2520 MSG_METHOD_THIS_DOES_NOT_IMPLEMENT_INTERFACE,
2521 protoID);
2522 return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
2523 args.rval());
2525 JS::Rooted<JSObject*> obj(cx, &args.thisv().toObject());
2527 void* self;
2529 nsresult rv = UnwrapObject<void>(obj, self, protoID, info->depth);
2530 if (NS_FAILED(rv)) {
2531 ThrowInvalidThis(cx, args,
2532 GetInvalidThisErrorForMethod(rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO),
2533 protoID);
2534 return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
2535 args.rval());
2538 MOZ_ASSERT(info->type() == JSJitInfo::Method);
2539 JSJitMethodOp method = info->method;
2540 bool ok = method(cx, obj, self, JSJitMethodCallArgs(args));
2541 if (ok) {
2542 #ifdef DEBUG
2543 AssertReturnTypeMatchesJitinfo(info, args.rval());
2544 #endif
2545 return true;
2548 // Promise-returning methods always return objects
2549 MOZ_ASSERT(info->returnType() == JSVAL_TYPE_OBJECT);
2550 return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
2551 args.rval());
2554 bool
2555 StaticMethodPromiseWrapper(JSContext* cx, unsigned argc, JS::Value* vp)
2557 // Make sure to save the callee before someone maybe messes with rval().
2558 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
2559 JS::Rooted<JSObject*> callee(cx, &args.callee());
2561 const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
2562 MOZ_ASSERT(info);
2563 MOZ_ASSERT(info->type() == JSJitInfo::StaticMethod);
2565 bool ok = info->staticMethod(cx, argc, vp);
2566 if (ok) {
2567 return true;
2570 return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
2571 args.rval());
2574 bool
2575 ConvertExceptionToPromise(JSContext* cx,
2576 JSObject* promiseScope,
2577 JS::MutableHandle<JS::Value> rval)
2579 GlobalObject global(cx, promiseScope);
2580 if (global.Failed()) {
2581 return false;
2584 JS::Rooted<JS::Value> exn(cx);
2585 if (!JS_GetPendingException(cx, &exn)) {
2586 return false;
2589 JS_ClearPendingException(cx);
2590 ErrorResult rv;
2591 nsRefPtr<Promise> promise = Promise::Reject(global, exn, rv);
2592 if (rv.Failed()) {
2593 // We just give up. Make sure to not leak memory on the
2594 // ErrorResult, but then just put the original exception back.
2595 ThrowMethodFailedWithDetails(cx, rv, "", "");
2596 JS_SetPendingException(cx, exn);
2597 return false;
2600 return WrapNewBindingObject(cx, promise, rval);
2603 /* static */
2604 void
2605 CreateGlobalOptions<nsGlobalWindow>::TraceGlobal(JSTracer* aTrc, JSObject* aObj)
2607 xpc::TraceXPCGlobal(aTrc, aObj);
2610 static bool sRegisteredDOMNames = false;
2612 nsresult
2613 RegisterDOMNames()
2615 if (sRegisteredDOMNames) {
2616 return NS_OK;
2619 nsresult rv = nsDOMClassInfo::Init();
2620 if (NS_FAILED(rv)) {
2621 NS_ERROR("Could not initialize nsDOMClassInfo");
2622 return rv;
2625 // Register new DOM bindings
2626 nsScriptNameSpaceManager* nameSpaceManager = GetNameSpaceManager();
2627 if (!nameSpaceManager) {
2628 NS_ERROR("Could not initialize nsScriptNameSpaceManager");
2629 return NS_ERROR_FAILURE;
2631 mozilla::dom::Register(nameSpaceManager);
2633 sRegisteredDOMNames = true;
2635 return NS_OK;
2638 /* static */
2639 bool
2640 CreateGlobalOptions<nsGlobalWindow>::PostCreateGlobal(JSContext* aCx,
2641 JS::Handle<JSObject*> aGlobal)
2643 nsresult rv = RegisterDOMNames();
2644 if (NS_FAILED(rv)) {
2645 return Throw(aCx, rv);
2648 // Invoking the XPCWrappedNativeScope constructor automatically hooks it
2649 // up to the compartment of aGlobal.
2650 (void) new XPCWrappedNativeScope(aCx, aGlobal);
2651 return true;
2654 #ifdef DEBUG
2655 void
2656 AssertReturnTypeMatchesJitinfo(const JSJitInfo* aJitInfo,
2657 JS::Handle<JS::Value> aValue)
2659 switch (aJitInfo->returnType()) {
2660 case JSVAL_TYPE_UNKNOWN:
2661 // Any value is good.
2662 break;
2663 case JSVAL_TYPE_DOUBLE:
2664 // The value could actually be an int32 value as well.
2665 MOZ_ASSERT(aValue.isNumber());
2666 break;
2667 case JSVAL_TYPE_INT32:
2668 MOZ_ASSERT(aValue.isInt32());
2669 break;
2670 case JSVAL_TYPE_UNDEFINED:
2671 MOZ_ASSERT(aValue.isUndefined());
2672 break;
2673 case JSVAL_TYPE_BOOLEAN:
2674 MOZ_ASSERT(aValue.isBoolean());
2675 break;
2676 case JSVAL_TYPE_STRING:
2677 MOZ_ASSERT(aValue.isString());
2678 break;
2679 case JSVAL_TYPE_NULL:
2680 MOZ_ASSERT(aValue.isNull());
2681 break;
2682 case JSVAL_TYPE_OBJECT:
2683 MOZ_ASSERT(aValue.isObject());
2684 break;
2685 default:
2686 // Someone messed up their jitinfo type.
2687 MOZ_ASSERT(false, "Unexpected JSValueType stored in jitinfo");
2688 break;
2691 #endif
2693 bool
2694 CallerSubsumes(JSObject *aObject)
2696 nsIPrincipal* objPrin = nsContentUtils::ObjectPrincipal(js::UncheckedUnwrap(aObject));
2697 return nsContentUtils::SubjectPrincipal()->Subsumes(objPrin);
2700 } // namespace dom
2701 } // namespace mozilla