Source code

Revision control

Other Tools

/* -*- 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 "JSServices.h"
#include "XPCJSWeakReference.h"
#include "WrapperFactory.h"
#include "nsJSUtils.h"
#include "mozJSComponentLoader.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/ContextOptions.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/URLPreloader.h"
#include "mozilla/dom/DOMException.h"
#include "mozilla/dom/DOMExceptionBinding.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 "nsGlobalWindow.h"
#include "nsScriptError.h"
#include "GeckoProfiler.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<nsIXPConnectWrappedNative> wn;
nsCOMPtr<nsISupports> 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<nsIID>& aArray) {
aArray = nsTArray<nsIID>{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 (!JSID_IS_STRING(id)) {
return NS_OK;
}
RootedString str(cx, JSID_TO_STRING(id));
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<nsIID>& aArray) {
aArray = nsTArray<nsIID>{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<nsIComponentRegistrar> compMgr;
if (NS_FAILED(NS_GetComponentRegistrar(getter_AddRefs(compMgr))) ||
!compMgr) {
return NS_ERROR_UNEXPECTED;
}
nsTArray<nsCString> 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 (JSID_IS_STRING(id) &&
xpc::ContractID2JSValue(cx, JSID_TO_STRING(id), &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<nsIID>& aArray) {
aArray = nsTArray<nsIID>{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 (!JSID_IS_STRING(id)) {
return NS_OK;
}
JS::UniqueChars name = JS_EncodeStringToLatin1(cx, JSID_TO_STRING(id));
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<nsIID>& aArray) {
aArray = nsTArray<nsIID>{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<nsIID>& aArray) {
aArray = nsTArray<nsIID>{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<nsIStackFrame> eStack;
nsCOMPtr<nsISupports> 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<Exception> 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<nsIID>& aArray) {
aArray = nsTArray<nsIID>{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<nsIXPCComponents> 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<nsIXPCComponents_Interfaces> 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<nsIXPCComponents_Classes> 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<nsIXPCComponents_utils_Sandbox> 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<nsIXPCComponents_utils_Sandbox> rval = mSandbox;
rval.forget(aSandbox);
return NS_OK;
}
NS_IMETHODIMP
nsXPCComponents_Utils::CreateServicesCache(JSContext* aCx,
MutableHandleValue aServices) {
if (JSObject* services = NewJSServices(aCx)) {
aServices.setObject(*services);
return NS_OK;
}
return NS_ERROR_FAILURE;
}
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<nsIConsoleService> console(
do_GetService(NS_CONSOLESERVICE_CONTRACTID));
if (!console) {
return NS_OK;
}
nsGlobalWindowInner* win = CurrentWindowOrNull(cx);
const uint64_t innerWindowID = win ? win->WindowID() : 0;
Rooted<Maybe<Value>> 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<nsIScriptError> 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<nsIStackFrame> frame = dom::GetCurrentJSStack();
if (frame) {
frame->GetFilename(cx, fileName);
lineNo = frame->GetLineNumber(cx);
JS::Rooted<JS::Value> stack(cx);
nsresult rv = frame->GetNativeSavedFrame(&stack);
if (NS_SUCCEEDED(rv) && stack.