/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* The "Components" xpcom objects for JavaScript. */ #include "xpcprivate.h" #include "xpc_make_class.h" #include "XPCJSWeakReference.h" #include "AccessCheck.h" #include "WrapperFactory.h" #include "nsJSUtils.h" #include "mozJSModuleLoader.h" #include "nsContentUtils.h" #include "nsCycleCollector.h" #include "jsfriendapi.h" #include "js/Array.h" // JS::IsArrayObject #include "js/CallAndConstruct.h" // JS::IsCallable, JS_CallFunctionName, JS_CallFunctionValue #include "js/CharacterEncoding.h" #include "js/friend/WindowProxy.h" // js::ToWindowProxyIfWindow #include "js/Object.h" // JS::GetClass, JS::GetCompartment #include "js/PropertyAndElement.h" // JS_DefineProperty, JS_DefinePropertyById, JS_Enumerate, JS_GetProperty, JS_GetPropertyById, JS_HasProperty, JS_SetProperty, JS_SetPropertyById #include "js/SavedFrameAPI.h" #include "js/StructuredClone.h" #include "mozilla/AppShutdown.h" #include "mozilla/Attributes.h" #include "mozilla/LoadContext.h" #include "mozilla/Preferences.h" #include "nsJSEnvironment.h" #include "mozilla/BasePrincipal.h" #include "mozilla/TimeStamp.h" #include "mozilla/ResultExtensions.h" #include "mozilla/Try.h" #include "mozilla/URLPreloader.h" #include "mozilla/dom/DOMException.h" #include "mozilla/dom/DOMExceptionBinding.h" #include "mozilla/dom/Exceptions.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/RemoteObjectProxy.h" #include "mozilla/dom/StructuredCloneTags.h" #include "mozilla/dom/WindowBinding.h" #include "nsZipArchive.h" #include "nsWindowMemoryReporter.h" #include "nsICycleCollectorListener.h" #include "nsIException.h" #include "nsIScriptError.h" #include "nsPIDOMWindow.h" #include "nsGlobalWindowInner.h" #include "nsScriptError.h" #include "GeckoProfiler.h" #include "ProfilerControl.h" #include "mozilla/EditorSpellCheck.h" #include "nsCommandLine.h" #include "nsCommandParams.h" #include "nsPersistentProperties.h" #include "nsIDocumentEncoder.h" using namespace mozilla; using namespace JS; using namespace js; using namespace xpc; using mozilla::dom::Exception; /***************************************************************************/ // stuff used by all nsresult xpc::ThrowAndFail(nsresult errNum, JSContext* cx, bool* retval) { XPCThrower::Throw(errNum, cx); *retval = false; return NS_OK; } static bool JSValIsInterfaceOfType(JSContext* cx, HandleValue v, REFNSIID iid) { nsCOMPtr wn; nsCOMPtr iface; if (v.isPrimitive()) { return false; } nsIXPConnect* xpc = nsIXPConnect::XPConnect(); RootedObject obj(cx, &v.toObject()); return NS_SUCCEEDED( xpc->GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wn))) && wn && NS_SUCCEEDED( wn->Native()->QueryInterface(iid, getter_AddRefs(iface))) && iface; } /***************************************************************************/ /***************************************************************************/ /***************************************************************************/ class nsXPCComponents_Interfaces final : public nsIXPCComponents_Interfaces, public nsIXPCScriptable, public nsIClassInfo { public: // all the interface method declarations... NS_DECL_ISUPPORTS NS_DECL_NSIXPCCOMPONENTS_INTERFACES NS_DECL_NSIXPCSCRIPTABLE NS_DECL_NSICLASSINFO public: nsXPCComponents_Interfaces(); private: virtual ~nsXPCComponents_Interfaces(); }; NS_IMETHODIMP nsXPCComponents_Interfaces::GetInterfaces(nsTArray& aArray) { aArray = nsTArray{NS_GET_IID(nsIXPCComponents_Interfaces), NS_GET_IID(nsIXPCScriptable)}; return NS_OK; } NS_IMETHODIMP nsXPCComponents_Interfaces::GetScriptableHelper(nsIXPCScriptable** retval) { *retval = nullptr; return NS_OK; } NS_IMETHODIMP nsXPCComponents_Interfaces::GetContractID(nsACString& aContractID) { aContractID.SetIsVoid(true); return NS_ERROR_NOT_AVAILABLE; } NS_IMETHODIMP nsXPCComponents_Interfaces::GetClassDescription(nsACString& aClassDescription) { aClassDescription.AssignLiteral("XPCComponents_Interfaces"); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Interfaces::GetClassID(nsCID** aClassID) { *aClassID = nullptr; return NS_OK; } NS_IMETHODIMP nsXPCComponents_Interfaces::GetFlags(uint32_t* aFlags) { *aFlags = 0; return NS_OK; } NS_IMETHODIMP nsXPCComponents_Interfaces::GetClassIDNoAlloc(nsCID* aClassIDNoAlloc) { return NS_ERROR_NOT_AVAILABLE; } nsXPCComponents_Interfaces::nsXPCComponents_Interfaces() = default; nsXPCComponents_Interfaces::~nsXPCComponents_Interfaces() { // empty } NS_IMPL_ISUPPORTS(nsXPCComponents_Interfaces, nsIXPCComponents_Interfaces, nsIXPCScriptable, nsIClassInfo); // The nsIXPCScriptable map declaration that will generate stubs for us... #define XPC_MAP_CLASSNAME nsXPCComponents_Interfaces #define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_Interfaces" #define XPC_MAP_FLAGS \ (XPC_SCRIPTABLE_WANT_RESOLVE | XPC_SCRIPTABLE_WANT_NEWENUMERATE | \ XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE) #include "xpc_map_end.h" /* This will #undef the above */ NS_IMETHODIMP nsXPCComponents_Interfaces::NewEnumerate(nsIXPConnectWrappedNative* wrapper, JSContext* cx, JSObject* obj, JS::MutableHandleIdVector properties, bool enumerableOnly, bool* _retval) { if (!properties.reserve(nsXPTInterfaceInfo::InterfaceCount())) { *_retval = false; return NS_OK; } for (uint32_t index = 0; index < nsXPTInterfaceInfo::InterfaceCount(); index++) { const nsXPTInterfaceInfo* interface = nsXPTInterfaceInfo::ByIndex(index); if (!interface) { continue; } const char* name = interface->Name(); if (!name) { continue; } RootedString idstr(cx, JS_NewStringCopyZ(cx, name)); if (!idstr) { *_retval = false; return NS_OK; } RootedId id(cx); if (!JS_StringToId(cx, idstr, &id)) { *_retval = false; return NS_OK; } properties.infallibleAppend(id); } return NS_OK; } NS_IMETHODIMP nsXPCComponents_Interfaces::Resolve(nsIXPConnectWrappedNative* wrapper, JSContext* cx, JSObject* objArg, jsid idArg, bool* resolvedp, bool* _retval) { RootedObject obj(cx, objArg); RootedId id(cx, idArg); if (!id.isString()) { return NS_OK; } RootedString str(cx, id.toString()); JS::UniqueChars name = JS_EncodeStringToLatin1(cx, str); // we only allow interfaces by name here if (name && name[0] != '{') { const nsXPTInterfaceInfo* info = nsXPTInterfaceInfo::ByName(name.get()); if (!info) { return NS_OK; } RootedValue iidv(cx); if (xpc::IfaceID2JSValue(cx, *info, &iidv)) { *resolvedp = true; *_retval = JS_DefinePropertyById(cx, obj, id, iidv, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_RESOLVING); } } return NS_OK; } /***************************************************************************/ /***************************************************************************/ /***************************************************************************/ class nsXPCComponents_Classes final : public nsIXPCComponents_Classes, public nsIXPCScriptable, public nsIClassInfo { public: // all the interface method declarations... NS_DECL_ISUPPORTS NS_DECL_NSIXPCCOMPONENTS_CLASSES NS_DECL_NSIXPCSCRIPTABLE NS_DECL_NSICLASSINFO public: nsXPCComponents_Classes(); private: virtual ~nsXPCComponents_Classes(); }; /***************************************************************************/ NS_IMETHODIMP nsXPCComponents_Classes::GetInterfaces(nsTArray& aArray) { aArray = nsTArray{NS_GET_IID(nsIXPCComponents_Classes), NS_GET_IID(nsIXPCScriptable)}; return NS_OK; } NS_IMETHODIMP nsXPCComponents_Classes::GetScriptableHelper(nsIXPCScriptable** retval) { *retval = nullptr; return NS_OK; } NS_IMETHODIMP nsXPCComponents_Classes::GetContractID(nsACString& aContractID) { aContractID.SetIsVoid(true); return NS_ERROR_NOT_AVAILABLE; } NS_IMETHODIMP nsXPCComponents_Classes::GetClassDescription(nsACString& aClassDescription) { aClassDescription.AssignLiteral("XPCComponents_Classes"); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Classes::GetClassID(nsCID** aClassID) { *aClassID = nullptr; return NS_OK; } NS_IMETHODIMP nsXPCComponents_Classes::GetFlags(uint32_t* aFlags) { *aFlags = 0; return NS_OK; } NS_IMETHODIMP nsXPCComponents_Classes::GetClassIDNoAlloc(nsCID* aClassIDNoAlloc) { return NS_ERROR_NOT_AVAILABLE; } nsXPCComponents_Classes::nsXPCComponents_Classes() = default; nsXPCComponents_Classes::~nsXPCComponents_Classes() { // empty } NS_IMPL_ISUPPORTS(nsXPCComponents_Classes, nsIXPCComponents_Classes, nsIXPCScriptable, nsIClassInfo) // The nsIXPCScriptable map declaration that will generate stubs for us... #define XPC_MAP_CLASSNAME nsXPCComponents_Classes #define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_Classes" #define XPC_MAP_FLAGS \ (XPC_SCRIPTABLE_WANT_RESOLVE | XPC_SCRIPTABLE_WANT_NEWENUMERATE | \ XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE) #include "xpc_map_end.h" /* This will #undef the above */ NS_IMETHODIMP nsXPCComponents_Classes::NewEnumerate(nsIXPConnectWrappedNative* wrapper, JSContext* cx, JSObject* obj, JS::MutableHandleIdVector properties, bool enumerableOnly, bool* _retval) { nsCOMPtr compMgr; if (NS_FAILED(NS_GetComponentRegistrar(getter_AddRefs(compMgr))) || !compMgr) { return NS_ERROR_UNEXPECTED; } nsTArray contractIDs; if (NS_FAILED(compMgr->GetContractIDs(contractIDs))) { return NS_ERROR_UNEXPECTED; } for (const auto& name : contractIDs) { RootedString idstr(cx, JS_NewStringCopyN(cx, name.get(), name.Length())); if (!idstr) { *_retval = false; return NS_OK; } RootedId id(cx); if (!JS_StringToId(cx, idstr, &id)) { *_retval = false; return NS_OK; } if (!properties.append(id)) { *_retval = false; return NS_OK; } } return NS_OK; } NS_IMETHODIMP nsXPCComponents_Classes::Resolve(nsIXPConnectWrappedNative* wrapper, JSContext* cx, JSObject* objArg, jsid idArg, bool* resolvedp, bool* _retval) { RootedId id(cx, idArg); RootedObject obj(cx, objArg); RootedValue cidv(cx); if (id.isString() && xpc::ContractID2JSValue(cx, id.toString(), &cidv)) { *resolvedp = true; *_retval = JS_DefinePropertyById(cx, obj, id, cidv, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_RESOLVING); } return NS_OK; } /***************************************************************************/ /***************************************************************************/ /***************************************************************************/ // Currently the possible results do not change at runtime, so they are only // cached once (unlike ContractIDs, CLSIDs, and IIDs) class nsXPCComponents_Results final : public nsIXPCComponents_Results, public nsIXPCScriptable, public nsIClassInfo { public: // all the interface method declarations... NS_DECL_ISUPPORTS NS_DECL_NSIXPCCOMPONENTS_RESULTS NS_DECL_NSIXPCSCRIPTABLE NS_DECL_NSICLASSINFO public: nsXPCComponents_Results(); private: virtual ~nsXPCComponents_Results(); }; /***************************************************************************/ NS_IMETHODIMP nsXPCComponents_Results::GetInterfaces(nsTArray& aArray) { aArray = nsTArray{NS_GET_IID(nsIXPCComponents_Results), NS_GET_IID(nsIXPCScriptable)}; return NS_OK; } NS_IMETHODIMP nsXPCComponents_Results::GetScriptableHelper(nsIXPCScriptable** retval) { *retval = nullptr; return NS_OK; } NS_IMETHODIMP nsXPCComponents_Results::GetContractID(nsACString& aContractID) { aContractID.SetIsVoid(true); return NS_ERROR_NOT_AVAILABLE; } NS_IMETHODIMP nsXPCComponents_Results::GetClassDescription(nsACString& aClassDescription) { aClassDescription.AssignLiteral("XPCComponents_Results"); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Results::GetClassID(nsCID** aClassID) { *aClassID = nullptr; return NS_OK; } NS_IMETHODIMP nsXPCComponents_Results::GetFlags(uint32_t* aFlags) { *aFlags = 0; return NS_OK; } NS_IMETHODIMP nsXPCComponents_Results::GetClassIDNoAlloc(nsCID* aClassIDNoAlloc) { return NS_ERROR_NOT_AVAILABLE; } nsXPCComponents_Results::nsXPCComponents_Results() = default; nsXPCComponents_Results::~nsXPCComponents_Results() { // empty } NS_IMPL_ISUPPORTS(nsXPCComponents_Results, nsIXPCComponents_Results, nsIXPCScriptable, nsIClassInfo) // The nsIXPCScriptable map declaration that will generate stubs for us... #define XPC_MAP_CLASSNAME nsXPCComponents_Results #define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_Results" #define XPC_MAP_FLAGS \ (XPC_SCRIPTABLE_WANT_RESOLVE | XPC_SCRIPTABLE_WANT_NEWENUMERATE | \ XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE) #include "xpc_map_end.h" /* This will #undef the above */ NS_IMETHODIMP nsXPCComponents_Results::NewEnumerate(nsIXPConnectWrappedNative* wrapper, JSContext* cx, JSObject* obj, JS::MutableHandleIdVector properties, bool enumerableOnly, bool* _retval) { const char* name; const void* iter = nullptr; while (nsXPCException::IterateNSResults(nullptr, &name, nullptr, &iter)) { RootedString idstr(cx, JS_NewStringCopyZ(cx, name)); if (!idstr) { *_retval = false; return NS_OK; } RootedId id(cx); if (!JS_StringToId(cx, idstr, &id)) { *_retval = false; return NS_OK; } if (!properties.append(id)) { *_retval = false; return NS_OK; } } return NS_OK; } NS_IMETHODIMP nsXPCComponents_Results::Resolve(nsIXPConnectWrappedNative* wrapper, JSContext* cx, JSObject* objArg, jsid idArg, bool* resolvedp, bool* _retval) { RootedObject obj(cx, objArg); RootedId id(cx, idArg); if (!id.isString()) { return NS_OK; } JS::UniqueChars name = JS_EncodeStringToLatin1(cx, id.toString()); if (name) { const char* rv_name; const void* iter = nullptr; nsresult rv; while (nsXPCException::IterateNSResults(&rv, &rv_name, nullptr, &iter)) { if (!strcmp(name.get(), rv_name)) { *resolvedp = true; if (!JS_DefinePropertyById(cx, obj, id, (uint32_t)rv, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_RESOLVING)) { return NS_ERROR_UNEXPECTED; } } } } return NS_OK; } /***************************************************************************/ // JavaScript Constructor for nsIJSID objects (Components.ID) class nsXPCComponents_ID final : public nsIXPCComponents_ID, public nsIXPCScriptable, public nsIClassInfo { public: // all the interface method declarations... NS_DECL_ISUPPORTS NS_DECL_NSIXPCCOMPONENTS_ID NS_DECL_NSIXPCSCRIPTABLE NS_DECL_NSICLASSINFO public: nsXPCComponents_ID(); private: virtual ~nsXPCComponents_ID(); static nsresult CallOrConstruct(nsIXPConnectWrappedNative* wrapper, JSContext* cx, HandleObject obj, const CallArgs& args, bool* _retval); }; /***************************************************************************/ NS_IMETHODIMP nsXPCComponents_ID::GetInterfaces(nsTArray& aArray) { aArray = nsTArray{NS_GET_IID(nsIXPCComponents_ID), NS_GET_IID(nsIXPCScriptable)}; return NS_OK; } NS_IMETHODIMP nsXPCComponents_ID::GetScriptableHelper(nsIXPCScriptable** retval) { *retval = nullptr; return NS_OK; } NS_IMETHODIMP nsXPCComponents_ID::GetContractID(nsACString& aContractID) { aContractID.SetIsVoid(true); return NS_ERROR_NOT_AVAILABLE; } NS_IMETHODIMP nsXPCComponents_ID::GetClassDescription(nsACString& aClassDescription) { aClassDescription.AssignLiteral("XPCComponents_ID"); return NS_OK; } NS_IMETHODIMP nsXPCComponents_ID::GetClassID(nsCID** aClassID) { *aClassID = nullptr; return NS_OK; } NS_IMETHODIMP nsXPCComponents_ID::GetFlags(uint32_t* aFlags) { *aFlags = 0; return NS_OK; } NS_IMETHODIMP nsXPCComponents_ID::GetClassIDNoAlloc(nsCID* aClassIDNoAlloc) { return NS_ERROR_NOT_AVAILABLE; } nsXPCComponents_ID::nsXPCComponents_ID() = default; nsXPCComponents_ID::~nsXPCComponents_ID() { // empty } NS_IMPL_ISUPPORTS(nsXPCComponents_ID, nsIXPCComponents_ID, nsIXPCScriptable, nsIClassInfo) // The nsIXPCScriptable map declaration that will generate stubs for us... #define XPC_MAP_CLASSNAME nsXPCComponents_ID #define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_ID" #define XPC_MAP_FLAGS \ (XPC_SCRIPTABLE_WANT_CALL | XPC_SCRIPTABLE_WANT_CONSTRUCT | \ XPC_SCRIPTABLE_WANT_HASINSTANCE | \ XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE) #include "xpc_map_end.h" /* This will #undef the above */ NS_IMETHODIMP nsXPCComponents_ID::Call(nsIXPConnectWrappedNative* wrapper, JSContext* cx, JSObject* objArg, const CallArgs& args, bool* _retval) { RootedObject obj(cx, objArg); return CallOrConstruct(wrapper, cx, obj, args, _retval); } NS_IMETHODIMP nsXPCComponents_ID::Construct(nsIXPConnectWrappedNative* wrapper, JSContext* cx, JSObject* objArg, const CallArgs& args, bool* _retval) { RootedObject obj(cx, objArg); return CallOrConstruct(wrapper, cx, obj, args, _retval); } // static nsresult nsXPCComponents_ID::CallOrConstruct(nsIXPConnectWrappedNative* wrapper, JSContext* cx, HandleObject obj, const CallArgs& args, bool* _retval) { // make sure we have at least one arg if (args.length() < 1) { return ThrowAndFail(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx, _retval); } // Prevent non-chrome code from creating ID objects. if (!nsContentUtils::IsCallerChrome()) { return ThrowAndFail(NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED, cx, _retval); } // convert the first argument into a string and see if it looks like an id JSString* jsstr = ToString(cx, args[0]); if (!jsstr) { return ThrowAndFail(NS_ERROR_XPC_BAD_ID_STRING, cx, _retval); } JS::UniqueChars bytes = JS_EncodeStringToLatin1(cx, jsstr); if (!bytes) { return ThrowAndFail(NS_ERROR_XPC_BAD_ID_STRING, cx, _retval); } nsID id; if (!id.Parse(bytes.get())) { return ThrowAndFail(NS_ERROR_XPC_BAD_ID_STRING, cx, _retval); } // make the new object and return it. if (!xpc::ID2JSValue(cx, id, args.rval())) { return NS_ERROR_UNEXPECTED; } return NS_OK; } NS_IMETHODIMP nsXPCComponents_ID::HasInstance(nsIXPConnectWrappedNative* wrapper, JSContext* cx, JSObject* obj, HandleValue val, bool* bp, bool* _retval) { if (bp) { *bp = xpc::JSValue2ID(cx, val).isSome(); } return NS_OK; } /***************************************************************************/ // JavaScript Constructor for Exception objects (Components.Exception) class nsXPCComponents_Exception final : public nsIXPCComponents_Exception, public nsIXPCScriptable, public nsIClassInfo { public: // all the interface method declarations... NS_DECL_ISUPPORTS NS_DECL_NSIXPCCOMPONENTS_EXCEPTION NS_DECL_NSIXPCSCRIPTABLE NS_DECL_NSICLASSINFO public: nsXPCComponents_Exception(); private: virtual ~nsXPCComponents_Exception(); static nsresult CallOrConstruct(nsIXPConnectWrappedNative* wrapper, JSContext* cx, HandleObject obj, const CallArgs& args, bool* _retval); }; /***************************************************************************/ NS_IMETHODIMP nsXPCComponents_Exception::GetInterfaces(nsTArray& aArray) { aArray = nsTArray{NS_GET_IID(nsIXPCComponents_Exception), NS_GET_IID(nsIXPCScriptable)}; return NS_OK; } NS_IMETHODIMP nsXPCComponents_Exception::GetScriptableHelper(nsIXPCScriptable** retval) { *retval = nullptr; return NS_OK; } NS_IMETHODIMP nsXPCComponents_Exception::GetContractID(nsACString& aContractID) { aContractID.SetIsVoid(true); return NS_ERROR_NOT_AVAILABLE; } NS_IMETHODIMP nsXPCComponents_Exception::GetClassDescription(nsACString& aClassDescription) { aClassDescription.AssignLiteral("XPCComponents_Exception"); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Exception::GetClassID(nsCID** aClassID) { *aClassID = nullptr; return NS_OK; } NS_IMETHODIMP nsXPCComponents_Exception::GetFlags(uint32_t* aFlags) { *aFlags = 0; return NS_OK; } NS_IMETHODIMP nsXPCComponents_Exception::GetClassIDNoAlloc(nsCID* aClassIDNoAlloc) { return NS_ERROR_NOT_AVAILABLE; } nsXPCComponents_Exception::nsXPCComponents_Exception() = default; nsXPCComponents_Exception::~nsXPCComponents_Exception() { // empty } NS_IMPL_ISUPPORTS(nsXPCComponents_Exception, nsIXPCComponents_Exception, nsIXPCScriptable, nsIClassInfo) // The nsIXPCScriptable map declaration that will generate stubs for us... #define XPC_MAP_CLASSNAME nsXPCComponents_Exception #define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_Exception" #define XPC_MAP_FLAGS \ (XPC_SCRIPTABLE_WANT_CALL | XPC_SCRIPTABLE_WANT_CONSTRUCT | \ XPC_SCRIPTABLE_WANT_HASINSTANCE | \ XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE) #include "xpc_map_end.h" /* This will #undef the above */ NS_IMETHODIMP nsXPCComponents_Exception::Call(nsIXPConnectWrappedNative* wrapper, JSContext* cx, JSObject* objArg, const CallArgs& args, bool* _retval) { RootedObject obj(cx, objArg); return CallOrConstruct(wrapper, cx, obj, args, _retval); } NS_IMETHODIMP nsXPCComponents_Exception::Construct(nsIXPConnectWrappedNative* wrapper, JSContext* cx, JSObject* objArg, const CallArgs& args, bool* _retval) { RootedObject obj(cx, objArg); return CallOrConstruct(wrapper, cx, obj, args, _retval); } struct MOZ_STACK_CLASS ExceptionArgParser { ExceptionArgParser(JSContext* context, nsIXPConnect* xpconnect) : eMsg("exception"), eResult(NS_ERROR_FAILURE), cx(context), xpc(xpconnect) {} // Public exception parameter values. During construction, these are // initialized to the appropriate defaults. const char* eMsg; nsresult eResult; nsCOMPtr eStack; nsCOMPtr eData; // Parse the constructor arguments into the above |eFoo| parameter values. bool parse(const CallArgs& args) { /* * The Components.Exception takes a series of arguments, all of them * optional: * * Argument 0: Exception message (defaults to 'exception'). * Argument 1: Result code (defaults to NS_ERROR_FAILURE) _or_ options * object (see below). * Argument 2: Stack (defaults to the current stack, which we trigger * by leaving this nullptr in the parser). * Argument 3: Optional user data (defaults to nullptr). * * To dig our way out of this clunky API, we now support passing an * options object as the second parameter (as opposed to a result code). * If this is the case, all subsequent arguments are ignored, and the * following properties are parsed out of the object (using the * associated default if the property does not exist): * * result: Result code (see argument 1). * stack: Call stack (see argument 2). * data: User data (see argument 3). */ if (args.length() > 0 && !parseMessage(args[0])) { return false; } if (args.length() > 1) { if (args[1].isObject()) { RootedObject obj(cx, &args[1].toObject()); return parseOptionsObject(obj); } if (!parseResult(args[1])) { return false; } } if (args.length() > 2) { if (!parseStack(args[2])) { return false; } } if (args.length() > 3) { if (!parseData(args[3])) { return false; } } return true; } protected: /* * Parsing helpers. */ bool parseMessage(HandleValue v) { JSString* str = ToString(cx, v); if (!str) { return false; } messageBytes = JS_EncodeStringToLatin1(cx, str); eMsg = messageBytes.get(); return !!eMsg; } bool parseResult(HandleValue v) { return JS::ToUint32(cx, v, (uint32_t*)&eResult); } bool parseStack(HandleValue v) { if (!v.isObject()) { // eStack has already been initialized to null, which is what we want // for any non-object values (including null). return true; } RootedObject stackObj(cx, &v.toObject()); return NS_SUCCEEDED(xpc->WrapJS(cx, stackObj, NS_GET_IID(nsIStackFrame), getter_AddRefs(eStack))); } bool parseData(HandleValue v) { if (!v.isObject()) { // eData has already been initialized to null, which is what we want // for any non-object values (including null). return true; } RootedObject obj(cx, &v.toObject()); return NS_SUCCEEDED( xpc->WrapJS(cx, obj, NS_GET_IID(nsISupports), getter_AddRefs(eData))); } bool parseOptionsObject(HandleObject obj) { RootedValue v(cx); if (!getOption(obj, "result", &v) || (!v.isUndefined() && !parseResult(v))) return false; if (!getOption(obj, "stack", &v) || (!v.isUndefined() && !parseStack(v))) return false; if (!getOption(obj, "data", &v) || (!v.isUndefined() && !parseData(v))) return false; return true; } bool getOption(HandleObject obj, const char* name, MutableHandleValue rv) { // Look for the property. bool found; if (!JS_HasProperty(cx, obj, name, &found)) { return false; } // If it wasn't found, indicate with undefined. if (!found) { rv.setUndefined(); return true; } // Get the property. return JS_GetProperty(cx, obj, name, rv); } /* * Internal data members. */ // If there's a non-default exception string, hold onto the allocated bytes. JS::UniqueChars messageBytes; // Various bits and pieces that are helpful to have around. JSContext* cx; nsIXPConnect* xpc; }; // static nsresult nsXPCComponents_Exception::CallOrConstruct( nsIXPConnectWrappedNative* wrapper, JSContext* cx, HandleObject obj, const CallArgs& args, bool* _retval) { nsIXPConnect* xpc = nsIXPConnect::XPConnect(); MOZ_DIAGNOSTIC_ASSERT(nsContentUtils::IsCallerChrome()); // Parse the arguments to the Exception constructor. ExceptionArgParser parser(cx, xpc); if (!parser.parse(args)) { return ThrowAndFail(NS_ERROR_XPC_BAD_CONVERT_JS, cx, _retval); } RefPtr e = new Exception(nsCString(parser.eMsg), parser.eResult, ""_ns, parser.eStack, parser.eData); RootedObject newObj(cx); if (NS_FAILED(xpc->WrapNative(cx, obj, e, NS_GET_IID(nsIException), newObj.address())) || !newObj) { return ThrowAndFail(NS_ERROR_XPC_CANT_CREATE_WN, cx, _retval); } args.rval().setObject(*newObj); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Exception::HasInstance(nsIXPConnectWrappedNative* wrapper, JSContext* cx, JSObject* obj, HandleValue val, bool* bp, bool* _retval) { using namespace mozilla::dom; if (bp) { *bp = (val.isObject() && IS_INSTANCE_OF(Exception, &val.toObject())) || JSValIsInterfaceOfType(cx, val, NS_GET_IID(nsIException)); } return NS_OK; } /*******************************************************/ // JavaScript Constructor for nsIXPCConstructor objects (Components.Constructor) class nsXPCComponents_Constructor final : public nsIXPCComponents_Constructor, public nsIXPCScriptable, public nsIClassInfo { public: // all the interface method declarations... NS_DECL_ISUPPORTS NS_DECL_NSIXPCCOMPONENTS_CONSTRUCTOR NS_DECL_NSIXPCSCRIPTABLE NS_DECL_NSICLASSINFO public: nsXPCComponents_Constructor(); private: virtual ~nsXPCComponents_Constructor(); static bool InnerConstructor(JSContext* cx, unsigned argc, JS::Value* vp); static nsresult CallOrConstruct(nsIXPConnectWrappedNative* wrapper, JSContext* cx, HandleObject obj, const CallArgs& args, bool* _retval); }; /***************************************************************************/ NS_IMETHODIMP nsXPCComponents_Constructor::GetInterfaces(nsTArray& aArray) { aArray = nsTArray{NS_GET_IID(nsIXPCComponents_Constructor), NS_GET_IID(nsIXPCScriptable)}; return NS_OK; } NS_IMETHODIMP nsXPCComponents_Constructor::GetScriptableHelper(nsIXPCScriptable** retval) { *retval = nullptr; return NS_OK; } NS_IMETHODIMP nsXPCComponents_Constructor::GetContractID(nsACString& aContractID) { aContractID.SetIsVoid(true); return NS_ERROR_NOT_AVAILABLE; } NS_IMETHODIMP nsXPCComponents_Constructor::GetClassDescription( nsACString& aClassDescription) { aClassDescription.AssignLiteral("XPCComponents_Constructor"); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Constructor::GetClassID(nsCID** aClassID) { *aClassID = nullptr; return NS_OK; } NS_IMETHODIMP nsXPCComponents_Constructor::GetFlags(uint32_t* aFlags) { *aFlags = 0; return NS_OK; } NS_IMETHODIMP nsXPCComponents_Constructor::GetClassIDNoAlloc(nsCID* aClassIDNoAlloc) { return NS_ERROR_NOT_AVAILABLE; } nsXPCComponents_Constructor::nsXPCComponents_Constructor() = default; nsXPCComponents_Constructor::~nsXPCComponents_Constructor() { // empty } NS_IMPL_ISUPPORTS(nsXPCComponents_Constructor, nsIXPCComponents_Constructor, nsIXPCScriptable, nsIClassInfo) // The nsIXPCScriptable map declaration that will generate stubs for us... #define XPC_MAP_CLASSNAME nsXPCComponents_Constructor #define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_Constructor" #define XPC_MAP_FLAGS \ (XPC_SCRIPTABLE_WANT_CALL | XPC_SCRIPTABLE_WANT_CONSTRUCT | \ XPC_SCRIPTABLE_WANT_HASINSTANCE | \ XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE) #include "xpc_map_end.h" /* This will #undef the above */ // static bool nsXPCComponents_Constructor::InnerConstructor(JSContext* cx, unsigned argc, JS::Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); RootedObject callee(cx, &args.callee()); // Fetch the property name ids, so we can look them up. XPCJSRuntime* runtime = XPCJSRuntime::Get(); HandleId classIDProp = runtime->GetStringID(XPCJSContext::IDX_CLASS_ID); HandleId interfaceIDProp = runtime->GetStringID(XPCJSContext::IDX_INTERFACE_ID); HandleId initializerProp = runtime->GetStringID(XPCJSContext::IDX_INITIALIZER); // Get properties ('classID', 'interfaceID', and 'initializer') off the // constructor object. RootedValue classIDv(cx); RootedValue interfaceID(cx); RootedValue initializer(cx); if (!JS_GetPropertyById(cx, callee, classIDProp, &classIDv) || !JS_GetPropertyById(cx, callee, interfaceIDProp, &interfaceID) || !JS_GetPropertyById(cx, callee, initializerProp, &initializer)) { return false; } if (!classIDv.isObject() || !interfaceID.isObject()) { XPCThrower::Throw(NS_ERROR_UNEXPECTED, cx); return false; } // Call 'createInstance' on the 'classID' object to create the object. RootedValue instancev(cx); RootedObject classID(cx, &classIDv.toObject()); if (!JS_CallFunctionName(cx, classID, "createInstance", HandleValueArray(interfaceID), &instancev)) { return false; } if (!instancev.isObject()) { XPCThrower::Throw(NS_ERROR_FAILURE, cx); return false; } // Call the method 'initializer' on the instance, passing in our parameters. if (!initializer.isUndefined()) { RootedValue dummy(cx); RootedValue initfunc(cx); RootedId initid(cx); RootedObject instance(cx, &instancev.toObject()); if (!JS_ValueToId(cx, initializer, &initid) || !JS_GetPropertyById(cx, instance, initid, &initfunc) || !JS_CallFunctionValue(cx, instance, initfunc, args, &dummy)) { return false; } } args.rval().set(instancev); return true; } NS_IMETHODIMP nsXPCComponents_Constructor::Call(nsIXPConnectWrappedNative* wrapper, JSContext* cx, JSObject* objArg, const CallArgs& args, bool* _retval) { RootedObject obj(cx, objArg); return CallOrConstruct(wrapper, cx, obj, args, _retval); } NS_IMETHODIMP nsXPCComponents_Constructor::Construct(nsIXPConnectWrappedNative* wrapper, JSContext* cx, JSObject* objArg, const CallArgs& args, bool* _retval) { RootedObject obj(cx, objArg); return CallOrConstruct(wrapper, cx, obj, args, _retval); } // static nsresult nsXPCComponents_Constructor::CallOrConstruct( nsIXPConnectWrappedNative* wrapper, JSContext* cx, HandleObject obj, const CallArgs& args, bool* _retval) { // make sure we have at least one arg if (args.length() < 1) { return ThrowAndFail(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx, _retval); } // Fetch the property name ids, so we can look them up. XPCJSRuntime* runtime = XPCJSRuntime::Get(); HandleId classIDProp = runtime->GetStringID(XPCJSContext::IDX_CLASS_ID); HandleId interfaceIDProp = runtime->GetStringID(XPCJSContext::IDX_INTERFACE_ID); HandleId initializerProp = runtime->GetStringID(XPCJSContext::IDX_INITIALIZER); // get the various other object pointers we need nsIXPConnect* xpc = nsIXPConnect::XPConnect(); XPCWrappedNativeScope* scope = ObjectScope(obj); nsCOMPtr comp; if (!xpc || !scope || !(comp = scope->GetComponents())) { return ThrowAndFail(NS_ERROR_XPC_UNEXPECTED, cx, _retval); } // Prevent non-chrome code from creating constructor objects. if (!nsContentUtils::IsCallerChrome()) { return ThrowAndFail(NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED, cx, _retval); } JSFunction* ctorfn = JS_NewFunction(cx, InnerConstructor, 0, JSFUN_CONSTRUCTOR, "XPCOM_Constructor"); if (!ctorfn) { return ThrowAndFail(NS_ERROR_OUT_OF_MEMORY, cx, _retval); } JS::RootedObject ctor(cx, JS_GetFunctionObject(ctorfn)); if (args.length() >= 3) { // args[2] is an initializer function or property name RootedString str(cx, ToString(cx, args[2])); if (!JS_DefinePropertyById( cx, ctor, initializerProp, str, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) { return ThrowAndFail(NS_ERROR_FAILURE, cx, _retval); } } RootedString ifaceName(cx); if (args.length() >= 2) { ifaceName = ToString(cx, args[1]); } else { ifaceName = JS_NewStringCopyZ(cx, "nsISupports"); } if (!ifaceName) { return ThrowAndFail(NS_ERROR_XPC_BAD_CONVERT_JS, cx, _retval); } // a new scope to avoid warnings about shadowed names { nsCOMPtr ifaces; RootedObject ifacesObj(cx); // we do the lookup by asking the Components.interfaces object // for the property with this name - i.e. we let its caching of these // nsIJSIID objects work for us. if (NS_FAILED(comp->GetInterfaces(getter_AddRefs(ifaces))) || NS_FAILED(xpc->WrapNative(cx, obj, ifaces, NS_GET_IID(nsIXPCComponents_Interfaces), ifacesObj.address())) || !ifacesObj) { return ThrowAndFail(NS_ERROR_XPC_UNEXPECTED, cx, _retval); } RootedId id(cx); if (!JS_StringToId(cx, ifaceName, &id)) { return ThrowAndFail(NS_ERROR_XPC_BAD_CONVERT_JS, cx, _retval); } RootedValue val(cx); if (!JS_GetPropertyById(cx, ifacesObj, id, &val) || val.isPrimitive()) { return ThrowAndFail(NS_ERROR_XPC_BAD_IID, cx, _retval); } if (!JS_DefinePropertyById( cx, ctor, interfaceIDProp, val, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) { return ThrowAndFail(NS_ERROR_FAILURE, cx, _retval); } } // a new scope to avoid warnings about shadowed names { // argv[0] is a contractid name string // we do the lookup by asking the Components.classes object // for the property with this name - i.e. we let its caching of these // nsIJSCID objects work for us. nsCOMPtr classes; RootedObject classesObj(cx); if (NS_FAILED(comp->GetClasses(getter_AddRefs(classes))) || NS_FAILED(xpc->WrapNative(cx, obj, classes, NS_GET_IID(nsIXPCComponents_Classes), classesObj.address())) || !classesObj) { return ThrowAndFail(NS_ERROR_XPC_UNEXPECTED, cx, _retval); } RootedString str(cx, ToString(cx, args[0])); RootedId id(cx); if (!str || !JS_StringToId(cx, str, &id)) { return ThrowAndFail(NS_ERROR_XPC_BAD_CONVERT_JS, cx, _retval); } RootedValue val(cx); if (!JS_GetPropertyById(cx, classesObj, id, &val) || val.isPrimitive()) { return ThrowAndFail(NS_ERROR_XPC_BAD_CID, cx, _retval); } if (!JS_DefinePropertyById( cx, ctor, classIDProp, val, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) { return ThrowAndFail(NS_ERROR_FAILURE, cx, _retval); } } args.rval().setObject(*ctor); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Constructor::HasInstance(nsIXPConnectWrappedNative* wrapper, JSContext* cx, JSObject* obj, HandleValue val, bool* isa, bool* _retval) { *isa = val.isObject() && JS_IsNativeFunction(&val.toObject(), InnerConstructor); return NS_OK; } class nsXPCComponents_Utils final : public nsIXPCComponents_Utils, public nsIXPCScriptable { public: // all the interface method declarations... NS_DECL_ISUPPORTS NS_DECL_NSIXPCSCRIPTABLE NS_DECL_NSIXPCCOMPONENTS_UTILS public: nsXPCComponents_Utils() = default; private: virtual ~nsXPCComponents_Utils() = default; nsCOMPtr mSandbox; }; NS_IMPL_ISUPPORTS(nsXPCComponents_Utils, nsIXPCComponents_Utils, nsIXPCScriptable) // The nsIXPCScriptable map declaration that will generate stubs for us... #define XPC_MAP_CLASSNAME nsXPCComponents_Utils #define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_Utils" #define XPC_MAP_FLAGS XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE #include "xpc_map_end.h" /* This will #undef the above */ NS_IMETHODIMP nsXPCComponents_Utils::GetSandbox(nsIXPCComponents_utils_Sandbox** aSandbox) { NS_ENSURE_ARG_POINTER(aSandbox); if (!mSandbox) { mSandbox = NewSandboxConstructor(); } nsCOMPtr rval = mSandbox; rval.forget(aSandbox); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::PrintStderr(const nsACString& message) { printf_stderr("%s", PromiseFlatUTF8String(message).get()); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::ReportError(HandleValue error, HandleValue stack, JSContext* cx) { // This function shall never fail! Silently eat any failure conditions. nsCOMPtr console( do_GetService(NS_CONSOLESERVICE_CONTRACTID)); if (!console) { return NS_OK; } nsGlobalWindowInner* win = CurrentWindowOrNull(cx); const uint64_t innerWindowID = win ? win->WindowID() : 0; Rooted> exception(cx, Some(error)); if (!innerWindowID) { // Leak mitigation: nsConsoleService::ClearMessagesForWindowID needs // a WindowID for cleanup and exception values could hold arbitrary // objects alive. exception = Nothing(); } nsCOMPtr scripterr; RootedObject errorObj(cx, error.isObject() ? &error.toObject() : nullptr); if (errorObj) { JS::RootedObject stackVal(cx); JS::RootedObject stackGlobal(cx); FindExceptionStackForConsoleReport(win, error, nullptr, &stackVal, &stackGlobal); if (stackVal) { scripterr = CreateScriptError(win, exception, stackVal, stackGlobal); } } nsString fileName; uint32_t lineNo = 0; if (!scripterr) { RootedObject stackObj(cx); RootedObject stackGlobal(cx); if (stack.isObject()) { if (!JS::IsMaybeWrappedSavedFrame(&stack.toObject())) { return NS_ERROR_INVALID_ARG; } // |stack| might be a wrapper, but it must be same-compartment with // the current global. stackObj = &stack.toObject(); stackGlobal = JS::CurrentGlobalOrNull(cx); js::AssertSameCompartment(stackObj, stackGlobal); JSPrincipals* principals = JS::GetRealmPrincipals(js::GetContextRealm(cx)); if (GetSavedFrameLine(cx, principals, stackObj, &lineNo) != SavedFrameResult::Ok) { JS_ClearPendingException(cx); } RootedString source(cx); nsAutoJSString str; if (GetSavedFrameSource(cx, principals, stackObj, &source) == SavedFrameResult::Ok && str.init(cx, source)) { fileName = str; } else { JS_ClearPendingException(cx); } } else { nsCOMPtr frame = dom::GetCurrentJSStack(); if (frame) { frame->GetFilename(cx, fileName); lineNo = frame->GetLineNumber(cx); JS::Rooted stack(cx); nsresult rv = frame->GetNativeSavedFrame(&stack); if (NS_SUCCEEDED(rv) && stack.isObject()) { stackObj = &stack.toObject(); MOZ_ASSERT(JS::IsUnwrappedSavedFrame(stackObj)); stackGlobal = JS::GetNonCCWObjectGlobal(stackObj); } } } if (stackObj) { scripterr = CreateScriptError(win, exception, stackObj, stackGlobal); } } if (!scripterr) { scripterr = CreateScriptError(win, exception, nullptr, nullptr); } JSErrorReport* err = errorObj ? JS_ErrorFromException(cx, errorObj) : nullptr; if (err) { // It's a proper JS Error nsAutoString fileUni; CopyUTF8toUTF16(mozilla::MakeStringSpan(err->filename.c_str()), fileUni); const char16_t* linebuf = err->linebuf(); uint32_t flags = err->isWarning() ? nsIScriptError::warningFlag : nsIScriptError::errorFlag; nsresult rv = scripterr->InitWithWindowID( err->message() ? NS_ConvertUTF8toUTF16(err->message().c_str()) : EmptyString(), fileUni, linebuf ? nsDependentString(linebuf, err->linebufLength()) : EmptyString(), err->lineno, err->column.oneOriginValue(), flags, "XPConnect JavaScript", innerWindowID, innerWindowID == 0 ? true : false); NS_ENSURE_SUCCESS(rv, NS_OK); console->LogMessage(scripterr); return NS_OK; } // It's not a JS Error object, so we synthesize as best we're able. RootedString msgstr(cx, ToString(cx, error)); if (!msgstr) { return NS_OK; } nsAutoJSString msg; if (!msg.init(cx, msgstr)) { return NS_OK; } nsresult rv = scripterr->InitWithWindowID( msg, fileName, u""_ns, lineNo, 0, 0, "XPConnect JavaScript", innerWindowID, innerWindowID == 0 ? true : false); NS_ENSURE_SUCCESS(rv, NS_OK); console->LogMessage(scripterr); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::EvalInSandbox( const nsAString& source, HandleValue sandboxVal, HandleValue version, const nsACString& filenameArg, int32_t lineNumber, bool enforceFilenameRestrictions, JSContext* cx, uint8_t optionalArgc, MutableHandleValue retval) { RootedObject sandbox(cx); if (!JS_ValueToObject(cx, sandboxVal, &sandbox) || !sandbox) { return NS_ERROR_INVALID_ARG; } // Optional third argument: JS version, as a string, is unused. // Optional fourth and fifth arguments: filename and line number. int32_t lineNo = (optionalArgc >= 3) ? lineNumber : 1; nsCString filename; if (!filenameArg.IsVoid()) { filename.Assign(filenameArg); } else { // Get the current source info. nsCOMPtr frame = dom::GetCurrentJSStack(); if (frame) { nsString frameFile; frame->GetFilename(cx, frameFile); CopyUTF16toUTF8(frameFile, filename); lineNo = frame->GetLineNumber(cx); } } enforceFilenameRestrictions = (optionalArgc >= 4) ? enforceFilenameRestrictions : true; return xpc::EvalInSandbox(cx, sandbox, source, filename, lineNo, enforceFilenameRestrictions, retval); } NS_IMETHODIMP nsXPCComponents_Utils::GetUAWidgetScope(nsIPrincipal* principal, JSContext* cx, MutableHandleValue rval) { rval.set(UndefinedValue()); JSObject* scope = xpc::GetUAWidgetScope(cx, principal); rval.set(JS::ObjectValue(*scope)); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::GetSandboxMetadata(HandleValue sandboxVal, JSContext* cx, MutableHandleValue rval) { if (!sandboxVal.isObject()) { return NS_ERROR_INVALID_ARG; } RootedObject sandbox(cx, &sandboxVal.toObject()); // We only care about sandboxes here, so CheckedUnwrapStatic is fine. sandbox = js::CheckedUnwrapStatic(sandbox); if (!sandbox || !xpc::IsSandbox(sandbox)) { return NS_ERROR_INVALID_ARG; } return xpc::GetSandboxMetadata(cx, sandbox, rval); } NS_IMETHODIMP nsXPCComponents_Utils::SetSandboxMetadata(HandleValue sandboxVal, HandleValue metadataVal, JSContext* cx) { if (!sandboxVal.isObject()) { return NS_ERROR_INVALID_ARG; } RootedObject sandbox(cx, &sandboxVal.toObject()); // We only care about sandboxes here, so CheckedUnwrapStatic is fine. sandbox = js::CheckedUnwrapStatic(sandbox); if (!sandbox || !xpc::IsSandbox(sandbox)) { return NS_ERROR_INVALID_ARG; } nsresult rv = xpc::SetSandboxMetadata(cx, sandbox, metadataVal); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::Import(const nsACString& registryLocation, HandleValue targetObj, JSContext* cx, uint8_t optionalArgc, MutableHandleValue retval) { RefPtr moduleloader = mozJSModuleLoader::Get(); MOZ_ASSERT(moduleloader); AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("nsXPCComponents_Utils::Import", OTHER, registryLocation); return moduleloader->ImportInto(registryLocation, targetObj, cx, optionalArgc, retval); } NS_IMETHODIMP nsXPCComponents_Utils::IsModuleLoaded(const nsACString& aResourceURI, bool* retval) { RefPtr moduleloader = mozJSModuleLoader::Get(); MOZ_ASSERT(moduleloader); return moduleloader->IsModuleLoaded(aResourceURI, retval); } NS_IMETHODIMP nsXPCComponents_Utils::IsJSModuleLoaded(const nsACString& aResourceURI, bool* retval) { RefPtr moduleloader = mozJSModuleLoader::Get(); MOZ_ASSERT(moduleloader); return moduleloader->IsJSModuleLoaded(aResourceURI, retval); } NS_IMETHODIMP nsXPCComponents_Utils::IsESModuleLoaded(const nsACString& aResourceURI, bool* retval) { RefPtr moduleloader = mozJSModuleLoader::Get(); MOZ_ASSERT(moduleloader); return moduleloader->IsESModuleLoaded(aResourceURI, retval); } NS_IMETHODIMP nsXPCComponents_Utils::Unload(const nsACString& registryLocation) { RefPtr moduleloader = mozJSModuleLoader::Get(); MOZ_ASSERT(moduleloader); return moduleloader->Unload(registryLocation); } NS_IMETHODIMP nsXPCComponents_Utils::ImportGlobalProperties(HandleValue aPropertyList, JSContext* cx) { // Ensure we're working in the scripted caller's realm. This is not guaranteed // to be the current realm because we switch realms when calling cross-realm // functions. RootedObject global(cx, JS::GetScriptedCallerGlobal(cx)); MOZ_ASSERT(global); js::AssertSameCompartment(cx, global); JSAutoRealm ar(cx, global); // Don't allow doing this if the global is a Window. nsGlobalWindowInner* win; if (NS_SUCCEEDED(UNWRAP_NON_WRAPPER_OBJECT(Window, global, win))) { return NS_ERROR_NOT_AVAILABLE; } GlobalProperties options; NS_ENSURE_TRUE(aPropertyList.isObject(), NS_ERROR_INVALID_ARG); RootedObject propertyList(cx, &aPropertyList.toObject()); bool isArray; if (NS_WARN_IF(!JS::IsArrayObject(cx, propertyList, &isArray))) { return NS_ERROR_FAILURE; } if (NS_WARN_IF(!isArray)) { return NS_ERROR_INVALID_ARG; } if (!options.Parse(cx, propertyList) || !options.DefineInXPCComponents(cx, global)) { return NS_ERROR_FAILURE; } return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::GetWeakReference(HandleValue object, JSContext* cx, xpcIJSWeakReference** _retval) { RefPtr ref = new xpcJSWeakReference(); nsresult rv = ref->Init(cx, object); NS_ENSURE_SUCCESS(rv, rv); ref.forget(_retval); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::ForceGC(JSContext* aCx) { PrepareForFullGC(aCx); NonIncrementalGC(aCx, GCOptions::Normal, GCReason::COMPONENT_UTILS); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::ForceCC(nsICycleCollectorListener* listener) { nsJSContext::CycleCollectNow(CCReason::API, listener); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::CreateCCLogger(nsICycleCollectorListener** aListener) { NS_ENSURE_ARG_POINTER(aListener); nsCOMPtr logger = nsCycleCollector_createLogger(); logger.forget(aListener); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::FinishCC() { nsCycleCollector_finishAnyCurrentCollection(); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::CcSlice(int64_t budget) { nsJSContext::RunCycleCollectorWorkSlice(budget); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::GetMaxCCSliceTimeSinceClear(int32_t* out) { *out = nsJSContext::GetMaxCCSliceTimeSinceClear(); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::ClearMaxCCTime() { nsJSContext::ClearMaxCCSliceTime(); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::ForceShrinkingGC(JSContext* aCx) { PrepareForFullGC(aCx); NonIncrementalGC(aCx, GCOptions::Shrink, GCReason::COMPONENT_UTILS); return NS_OK; } class PreciseGCRunnable : public Runnable { public: PreciseGCRunnable(nsIScheduledGCCallback* aCallback, bool aShrinking) : mozilla::Runnable("PreciseGCRunnable"), mCallback(aCallback), mShrinking(aShrinking) {} NS_IMETHOD Run() override { nsJSContext::GarbageCollectNow( GCReason::COMPONENT_UTILS, mShrinking ? nsJSContext::ShrinkingGC : nsJSContext::NonShrinkingGC); mCallback->Callback(); return NS_OK; } private: nsCOMPtr mCallback; bool mShrinking; }; NS_IMETHODIMP nsXPCComponents_Utils::SchedulePreciseGC(nsIScheduledGCCallback* aCallback) { RefPtr event = new PreciseGCRunnable(aCallback, false); return NS_DispatchToMainThread(event); } NS_IMETHODIMP nsXPCComponents_Utils::SchedulePreciseShrinkingGC( nsIScheduledGCCallback* aCallback) { RefPtr event = new PreciseGCRunnable(aCallback, true); return NS_DispatchToMainThread(event); } NS_IMETHODIMP nsXPCComponents_Utils::UnlinkGhostWindows() { #ifdef DEBUG nsWindowMemoryReporter::UnlinkGhostWindows(); if (XRE_IsParentProcess()) { nsCOMPtr obsvc = services::GetObserverService(); if (obsvc) { obsvc->NotifyObservers(nullptr, "child-ghost-request", nullptr); } } return NS_OK; #else return NS_ERROR_NOT_IMPLEMENTED; #endif } #ifdef NS_FREE_PERMANENT_DATA struct IntentionallyLeakedObject { MOZ_COUNTED_DEFAULT_CTOR(IntentionallyLeakedObject) MOZ_COUNTED_DTOR(IntentionallyLeakedObject) }; #endif NS_IMETHODIMP nsXPCComponents_Utils::IntentionallyLeak() { #ifdef NS_FREE_PERMANENT_DATA Unused << new IntentionallyLeakedObject(); return NS_OK; #else return NS_ERROR_NOT_IMPLEMENTED; #endif } NS_IMETHODIMP nsXPCComponents_Utils::GetJSTestingFunctions(JSContext* cx, MutableHandleValue retval) { JSObject* obj = js::GetTestingFunctions(cx); if (!obj) { return NS_ERROR_XPC_JAVASCRIPT_ERROR; } retval.setObject(*obj); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::GetFunctionSourceLocation(HandleValue funcValue, JSContext* cx, MutableHandleValue retval) { NS_ENSURE_TRUE(funcValue.isObject(), NS_ERROR_INVALID_ARG); nsAutoString filename; uint32_t lineNumber; { RootedObject funcObj(cx, UncheckedUnwrap(&funcValue.toObject())); JSAutoRealm ar(cx, funcObj); Rooted func(cx, JS_GetObjectFunction(funcObj)); NS_ENSURE_TRUE(func, NS_ERROR_INVALID_ARG); RootedScript script(cx, JS_GetFunctionScript(cx, func)); NS_ENSURE_TRUE(script, NS_ERROR_FAILURE); AppendUTF8toUTF16(nsDependentCString(JS_GetScriptFilename(script)), filename); lineNumber = JS_GetScriptBaseLineNumber(cx, script) + 1; } RootedObject res(cx, JS_NewPlainObject(cx)); NS_ENSURE_TRUE(res, NS_ERROR_OUT_OF_MEMORY); RootedValue filenameVal(cx); if (!xpc::NonVoidStringToJsval(cx, filename, &filenameVal) || !JS_DefineProperty(cx, res, "filename", filenameVal, JSPROP_ENUMERATE)) { return NS_ERROR_OUT_OF_MEMORY; } if (!JS_DefineProperty(cx, res, "lineNumber", lineNumber, JSPROP_ENUMERATE)) { return NS_ERROR_OUT_OF_MEMORY; } retval.setObject(*res); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::CallFunctionWithAsyncStack(HandleValue function, nsIStackFrame* stack, const nsAString& asyncCause, JSContext* cx, MutableHandleValue retval) { nsresult rv; if (!stack || asyncCause.IsEmpty()) { return NS_ERROR_INVALID_ARG; } JS::Rooted asyncStack(cx); rv = stack->GetNativeSavedFrame(&asyncStack); if (NS_FAILED(rv)) { return rv; } if (!asyncStack.isObject()) { JS_ReportErrorASCII(cx, "Must use a native JavaScript stack frame"); return NS_ERROR_INVALID_ARG; } JS::Rooted asyncStackObj(cx, &asyncStack.toObject()); NS_ConvertUTF16toUTF8 utf8Cause(asyncCause); JS::AutoSetAsyncStackForNewCalls sas( cx, asyncStackObj, utf8Cause.get(), JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::EXPLICIT); if (!JS_CallFunctionValue(cx, nullptr, function, JS::HandleValueArray::empty(), retval)) { return NS_ERROR_XPC_JAVASCRIPT_ERROR; } return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::GetGlobalForObject(HandleValue object, JSContext* cx, MutableHandleValue retval) { // First argument must be an object. if (object.isPrimitive()) { return NS_ERROR_XPC_BAD_CONVERT_JS; } // When getting the global for a cross-compartment wrapper, we really want // a wrapper for the foreign global. So we need to unwrap before getting the // global and then wrap the result. Rooted obj(cx, &object.toObject()); obj = JS::GetNonCCWObjectGlobal(js::UncheckedUnwrap(obj)); if (!JS_WrapObject(cx, &obj)) { return NS_ERROR_FAILURE; } // Get the WindowProxy if necessary. obj = js::ToWindowProxyIfWindow(obj); retval.setObject(*obj); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::IsProxy(HandleValue vobj, JSContext* cx, bool* rval) { if (!vobj.isObject()) { *rval = false; return NS_OK; } RootedObject obj(cx, &vobj.toObject()); // We need to do a dynamic unwrap, because we apparently want to treat // "failure to unwrap" differently from "not a proxy" (throw for the former, // return false for the latter). obj = js::CheckedUnwrapDynamic(obj, cx, /* stopAtWindowProxy = */ false); NS_ENSURE_TRUE(obj, NS_ERROR_FAILURE); *rval = js::IsScriptedProxy(obj); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::ExportFunction(HandleValue vfunction, HandleValue vscope, HandleValue voptions, JSContext* cx, MutableHandleValue rval) { if (!xpc::ExportFunction(cx, vfunction, vscope, voptions, rval)) { return NS_ERROR_FAILURE; } return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::CreateObjectIn(HandleValue vobj, HandleValue voptions, JSContext* cx, MutableHandleValue rval) { RootedObject optionsObject( cx, voptions.isObject() ? &voptions.toObject() : nullptr); CreateObjectInOptions options(cx, optionsObject); if (voptions.isObject() && !options.Parse()) { return NS_ERROR_FAILURE; } if (!xpc::CreateObjectIn(cx, vobj, options, rval)) { return NS_ERROR_FAILURE; } return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::MakeObjectPropsNormal(HandleValue vobj, JSContext* cx) { if (!cx) { return NS_ERROR_FAILURE; } // first argument must be an object if (vobj.isPrimitive()) { return NS_ERROR_XPC_BAD_CONVERT_JS; } RootedObject obj(cx, js::UncheckedUnwrap(&vobj.toObject())); JSAutoRealm ar(cx, obj); Rooted ida(cx, IdVector(cx)); if (!JS_Enumerate(cx, obj, &ida)) { return NS_ERROR_FAILURE; } RootedId id(cx); RootedValue v(cx); for (size_t i = 0; i < ida.length(); ++i) { id = ida[i]; if (!JS_GetPropertyById(cx, obj, id, &v)) { return NS_ERROR_FAILURE; } if (v.isPrimitive()) { continue; } RootedObject propobj(cx, &v.toObject()); // TODO Deal with non-functions. if (!js::IsWrapper(propobj) || !JS::IsCallable(propobj)) { continue; } FunctionForwarderOptions forwarderOptions; if (!NewFunctionForwarder(cx, id, propobj, forwarderOptions, &v) || !JS_SetPropertyById(cx, obj, id, v)) return NS_ERROR_FAILURE; } return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::IsDeadWrapper(HandleValue obj, bool* out) { *out = false; if (obj.isPrimitive()) { return NS_ERROR_INVALID_ARG; } // We should never have cross-compartment wrappers for dead wrappers. MOZ_ASSERT_IF(js::IsCrossCompartmentWrapper(&obj.toObject()), !JS_IsDeadWrapper(js::UncheckedUnwrap(&obj.toObject()))); *out = JS_IsDeadWrapper(&obj.toObject()); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::IsRemoteProxy(HandleValue val, bool* out) { if (val.isObject()) { *out = dom::IsRemoteObjectProxy(UncheckedUnwrap(&val.toObject())); ; } else { *out = false; } return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::RecomputeWrappers(HandleValue vobj, JSContext* cx) { // Determine the compartment of the given object, if any. JS::Compartment* c = vobj.isObject() ? JS::GetCompartment(js::UncheckedUnwrap(&vobj.toObject())) : nullptr; // If no compartment was given, recompute all. if (!c) { js::RecomputeWrappers(cx, js::AllCompartments(), js::AllCompartments()); // Otherwise, recompute wrappers for the given compartment. } else { js::RecomputeWrappers(cx, js::SingleCompartment(c), js::AllCompartments()) && js::RecomputeWrappers(cx, js::AllCompartments(), js::SingleCompartment(c)); } return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::SetWantXrays(HandleValue vscope, JSContext* cx) { if (!vscope.isObject()) { return NS_ERROR_INVALID_ARG; } JSObject* scopeObj = js::UncheckedUnwrap(&vscope.toObject()); MOZ_RELEASE_ASSERT(!AccessCheck::isChrome(scopeObj), "Don't call setWantXrays on system-principal scopes"); JS::Compartment* compartment = JS::GetCompartment(scopeObj); CompartmentPrivate::Get(scopeObj)->wantXrays = true; bool ok = js::RecomputeWrappers(cx, js::SingleCompartment(compartment), js::AllCompartments()); NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::Dispatch(HandleValue runnableArg, HandleValue scope, JSContext* cx) { RootedValue runnable(cx, runnableArg); // Enter the given realm, if any, and rewrap runnable. Maybe ar; if (scope.isObject()) { JSObject* scopeObj = js::UncheckedUnwrap(&scope.toObject()); if (!scopeObj) { return NS_ERROR_FAILURE; } ar.emplace(cx, scopeObj); if (!JS_WrapValue(cx, &runnable)) { return NS_ERROR_FAILURE; } } // Get an XPCWrappedJS for |runnable|. if (!runnable.isObject()) { return NS_ERROR_INVALID_ARG; } RootedObject runnableObj(cx, &runnable.toObject()); nsCOMPtr run; nsresult rv = nsXPConnect::XPConnect()->WrapJS( cx, runnableObj, NS_GET_IID(nsIRunnable), getter_AddRefs(run)); NS_ENSURE_SUCCESS(rv, rv); MOZ_ASSERT(run); // Dispatch. return NS_DispatchToMainThread(run); } NS_IMETHODIMP nsXPCComponents_Utils::SetGCZeal(int32_t aValue, JSContext* cx) { #ifdef JS_GC_ZEAL JS_SetGCZeal(cx, uint8_t(aValue), JS_DEFAULT_ZEAL_FREQ); #endif return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::GetIsInAutomation(bool* aResult) { NS_ENSURE_ARG_POINTER(aResult); *aResult = xpc::IsInAutomation(); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::ExitIfInAutomation() { NS_ENSURE_TRUE(xpc::IsInAutomation(), NS_ERROR_FAILURE); profiler_shutdown(IsFastShutdown::Yes); mozilla::AppShutdown::DoImmediateExit(); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::CrashIfNotInAutomation() { xpc::CrashIfNotInAutomation(); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::NukeSandbox(HandleValue obj, JSContext* cx) { AUTO_PROFILER_LABEL("nsXPCComponents_Utils::NukeSandbox", OTHER); NS_ENSURE_TRUE(obj.isObject(), NS_ERROR_INVALID_ARG); JSObject* wrapper = &obj.toObject(); NS_ENSURE_TRUE(IsWrapper(wrapper), NS_ERROR_INVALID_ARG); RootedObject sb(cx, UncheckedUnwrap(wrapper)); NS_ENSURE_TRUE(IsSandbox(sb), NS_ERROR_INVALID_ARG); xpc::NukeAllWrappersForRealm(cx, GetNonCCWObjectRealm(sb)); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::BlockScriptForGlobal(HandleValue globalArg, JSContext* cx) { NS_ENSURE_TRUE(globalArg.isObject(), NS_ERROR_INVALID_ARG); RootedObject global(cx, UncheckedUnwrap(&globalArg.toObject(), /* stopAtWindowProxy = */ false)); NS_ENSURE_TRUE(JS_IsGlobalObject(global), NS_ERROR_INVALID_ARG); if (xpc::GetObjectPrincipal(global)->IsSystemPrincipal()) { JS_ReportErrorASCII(cx, "Script may not be disabled for system globals"); return NS_ERROR_FAILURE; } Scriptability::Get(global).Block(); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::UnblockScriptForGlobal(HandleValue globalArg, JSContext* cx) { NS_ENSURE_TRUE(globalArg.isObject(), NS_ERROR_INVALID_ARG); RootedObject global(cx, UncheckedUnwrap(&globalArg.toObject(), /* stopAtWindowProxy = */ false)); NS_ENSURE_TRUE(JS_IsGlobalObject(global), NS_ERROR_INVALID_ARG); if (xpc::GetObjectPrincipal(global)->IsSystemPrincipal()) { JS_ReportErrorASCII(cx, "Script may not be disabled for system globals"); return NS_ERROR_FAILURE; } Scriptability::Get(global).Unblock(); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::IsOpaqueWrapper(HandleValue obj, bool* aRetval) { *aRetval = obj.isObject() && xpc::WrapperFactory::IsOpaqueWrapper(&obj.toObject()); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::IsXrayWrapper(HandleValue obj, bool* aRetval) { *aRetval = obj.isObject() && xpc::WrapperFactory::IsXrayWrapper(&obj.toObject()); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::WaiveXrays(HandleValue aVal, JSContext* aCx, MutableHandleValue aRetval) { RootedValue value(aCx, aVal); if (!xpc::WrapperFactory::WaiveXrayAndWrap(aCx, &value)) { return NS_ERROR_FAILURE; } aRetval.set(value); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::UnwaiveXrays(HandleValue aVal, JSContext* aCx, MutableHandleValue aRetval) { if (!aVal.isObject()) { aRetval.set(aVal); return NS_OK; } RootedObject obj(aCx, js::UncheckedUnwrap(&aVal.toObject())); if (!JS_WrapObject(aCx, &obj)) { return NS_ERROR_FAILURE; } aRetval.setObject(*obj); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::GetClassName(HandleValue aObj, bool aUnwrap, JSContext* aCx, char** aRv) { if (!aObj.isObject()) { return NS_ERROR_INVALID_ARG; } RootedObject obj(aCx, &aObj.toObject()); if (aUnwrap) { obj = js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false); } *aRv = NS_xstrdup(JS::GetClass(obj)->name); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::GetIncumbentGlobal(HandleValue aCallback, JSContext* aCx, MutableHandleValue aOut) { nsCOMPtr global = mozilla::dom::GetIncumbentGlobal(); RootedValue globalVal(aCx); if (!global) { globalVal = NullValue(); } else { // Note: We rely on the wrap call for outerization. globalVal = ObjectValue(*global->GetGlobalJSObject()); if (!JS_WrapValue(aCx, &globalVal)) { return NS_ERROR_FAILURE; } } // Invoke the callback, if passed. if (aCallback.isObject()) { RootedValue ignored(aCx); if (!JS_CallFunctionValue(aCx, nullptr, aCallback, JS::HandleValueArray(globalVal), &ignored)) { return NS_ERROR_FAILURE; } } aOut.set(globalVal); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::GetDebugName(HandleValue aObj, JSContext* aCx, nsACString& aOut) { if (!aObj.isObject()) { return NS_ERROR_INVALID_ARG; } RootedObject obj(aCx, &aObj.toObject()); aOut = xpc::GetFunctionName(aCx, obj); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::GetWatchdogTimestamp(const nsAString& aCategory, PRTime* aOut) { WatchdogTimestampCategory category; if (aCategory.EqualsLiteral("ContextStateChange")) { category = TimestampContextStateChange; } else if (aCategory.EqualsLiteral("WatchdogWakeup")) { category = TimestampWatchdogWakeup; } else if (aCategory.EqualsLiteral("WatchdogHibernateStart")) { category = TimestampWatchdogHibernateStart; } else if (aCategory.EqualsLiteral("WatchdogHibernateStop")) { category = TimestampWatchdogHibernateStop; } else { return NS_ERROR_INVALID_ARG; } *aOut = XPCJSContext::Get()->GetWatchdogTimestamp(category); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::GetJSEngineTelemetryValue(JSContext* cx, MutableHandleValue rval) { RootedObject obj(cx, JS_NewPlainObject(cx)); if (!obj) { return NS_ERROR_OUT_OF_MEMORY; } // No JS engine telemetry in use at the moment. rval.setObject(*obj); return NS_OK; } bool xpc::CloneInto(JSContext* aCx, HandleValue aValue, HandleValue aScope, HandleValue aOptions, MutableHandleValue aCloned) { if (!aScope.isObject()) { return false; } RootedObject scope(aCx, &aScope.toObject()); // The scope could be a Window, so we need to CheckedUnwrapDynamic. scope = js::CheckedUnwrapDynamic(scope, aCx); if (!scope) { JS_ReportErrorASCII(aCx, "Permission denied to clone object into scope"); return false; } if (!aOptions.isUndefined() && !aOptions.isObject()) { JS_ReportErrorASCII(aCx, "Invalid argument"); return false; } RootedObject optionsObject( aCx, aOptions.isObject() ? &aOptions.toObject() : nullptr); StackScopedCloneOptions options(aCx, optionsObject); if (aOptions.isObject() && !options.Parse()) { return false; } js::AssertSameCompartment(aCx, aValue); RootedObject sourceScope(aCx, JS::CurrentGlobalOrNull(aCx)); { JSAutoRealm ar(aCx, scope); aCloned.set(aValue); if (!StackScopedClone(aCx, options, sourceScope, aCloned)) { return false; } } return JS_WrapValue(aCx, aCloned); } NS_IMETHODIMP nsXPCComponents_Utils::CloneInto(HandleValue aValue, HandleValue aScope, HandleValue aOptions, JSContext* aCx, MutableHandleValue aCloned) { return xpc::CloneInto(aCx, aValue, aScope, aOptions, aCloned) ? NS_OK : NS_ERROR_FAILURE; } NS_IMETHODIMP nsXPCComponents_Utils::GetWebIDLCallerPrincipal(nsIPrincipal** aResult) { // This API may only be when the Entry Settings Object corresponds to a // JS-implemented WebIDL call. In all other cases, the value will be null, // and we throw. nsCOMPtr callerPrin = mozilla::dom::GetWebIDLCallerPrincipal(); if (!callerPrin) { return NS_ERROR_NOT_AVAILABLE; } callerPrin.forget(aResult); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::GetObjectPrincipal(HandleValue val, JSContext* cx, nsIPrincipal** result) { if (!val.isObject()) { return NS_ERROR_INVALID_ARG; } RootedObject obj(cx, &val.toObject()); // We need to be able to unwrap to WindowProxy or Location here, so // use CheckedUnwrapDynamic. obj = js::CheckedUnwrapDynamic(obj, cx); MOZ_ASSERT(obj); nsCOMPtr prin = nsContentUtils::ObjectPrincipal(obj); prin.forget(result); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::GetRealmLocation(HandleValue val, JSContext* cx, nsACString& result) { if (!val.isObject()) { return NS_ERROR_INVALID_ARG; } RootedObject obj(cx, &val.toObject()); // We need to be able to unwrap to WindowProxy or Location here, so // use CheckedUnwrapDynamic. obj = js::CheckedUnwrapDynamic(obj, cx); MOZ_ASSERT(obj); result = xpc::RealmPrivate::Get(obj)->GetLocation(); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::ReadUTF8File(nsIFile* aFile, nsACString& aResult) { NS_ENSURE_TRUE(aFile, NS_ERROR_INVALID_ARG); MOZ_TRY_VAR(aResult, URLPreloader::ReadFile(aFile)); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::ReadUTF8URI(nsIURI* aURI, nsACString& aResult) { NS_ENSURE_TRUE(aURI, NS_ERROR_INVALID_ARG); MOZ_TRY_VAR(aResult, URLPreloader::ReadURI(aURI)); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::Now(double* aRetval) { TimeStamp start = TimeStamp::ProcessCreation(); *aRetval = (TimeStamp::Now() - start).ToMilliseconds(); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::CreateSpellChecker(nsIEditorSpellCheck** aSpellChecker) { NS_ENSURE_ARG_POINTER(aSpellChecker); nsCOMPtr spellChecker = new mozilla::EditorSpellCheck(); spellChecker.forget(aSpellChecker); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::CreateCommandLine(const nsTArray& aArgs, nsIFile* aWorkingDir, uint32_t aState, nsISupports** aCommandLine) { NS_ENSURE_ARG_MAX(aState, nsICommandLine::STATE_REMOTE_EXPLICIT); NS_ENSURE_ARG_POINTER(aCommandLine); nsCOMPtr commandLine = new nsCommandLine(); nsCOMPtr runner = do_QueryInterface(commandLine); nsTArray fakeArgv(aArgs.Length() + 2); // Prepend a dummy argument for the program name, which will be ignored. fakeArgv.AppendElement(nullptr); for (const nsCString& arg : aArgs) { fakeArgv.AppendElement(arg.get()); } // Append a null terminator. fakeArgv.AppendElement(nullptr); nsresult rv = runner->Init(fakeArgv.Length() - 1, fakeArgv.Elements(), aWorkingDir, aState); NS_ENSURE_SUCCESS(rv, rv); commandLine.forget(aCommandLine); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::CreateCommandParams(nsICommandParams** aCommandParams) { NS_ENSURE_ARG_POINTER(aCommandParams); nsCOMPtr commandParams = new nsCommandParams(); commandParams.forget(aCommandParams); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::CreateLoadContext(nsILoadContext** aLoadContext) { NS_ENSURE_ARG_POINTER(aLoadContext); nsCOMPtr loadContext = ::CreateLoadContext(); loadContext.forget(aLoadContext); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::CreatePrivateLoadContext(nsILoadContext** aLoadContext) { NS_ENSURE_ARG_POINTER(aLoadContext); nsCOMPtr loadContext = ::CreatePrivateLoadContext(); loadContext.forget(aLoadContext); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::CreatePersistentProperties( nsIPersistentProperties** aPersistentProperties) { NS_ENSURE_ARG_POINTER(aPersistentProperties); nsCOMPtr props = new nsPersistentProperties(); props.forget(aPersistentProperties); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::CreateDocumentEncoder( const char* aContentType, nsIDocumentEncoder** aDocumentEncoder) { NS_ENSURE_ARG_POINTER(aDocumentEncoder); nsCOMPtr encoder = do_createDocumentEncoder(aContentType); encoder.forget(aDocumentEncoder); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::CreateHTMLCopyEncoder( nsIDocumentEncoder** aDocumentEncoder) { NS_ENSURE_ARG_POINTER(aDocumentEncoder); nsCOMPtr encoder = do_createHTMLCopyEncoder(); encoder.forget(aDocumentEncoder); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::GetLoadedModules(nsTArray& aLoadedModules) { return mozJSModuleLoader::Get()->GetLoadedJSAndESModules(aLoadedModules); } NS_IMETHODIMP nsXPCComponents_Utils::GetLoadedJSModules( nsTArray& aLoadedJSModules) { mozJSModuleLoader::Get()->GetLoadedModules(aLoadedJSModules); return NS_OK; } NS_IMETHODIMP nsXPCComponents_Utils::GetLoadedESModules( nsTArray& aLoadedESModules) { return mozJSModuleLoader::Get()->GetLoadedESModules(aLoadedESModules); } NS_IMETHODIMP nsXPCComponents_Utils::GetModuleImportStack(const nsACString& aLocation, nsACString& aRetval) { nsresult rv = mozJSModuleLoader::Get()->GetModuleImportStack(aLocation, aRetval); // Fallback the query to the DevTools loader if not found in the shared loader if (rv == NS_ERROR_FAILURE && mozJSModuleLoader::GetDevToolsLoader()) { return mozJSModuleLoader::GetDevToolsLoader()->GetModuleImportStack( aLocation, aRetval); } return rv; } /***************************************************************************/ /***************************************************************************/ /***************************************************************************/ nsXPCComponents::nsXPCComponents(XPCWrappedNativeScope* aScope) : mScope(aScope) { MOZ_ASSERT(aScope, "aScope must not be null"); } nsXPCComponents::~nsXPCComponents() = default; void nsXPCComponents::ClearMembers() { mInterfaces = nullptr; mResults = nullptr; mClasses = nullptr; mID = nullptr; mException = nullptr; mConstructor = nullptr; mUtils = nullptr; } /*******************************************/ #define XPC_IMPL_GET_OBJ_METHOD(_class, _n) \ NS_IMETHODIMP _class::Get##_n(nsIXPCComponents_##_n** a##_n) { \ NS_ENSURE_ARG_POINTER(a##_n); \ if (!m##_n) m##_n = new nsXPCComponents_##_n(); \ RefPtr ret = m##_n; \ ret.forget(a##_n); \ return NS_OK; \ } XPC_IMPL_GET_OBJ_METHOD(nsXPCComponents, Interfaces) XPC_IMPL_GET_OBJ_METHOD(nsXPCComponents, Classes) XPC_IMPL_GET_OBJ_METHOD(nsXPCComponents, Results) XPC_IMPL_GET_OBJ_METHOD(nsXPCComponents, ID) XPC_IMPL_GET_OBJ_METHOD(nsXPCComponents, Exception) XPC_IMPL_GET_OBJ_METHOD(nsXPCComponents, Constructor) XPC_IMPL_GET_OBJ_METHOD(nsXPCComponents, Utils) #undef XPC_IMPL_GET_OBJ_METHOD /*******************************************/ NS_IMETHODIMP nsXPCComponents::IsSuccessCode(nsresult result, bool* out) { *out = NS_SUCCEEDED(result); return NS_OK; } NS_IMETHODIMP nsXPCComponents::GetStack(nsIStackFrame** aStack) { nsCOMPtr frame = dom::GetCurrentJSStack(); frame.forget(aStack); return NS_OK; } NS_IMETHODIMP nsXPCComponents::GetManager(nsIComponentManager** aManager) { MOZ_ASSERT(aManager, "bad param"); return NS_GetComponentManager(aManager); } NS_IMETHODIMP nsXPCComponents::GetReturnCode(JSContext* aCx, MutableHandleValue aOut) { nsresult res = XPCJSContext::Get()->GetPendingResult(); aOut.setNumber(static_cast(res)); return NS_OK; } NS_IMETHODIMP nsXPCComponents::SetReturnCode(JSContext* aCx, HandleValue aCode) { nsresult rv; if (!ToUint32(aCx, aCode, (uint32_t*)&rv)) { return NS_ERROR_FAILURE; } XPCJSContext::Get()->SetPendingResult(rv); return NS_OK; } /**********************************************/ class ComponentsSH : public nsIXPCScriptable { public: explicit constexpr ComponentsSH(unsigned dummy) {} // We don't actually inherit any ref counting infrastructure, but we don't // need an nsAutoRefCnt member, so the _INHERITED macro is a hack to avoid // having one. NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIXPCSCRIPTABLE static nsresult Get(nsIXPCScriptable** helper) { *helper = &singleton; return NS_OK; } private: static ComponentsSH singleton; }; ComponentsSH ComponentsSH::singleton(0); // Singleton refcounting. NS_IMETHODIMP_(MozExternalRefCountType) ComponentsSH::AddRef(void) { return 1; } NS_IMETHODIMP_(MozExternalRefCountType) ComponentsSH::Release(void) { return 1; } NS_IMPL_QUERY_INTERFACE(ComponentsSH, nsIXPCScriptable) #define NSXPCCOMPONENTS_CID \ { \ 0x3649f405, 0xf0ec, 0x4c28, { \ 0xae, 0xb0, 0xaf, 0x9a, 0x51, 0xe4, 0x4c, 0x81 \ } \ } NS_IMPL_CLASSINFO(nsXPCComponents, &ComponentsSH::Get, 0, NSXPCCOMPONENTS_CID) NS_IMPL_ISUPPORTS_CI(nsXPCComponents, nsIXPCComponents) // The nsIXPCScriptable map declaration that will generate stubs for us #define XPC_MAP_CLASSNAME ComponentsSH #define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents" #define XPC_MAP_FLAGS XPC_SCRIPTABLE_WANT_PRECREATE #include "xpc_map_end.h" /* This will #undef the above */ NS_IMETHODIMP ComponentsSH::PreCreate(nsISupports* nativeObj, JSContext* cx, JSObject* globalObj, JSObject** parentObj) { nsXPCComponents* self = static_cast(nativeObj); // this should never happen if (!self->GetScope()) { NS_WARNING( "mScope must not be null when nsXPCComponents::PreCreate is called"); return NS_ERROR_FAILURE; } *parentObj = self->GetScope()->GetGlobalForWrappedNatives(); return NS_OK; }