зеркало из https://github.com/mozilla/gecko-dev.git
- made nsJSCID's createInstance and getService work as attributes using nsIXPCScriptable::call to support security and sevicemanager protocol.
- made xpccontext strings id scheme more generic. - fixed stupif IID hash function. - fixed nsIXPCScriptable call and construct to work with the correct function object. - added gc and dump (using xpclog) to xpcshell.
This commit is contained in:
Родитель
3638cc03ab
Коммит
a0a7da258c
|
@ -44,8 +44,10 @@ interface nsIJSIID : nsIJSID
|
||||||
[scriptable, uuid(e3a24a60-d651-11d2-9843-006008962422)]
|
[scriptable, uuid(e3a24a60-d651-11d2-9843-006008962422)]
|
||||||
interface nsIJSCID : nsIJSID
|
interface nsIJSCID : nsIJSID
|
||||||
{
|
{
|
||||||
nsISupports createInstance();
|
// these are attributes that are made to be called
|
||||||
nsISupports getService();
|
// as methods from JavaScript only.
|
||||||
|
readonly attribute nsISupports createInstance;
|
||||||
|
readonly attribute nsISupports getService;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* this goes into the C++ header verbatim. */
|
/* this goes into the C++ header verbatim. */
|
||||||
|
|
|
@ -81,11 +81,11 @@ class nsIJSCID : public nsIJSID {
|
||||||
public:
|
public:
|
||||||
NS_DEFINE_STATIC_IID_ACCESSOR(NS_IJSCID_IID)
|
NS_DEFINE_STATIC_IID_ACCESSOR(NS_IJSCID_IID)
|
||||||
|
|
||||||
/* nsISupports createInstance (); */
|
/* readonly attribute nsISupports createInstance; */
|
||||||
NS_IMETHOD createInstance(nsISupports **_retval) = 0;
|
NS_IMETHOD GetCreateInstance(nsISupports * *aCreateInstance) = 0;
|
||||||
|
|
||||||
/* nsISupports getService (); */
|
/* readonly attribute nsISupports getService; */
|
||||||
NS_IMETHOD getService(nsISupports **_retval) = 0;
|
NS_IMETHOD GetGetService(nsISupports * *aGetService) = 0;
|
||||||
|
|
||||||
#ifdef XPIDL_JS_STUBS
|
#ifdef XPIDL_JS_STUBS
|
||||||
static NS_EXPORT_(JSObject *) InitJSClass(JSContext *cx);
|
static NS_EXPORT_(JSObject *) InitJSClass(JSContext *cx);
|
||||||
|
|
|
@ -186,12 +186,68 @@ Quit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static JSBool
|
||||||
|
Dump(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||||
|
{
|
||||||
|
int32 depth = 2;
|
||||||
|
|
||||||
|
if (argc > 0) {
|
||||||
|
if (!JS_ValueToInt32(cx, argv[0], &depth))
|
||||||
|
return JS_FALSE;
|
||||||
|
}
|
||||||
|
XPC_DUMP(XPC_GetXPConnect(), depth);
|
||||||
|
return JS_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSBool
|
||||||
|
GC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||||
|
{
|
||||||
|
JSRuntime *rt;
|
||||||
|
uint32 preBytes;
|
||||||
|
|
||||||
|
rt = cx->runtime;
|
||||||
|
preBytes = rt->gcBytes;
|
||||||
|
#ifdef GC_MARK_DEBUG
|
||||||
|
if (argc && JSVAL_IS_STRING(argv[0])) {
|
||||||
|
char *name = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
|
||||||
|
FILE *file = fopen(name, "w");
|
||||||
|
if (!file) {
|
||||||
|
fprintf(gErrFile, "gc: can't open %s: %s\n", strerror(errno));
|
||||||
|
return JS_FALSE;
|
||||||
|
}
|
||||||
|
js_DumpGCHeap = file;
|
||||||
|
} else {
|
||||||
|
js_DumpGCHeap = stdout;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
js_ForceGC(cx);
|
||||||
|
#ifdef GC_MARK_DEBUG
|
||||||
|
if (js_DumpGCHeap != stdout)
|
||||||
|
fclose(js_DumpGCHeap);
|
||||||
|
js_DumpGCHeap = NULL;
|
||||||
|
#endif
|
||||||
|
fprintf(gOutFile, "before %lu, after %lu, break %08lx\n",
|
||||||
|
(unsigned long)preBytes, (unsigned long)rt->gcBytes,
|
||||||
|
#ifdef XP_UNIX
|
||||||
|
(unsigned long)sbrk(0)
|
||||||
|
#else
|
||||||
|
0
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
#ifdef JS_GCMETER
|
||||||
|
js_DumpGCStats(rt, stdout);
|
||||||
|
#endif
|
||||||
|
return JS_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
static JSFunctionSpec glob_functions[] = {
|
static JSFunctionSpec glob_functions[] = {
|
||||||
{"print", Print, 0},
|
{"print", Print, 0},
|
||||||
{"load", Load, 1},
|
{"load", Load, 1},
|
||||||
{"quit", Quit, 0},
|
{"quit", Quit, 0},
|
||||||
{"version", Version, 1},
|
{"version", Version, 1},
|
||||||
{"build", BuildDate, 0},
|
{"build", BuildDate, 0},
|
||||||
|
{"dump", Dump, 1},
|
||||||
|
{"gc", GC, 0},
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -360,7 +360,7 @@ ComponentsScriptable::GetProperty(JSContext *cx, JSObject *obj,
|
||||||
*retval = JS_TRUE;
|
*retval = JS_TRUE;
|
||||||
|
|
||||||
XPCContext* xpcc = nsXPConnect::GetContext(cx);
|
XPCContext* xpcc = nsXPConnect::GetContext(cx);
|
||||||
if(xpcc && xpcc->GetLastResultStrID() == id)
|
if(xpcc && xpcc->GetStringID(XPCContext::IDX_LAST_RESULT) == id)
|
||||||
{
|
{
|
||||||
if(JS_NewDoubleValue(cx, (jsdouble) (PRInt32)xpcc->GetLastResult(), vp))
|
if(JS_NewDoubleValue(cx, (jsdouble) (PRInt32)xpcc->GetLastResult(), vp))
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
|
|
@ -20,6 +20,12 @@
|
||||||
|
|
||||||
#include "xpcprivate.h"
|
#include "xpcprivate.h"
|
||||||
|
|
||||||
|
const char* XPCContext::mStrings[] = {
|
||||||
|
"constructor", // IDX_CONSTRUCTOR
|
||||||
|
"toString", // IDX_TO_STRING
|
||||||
|
"lastResult" // IDX_LAST_RESULT
|
||||||
|
};
|
||||||
|
|
||||||
// static
|
// static
|
||||||
XPCContext*
|
XPCContext*
|
||||||
XPCContext::newXPCContext(JSContext* aJSContext,
|
XPCContext::newXPCContext(JSContext* aJSContext,
|
||||||
|
@ -50,8 +56,7 @@ XPCContext::newXPCContext(JSContext* aJSContext,
|
||||||
xpcc->GetWrappedNativeMap() &&
|
xpcc->GetWrappedNativeMap() &&
|
||||||
xpcc->GetWrappedJSClassMap() &&
|
xpcc->GetWrappedJSClassMap() &&
|
||||||
xpcc->GetWrappedNativeClassMap() &&
|
xpcc->GetWrappedNativeClassMap() &&
|
||||||
xpcc->mConstuctorStrID &&
|
xpcc->mStrIDs[0])
|
||||||
xpcc->mToStringStrID)
|
|
||||||
{
|
{
|
||||||
return xpcc;
|
return xpcc;
|
||||||
}
|
}
|
||||||
|
@ -73,15 +78,17 @@ XPCContext::XPCContext(JSContext* aJSContext,
|
||||||
mWrappedNativeMap = Native2WrappedNativeMap::newMap(WrappedNativeMapSize);
|
mWrappedNativeMap = Native2WrappedNativeMap::newMap(WrappedNativeMapSize);
|
||||||
mWrappedJSClassMap = IID2WrappedJSClassMap::newMap(WrappedJSClassMapSize);
|
mWrappedJSClassMap = IID2WrappedJSClassMap::newMap(WrappedJSClassMapSize);
|
||||||
mWrappedNativeClassMap = IID2WrappedNativeClassMap::newMap(WrappedNativeClassMapSize);
|
mWrappedNativeClassMap = IID2WrappedNativeClassMap::newMap(WrappedNativeClassMapSize);
|
||||||
JS_ValueToId(aJSContext,
|
for(uintN i = 0; i < IDX_TOTAL_COUNT; i++)
|
||||||
STRING_TO_JSVAL(JS_InternString(aJSContext, "constructor")),
|
{
|
||||||
&mConstuctorStrID);
|
JS_ValueToId(aJSContext,
|
||||||
JS_ValueToId(aJSContext,
|
STRING_TO_JSVAL(JS_InternString(aJSContext, mStrings[i])),
|
||||||
STRING_TO_JSVAL(JS_InternString(aJSContext, "toString")),
|
&mStrIDs[i]);
|
||||||
&mToStringStrID);
|
if(!mStrIDs[i])
|
||||||
JS_ValueToId(aJSContext,
|
{
|
||||||
STRING_TO_JSVAL(JS_InternString(aJSContext, "lastResult")),
|
mStrIDs[0] = 0;
|
||||||
&mLastResultStrID);
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
mLastResult = NS_OK;
|
mLastResult = NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -231,6 +231,357 @@ nsJSIID::toString(char **_retval)
|
||||||
return GetName(_retval);
|
return GetName(_retval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***************************************************************************/
|
||||||
|
/***************************************************************************/
|
||||||
|
/*
|
||||||
|
* nsJSCID supports 'createInstance' and 'getService'. These are implemented
|
||||||
|
* using the (somewhat complex) nsIXPCScriptable scheme so that when called
|
||||||
|
* they can do security checks (knows in which JSContect is calling) and also
|
||||||
|
* so that 'getService' can add a fancy FinalizeListener to follow the
|
||||||
|
* ServiceManager's protocol for releasing a service.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class CIDCreateInstanceScriptable : public nsIXPCScriptable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NS_DECL_ISUPPORTS
|
||||||
|
XPC_DECLARE_IXPCSCRIPTABLE
|
||||||
|
CIDCreateInstanceScriptable();
|
||||||
|
virtual ~CIDCreateInstanceScriptable();
|
||||||
|
};
|
||||||
|
|
||||||
|
// {16A43B00-F116-11d2-985A-006008962422}
|
||||||
|
#define NS_CIDCREATEINSTANCE_IID \
|
||||||
|
{ 0x16a43b00, 0xf116, 0x11d2, \
|
||||||
|
{ 0x98, 0x5a, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } }
|
||||||
|
|
||||||
|
class CIDCreateInstance : public nsISupports
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NS_DEFINE_STATIC_IID_ACCESSOR(NS_CIDCREATEINSTANCE_IID)
|
||||||
|
NS_DECL_ISUPPORTS
|
||||||
|
CIDCreateInstance(nsJSCID* aCID);
|
||||||
|
virtual ~CIDCreateInstance();
|
||||||
|
nsJSCID* GetCID() const {return mCID;}
|
||||||
|
private:
|
||||||
|
CIDCreateInstance(); // not implemented
|
||||||
|
static CIDCreateInstanceScriptable* GetScriptable();
|
||||||
|
nsJSCID* mCID;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**********************************************/
|
||||||
|
|
||||||
|
CIDCreateInstanceScriptable::CIDCreateInstanceScriptable()
|
||||||
|
{
|
||||||
|
NS_INIT_REFCNT();
|
||||||
|
NS_ADDREF_THIS();
|
||||||
|
}
|
||||||
|
|
||||||
|
CIDCreateInstanceScriptable::~CIDCreateInstanceScriptable() {}
|
||||||
|
|
||||||
|
static NS_DEFINE_IID(kCIDCreateInstanceScriptableIID, NS_IXPCSCRIPTABLE_IID);
|
||||||
|
NS_IMPL_ISUPPORTS(CIDCreateInstanceScriptable, kCIDCreateInstanceScriptableIID);
|
||||||
|
|
||||||
|
XPC_IMPLEMENT_FORWARD_CREATE(CIDCreateInstanceScriptable);
|
||||||
|
XPC_IMPLEMENT_IGNORE_LOOKUPPROPERTY(CIDCreateInstanceScriptable);
|
||||||
|
XPC_IMPLEMENT_IGNORE_DEFINEPROPERTY(CIDCreateInstanceScriptable);
|
||||||
|
XPC_IMPLEMENT_IGNORE_GETPROPERTY(CIDCreateInstanceScriptable);
|
||||||
|
XPC_IMPLEMENT_IGNORE_SETPROPERTY(CIDCreateInstanceScriptable);
|
||||||
|
XPC_IMPLEMENT_IGNORE_GETATTRIBUTES(CIDCreateInstanceScriptable);
|
||||||
|
XPC_IMPLEMENT_IGNORE_SETATTRIBUTES(CIDCreateInstanceScriptable);
|
||||||
|
XPC_IMPLEMENT_IGNORE_DELETEPROPERTY(CIDCreateInstanceScriptable);
|
||||||
|
XPC_IMPLEMENT_FORWARD_DEFAULTVALUE(CIDCreateInstanceScriptable);
|
||||||
|
XPC_IMPLEMENT_FORWARD_ENUMERATE(CIDCreateInstanceScriptable);
|
||||||
|
XPC_IMPLEMENT_FORWARD_CHECKACCESS(CIDCreateInstanceScriptable);
|
||||||
|
// XPC_IMPLEMENT_IGNORE_CALL(CIDCreateInstanceScriptable);
|
||||||
|
XPC_IMPLEMENT_IGNORE_CONSTRUCT(CIDCreateInstanceScriptable);
|
||||||
|
XPC_IMPLEMENT_FORWARD_FINALIZE(CIDCreateInstanceScriptable);
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
CIDCreateInstanceScriptable::Call(JSContext *cx, JSObject *obj,
|
||||||
|
uintN argc, jsval *argv,
|
||||||
|
jsval *rval,
|
||||||
|
nsIXPConnectWrappedNative* wrapper,
|
||||||
|
nsIXPCScriptable* arbitrary,
|
||||||
|
JSBool* retval)
|
||||||
|
{
|
||||||
|
CIDCreateInstance* self;
|
||||||
|
nsJSCID* cidObj;
|
||||||
|
nsCID* cid;
|
||||||
|
PRBool valid;
|
||||||
|
|
||||||
|
if(NS_FAILED(wrapper->GetNative((nsISupports**)&self)) ||
|
||||||
|
!(cidObj = self->GetCID()) ||
|
||||||
|
NS_FAILED(cidObj->GetValid(&valid)) ||
|
||||||
|
!valid ||
|
||||||
|
NS_FAILED(cidObj->GetId(&cid)) ||
|
||||||
|
!cid)
|
||||||
|
{
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsISupports* inst;
|
||||||
|
nsresult rv;
|
||||||
|
|
||||||
|
// XXX can do security check here (don't forget to free cid)
|
||||||
|
|
||||||
|
// XXX could allow for passing an IID
|
||||||
|
rv = nsComponentManager::CreateInstance(*cid, NULL,
|
||||||
|
nsISupports::GetIID(),
|
||||||
|
(void**) &inst);
|
||||||
|
nsAllocator::Free(cid);
|
||||||
|
|
||||||
|
if(NS_FAILED(rv))
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
|
||||||
|
nsIXPConnectWrappedNative* instWrapper;
|
||||||
|
rv = XPC_GetXPConnect()->WrapNative(cx, inst, nsISupports::GetIID(),
|
||||||
|
&instWrapper);
|
||||||
|
NS_RELEASE(inst);
|
||||||
|
if(NS_FAILED(rv) || !instWrapper)
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
|
||||||
|
JSObject* instJSObj;
|
||||||
|
instWrapper->GetJSObject(&instJSObj);
|
||||||
|
*rval = OBJECT_TO_JSVAL(instJSObj);
|
||||||
|
*retval = JS_TRUE;
|
||||||
|
|
||||||
|
NS_RELEASE(instWrapper);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************/
|
||||||
|
|
||||||
|
NS_IMPL_QUERY_INTERFACE_SCRIPTABLE(CIDCreateInstance, GetScriptable())
|
||||||
|
NS_IMPL_ADDREF(CIDCreateInstance)
|
||||||
|
NS_IMPL_RELEASE(CIDCreateInstance)
|
||||||
|
|
||||||
|
CIDCreateInstance::CIDCreateInstance(nsJSCID *aCID)
|
||||||
|
: mCID(aCID)
|
||||||
|
{
|
||||||
|
NS_PRECONDITION(mCID, "bad cid");
|
||||||
|
NS_INIT_ISUPPORTS();
|
||||||
|
NS_ADDREF_THIS();
|
||||||
|
NS_ADDREF(mCID);
|
||||||
|
}
|
||||||
|
|
||||||
|
CIDCreateInstance::~CIDCreateInstance()
|
||||||
|
{
|
||||||
|
if(mCID)
|
||||||
|
NS_RELEASE(mCID);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
CIDCreateInstanceScriptable*
|
||||||
|
CIDCreateInstance::GetScriptable()
|
||||||
|
{
|
||||||
|
static CIDCreateInstanceScriptable* scriptable = NULL;
|
||||||
|
// we leak this singleton
|
||||||
|
if(!scriptable)
|
||||||
|
scriptable = new CIDCreateInstanceScriptable();
|
||||||
|
return scriptable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************************************************************************/
|
||||||
|
|
||||||
|
class CIDGetServiceScriptable : public nsIXPCScriptable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NS_DECL_ISUPPORTS
|
||||||
|
XPC_DECLARE_IXPCSCRIPTABLE
|
||||||
|
CIDGetServiceScriptable();
|
||||||
|
virtual ~CIDGetServiceScriptable();
|
||||||
|
};
|
||||||
|
|
||||||
|
// {C46BC320-F13E-11d2-985A-006008962422}
|
||||||
|
#define NS_CIDGETSERVICE_IID \
|
||||||
|
{ 0xc46bc320, 0xf13e, 0x11d2, \
|
||||||
|
{ 0x98, 0x5a, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } }
|
||||||
|
|
||||||
|
class CIDGetService : public nsISupports
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NS_DEFINE_STATIC_IID_ACCESSOR(NS_CIDGETSERVICE_IID)
|
||||||
|
NS_DECL_ISUPPORTS
|
||||||
|
CIDGetService(nsJSCID* aCID);
|
||||||
|
virtual ~CIDGetService();
|
||||||
|
nsJSCID* GetCID() const {return mCID;}
|
||||||
|
private:
|
||||||
|
CIDGetService(); // not implemented
|
||||||
|
|
||||||
|
static CIDGetServiceScriptable* CIDGetService::GetScriptable();
|
||||||
|
nsJSCID* mCID;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**********************************************/
|
||||||
|
|
||||||
|
// {23423AA0-F142-11d2-985A-006008962422}
|
||||||
|
#define NS_SERVICE_RELEASER_IID \
|
||||||
|
{ 0x23423aa0, 0xf142, 0x11d2, \
|
||||||
|
{ 0x98, 0x5a, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } }
|
||||||
|
|
||||||
|
class ServiceReleaser : public nsIXPConnectFinalizeListener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NS_DEFINE_STATIC_IID_ACCESSOR(NS_SERVICE_RELEASER_IID)
|
||||||
|
NS_DECL_ISUPPORTS
|
||||||
|
NS_IMETHOD AboutToRelease(nsISupports* aObj);
|
||||||
|
ServiceReleaser(const nsCID& aCID);
|
||||||
|
|
||||||
|
private:
|
||||||
|
ServiceReleaser(); // not implemented
|
||||||
|
virtual ~ServiceReleaser();
|
||||||
|
nsCID mCID;
|
||||||
|
};
|
||||||
|
|
||||||
|
static NS_DEFINE_IID(kServiceReleaserIID, NS_SERVICE_RELEASER_IID);
|
||||||
|
NS_IMPL_ISUPPORTS(ServiceReleaser, kServiceReleaserIID);
|
||||||
|
|
||||||
|
|
||||||
|
ServiceReleaser::ServiceReleaser(const nsCID& aCID)
|
||||||
|
: mCID(aCID)
|
||||||
|
{
|
||||||
|
NS_INIT_ISUPPORTS();
|
||||||
|
NS_ADDREF_THIS();
|
||||||
|
}
|
||||||
|
|
||||||
|
ServiceReleaser::~ServiceReleaser() {}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
ServiceReleaser::AboutToRelease(nsISupports* aObj)
|
||||||
|
{
|
||||||
|
return nsServiceManager::ReleaseService(mCID, aObj, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**********************************************/
|
||||||
|
|
||||||
|
CIDGetServiceScriptable::CIDGetServiceScriptable()
|
||||||
|
{
|
||||||
|
NS_INIT_REFCNT();
|
||||||
|
NS_ADDREF_THIS();
|
||||||
|
}
|
||||||
|
|
||||||
|
CIDGetServiceScriptable::~CIDGetServiceScriptable() {}
|
||||||
|
|
||||||
|
static NS_DEFINE_IID(kCIDGetServiceScriptableIID, NS_IXPCSCRIPTABLE_IID);
|
||||||
|
NS_IMPL_ISUPPORTS(CIDGetServiceScriptable, kCIDGetServiceScriptableIID);
|
||||||
|
|
||||||
|
XPC_IMPLEMENT_FORWARD_CREATE(CIDGetServiceScriptable);
|
||||||
|
XPC_IMPLEMENT_IGNORE_LOOKUPPROPERTY(CIDGetServiceScriptable);
|
||||||
|
XPC_IMPLEMENT_IGNORE_DEFINEPROPERTY(CIDGetServiceScriptable);
|
||||||
|
XPC_IMPLEMENT_IGNORE_GETPROPERTY(CIDGetServiceScriptable);
|
||||||
|
XPC_IMPLEMENT_IGNORE_SETPROPERTY(CIDGetServiceScriptable);
|
||||||
|
XPC_IMPLEMENT_IGNORE_GETATTRIBUTES(CIDGetServiceScriptable);
|
||||||
|
XPC_IMPLEMENT_IGNORE_SETATTRIBUTES(CIDGetServiceScriptable);
|
||||||
|
XPC_IMPLEMENT_IGNORE_DELETEPROPERTY(CIDGetServiceScriptable);
|
||||||
|
XPC_IMPLEMENT_FORWARD_DEFAULTVALUE(CIDGetServiceScriptable);
|
||||||
|
XPC_IMPLEMENT_FORWARD_ENUMERATE(CIDGetServiceScriptable);
|
||||||
|
XPC_IMPLEMENT_FORWARD_CHECKACCESS(CIDGetServiceScriptable);
|
||||||
|
// XPC_IMPLEMENT_IGNORE_CALL(CIDGetServiceScriptable);
|
||||||
|
XPC_IMPLEMENT_IGNORE_CONSTRUCT(CIDGetServiceScriptable);
|
||||||
|
XPC_IMPLEMENT_FORWARD_FINALIZE(CIDGetServiceScriptable);
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
CIDGetServiceScriptable::Call(JSContext *cx, JSObject *obj,
|
||||||
|
uintN argc, jsval *argv,
|
||||||
|
jsval *rval,
|
||||||
|
nsIXPConnectWrappedNative* wrapper,
|
||||||
|
nsIXPCScriptable* arbitrary,
|
||||||
|
JSBool* retval)
|
||||||
|
{
|
||||||
|
CIDGetService* self;
|
||||||
|
nsJSCID* cidObj;
|
||||||
|
nsCID* cid;
|
||||||
|
PRBool valid;
|
||||||
|
|
||||||
|
if(NS_FAILED(wrapper->GetNative((nsISupports**)&self)) ||
|
||||||
|
!(cidObj = self->GetCID()) ||
|
||||||
|
NS_FAILED(cidObj->GetValid(&valid)) ||
|
||||||
|
!valid ||
|
||||||
|
NS_FAILED(cidObj->GetId(&cid)) ||
|
||||||
|
!cid)
|
||||||
|
{
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsISupports* srvc;
|
||||||
|
nsresult rv;
|
||||||
|
|
||||||
|
// XXX can do security check here (don't forget to free cid)
|
||||||
|
|
||||||
|
// XXX could allow for passing an IID
|
||||||
|
rv = nsServiceManager::GetService(*cid, nsISupports::GetIID(),
|
||||||
|
&srvc, NULL);
|
||||||
|
|
||||||
|
if(NS_FAILED(rv))
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
|
||||||
|
nsIXPConnectWrappedNative* srvcWrapper;
|
||||||
|
rv = XPC_GetXPConnect()->WrapNative(cx, srvc, nsISupports::GetIID(),
|
||||||
|
&srvcWrapper);
|
||||||
|
if(NS_FAILED(rv) || !srvcWrapper)
|
||||||
|
{
|
||||||
|
nsServiceManager::ReleaseService(*cid, srvc, NULL);
|
||||||
|
nsAllocator::Free(cid);
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will eventually release the reference we got from
|
||||||
|
// nsServiceManager::GetService
|
||||||
|
ServiceReleaser* releaser = new ServiceReleaser(*cid);
|
||||||
|
if(NS_FAILED(srvcWrapper->SetFinalizeListener(releaser)))
|
||||||
|
{
|
||||||
|
// Failure means that we are using a preexisting wrapper on
|
||||||
|
// this service that has already setup a listener. So, we just
|
||||||
|
// release our extra ref and trust the lister that is already in
|
||||||
|
// place to do the right thing.
|
||||||
|
NS_RELEASE(srvc);
|
||||||
|
NS_RELEASE(releaser);
|
||||||
|
}
|
||||||
|
nsAllocator::Free(cid);
|
||||||
|
|
||||||
|
JSObject* srvcJSObj;
|
||||||
|
srvcWrapper->GetJSObject(&srvcJSObj);
|
||||||
|
*rval = OBJECT_TO_JSVAL(srvcJSObj);
|
||||||
|
*retval = JS_TRUE;
|
||||||
|
NS_RELEASE(srvcWrapper);
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************/
|
||||||
|
|
||||||
|
NS_IMPL_QUERY_INTERFACE_SCRIPTABLE(CIDGetService, GetScriptable())
|
||||||
|
NS_IMPL_ADDREF(CIDGetService)
|
||||||
|
NS_IMPL_RELEASE(CIDGetService)
|
||||||
|
|
||||||
|
CIDGetService::CIDGetService(nsJSCID *aCID)
|
||||||
|
: mCID(aCID)
|
||||||
|
{
|
||||||
|
NS_PRECONDITION(mCID, "bad cid");
|
||||||
|
NS_INIT_ISUPPORTS();
|
||||||
|
NS_ADDREF_THIS();
|
||||||
|
NS_ADDREF(mCID);
|
||||||
|
}
|
||||||
|
|
||||||
|
CIDGetService::~CIDGetService()
|
||||||
|
{
|
||||||
|
if(mCID)
|
||||||
|
NS_RELEASE(mCID);
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
CIDGetServiceScriptable*
|
||||||
|
CIDGetService::GetScriptable()
|
||||||
|
{
|
||||||
|
static CIDGetServiceScriptable* scriptable = NULL;
|
||||||
|
// we leak this singleton
|
||||||
|
if(!scriptable)
|
||||||
|
scriptable = new CIDGetServiceScriptable();
|
||||||
|
return scriptable;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/***************************************************************************/
|
/***************************************************************************/
|
||||||
// nsJSCID
|
// nsJSCID
|
||||||
|
|
||||||
|
@ -255,7 +606,9 @@ NS_IMPL_ADDREF(nsJSCID)
|
||||||
NS_IMPL_RELEASE(nsJSCID)
|
NS_IMPL_RELEASE(nsJSCID)
|
||||||
|
|
||||||
nsJSCID::nsJSCID()
|
nsJSCID::nsJSCID()
|
||||||
: mID(GetInvalidIID()), mNumber(gNoString), mName(gNoString)
|
: mID(GetInvalidIID()),
|
||||||
|
mNumber(gNoString),
|
||||||
|
mName(gNoString)
|
||||||
{
|
{
|
||||||
NS_INIT_ISUPPORTS();
|
NS_INIT_ISUPPORTS();
|
||||||
NS_ADDREF_THIS();
|
NS_ADDREF_THIS();
|
||||||
|
@ -408,39 +761,26 @@ nsJSCID::toString(char **_retval)
|
||||||
return GetName(_retval);
|
return GetName(_retval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* readonly attribute nsISupports createInstance; */
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsJSCID::createInstance(nsISupports **_retval)
|
nsJSCID::GetCreateInstance(nsISupports * *aCreateInstance)
|
||||||
{
|
{
|
||||||
if(!_retval)
|
if(!aCreateInstance)
|
||||||
return NS_ERROR_NULL_POINTER;
|
return NS_ERROR_NULL_POINTER;
|
||||||
|
|
||||||
*_retval = NULL;
|
*aCreateInstance = new CIDCreateInstance(this);
|
||||||
if(mID.Equals(GetInvalidIID()))
|
return NS_OK;
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
|
|
||||||
return nsComponentManager::CreateInstance(mID, NULL,
|
|
||||||
nsISupports::GetIID(),
|
|
||||||
(void**) _retval);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX this does not yet address the issue of using the SM to release
|
/* readonly attribute nsISupports getService; */
|
||||||
// XXX this does not yet address the issue of security protections
|
|
||||||
// XXX we'll need to make createInstance and getService dynamic to
|
|
||||||
// support both of these issues
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsJSCID::getService(nsISupports **_retval)
|
nsJSCID::GetGetService(nsISupports * *aGetService)
|
||||||
{
|
{
|
||||||
if(!_retval)
|
if(!aGetService)
|
||||||
return NS_ERROR_NULL_POINTER;
|
return NS_ERROR_NULL_POINTER;
|
||||||
|
|
||||||
*_retval = NULL;
|
*aGetService = new CIDGetService(this);
|
||||||
if(mID.Equals(GetInvalidIID()))
|
return NS_OK;
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
|
|
||||||
return nsServiceManager::GetService(mID,
|
|
||||||
nsISupports::GetIID(),
|
|
||||||
_retval, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/***************************************************************************/
|
/***************************************************************************/
|
||||||
|
|
|
@ -29,18 +29,10 @@ hash_root(const void *key)
|
||||||
return ((JSHashNumber) key) >> 2; /* help lame MSVC1.5 on Win16 */
|
return ((JSHashNumber) key) >> 2; /* help lame MSVC1.5 on Win16 */
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX this is just the hacked String hash function, should do better...
|
|
||||||
JS_STATIC_DLL_CALLBACK(JSHashNumber)
|
JS_STATIC_DLL_CALLBACK(JSHashNumber)
|
||||||
hash_IID(const void *key)
|
hash_IID(const void *key)
|
||||||
{
|
{
|
||||||
JSHashNumber h;
|
return (JSHashNumber) *((PRUint32*)key);
|
||||||
const PRUint8 *s;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
h = 0;
|
|
||||||
for (s = (const PRUint8 *)key, i = 0; i < 16; s++, i++)
|
|
||||||
h = (h >> 28) ^ (h << 4) ^ *s;
|
|
||||||
return h;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_STATIC_DLL_CALLBACK(intN)
|
JS_STATIC_DLL_CALLBACK(intN)
|
||||||
|
|
|
@ -138,9 +138,25 @@ public:
|
||||||
IID2WrappedNativeClassMap* GetWrappedNativeClassMap() const
|
IID2WrappedNativeClassMap* GetWrappedNativeClassMap() const
|
||||||
{return mWrappedNativeClassMap;}
|
{return mWrappedNativeClassMap;}
|
||||||
|
|
||||||
jsid GetConstructorStrID() const {return mConstuctorStrID;}
|
// To add a new string: add to this list and to XPCContext::mStrings
|
||||||
jsid GetToStringStrID() const {return mToStringStrID;}
|
// at the top of xpccontext.cpp
|
||||||
jsid GetLastResultStrID() const {return mLastResultStrID;}
|
enum {
|
||||||
|
IDX_CONSTRUCTOR = 0 ,
|
||||||
|
IDX_TO_STRING ,
|
||||||
|
IDX_LAST_RESULT ,
|
||||||
|
IDX_TOTAL_COUNT // just a count of the above
|
||||||
|
};
|
||||||
|
|
||||||
|
jsid GetStringID(uintN index) const
|
||||||
|
{
|
||||||
|
NS_ASSERTION(index < IDX_TOTAL_COUNT, "index out of range");
|
||||||
|
return mStrIDs[index];
|
||||||
|
}
|
||||||
|
const char* GetStringName(uintN index) const
|
||||||
|
{
|
||||||
|
NS_ASSERTION(index < IDX_TOTAL_COUNT, "index out of range");
|
||||||
|
return mStrings[index];
|
||||||
|
}
|
||||||
|
|
||||||
nsresult GetLastResult() {return mLastResult;}
|
nsresult GetLastResult() {return mLastResult;}
|
||||||
void SetLastResult(nsresult rc) {mLastResult = rc;}
|
void SetLastResult(nsresult rc) {mLastResult = rc;}
|
||||||
|
@ -158,6 +174,7 @@ private:
|
||||||
int WrappedJSClassMapSize,
|
int WrappedJSClassMapSize,
|
||||||
int WrappedNativeClassMapSize);
|
int WrappedNativeClassMapSize);
|
||||||
private:
|
private:
|
||||||
|
static const char* mStrings[IDX_TOTAL_COUNT];
|
||||||
nsXPConnect* mXPConnect;
|
nsXPConnect* mXPConnect;
|
||||||
JSContext* mJSContext;
|
JSContext* mJSContext;
|
||||||
JSObject* mGlobalObj;
|
JSObject* mGlobalObj;
|
||||||
|
@ -165,9 +182,7 @@ private:
|
||||||
Native2WrappedNativeMap* mWrappedNativeMap;
|
Native2WrappedNativeMap* mWrappedNativeMap;
|
||||||
IID2WrappedJSClassMap* mWrappedJSClassMap;
|
IID2WrappedJSClassMap* mWrappedJSClassMap;
|
||||||
IID2WrappedNativeClassMap* mWrappedNativeClassMap;
|
IID2WrappedNativeClassMap* mWrappedNativeClassMap;
|
||||||
jsid mConstuctorStrID;
|
jsid mStrIDs[IDX_TOTAL_COUNT];
|
||||||
jsid mToStringStrID;
|
|
||||||
jsid mLastResultStrID;
|
|
||||||
nsresult mLastResult;
|
nsresult mLastResult;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -678,11 +693,11 @@ public:
|
||||||
/* boolean init (in string idString); */
|
/* boolean init (in string idString); */
|
||||||
NS_IMETHOD init(const char *idString, PRBool *_retval);
|
NS_IMETHOD init(const char *idString, PRBool *_retval);
|
||||||
|
|
||||||
/* nsISupports createInstance (); */
|
/* readonly attribute nsISupports createInstance; */
|
||||||
NS_IMETHOD createInstance(nsISupports **_retval);
|
NS_IMETHOD GetCreateInstance(nsISupports * *aCreateInstance);
|
||||||
|
|
||||||
/* nsISupports getService (); */
|
/* readonly attribute nsISupports getService; */
|
||||||
NS_IMETHOD getService(nsISupports **_retval);
|
NS_IMETHOD GetGetService(nsISupports * *aGetService);
|
||||||
|
|
||||||
/* string toString (); */
|
/* string toString (); */
|
||||||
NS_IMETHOD toString(char **_retval);
|
NS_IMETHOD toString(char **_retval);
|
||||||
|
|
|
@ -268,16 +268,16 @@ nsXPCWrappedNative::~nsXPCWrappedNative()
|
||||||
map->Remove(this);
|
map->Remove(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(mDynamicScriptable)
|
|
||||||
NS_RELEASE(mDynamicScriptable);
|
|
||||||
if(mClass)
|
|
||||||
NS_RELEASE(mClass);
|
|
||||||
if(mFinalizeListener)
|
if(mFinalizeListener)
|
||||||
{
|
{
|
||||||
if(mObj)
|
if(mObj)
|
||||||
mFinalizeListener->AboutToRelease(mObj);
|
mFinalizeListener->AboutToRelease(mObj);
|
||||||
NS_RELEASE(mFinalizeListener);
|
NS_RELEASE(mFinalizeListener);
|
||||||
}
|
}
|
||||||
|
if(mDynamicScriptable)
|
||||||
|
NS_RELEASE(mDynamicScriptable);
|
||||||
|
if(mClass)
|
||||||
|
NS_RELEASE(mClass);
|
||||||
if(mObj)
|
if(mObj)
|
||||||
NS_RELEASE(mObj);
|
NS_RELEASE(mObj);
|
||||||
}
|
}
|
||||||
|
@ -378,11 +378,10 @@ nsXPCWrappedNative::GetIID(nsIID** iid)
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsXPCWrappedNative::SetFinalizeListener(nsIXPConnectFinalizeListener* aListener)
|
nsXPCWrappedNative::SetFinalizeListener(nsIXPConnectFinalizeListener* aListener)
|
||||||
{
|
{
|
||||||
|
/* if the object already has a listener, then we fail */
|
||||||
if(mFinalizeListener && aListener)
|
if(mFinalizeListener && aListener)
|
||||||
{
|
|
||||||
NS_ASSERTION(0,"tried to set two FinalizeListeners on a wrapper");
|
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
|
||||||
if(mFinalizeListener)
|
if(mFinalizeListener)
|
||||||
NS_RELEASE(mFinalizeListener);
|
NS_RELEASE(mFinalizeListener);
|
||||||
mFinalizeListener = aListener;
|
mFinalizeListener = aListener;
|
||||||
|
|
|
@ -237,6 +237,11 @@ WrappedNative_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
|
||||||
return JS_TRUE;
|
return JS_TRUE;
|
||||||
|
|
||||||
case JSTYPE_FUNCTION:
|
case JSTYPE_FUNCTION:
|
||||||
|
if(wrapper->GetDynamicScriptable())
|
||||||
|
{
|
||||||
|
*vp = OBJECT_TO_JSVAL(obj);
|
||||||
|
return JS_TRUE;
|
||||||
|
}
|
||||||
JS_ReportError(cx, "can't convert WrappedNative to function");
|
JS_ReportError(cx, "can't convert WrappedNative to function");
|
||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
|
|
||||||
|
@ -246,7 +251,8 @@ WrappedNative_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
|
||||||
nsXPCWrappedNativeClass* clazz = wrapper->GetClass();
|
nsXPCWrappedNativeClass* clazz = wrapper->GetClass();
|
||||||
NS_ASSERTION(clazz,"wrapper without class");
|
NS_ASSERTION(clazz,"wrapper without class");
|
||||||
const XPCNativeMemberDescriptor* desc =
|
const XPCNativeMemberDescriptor* desc =
|
||||||
clazz->LookupMemberByID(clazz->GetXPCContext()->GetToStringStrID());
|
clazz->LookupMemberByID(clazz->GetXPCContext()->
|
||||||
|
GetStringID(XPCContext::IDX_TO_STRING));
|
||||||
if(desc && desc->IsMethod())
|
if(desc && desc->IsMethod())
|
||||||
{
|
{
|
||||||
if(!clazz->CallWrappedMethod(cx, wrapper, desc,
|
if(!clazz->CallWrappedMethod(cx, wrapper, desc,
|
||||||
|
@ -636,7 +642,6 @@ WrappedNative_CallMethod(JSContext *cx, JSObject *obj,
|
||||||
jsval idval;
|
jsval idval;
|
||||||
|
|
||||||
nsXPCWrappedNative* wrapper;
|
nsXPCWrappedNative* wrapper;
|
||||||
|
|
||||||
wrapper = (nsXPCWrappedNative*) JS_GetPrivate(cx, obj);
|
wrapper = (nsXPCWrappedNative*) JS_GetPrivate(cx, obj);
|
||||||
if(!wrapper)
|
if(!wrapper)
|
||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
|
@ -690,7 +695,7 @@ WrappedNative_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
|
||||||
if(!wrapper)
|
if(!wrapper)
|
||||||
{
|
{
|
||||||
XPCContext* xpcc = nsXPConnect::GetContext(cx);
|
XPCContext* xpcc = nsXPConnect::GetContext(cx);
|
||||||
if(xpcc && id == xpcc->GetConstructorStrID())
|
if(xpcc && id == xpcc->GetStringID(XPCContext::IDX_CONSTRUCTOR))
|
||||||
{
|
{
|
||||||
// silently fail when looking for constructor property
|
// silently fail when looking for constructor property
|
||||||
*vp = JSVAL_VOID;
|
*vp = JSVAL_VOID;
|
||||||
|
@ -1102,6 +1107,11 @@ JS_STATIC_DLL_CALLBACK(JSBool)
|
||||||
WrappedNative_Call(JSContext *cx, JSObject *obj,
|
WrappedNative_Call(JSContext *cx, JSObject *obj,
|
||||||
uintN argc, jsval *argv, jsval *rval)
|
uintN argc, jsval *argv, jsval *rval)
|
||||||
{
|
{
|
||||||
|
// this is a hack to get the obj of the actual object not the object
|
||||||
|
// that JS thinks is the 'this' (which it passes as 'obj').
|
||||||
|
if(!(obj = (JSObject*)argv[-2]))
|
||||||
|
return JS_FALSE;
|
||||||
|
|
||||||
nsIXPCScriptable* ds;
|
nsIXPCScriptable* ds;
|
||||||
nsXPCWrappedNative* wrapper = (nsXPCWrappedNative*) JS_GetPrivate(cx,obj);
|
nsXPCWrappedNative* wrapper = (nsXPCWrappedNative*) JS_GetPrivate(cx,obj);
|
||||||
if(wrapper && NULL != (ds = wrapper->GetDynamicScriptable()))
|
if(wrapper && NULL != (ds = wrapper->GetDynamicScriptable()))
|
||||||
|
@ -1121,6 +1131,11 @@ JS_STATIC_DLL_CALLBACK(JSBool)
|
||||||
WrappedNative_Construct(JSContext *cx, JSObject *obj,
|
WrappedNative_Construct(JSContext *cx, JSObject *obj,
|
||||||
uintN argc, jsval *argv, jsval *rval)
|
uintN argc, jsval *argv, jsval *rval)
|
||||||
{
|
{
|
||||||
|
// this is a hack to get the obj of the actual object not the object
|
||||||
|
// that JS thinks is the 'this' (which it passes as 'obj').
|
||||||
|
if(!(obj = (JSObject*)argv[-2]))
|
||||||
|
return JS_FALSE;
|
||||||
|
|
||||||
nsIXPCScriptable* ds;
|
nsIXPCScriptable* ds;
|
||||||
nsXPCWrappedNative* wrapper = (nsXPCWrappedNative*) JS_GetPrivate(cx,obj);
|
nsXPCWrappedNative* wrapper = (nsXPCWrappedNative*) JS_GetPrivate(cx,obj);
|
||||||
if(wrapper && NULL != (ds = wrapper->GetDynamicScriptable()))
|
if(wrapper && NULL != (ds = wrapper->GetDynamicScriptable()))
|
||||||
|
|
|
@ -66,7 +66,9 @@ extern "C" PR_IMPLEMENT(nsresult)
|
||||||
NSRegisterSelf(nsISupports* aServMgr , const char* aPath)
|
NSRegisterSelf(nsISupports* aServMgr , const char* aPath)
|
||||||
{
|
{
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
printf("registering xpctest\n");
|
#ifdef DEBUG
|
||||||
|
printf("*** Register XPConnect test components\n");
|
||||||
|
#endif
|
||||||
NS_WITH_SERVICE1(nsIComponentManager, compMgr,
|
NS_WITH_SERVICE1(nsIComponentManager, compMgr,
|
||||||
aServMgr, kComponentManagerCID, &rv);
|
aServMgr, kComponentManagerCID, &rv);
|
||||||
if (NS_FAILED(rv)) return rv;
|
if (NS_FAILED(rv)) return rv;
|
||||||
|
|
Двоичные данные
js/src/xpconnect/typelib/nsISupports.xpt
Двоичные данные
js/src/xpconnect/typelib/nsISupports.xpt
Двоичный файл не отображается.
Двоичные данные
js/src/xpconnect/typelib/xpccomponents.xpt
Двоичные данные
js/src/xpconnect/typelib/xpccomponents.xpt
Двоичный файл не отображается.
Двоичные данные
js/src/xpconnect/typelib/xpcjsid.xpt
Двоичные данные
js/src/xpconnect/typelib/xpcjsid.xpt
Двоичный файл не отображается.
Двоичные данные
js/src/xpconnect/typelib/xpctest.xpt
Двоичные данные
js/src/xpconnect/typelib/xpctest.xpt
Двоичный файл не отображается.
Загрузка…
Ссылка в новой задаче