mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
4375 lines
148 KiB
C++
4375 lines
148 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
* vim: set ts=8 sw=4 et tw=78:
|
|
*
|
|
* 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/. */
|
|
|
|
/*
|
|
* XPConnect allows JS code to manipulate C++ object and C++ code to manipulate
|
|
* JS objects. JS manipulation of C++ objects tends to be significantly more
|
|
* complex. This comment explains how it is orchestrated by XPConnect.
|
|
*
|
|
* For each C++ object to be manipulated in JS, there is a corresponding JS
|
|
* object. This is called the "flattened JS object". By default, there is an
|
|
* additional C++ object involved of type XPCWrappedNative. The XPCWrappedNative
|
|
* holds pointers to the C++ object and the flat JS object.
|
|
*
|
|
* As an optimization, some C++ objects don't have XPCWrappedNatives, although
|
|
* they still have a corresponding flattened JS object. These are called "slim
|
|
* wrappers": all the wrapping information is stored in extra fields of the C++
|
|
* object and the JS object. Slim wrappers are only used for DOM objects. As a
|
|
* deoptimization, slim wrappers can be "morphed" into XPCWrappedNatives if the
|
|
* extra fields of the XPCWrappedNative become necessary.
|
|
*
|
|
* All XPCWrappedNative objects belong to an XPCWrappedNativeScope. These scopes
|
|
* are essentially in 1:1 correspondence with JS global objects. The
|
|
* XPCWrappedNativeScope has a pointer to the JS global object. The parent of a
|
|
* flattened JS object is, by default, the global JS object corresponding to the
|
|
* wrapper's XPCWrappedNativeScope (the exception to this rule is when a
|
|
* PreCreate hook asks for a different parent; see nsIXPCScriptable below).
|
|
*
|
|
* Some C++ objects (notably DOM objects) have information associated with them
|
|
* that lists the interfaces implemented by these objects. A C++ object exposes
|
|
* this information by implementing nsIClassInfo. If a C++ object implements
|
|
* nsIClassInfo, then JS code can call its methods without needing to use
|
|
* QueryInterface first. Typically, all instances of a C++ class share the same
|
|
* nsIClassInfo instance. (That is, obj->QueryInterface(nsIClassInfo) returns
|
|
* the same result for every obj of a given class.)
|
|
*
|
|
* XPConnect tracks nsIClassInfo information in an XPCWrappedNativeProto object.
|
|
* A given XPCWrappedNativeScope will have one XPCWrappedNativeProto for each
|
|
* nsIClassInfo instance being used. The XPCWrappedNativeProto has an associated
|
|
* JS object, which is used as the prototype of all flattened JS objects created
|
|
* for C++ objects with the given nsIClassInfo.
|
|
*
|
|
* Each XPCWrappedNativeProto has a pointer to its XPCWrappedNativeScope. If an
|
|
* XPCWrappedNative wraps a C++ object with class info, then it points to its
|
|
* XPCWrappedNativeProto. Otherwise it points to its XPCWrappedNativeScope. (The
|
|
* pointers are smooshed together in a tagged union.) Either way it can reach
|
|
* its scope.
|
|
*
|
|
* In the case of slim wrappers (where there is no XPCWrappedNative), the
|
|
* flattened JS object has a pointer to the XPCWrappedNativeProto stored in a
|
|
* reserved slot.
|
|
*
|
|
* An XPCWrappedNativeProto keeps track of the set of interfaces implemented by
|
|
* the C++ object in an XPCNativeSet. (The list of interfaces is obtained by
|
|
* calling a method on the nsIClassInfo.) An XPCNativeSet is a collection of
|
|
* XPCNativeInterfaces. Each interface stores the list of members, which can be
|
|
* methods, constants, getters, or setters.
|
|
*
|
|
* An XPCWrappedNative also points to an XPCNativeSet. Initially this starts out
|
|
* the same as the XPCWrappedNativeProto's set. If there is no proto, it starts
|
|
* out as a singleton set containing nsISupports. If JS code QI's new interfaces
|
|
* outside of the existing set, the set will grow. All QueryInterface results
|
|
* are cached in XPCWrappedNativeTearOff objects, which are linked off of the
|
|
* XPCWrappedNative.
|
|
*
|
|
* Besides having class info, a C++ object may be "scriptable" (i.e., implement
|
|
* nsIXPCScriptable). This allows it to implement a more DOM-like interface,
|
|
* besides just exposing XPCOM methods and constants. An nsIXPCScriptable
|
|
* instance has hooks that correspond to all the normal JSClass hooks. Each
|
|
* nsIXPCScriptable instance is mirrored by an XPCNativeScriptableInfo in
|
|
* XPConnect. These can have pointers from XPCWrappedNativeProto and
|
|
* XPCWrappedNative (since C++ objects can have scriptable info without having
|
|
* class info).
|
|
*
|
|
* Most data in an XPCNativeScriptableInfo is shared between instances. The
|
|
* shared data is stored in an XPCNativeScriptableShared object. This type is
|
|
* important because it holds the JSClass of the flattened JS objects with the
|
|
* given scriptable info.
|
|
*/
|
|
|
|
/* All the XPConnect private declarations - only include locally. */
|
|
|
|
#ifndef xpcprivate_h___
|
|
#define xpcprivate_h___
|
|
|
|
#include "mozilla/Assertions.h"
|
|
#include "mozilla/Attributes.h"
|
|
#include "mozilla/StandardInteger.h"
|
|
#include "mozilla/Util.h"
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <math.h>
|
|
#include "xpcpublic.h"
|
|
#include "jsapi.h"
|
|
#include "jsdhash.h"
|
|
#include "jsprf.h"
|
|
#include "prprf.h"
|
|
#include "jsdbgapi.h"
|
|
#include "jsfriendapi.h"
|
|
#include "jsgc.h"
|
|
#include "jswrapper.h"
|
|
#include "nscore.h"
|
|
#include "nsXPCOM.h"
|
|
#include "nsAutoPtr.h"
|
|
#include "nsCycleCollectionParticipant.h"
|
|
#include "nsCycleCollector.h"
|
|
#include "nsDebug.h"
|
|
#include "nsISupports.h"
|
|
#include "nsIServiceManager.h"
|
|
#include "nsIClassInfoImpl.h"
|
|
#include "nsIComponentManager.h"
|
|
#include "nsIComponentRegistrar.h"
|
|
#include "nsISupportsPrimitives.h"
|
|
#include "nsMemory.h"
|
|
#include "nsIXPConnect.h"
|
|
#include "nsIInterfaceInfo.h"
|
|
#include "nsIInterfaceInfoManager.h"
|
|
#include "nsIXPCScriptable.h"
|
|
#include "nsIXPCSecurityManager.h"
|
|
#include "nsIJSRuntimeService.h"
|
|
#include "nsWeakReference.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsXPTCUtils.h"
|
|
#include "xptinfo.h"
|
|
#include "XPCForwards.h"
|
|
#include "XPCLog.h"
|
|
#include "xpccomponents.h"
|
|
#include "xpcexception.h"
|
|
#include "xpcjsid.h"
|
|
#include "prlong.h"
|
|
#include "prmem.h"
|
|
#include "prenv.h"
|
|
#include "prclist.h"
|
|
#include "nsString.h"
|
|
#include "nsReadableUtils.h"
|
|
#include "nsXPIDLString.h"
|
|
#include "nsAutoJSValHolder.h"
|
|
|
|
#include "js/HashTable.h"
|
|
#include "mozilla/GuardObjects.h"
|
|
#include "mozilla/ReentrantMonitor.h"
|
|
#include "mozilla/Mutex.h"
|
|
|
|
#include "nsThreadUtils.h"
|
|
#include "nsIJSContextStack.h"
|
|
#include "nsIJSEngineTelemetryStats.h"
|
|
|
|
#include "nsIConsoleService.h"
|
|
#include "nsIScriptError.h"
|
|
#include "nsIExceptionService.h"
|
|
|
|
#include "nsVariant.h"
|
|
#include "nsIPropertyBag.h"
|
|
#include "nsIProperty.h"
|
|
#include "nsCOMArray.h"
|
|
#include "nsTArray.h"
|
|
#include "nsBaseHashtable.h"
|
|
#include "nsHashKeys.h"
|
|
#include "nsWrapperCache.h"
|
|
#include "nsStringBuffer.h"
|
|
#include "nsDataHashtable.h"
|
|
#include "nsDeque.h"
|
|
|
|
#include "nsIScriptSecurityManager.h"
|
|
#include "nsNetUtil.h"
|
|
|
|
#include "nsIXPCScriptNotify.h" // used to notify: ScriptEvaluated
|
|
|
|
#include "nsIScriptObjectPrincipal.h"
|
|
#include "nsIPrincipal.h"
|
|
#include "nsISecurityCheckedComponent.h"
|
|
#include "xpcObjectHelper.h"
|
|
#include "nsIThreadInternal.h"
|
|
|
|
#ifdef XP_WIN
|
|
// Nasty MS defines
|
|
#ifdef GetClassInfo
|
|
#undef GetClassInfo
|
|
#endif
|
|
#ifdef GetClassName
|
|
#undef GetClassName
|
|
#endif
|
|
#endif /* XP_WIN */
|
|
|
|
#include "nsINode.h"
|
|
|
|
/***************************************************************************/
|
|
// Compile time switches for instrumentation and stuff....
|
|
|
|
// Note that one would not normally turn *any* of these on in a non-DEBUG build.
|
|
|
|
#if defined(DEBUG_jband) || defined(DEBUG_jst) || defined(DEBUG_dbradley) || defined(DEBUG_shaver_no) || defined(DEBUG_timeless)
|
|
#define DEBUG_xpc_hacker
|
|
#endif
|
|
|
|
#if defined(DEBUG_brendan)
|
|
#define DEBUG_XPCNativeWrapper 1
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
#define XPC_DETECT_LEADING_UPPERCASE_ACCESS_ERRORS
|
|
#endif
|
|
|
|
#if defined(DEBUG_xpc_hacker)
|
|
#define XPC_DUMP_AT_SHUTDOWN
|
|
#define XPC_TRACK_WRAPPER_STATS
|
|
#define XPC_TRACK_SCOPE_STATS
|
|
#define XPC_TRACK_PROTO_STATS
|
|
#define XPC_TRACK_DEFERRED_RELEASES
|
|
#define XPC_CHECK_WRAPPERS_AT_SHUTDOWN
|
|
#define XPC_REPORT_SHADOWED_WRAPPED_NATIVE_MEMBERS
|
|
#define XPC_CHECK_CLASSINFO_CLAIMS
|
|
#if defined(DEBUG_jst)
|
|
#define XPC_ASSERT_CLASSINFO_CLAIMS
|
|
#endif
|
|
//#define DEBUG_stats_jband 1
|
|
//#define XPC_REPORT_NATIVE_INTERFACE_AND_SET_FLUSHING
|
|
//#define XPC_REPORT_JSCLASS_FLUSHING
|
|
//#define XPC_TRACK_AUTOMARKINGPTR_STATS
|
|
#endif
|
|
|
|
#if defined(DEBUG_dbaron) || defined(DEBUG_bzbarsky) // only part of DEBUG_xpc_hacker!
|
|
#define XPC_DUMP_AT_SHUTDOWN
|
|
#endif
|
|
|
|
/***************************************************************************/
|
|
// conditional forward declarations....
|
|
|
|
#ifdef XPC_REPORT_SHADOWED_WRAPPED_NATIVE_MEMBERS
|
|
void DEBUG_ReportShadowedMembers(XPCNativeSet* set,
|
|
XPCWrappedNative* wrapper,
|
|
XPCWrappedNativeProto* proto);
|
|
#else
|
|
#define DEBUG_ReportShadowedMembers(set, wrapper, proto) ((void)0)
|
|
#endif
|
|
|
|
/***************************************************************************/
|
|
// default initial sizes for maps (hashtables)
|
|
|
|
#define XPC_CONTEXT_MAP_SIZE 16
|
|
#define XPC_JS_MAP_SIZE 64
|
|
#define XPC_JS_CLASS_MAP_SIZE 64
|
|
|
|
#define XPC_NATIVE_MAP_SIZE 64
|
|
#define XPC_NATIVE_PROTO_MAP_SIZE 16
|
|
#define XPC_DYING_NATIVE_PROTO_MAP_SIZE 16
|
|
#define XPC_DETACHED_NATIVE_PROTO_MAP_SIZE 32
|
|
#define XPC_NATIVE_INTERFACE_MAP_SIZE 64
|
|
#define XPC_NATIVE_SET_MAP_SIZE 64
|
|
#define XPC_NATIVE_JSCLASS_MAP_SIZE 32
|
|
#define XPC_THIS_TRANSLATOR_MAP_SIZE 8
|
|
#define XPC_NATIVE_WRAPPER_MAP_SIZE 16
|
|
#define XPC_WRAPPER_MAP_SIZE 16
|
|
|
|
/***************************************************************************/
|
|
// data declarations...
|
|
extern const char XPC_CONTEXT_STACK_CONTRACTID[];
|
|
extern const char XPC_RUNTIME_CONTRACTID[];
|
|
extern const char XPC_EXCEPTION_CONTRACTID[];
|
|
extern const char XPC_CONSOLE_CONTRACTID[];
|
|
extern const char XPC_SCRIPT_ERROR_CONTRACTID[];
|
|
extern const char XPC_ID_CONTRACTID[];
|
|
extern const char XPC_XPCONNECT_CONTRACTID[];
|
|
|
|
typedef js::HashSet<JSCompartment *,
|
|
js::DefaultHasher<JSCompartment *>,
|
|
js::SystemAllocPolicy> XPCCompartmentSet;
|
|
|
|
typedef XPCCompartmentSet::Range XPCCompartmentRange;
|
|
|
|
/***************************************************************************/
|
|
// Useful macros...
|
|
|
|
#define XPC_STRING_GETTER_BODY(dest, src) \
|
|
NS_ENSURE_ARG_POINTER(dest); \
|
|
char* result; \
|
|
if (src) \
|
|
result = (char*) nsMemory::Clone(src, \
|
|
sizeof(char)*(strlen(src)+1)); \
|
|
else \
|
|
result = nsnull; \
|
|
*dest = result; \
|
|
return (result || !src) ? NS_OK : NS_ERROR_OUT_OF_MEMORY
|
|
|
|
|
|
#define WRAPPER_SLOTS (JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | \
|
|
JSCLASS_HAS_RESERVED_SLOTS(1))
|
|
|
|
// WRAPPER_MULTISLOT is defined in xpcpublic.h
|
|
|
|
#define INVALID_OBJECT ((JSObject *)1)
|
|
|
|
inline void SetSlimWrapperProto(JSObject *obj, XPCWrappedNativeProto *proto)
|
|
{
|
|
JS_SetReservedSlot(obj, WRAPPER_MULTISLOT, PRIVATE_TO_JSVAL(proto));
|
|
}
|
|
|
|
inline XPCWrappedNativeProto* GetSlimWrapperProto(JSObject *obj)
|
|
{
|
|
MOZ_ASSERT(IS_SLIM_WRAPPER(obj));
|
|
const JS::Value &v = js::GetReservedSlot(obj, WRAPPER_MULTISLOT);
|
|
return static_cast<XPCWrappedNativeProto*>(v.toPrivate());
|
|
}
|
|
|
|
// A slim wrapper is identified by having a native pointer in its reserved slot.
|
|
// This function, therefore, does the official transition from a slim wrapper to
|
|
// a non-slim wrapper.
|
|
inline void MorphMultiSlot(JSObject *obj)
|
|
{
|
|
MOZ_ASSERT(IS_SLIM_WRAPPER(obj));
|
|
JS_SetReservedSlot(obj, WRAPPER_MULTISLOT, JSVAL_NULL);
|
|
MOZ_ASSERT(!IS_SLIM_WRAPPER(obj));
|
|
}
|
|
|
|
inline void SetExpandoChain(JSObject *obj, JSObject *chain)
|
|
{
|
|
MOZ_ASSERT(IS_WN_WRAPPER(obj));
|
|
JS_SetReservedSlot(obj, WRAPPER_MULTISLOT, JS::ObjectOrNullValue(chain));
|
|
}
|
|
|
|
inline JSObject* GetExpandoChain(JSObject *obj)
|
|
{
|
|
MOZ_ASSERT(IS_WN_WRAPPER(obj));
|
|
return JS_GetReservedSlot(obj, WRAPPER_MULTISLOT).toObjectOrNull();
|
|
}
|
|
|
|
/***************************************************************************/
|
|
// Auto locking support class...
|
|
|
|
// We PROMISE to never screw this up.
|
|
#ifdef _MSC_VER
|
|
#pragma warning(disable : 4355) // OK to pass "this" in member initializer
|
|
#endif
|
|
|
|
typedef mozilla::ReentrantMonitor XPCLock;
|
|
|
|
static inline void xpc_Wait(XPCLock* lock)
|
|
{
|
|
NS_ASSERTION(lock, "xpc_Wait called with null lock!");
|
|
lock->Wait();
|
|
}
|
|
|
|
static inline void xpc_NotifyAll(XPCLock* lock)
|
|
{
|
|
NS_ASSERTION(lock, "xpc_NotifyAll called with null lock!");
|
|
lock->NotifyAll();
|
|
}
|
|
|
|
// This is a cloned subset of nsAutoMonitor. We want the use of a monitor -
|
|
// mostly because we need reenterability - but we also want to support passing
|
|
// a null monitor in without things blowing up. This is used for wrappers that
|
|
// are guaranteed to be used only on one thread. We avoid lock overhead by
|
|
// using a null monitor. By changing this class we can avoid having multiplte
|
|
// code paths or (conditional) manual calls to PR_{Enter,Exit}Monitor.
|
|
//
|
|
// Note that xpconnect only makes *one* monitor and *mostly* holds it locked
|
|
// only through very small critical sections.
|
|
|
|
class NS_STACK_CLASS XPCAutoLock {
|
|
public:
|
|
|
|
static XPCLock* NewLock(const char* name)
|
|
{return new mozilla::ReentrantMonitor(name);}
|
|
static void DestroyLock(XPCLock* lock)
|
|
{delete lock;}
|
|
|
|
XPCAutoLock(XPCLock* lock MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
|
: mLock(lock)
|
|
{
|
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
if (mLock)
|
|
mLock->Enter();
|
|
}
|
|
|
|
~XPCAutoLock()
|
|
{
|
|
if (mLock) {
|
|
mLock->Exit();
|
|
}
|
|
}
|
|
|
|
private:
|
|
XPCLock* mLock;
|
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
|
|
// Not meant to be implemented. This makes it a compiler error to
|
|
// construct or assign an XPCAutoLock object incorrectly.
|
|
XPCAutoLock(void) {}
|
|
XPCAutoLock(XPCAutoLock& /*aMon*/) {}
|
|
XPCAutoLock& operator =(XPCAutoLock& /*aMon*/) {
|
|
return *this;
|
|
}
|
|
|
|
// Not meant to be implemented. This makes it a compiler error to
|
|
// attempt to create an XPCAutoLock object on the heap.
|
|
static void* operator new(size_t /*size*/) CPP_THROW_NEW {
|
|
return nsnull;
|
|
}
|
|
static void operator delete(void* /*memory*/) {}
|
|
};
|
|
|
|
/************************************************/
|
|
|
|
class NS_STACK_CLASS XPCAutoUnlock {
|
|
public:
|
|
XPCAutoUnlock(XPCLock* lock MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
|
: mLock(lock)
|
|
{
|
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
if (mLock) {
|
|
mLock->Exit();
|
|
}
|
|
}
|
|
|
|
~XPCAutoUnlock()
|
|
{
|
|
if (mLock)
|
|
mLock->Enter();
|
|
}
|
|
|
|
private:
|
|
XPCLock* mLock;
|
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
|
|
// Not meant to be implemented. This makes it a compiler error to
|
|
// construct or assign an XPCAutoUnlock object incorrectly.
|
|
XPCAutoUnlock(void) {}
|
|
XPCAutoUnlock(XPCAutoUnlock& /*aMon*/) {}
|
|
XPCAutoUnlock& operator =(XPCAutoUnlock& /*aMon*/) {
|
|
return *this;
|
|
}
|
|
|
|
// Not meant to be implemented. This makes it a compiler error to
|
|
// attempt to create an XPCAutoUnlock object on the heap.
|
|
static void* operator new(size_t /*size*/) CPP_THROW_NEW {
|
|
return nsnull;
|
|
}
|
|
static void operator delete(void* /*memory*/) {}
|
|
};
|
|
|
|
/***************************************************************************
|
|
****************************************************************************
|
|
*
|
|
* Core runtime and context classes...
|
|
*
|
|
****************************************************************************
|
|
***************************************************************************/
|
|
|
|
// We have a general rule internally that getters that return addref'd interface
|
|
// pointer generally do so using an 'out' parm. When interface pointers are
|
|
// returned as function call result values they are not addref'd. Exceptions
|
|
// to this rule are noted explicitly.
|
|
|
|
// JSTRACE_XML can recursively hold on to more JSTRACE_XML objects, adding it to
|
|
// the cycle collector avoids stack overflow.
|
|
inline bool
|
|
AddToCCKind(JSGCTraceKind kind)
|
|
{
|
|
return kind == JSTRACE_OBJECT || kind == JSTRACE_XML || kind == JSTRACE_SCRIPT;
|
|
}
|
|
|
|
class nsXPConnect : public nsIXPConnect,
|
|
public nsIThreadObserver,
|
|
public nsSupportsWeakReference,
|
|
public nsCycleCollectionJSRuntime,
|
|
public nsIJSRuntimeService,
|
|
public nsIThreadJSContextStack,
|
|
public nsIJSEngineTelemetryStats
|
|
{
|
|
public:
|
|
// all the interface method declarations...
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSIXPCONNECT
|
|
NS_DECL_NSITHREADOBSERVER
|
|
NS_DECL_NSIJSRUNTIMESERVICE
|
|
NS_DECL_NSIJSCONTEXTSTACK
|
|
NS_DECL_NSITHREADJSCONTEXTSTACK
|
|
NS_DECL_NSIJSENGINETELEMETRYSTATS
|
|
|
|
// non-interface implementation
|
|
public:
|
|
// These get non-addref'd pointers
|
|
static nsXPConnect* GetXPConnect();
|
|
static nsXPConnect* FastGetXPConnect() { return gSelf ? gSelf : GetXPConnect(); }
|
|
static XPCJSRuntime* GetRuntimeInstance();
|
|
XPCJSRuntime* GetRuntime() {return mRuntime;}
|
|
|
|
// Gets addref'd pointer
|
|
static nsresult GetInterfaceInfoManager(nsIInterfaceInfoSuperManager** iim,
|
|
nsXPConnect* xpc = nsnull);
|
|
|
|
static JSBool IsISupportsDescendant(nsIInterfaceInfo* info);
|
|
|
|
nsIXPCSecurityManager* GetDefaultSecurityManager() const
|
|
{
|
|
// mDefaultSecurityManager is main-thread only.
|
|
if (!NS_IsMainThread()) {
|
|
return nsnull;
|
|
}
|
|
return mDefaultSecurityManager;
|
|
}
|
|
|
|
PRUint16 GetDefaultSecurityManagerFlags() const
|
|
{return mDefaultSecurityManagerFlags;}
|
|
|
|
// This returns an AddRef'd pointer. It does not do this with an 'out' param
|
|
// only because this form is required by the generic module macro:
|
|
// NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR
|
|
static nsXPConnect* GetSingleton();
|
|
|
|
// Called by module code in dll startup
|
|
static void InitStatics() { gSelf = nsnull; gOnceAliveNowDead = false; }
|
|
// Called by module code on dll shutdown.
|
|
static void ReleaseXPConnectSingleton();
|
|
|
|
virtual ~nsXPConnect();
|
|
|
|
JSBool IsShuttingDown() const {return mShuttingDown;}
|
|
|
|
void EnsureGCBeforeCC() { mNeedGCBeforeCC = true; }
|
|
void ClearGCBeforeCC() { mNeedGCBeforeCC = false; }
|
|
|
|
nsresult GetInfoForIID(const nsIID * aIID, nsIInterfaceInfo** info);
|
|
nsresult GetInfoForName(const char * name, nsIInterfaceInfo** info);
|
|
|
|
// nsCycleCollectionLanguageRuntime
|
|
virtual bool NotifyLeaveMainThread();
|
|
virtual void NotifyEnterCycleCollectionThread();
|
|
virtual void NotifyLeaveCycleCollectionThread();
|
|
virtual void NotifyEnterMainThread();
|
|
virtual nsresult BeginCycleCollection(nsCycleCollectionTraversalCallback &cb);
|
|
virtual nsresult FinishTraverse();
|
|
virtual nsCycleCollectionParticipant *GetParticipant();
|
|
virtual bool NeedCollect();
|
|
virtual void Collect(PRUint32 reason, PRUint32 kind);
|
|
|
|
XPCCallContext *GetCycleCollectionContext()
|
|
{
|
|
return mCycleCollectionContext;
|
|
}
|
|
|
|
unsigned GetOutstandingRequests(JSContext* cx);
|
|
|
|
// This returns the singleton nsCycleCollectionParticipant for JSContexts.
|
|
static nsCycleCollectionParticipant *JSContextParticipant();
|
|
|
|
virtual nsIPrincipal* GetPrincipal(JSObject* obj,
|
|
bool allowShortCircuit) const;
|
|
|
|
void RecordTraversal(void *p, nsISupports *s);
|
|
virtual char* DebugPrintJSStack(bool showArgs,
|
|
bool showLocals,
|
|
bool showThisProps);
|
|
|
|
|
|
static bool ReportAllJSExceptions()
|
|
{
|
|
return gReportAllJSExceptions > 0;
|
|
}
|
|
|
|
static void CheckForDebugMode(JSRuntime *rt);
|
|
|
|
protected:
|
|
nsXPConnect();
|
|
|
|
private:
|
|
static PRThread* FindMainThread();
|
|
|
|
private:
|
|
// Singleton instance
|
|
static nsXPConnect* gSelf;
|
|
static JSBool gOnceAliveNowDead;
|
|
|
|
XPCJSRuntime* mRuntime;
|
|
nsCOMPtr<nsIInterfaceInfoSuperManager> mInterfaceInfoManager;
|
|
nsIXPCSecurityManager* mDefaultSecurityManager;
|
|
PRUint16 mDefaultSecurityManagerFlags;
|
|
JSBool mShuttingDown;
|
|
JSBool mNeedGCBeforeCC;
|
|
|
|
// nsIThreadInternal doesn't remember which observers it called
|
|
// OnProcessNextEvent on when it gets around to calling AfterProcessNextEvent.
|
|
// So if XPConnect gets initialized mid-event (which can happen), we'll get
|
|
// an 'after' notification without getting an 'on' notification. If we don't
|
|
// watch out for this, we'll do an unmatched |pop| on the context stack.
|
|
PRUint16 mEventDepth;
|
|
nsAutoPtr<XPCCallContext> mCycleCollectionContext;
|
|
|
|
typedef nsBaseHashtable<nsPtrHashKey<void>, nsISupports*, nsISupports*> ScopeSet;
|
|
ScopeSet mScopes;
|
|
nsCOMPtr<nsIXPCScriptable> mBackstagePass;
|
|
|
|
static PRUint32 gReportAllJSExceptions;
|
|
static JSBool gDebugMode;
|
|
static JSBool gDesiredDebugMode;
|
|
|
|
public:
|
|
static nsIScriptSecurityManager *gScriptSecurityManager;
|
|
};
|
|
|
|
/***************************************************************************/
|
|
|
|
class XPCRootSetElem
|
|
{
|
|
public:
|
|
XPCRootSetElem()
|
|
{
|
|
#ifdef DEBUG
|
|
mNext = nsnull;
|
|
mSelfp = nsnull;
|
|
#endif
|
|
}
|
|
|
|
~XPCRootSetElem()
|
|
{
|
|
NS_ASSERTION(!mNext, "Must be unlinked");
|
|
NS_ASSERTION(!mSelfp, "Must be unlinked");
|
|
}
|
|
|
|
inline XPCRootSetElem* GetNextRoot() { return mNext; }
|
|
void AddToRootSet(XPCLock *lock, XPCRootSetElem **listHead);
|
|
void RemoveFromRootSet(XPCLock *lock);
|
|
|
|
private:
|
|
XPCRootSetElem *mNext;
|
|
XPCRootSetElem **mSelfp;
|
|
};
|
|
|
|
/***************************************************************************/
|
|
|
|
// In the current xpconnect system there can only be one XPCJSRuntime.
|
|
// So, xpconnect can only be used on one JSRuntime within the process.
|
|
|
|
// no virtuals. no refcounting.
|
|
class XPCJSContextStack;
|
|
class XPCJSRuntime
|
|
{
|
|
public:
|
|
static XPCJSRuntime* newXPCJSRuntime(nsXPConnect* aXPConnect);
|
|
static XPCJSRuntime* Get() { return nsXPConnect::GetXPConnect()->GetRuntime(); }
|
|
|
|
JSRuntime* GetJSRuntime() const {return mJSRuntime;}
|
|
nsXPConnect* GetXPConnect() const {return mXPConnect;}
|
|
|
|
XPCJSContextStack* GetJSContextStack() {return mJSContextStack;}
|
|
void DestroyJSContextStack();
|
|
|
|
JSContext* GetJSCycleCollectionContext();
|
|
|
|
XPCCallContext* GetCallContext() const {return mCallContext;}
|
|
XPCCallContext* SetCallContext(XPCCallContext* ccx)
|
|
{XPCCallContext* old = mCallContext; mCallContext = ccx; return old;}
|
|
|
|
jsid GetResolveName() const {return mResolveName;}
|
|
jsid SetResolveName(jsid name)
|
|
{jsid old = mResolveName; mResolveName = name; return old;}
|
|
|
|
XPCWrappedNative* GetResolvingWrapper() const {return mResolvingWrapper;}
|
|
XPCWrappedNative* SetResolvingWrapper(XPCWrappedNative* w)
|
|
{XPCWrappedNative* old = mResolvingWrapper;
|
|
mResolvingWrapper = w; return old;}
|
|
|
|
JSObject2WrappedJSMap* GetWrappedJSMap() const
|
|
{return mWrappedJSMap;}
|
|
|
|
IID2WrappedJSClassMap* GetWrappedJSClassMap() const
|
|
{return mWrappedJSClassMap;}
|
|
|
|
IID2NativeInterfaceMap* GetIID2NativeInterfaceMap() const
|
|
{return mIID2NativeInterfaceMap;}
|
|
|
|
ClassInfo2NativeSetMap* GetClassInfo2NativeSetMap() const
|
|
{return mClassInfo2NativeSetMap;}
|
|
|
|
NativeSetMap* GetNativeSetMap() const
|
|
{return mNativeSetMap;}
|
|
|
|
IID2ThisTranslatorMap* GetThisTranslatorMap() const
|
|
{return mThisTranslatorMap;}
|
|
|
|
XPCNativeScriptableSharedMap* GetNativeScriptableSharedMap() const
|
|
{return mNativeScriptableSharedMap;}
|
|
|
|
XPCWrappedNativeProtoMap* GetDyingWrappedNativeProtoMap() const
|
|
{return mDyingWrappedNativeProtoMap;}
|
|
|
|
XPCWrappedNativeProtoMap* GetDetachedWrappedNativeProtoMap() const
|
|
{return mDetachedWrappedNativeProtoMap;}
|
|
|
|
XPCCompartmentSet& GetCompartmentSet()
|
|
{return mCompartmentSet;}
|
|
|
|
XPCLock* GetMapLock() const {return mMapLock;}
|
|
|
|
JSBool OnJSContextNew(JSContext* cx);
|
|
|
|
bool DeferredRelease(nsISupports* obj);
|
|
|
|
JSBool GetDoingFinalization() const {return mDoingFinalization;}
|
|
|
|
// Mapping of often used strings to jsid atoms that live 'forever'.
|
|
//
|
|
// To add a new string: add to this list and to XPCJSRuntime::mStrings
|
|
// at the top of xpcjsruntime.cpp
|
|
enum {
|
|
IDX_CONSTRUCTOR = 0 ,
|
|
IDX_TO_STRING ,
|
|
IDX_TO_SOURCE ,
|
|
IDX_LAST_RESULT ,
|
|
IDX_RETURN_CODE ,
|
|
IDX_VALUE ,
|
|
IDX_QUERY_INTERFACE ,
|
|
IDX_COMPONENTS ,
|
|
IDX_WRAPPED_JSOBJECT ,
|
|
IDX_OBJECT ,
|
|
IDX_FUNCTION ,
|
|
IDX_PROTOTYPE ,
|
|
IDX_CREATE_INSTANCE ,
|
|
IDX_ITEM ,
|
|
IDX_PROTO ,
|
|
IDX_ITERATOR ,
|
|
IDX_EXPOSEDPROPS ,
|
|
IDX_SCRIPTONLY ,
|
|
IDX_BASEURIOBJECT ,
|
|
IDX_NODEPRINCIPAL ,
|
|
IDX_DOCUMENTURIOBJECT ,
|
|
IDX_TOTAL_COUNT // just a count of the above
|
|
};
|
|
|
|
jsid GetStringID(unsigned index) const
|
|
{
|
|
NS_ASSERTION(index < IDX_TOTAL_COUNT, "index out of range");
|
|
return mStrIDs[index];
|
|
}
|
|
jsval GetStringJSVal(unsigned index) const
|
|
{
|
|
NS_ASSERTION(index < IDX_TOTAL_COUNT, "index out of range");
|
|
return mStrJSVals[index];
|
|
}
|
|
const char* GetStringName(unsigned index) const
|
|
{
|
|
NS_ASSERTION(index < IDX_TOTAL_COUNT, "index out of range");
|
|
return mStrings[index];
|
|
}
|
|
|
|
static void TraceBlackJS(JSTracer* trc, void* data);
|
|
static void TraceGrayJS(JSTracer* trc, void* data);
|
|
void TraceXPConnectRoots(JSTracer *trc);
|
|
void AddXPConnectRoots(nsCycleCollectionTraversalCallback& cb);
|
|
void UnmarkSkippableJSHolders();
|
|
|
|
static void GCCallback(JSRuntime *rt, JSGCStatus status);
|
|
static void FinalizeCallback(JSFreeOp *fop, JSFinalizeStatus status, JSBool isCompartmentGC);
|
|
|
|
inline void AddVariantRoot(XPCTraceableVariant* variant);
|
|
inline void AddWrappedJSRoot(nsXPCWrappedJS* wrappedJS);
|
|
inline void AddObjectHolderRoot(XPCJSObjectHolder* holder);
|
|
|
|
nsresult AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer);
|
|
nsresult RemoveJSHolder(void* aHolder);
|
|
|
|
static void SuspectWrappedNative(XPCWrappedNative *wrapper,
|
|
nsCycleCollectionTraversalCallback &cb);
|
|
|
|
void DebugDump(PRInt16 depth);
|
|
|
|
void SystemIsBeingShutDown();
|
|
|
|
PRThread* GetThreadRunningGC() const {return mThreadRunningGC;}
|
|
|
|
~XPCJSRuntime();
|
|
|
|
nsresult GetPendingException(nsIException** aException)
|
|
{
|
|
if (EnsureExceptionManager())
|
|
return mExceptionManager->GetCurrentException(aException);
|
|
nsCOMPtr<nsIException> out = mPendingException;
|
|
out.forget(aException);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult SetPendingException(nsIException* aException)
|
|
{
|
|
if (EnsureExceptionManager())
|
|
return mExceptionManager->SetCurrentException(aException);
|
|
|
|
mPendingException = aException;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsIExceptionManager* GetExceptionManager()
|
|
{
|
|
if (EnsureExceptionManager())
|
|
return mExceptionManager;
|
|
return nsnull;
|
|
}
|
|
|
|
bool EnsureExceptionManager()
|
|
{
|
|
if (mExceptionManager)
|
|
return true;
|
|
|
|
if (mExceptionManagerNotAvailable)
|
|
return false;
|
|
|
|
nsCOMPtr<nsIExceptionService> xs =
|
|
do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID);
|
|
if (xs)
|
|
xs->GetCurrentExceptionManager(getter_AddRefs(mExceptionManager));
|
|
if (mExceptionManager)
|
|
return true;
|
|
|
|
mExceptionManagerNotAvailable = true;
|
|
return false;
|
|
}
|
|
|
|
|
|
#ifdef XPC_CHECK_WRAPPERS_AT_SHUTDOWN
|
|
void DEBUG_AddWrappedNative(nsIXPConnectWrappedNative* wrapper)
|
|
{XPCAutoLock lock(GetMapLock());
|
|
JSDHashEntryHdr *entry =
|
|
JS_DHashTableOperate(DEBUG_WrappedNativeHashtable,
|
|
wrapper, JS_DHASH_ADD);
|
|
if (entry) ((JSDHashEntryStub *)entry)->key = wrapper;}
|
|
|
|
void DEBUG_RemoveWrappedNative(nsIXPConnectWrappedNative* wrapper)
|
|
{XPCAutoLock lock(GetMapLock());
|
|
JS_DHashTableOperate(DEBUG_WrappedNativeHashtable,
|
|
wrapper, JS_DHASH_REMOVE);}
|
|
private:
|
|
JSDHashTable* DEBUG_WrappedNativeHashtable;
|
|
public:
|
|
#endif
|
|
|
|
void AddGCCallback(JSGCCallback cb);
|
|
void RemoveGCCallback(JSGCCallback cb);
|
|
|
|
static void ActivityCallback(void *arg, JSBool active);
|
|
|
|
bool NewDOMBindingsEnabled()
|
|
{
|
|
return gNewDOMBindingsEnabled;
|
|
}
|
|
|
|
bool ExperimentalBindingsEnabled()
|
|
{
|
|
return gExperimentalBindingsEnabled;
|
|
}
|
|
|
|
size_t SizeOfIncludingThis(nsMallocSizeOfFun mallocSizeOf);
|
|
|
|
AutoMarkingPtr** GetAutoRootsAdr() {return &mAutoRoots;}
|
|
|
|
private:
|
|
XPCJSRuntime(); // no implementation
|
|
XPCJSRuntime(nsXPConnect* aXPConnect);
|
|
|
|
// The caller must be holding the GC lock
|
|
void RescheduleWatchdog(XPCContext* ccx);
|
|
|
|
static void WatchdogMain(void *arg);
|
|
|
|
static bool gNewDOMBindingsEnabled;
|
|
static bool gExperimentalBindingsEnabled;
|
|
|
|
static const char* mStrings[IDX_TOTAL_COUNT];
|
|
jsid mStrIDs[IDX_TOTAL_COUNT];
|
|
jsval mStrJSVals[IDX_TOTAL_COUNT];
|
|
|
|
nsXPConnect* mXPConnect;
|
|
JSRuntime* mJSRuntime;
|
|
XPCJSContextStack* mJSContextStack;
|
|
JSContext* mJSCycleCollectionContext;
|
|
XPCCallContext* mCallContext;
|
|
AutoMarkingPtr* mAutoRoots;
|
|
jsid mResolveName;
|
|
XPCWrappedNative* mResolvingWrapper;
|
|
JSObject2WrappedJSMap* mWrappedJSMap;
|
|
IID2WrappedJSClassMap* mWrappedJSClassMap;
|
|
IID2NativeInterfaceMap* mIID2NativeInterfaceMap;
|
|
ClassInfo2NativeSetMap* mClassInfo2NativeSetMap;
|
|
NativeSetMap* mNativeSetMap;
|
|
IID2ThisTranslatorMap* mThisTranslatorMap;
|
|
XPCNativeScriptableSharedMap* mNativeScriptableSharedMap;
|
|
XPCWrappedNativeProtoMap* mDyingWrappedNativeProtoMap;
|
|
XPCWrappedNativeProtoMap* mDetachedWrappedNativeProtoMap;
|
|
XPCCompartmentSet mCompartmentSet;
|
|
XPCLock* mMapLock;
|
|
PRThread* mThreadRunningGC;
|
|
nsTArray<nsXPCWrappedJS*> mWrappedJSToReleaseArray;
|
|
nsTArray<nsISupports*> mNativesToReleaseArray;
|
|
JSBool mDoingFinalization;
|
|
XPCRootSetElem *mVariantRoots;
|
|
XPCRootSetElem *mWrappedJSRoots;
|
|
XPCRootSetElem *mObjectHolderRoots;
|
|
JSDHashTable mJSHolders;
|
|
PRLock *mWatchdogLock;
|
|
PRCondVar *mWatchdogWakeup;
|
|
PRThread *mWatchdogThread;
|
|
nsTArray<JSGCCallback> extraGCCallbacks;
|
|
bool mWatchdogHibernating;
|
|
PRTime mLastActiveTime; // -1 if active NOW
|
|
|
|
nsCOMPtr<nsIException> mPendingException;
|
|
nsCOMPtr<nsIExceptionManager> mExceptionManager;
|
|
bool mExceptionManagerNotAvailable;
|
|
|
|
friend class AutoLockWatchdog;
|
|
};
|
|
|
|
/***************************************************************************/
|
|
/***************************************************************************/
|
|
// XPCContext is mostly a dumb class to hold JSContext specific data and
|
|
// maps that let us find wrappers created for the given JSContext.
|
|
|
|
// no virtuals
|
|
class XPCContext
|
|
{
|
|
friend class XPCJSRuntime;
|
|
public:
|
|
static XPCContext* GetXPCContext(JSContext* aJSContext)
|
|
{
|
|
NS_ASSERTION(JS_GetSecondContextPrivate(aJSContext), "should already have XPCContext");
|
|
return static_cast<XPCContext *>(JS_GetSecondContextPrivate(aJSContext));
|
|
}
|
|
|
|
XPCJSRuntime* GetRuntime() const {return mRuntime;}
|
|
JSContext* GetJSContext() const {return mJSContext;}
|
|
|
|
enum LangType {LANG_UNKNOWN, LANG_JS, LANG_NATIVE};
|
|
|
|
LangType GetCallingLangType() const
|
|
{
|
|
return mCallingLangType;
|
|
}
|
|
LangType SetCallingLangType(LangType lt)
|
|
{
|
|
LangType tmp = mCallingLangType;
|
|
mCallingLangType = lt;
|
|
return tmp;
|
|
}
|
|
JSBool CallerTypeIsJavaScript() const
|
|
{
|
|
return LANG_JS == mCallingLangType;
|
|
}
|
|
JSBool CallerTypeIsNative() const
|
|
{
|
|
return LANG_NATIVE == mCallingLangType;
|
|
}
|
|
JSBool CallerTypeIsKnown() const
|
|
{
|
|
return LANG_UNKNOWN != mCallingLangType;
|
|
}
|
|
|
|
nsresult GetException(nsIException** e)
|
|
{
|
|
NS_IF_ADDREF(mException);
|
|
*e = mException;
|
|
return NS_OK;
|
|
}
|
|
void SetException(nsIException* e)
|
|
{
|
|
NS_IF_ADDREF(e);
|
|
NS_IF_RELEASE(mException);
|
|
mException = e;
|
|
}
|
|
|
|
nsresult GetLastResult() {return mLastResult;}
|
|
void SetLastResult(nsresult rc) {mLastResult = rc;}
|
|
|
|
nsresult GetPendingResult() {return mPendingResult;}
|
|
void SetPendingResult(nsresult rc) {mPendingResult = rc;}
|
|
|
|
nsIXPCSecurityManager* GetSecurityManager() const
|
|
{return mSecurityManager;}
|
|
void SetSecurityManager(nsIXPCSecurityManager* aSecurityManager)
|
|
{mSecurityManager = aSecurityManager;}
|
|
|
|
PRUint16 GetSecurityManagerFlags() const
|
|
{return mSecurityManagerFlags;}
|
|
void SetSecurityManagerFlags(PRUint16 f)
|
|
{mSecurityManagerFlags = f;}
|
|
|
|
nsIXPCSecurityManager* GetAppropriateSecurityManager(PRUint16 flags) const
|
|
{
|
|
NS_ASSERTION(CallerTypeIsKnown(),"missing caller type set somewhere");
|
|
if (!CallerTypeIsJavaScript())
|
|
return nsnull;
|
|
if (mSecurityManager) {
|
|
if (flags & mSecurityManagerFlags)
|
|
return mSecurityManager;
|
|
} else {
|
|
nsIXPCSecurityManager* mgr;
|
|
nsXPConnect* xpc = mRuntime->GetXPConnect();
|
|
mgr = xpc->GetDefaultSecurityManager();
|
|
if (mgr && (flags & xpc->GetDefaultSecurityManagerFlags()))
|
|
return mgr;
|
|
}
|
|
return nsnull;
|
|
}
|
|
|
|
void DebugDump(PRInt16 depth);
|
|
void AddScope(PRCList *scope) { PR_INSERT_AFTER(scope, &mScopes); }
|
|
void RemoveScope(PRCList *scope) { PR_REMOVE_LINK(scope); }
|
|
|
|
~XPCContext();
|
|
|
|
private:
|
|
XPCContext(); // no implementation
|
|
XPCContext(XPCJSRuntime* aRuntime, JSContext* aJSContext);
|
|
|
|
static XPCContext* newXPCContext(XPCJSRuntime* aRuntime,
|
|
JSContext* aJSContext);
|
|
private:
|
|
XPCJSRuntime* mRuntime;
|
|
JSContext* mJSContext;
|
|
nsresult mLastResult;
|
|
nsresult mPendingResult;
|
|
nsIXPCSecurityManager* mSecurityManager;
|
|
nsIException* mException;
|
|
LangType mCallingLangType;
|
|
PRUint16 mSecurityManagerFlags;
|
|
|
|
// A linked list of scopes to notify when we are destroyed.
|
|
PRCList mScopes;
|
|
};
|
|
|
|
/***************************************************************************/
|
|
|
|
#define NATIVE_CALLER XPCContext::LANG_NATIVE
|
|
#define JS_CALLER XPCContext::LANG_JS
|
|
|
|
// class to export a JSString as an const nsAString, no refcounting :(
|
|
class XPCReadableJSStringWrapper : public nsDependentString
|
|
{
|
|
public:
|
|
typedef nsDependentString::char_traits char_traits;
|
|
|
|
XPCReadableJSStringWrapper(const PRUnichar *chars, size_t length) :
|
|
nsDependentString(chars, length)
|
|
{ }
|
|
|
|
XPCReadableJSStringWrapper() :
|
|
nsDependentString(char_traits::sEmptyBuffer, char_traits::sEmptyBuffer)
|
|
{ SetIsVoid(true); }
|
|
|
|
JSBool init(JSContext* aContext, JSString* str)
|
|
{
|
|
size_t length;
|
|
const jschar* chars = JS_GetStringCharsZAndLength(aContext, str, &length);
|
|
if (!chars)
|
|
return false;
|
|
|
|
NS_ASSERTION(IsEmpty(), "init() on initialized string");
|
|
new(static_cast<nsDependentString *>(this)) nsDependentString(chars, length);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
// No virtuals
|
|
// XPCCallContext is ALWAYS declared as a local variable in some function;
|
|
// i.e. instance lifetime is always controled by some C++ function returning.
|
|
//
|
|
// These things are created frequently in many places. We *intentionally* do
|
|
// not inialialize all members in order to save on construction overhead.
|
|
// Some constructor pass more valid params than others. We init what must be
|
|
// init'd and leave other members undefined. In debug builds the accessors
|
|
// use a CHECK_STATE macro to track whether or not the object is in a valid
|
|
// state to answer the question a caller might be asking. As long as this
|
|
// class is maintained correctly it can do its job without a bunch of added
|
|
// overhead from useless initializations and non-DEBUG error checking.
|
|
//
|
|
// Note that most accessors are inlined.
|
|
|
|
class XPCCallContext : public nsAXPCNativeCallContext
|
|
{
|
|
public:
|
|
NS_IMETHOD GetCallee(nsISupports **aResult);
|
|
NS_IMETHOD GetCalleeMethodIndex(PRUint16 *aResult);
|
|
NS_IMETHOD GetCalleeWrapper(nsIXPConnectWrappedNative **aResult);
|
|
NS_IMETHOD GetJSContext(JSContext **aResult);
|
|
NS_IMETHOD GetArgc(PRUint32 *aResult);
|
|
NS_IMETHOD GetArgvPtr(jsval **aResult);
|
|
NS_IMETHOD GetCalleeInterface(nsIInterfaceInfo **aResult);
|
|
NS_IMETHOD GetCalleeClassInfo(nsIClassInfo **aResult);
|
|
NS_IMETHOD GetPreviousCallContext(nsAXPCNativeCallContext **aResult);
|
|
NS_IMETHOD GetLanguage(PRUint16 *aResult);
|
|
|
|
enum {NO_ARGS = (unsigned) -1};
|
|
|
|
XPCCallContext(XPCContext::LangType callerLanguage,
|
|
JSContext* cx = nsnull,
|
|
JSObject* obj = nsnull,
|
|
JSObject* funobj = nsnull,
|
|
jsid id = JSID_VOID,
|
|
unsigned argc = NO_ARGS,
|
|
jsval *argv = nsnull,
|
|
jsval *rval = nsnull);
|
|
|
|
virtual ~XPCCallContext();
|
|
|
|
inline JSBool IsValid() const ;
|
|
|
|
inline nsXPConnect* GetXPConnect() const ;
|
|
inline XPCJSRuntime* GetRuntime() const ;
|
|
inline XPCContext* GetXPCContext() const ;
|
|
inline JSContext* GetJSContext() const ;
|
|
inline JSBool GetContextPopRequired() const ;
|
|
inline XPCContext::LangType GetCallerLanguage() const ;
|
|
inline XPCContext::LangType GetPrevCallerLanguage() const ;
|
|
inline XPCCallContext* GetPrevCallContext() const ;
|
|
|
|
/*
|
|
* The 'scope for new JSObjects' will be the scope for objects created when
|
|
* carrying out a JS/C++ call. This member is only available if HAVE_SCOPE.
|
|
* The object passed to the ccx constructor is used as the scope for new
|
|
* JSObjects. However, this object is also queried for a wrapper, so
|
|
* clients that don't want a wrapper (and thus pass NULL to the ccx
|
|
* constructor) need to manually call SetScopeForNewJSObjects.
|
|
*/
|
|
inline JSObject* GetScopeForNewJSObjects() const ;
|
|
inline void SetScopeForNewJSObjects(JSObject *obj) ;
|
|
|
|
inline JSObject* GetFlattenedJSObject() const ;
|
|
inline nsISupports* GetIdentityObject() const ;
|
|
inline XPCWrappedNative* GetWrapper() const ;
|
|
inline XPCWrappedNativeProto* GetProto() const ;
|
|
|
|
inline JSBool CanGetTearOff() const ;
|
|
inline XPCWrappedNativeTearOff* GetTearOff() const ;
|
|
|
|
inline XPCNativeScriptableInfo* GetScriptableInfo() const ;
|
|
inline JSBool CanGetSet() const ;
|
|
inline XPCNativeSet* GetSet() const ;
|
|
inline JSBool CanGetInterface() const ;
|
|
inline XPCNativeInterface* GetInterface() const ;
|
|
inline XPCNativeMember* GetMember() const ;
|
|
inline JSBool HasInterfaceAndMember() const ;
|
|
inline jsid GetName() const ;
|
|
inline JSBool GetStaticMemberIsLocal() const ;
|
|
inline unsigned GetArgc() const ;
|
|
inline jsval* GetArgv() const ;
|
|
inline jsval* GetRetVal() const ;
|
|
|
|
inline PRUint16 GetMethodIndex() const ;
|
|
inline void SetMethodIndex(PRUint16 index) ;
|
|
|
|
inline JSBool GetDestroyJSContextInDestructor() const;
|
|
inline void SetDestroyJSContextInDestructor(JSBool b);
|
|
|
|
inline jsid GetResolveName() const;
|
|
inline jsid SetResolveName(jsid name);
|
|
|
|
inline XPCWrappedNative* GetResolvingWrapper() const;
|
|
inline XPCWrappedNative* SetResolvingWrapper(XPCWrappedNative* w);
|
|
|
|
inline void SetRetVal(jsval val);
|
|
|
|
void SetName(jsid name);
|
|
void SetArgsAndResultPtr(unsigned argc, jsval *argv, jsval *rval);
|
|
void SetCallInfo(XPCNativeInterface* iface, XPCNativeMember* member,
|
|
JSBool isSetter);
|
|
|
|
nsresult CanCallNow();
|
|
|
|
void SystemIsBeingShutDown();
|
|
|
|
operator JSContext*() const {return GetJSContext();}
|
|
|
|
XPCReadableJSStringWrapper *NewStringWrapper(const PRUnichar *str, PRUint32 len);
|
|
void DeleteString(nsAString *string);
|
|
|
|
private:
|
|
|
|
// no copy ctor or assignment allowed
|
|
XPCCallContext(const XPCCallContext& r); // not implemented
|
|
XPCCallContext& operator= (const XPCCallContext& r); // not implemented
|
|
|
|
friend class XPCLazyCallContext;
|
|
XPCCallContext(XPCContext::LangType callerLanguage,
|
|
JSContext* cx,
|
|
JSBool callBeginRequest,
|
|
JSObject* obj,
|
|
JSObject* flattenedJSObject,
|
|
XPCWrappedNative* wn,
|
|
XPCWrappedNativeTearOff* tearoff);
|
|
|
|
enum WrapperInitOptions {
|
|
WRAPPER_PASSED_TO_CONSTRUCTOR,
|
|
INIT_SHOULD_LOOKUP_WRAPPER
|
|
};
|
|
|
|
void Init(XPCContext::LangType callerLanguage,
|
|
JSBool callBeginRequest,
|
|
JSObject* obj,
|
|
JSObject* funobj,
|
|
WrapperInitOptions wrapperInitOptions,
|
|
jsid name,
|
|
unsigned argc,
|
|
jsval *argv,
|
|
jsval *rval);
|
|
|
|
private:
|
|
// posible values for mState
|
|
enum State {
|
|
INIT_FAILED,
|
|
SYSTEM_SHUTDOWN,
|
|
HAVE_CONTEXT,
|
|
HAVE_SCOPE,
|
|
HAVE_OBJECT,
|
|
HAVE_NAME,
|
|
HAVE_ARGS,
|
|
READY_TO_CALL,
|
|
CALL_DONE
|
|
};
|
|
|
|
#ifdef DEBUG
|
|
inline void CHECK_STATE(int s) const {NS_ASSERTION(mState >= s, "bad state");}
|
|
#else
|
|
#define CHECK_STATE(s) ((void)0)
|
|
#endif
|
|
|
|
private:
|
|
State mState;
|
|
|
|
nsXPConnect* mXPC;
|
|
|
|
XPCContext* mXPCContext;
|
|
JSContext* mJSContext;
|
|
JSBool mContextPopRequired;
|
|
JSBool mDestroyJSContextInDestructor;
|
|
|
|
XPCContext::LangType mCallerLanguage;
|
|
|
|
// ctor does not necessarily init the following. BEWARE!
|
|
|
|
XPCContext::LangType mPrevCallerLanguage;
|
|
|
|
XPCCallContext* mPrevCallContext;
|
|
|
|
JSObject* mScopeForNewJSObjects;
|
|
JSObject* mFlattenedJSObject;
|
|
XPCWrappedNative* mWrapper;
|
|
XPCWrappedNativeTearOff* mTearOff;
|
|
|
|
XPCNativeScriptableInfo* mScriptableInfo;
|
|
|
|
XPCNativeSet* mSet;
|
|
XPCNativeInterface* mInterface;
|
|
XPCNativeMember* mMember;
|
|
|
|
jsid mName;
|
|
JSBool mStaticMemberIsLocal;
|
|
|
|
unsigned mArgc;
|
|
jsval* mArgv;
|
|
jsval* mRetVal;
|
|
|
|
PRUint16 mMethodIndex;
|
|
|
|
#define XPCCCX_STRING_CACHE_SIZE 2
|
|
|
|
// String wrapper entry, holds a string, and a boolean that tells
|
|
// whether the string is in use or not.
|
|
//
|
|
// NB: The string is not stored by value so that we avoid the cost of
|
|
// construction/destruction.
|
|
struct StringWrapperEntry
|
|
{
|
|
StringWrapperEntry() : mInUse(false) { }
|
|
|
|
js::AlignedStorage2<XPCReadableJSStringWrapper> mString;
|
|
bool mInUse;
|
|
};
|
|
|
|
StringWrapperEntry mScratchStrings[XPCCCX_STRING_CACHE_SIZE];
|
|
};
|
|
|
|
class XPCLazyCallContext
|
|
{
|
|
public:
|
|
XPCLazyCallContext(XPCCallContext& ccx)
|
|
: mCallBeginRequest(DONT_CALL_BEGINREQUEST),
|
|
mCcx(&ccx),
|
|
mCcxToDestroy(nsnull)
|
|
#ifdef DEBUG
|
|
, mCx(nsnull)
|
|
, mCallerLanguage(JS_CALLER)
|
|
, mObj(nsnull)
|
|
, mFlattenedJSObject(nsnull)
|
|
, mWrapper(nsnull)
|
|
, mTearOff(nsnull)
|
|
#endif
|
|
{
|
|
}
|
|
XPCLazyCallContext(XPCContext::LangType callerLanguage, JSContext* cx,
|
|
JSObject* obj = nsnull,
|
|
JSObject* flattenedJSObject = nsnull,
|
|
XPCWrappedNative* wrapper = nsnull,
|
|
XPCWrappedNativeTearOff* tearoff = nsnull)
|
|
: mCallBeginRequest(callerLanguage == NATIVE_CALLER ?
|
|
CALL_BEGINREQUEST : DONT_CALL_BEGINREQUEST),
|
|
mCcx(nsnull),
|
|
mCcxToDestroy(nsnull),
|
|
mCx(cx),
|
|
mCallerLanguage(callerLanguage),
|
|
mObj(obj),
|
|
mFlattenedJSObject(flattenedJSObject),
|
|
mWrapper(wrapper),
|
|
mTearOff(tearoff)
|
|
{
|
|
NS_ASSERTION(cx, "Need a JS context!");
|
|
NS_ASSERTION(callerLanguage == NATIVE_CALLER ||
|
|
callerLanguage == JS_CALLER,
|
|
"Can't deal with unknown caller language!");
|
|
#ifdef DEBUG
|
|
AssertContextIsTopOfStack(cx);
|
|
#endif
|
|
}
|
|
~XPCLazyCallContext()
|
|
{
|
|
if (mCcxToDestroy)
|
|
mCcxToDestroy->~XPCCallContext();
|
|
else if (mCallBeginRequest == CALLED_BEGINREQUEST)
|
|
JS_EndRequest(mCx);
|
|
}
|
|
void SetWrapper(XPCWrappedNative* wrapper,
|
|
XPCWrappedNativeTearOff* tearoff);
|
|
void SetWrapper(JSObject* flattenedJSObject);
|
|
|
|
JSContext *GetJSContext()
|
|
{
|
|
if (mCcx)
|
|
return mCcx->GetJSContext();
|
|
|
|
if (mCallBeginRequest == CALL_BEGINREQUEST) {
|
|
JS_BeginRequest(mCx);
|
|
mCallBeginRequest = CALLED_BEGINREQUEST;
|
|
}
|
|
|
|
return mCx;
|
|
}
|
|
JSObject *GetScopeForNewJSObjects() const
|
|
{
|
|
if (mCcx)
|
|
return mCcx->GetScopeForNewJSObjects();
|
|
|
|
return xpc_UnmarkGrayObject(mObj);
|
|
}
|
|
void SetScopeForNewJSObjects(JSObject *obj)
|
|
{
|
|
if (mCcx) {
|
|
mCcx->SetScopeForNewJSObjects(obj);
|
|
return;
|
|
}
|
|
NS_ABORT_IF_FALSE(!mObj, "already set!");
|
|
mObj = obj;
|
|
}
|
|
JSObject *GetFlattenedJSObject() const
|
|
{
|
|
if (mCcx)
|
|
return mCcx->GetFlattenedJSObject();
|
|
|
|
return xpc_UnmarkGrayObject(mFlattenedJSObject);
|
|
}
|
|
XPCCallContext &GetXPCCallContext()
|
|
{
|
|
if (!mCcx) {
|
|
XPCCallContext *data = mData.addr();
|
|
mCcxToDestroy = mCcx =
|
|
new (data) XPCCallContext(mCallerLanguage, mCx,
|
|
mCallBeginRequest == CALL_BEGINREQUEST,
|
|
xpc_UnmarkGrayObject(mObj),
|
|
xpc_UnmarkGrayObject(mFlattenedJSObject),
|
|
mWrapper,
|
|
mTearOff);
|
|
if (!mCcx->IsValid()) {
|
|
NS_ERROR("This is not supposed to fail!");
|
|
}
|
|
}
|
|
|
|
return *mCcx;
|
|
}
|
|
|
|
private:
|
|
#ifdef DEBUG
|
|
static void AssertContextIsTopOfStack(JSContext* cx);
|
|
#endif
|
|
|
|
enum {
|
|
DONT_CALL_BEGINREQUEST,
|
|
CALL_BEGINREQUEST,
|
|
CALLED_BEGINREQUEST
|
|
} mCallBeginRequest;
|
|
|
|
XPCCallContext *mCcx;
|
|
XPCCallContext *mCcxToDestroy;
|
|
JSContext *mCx;
|
|
XPCContext::LangType mCallerLanguage;
|
|
JSObject *mObj;
|
|
JSObject *mFlattenedJSObject;
|
|
XPCWrappedNative *mWrapper;
|
|
XPCWrappedNativeTearOff *mTearOff;
|
|
mozilla::AlignedStorage2<XPCCallContext> mData;
|
|
};
|
|
|
|
/***************************************************************************
|
|
****************************************************************************
|
|
*
|
|
* Core classes for wrapped native objects for use from JavaScript...
|
|
*
|
|
****************************************************************************
|
|
***************************************************************************/
|
|
|
|
// These are the various JSClasses and callbacks whose use that required
|
|
// visibility from more than one .cpp file.
|
|
|
|
struct XPCWrappedNativeJSClass;
|
|
extern XPCWrappedNativeJSClass XPC_WN_NoHelper_JSClass;
|
|
extern js::Class XPC_WN_NoMods_WithCall_Proto_JSClass;
|
|
extern js::Class XPC_WN_NoMods_NoCall_Proto_JSClass;
|
|
extern js::Class XPC_WN_ModsAllowed_WithCall_Proto_JSClass;
|
|
extern js::Class XPC_WN_ModsAllowed_NoCall_Proto_JSClass;
|
|
extern js::Class XPC_WN_Tearoff_JSClass;
|
|
extern js::Class XPC_WN_NoHelper_Proto_JSClass;
|
|
|
|
extern JSBool
|
|
XPC_WN_Equality(JSContext *cx, JSHandleObject obj, const jsval *v, JSBool *bp);
|
|
|
|
extern JSBool
|
|
XPC_WN_CallMethod(JSContext *cx, unsigned argc, jsval *vp);
|
|
|
|
extern JSBool
|
|
XPC_WN_GetterSetter(JSContext *cx, unsigned argc, jsval *vp);
|
|
|
|
extern JSBool
|
|
XPC_WN_JSOp_Enumerate(JSContext *cx, JSHandleObject obj, JSIterateOp enum_op,
|
|
jsval *statep, jsid *idp);
|
|
|
|
extern JSType
|
|
XPC_WN_JSOp_TypeOf_Object(JSContext *cx, JSHandleObject obj);
|
|
|
|
extern JSType
|
|
XPC_WN_JSOp_TypeOf_Function(JSContext *cx, JSHandleObject obj);
|
|
|
|
extern void
|
|
XPC_WN_JSOp_Clear(JSContext *cx, JSHandleObject obj);
|
|
|
|
extern JSObject*
|
|
XPC_WN_JSOp_ThisObject(JSContext *cx, JSHandleObject obj);
|
|
|
|
// Macros to initialize Object or Function like XPC_WN classes
|
|
#define XPC_WN_WithCall_ObjectOps \
|
|
{ \
|
|
nsnull, /* lookupGeneric */ \
|
|
nsnull, /* lookupProperty */ \
|
|
nsnull, /* lookupElement */ \
|
|
nsnull, /* lookupSpecial */ \
|
|
nsnull, /* defineGeneric */ \
|
|
nsnull, /* defineProperty */ \
|
|
nsnull, /* defineElement */ \
|
|
nsnull, /* defineSpecial */ \
|
|
nsnull, /* getGeneric */ \
|
|
nsnull, /* getProperty */ \
|
|
nsnull, /* getElement */ \
|
|
nsnull, /* getElementIfPresent */ \
|
|
nsnull, /* getSpecial */ \
|
|
nsnull, /* setGeneric */ \
|
|
nsnull, /* setProperty */ \
|
|
nsnull, /* setElement */ \
|
|
nsnull, /* setSpecial */ \
|
|
nsnull, /* getGenericAttributes */ \
|
|
nsnull, /* getAttributes */ \
|
|
nsnull, /* getElementAttributes */ \
|
|
nsnull, /* getSpecialAttributes */ \
|
|
nsnull, /* setGenericAttributes */ \
|
|
nsnull, /* setAttributes */ \
|
|
nsnull, /* setElementAttributes */ \
|
|
nsnull, /* setSpecialAttributes */ \
|
|
nsnull, /* deleteProperty */ \
|
|
nsnull, /* deleteElement */ \
|
|
nsnull, /* deleteSpecial */ \
|
|
XPC_WN_JSOp_Enumerate, \
|
|
XPC_WN_JSOp_TypeOf_Function, \
|
|
XPC_WN_JSOp_ThisObject, \
|
|
XPC_WN_JSOp_Clear \
|
|
}
|
|
|
|
#define XPC_WN_NoCall_ObjectOps \
|
|
{ \
|
|
nsnull, /* lookupGeneric */ \
|
|
nsnull, /* lookupProperty */ \
|
|
nsnull, /* lookupElement */ \
|
|
nsnull, /* lookupSpecial */ \
|
|
nsnull, /* defineGeneric */ \
|
|
nsnull, /* defineProperty */ \
|
|
nsnull, /* defineElement */ \
|
|
nsnull, /* defineSpecial */ \
|
|
nsnull, /* getGeneric */ \
|
|
nsnull, /* getProperty */ \
|
|
nsnull, /* getElement */ \
|
|
nsnull, /* getElementIfPresent */ \
|
|
nsnull, /* getSpecial */ \
|
|
nsnull, /* setGeneric */ \
|
|
nsnull, /* setProperty */ \
|
|
nsnull, /* setElement */ \
|
|
nsnull, /* setSpecial */ \
|
|
nsnull, /* getGenericAttributes */ \
|
|
nsnull, /* getAttributes */ \
|
|
nsnull, /* getElementAttributes */ \
|
|
nsnull, /* getSpecialAttributes */ \
|
|
nsnull, /* setGenericAttributes */ \
|
|
nsnull, /* setAttributes */ \
|
|
nsnull, /* setElementAttributes */ \
|
|
nsnull, /* setSpecialAttributes */ \
|
|
nsnull, /* deleteProperty */ \
|
|
nsnull, /* deleteElement */ \
|
|
nsnull, /* deleteSpecial */ \
|
|
XPC_WN_JSOp_Enumerate, \
|
|
XPC_WN_JSOp_TypeOf_Object, \
|
|
XPC_WN_JSOp_ThisObject, \
|
|
XPC_WN_JSOp_Clear \
|
|
}
|
|
|
|
// Maybe this macro should check for class->enumerate ==
|
|
// XPC_WN_Shared_Proto_Enumerate or something rather than checking for
|
|
// 4 classes?
|
|
static inline bool IS_PROTO_CLASS(js::Class *clazz)
|
|
{
|
|
return clazz == &XPC_WN_NoMods_WithCall_Proto_JSClass ||
|
|
clazz == &XPC_WN_NoMods_NoCall_Proto_JSClass ||
|
|
clazz == &XPC_WN_ModsAllowed_WithCall_Proto_JSClass ||
|
|
clazz == &XPC_WN_ModsAllowed_NoCall_Proto_JSClass;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
namespace XPCWrapper {
|
|
|
|
enum WrapperType {
|
|
UNKNOWN = 0,
|
|
NONE = 0,
|
|
XPCNW_IMPLICIT = 1 << 0,
|
|
XPCNW_EXPLICIT = 1 << 1,
|
|
XPCNW = (XPCNW_IMPLICIT | XPCNW_EXPLICIT),
|
|
SJOW = 1 << 2,
|
|
// SJOW must be the last wrapper type that can be returned to chrome.
|
|
|
|
XOW = 1 << 3,
|
|
COW = 1 << 4,
|
|
SOW = 1 << 5
|
|
};
|
|
|
|
}
|
|
|
|
/***************************************************************************/
|
|
// XPCWrappedNativeScope is one-to-one with a JS global object.
|
|
|
|
class XPCWrappedNativeScope : public PRCList
|
|
{
|
|
public:
|
|
|
|
static XPCWrappedNativeScope*
|
|
GetNewOrUsed(XPCCallContext& ccx, JSObject* aGlobal, nsISupports* aNative = nsnull);
|
|
|
|
XPCJSRuntime*
|
|
GetRuntime() const {return mRuntime;}
|
|
|
|
Native2WrappedNativeMap*
|
|
GetWrappedNativeMap() const {return mWrappedNativeMap;}
|
|
|
|
ClassInfo2WrappedNativeProtoMap*
|
|
GetWrappedNativeProtoMap(JSBool aMainThreadOnly) const
|
|
{return aMainThreadOnly ?
|
|
mMainThreadWrappedNativeProtoMap :
|
|
mWrappedNativeProtoMap;}
|
|
|
|
nsXPCComponents*
|
|
GetComponents() const {return mComponents;}
|
|
|
|
JSObject*
|
|
GetGlobalJSObject() const
|
|
{return xpc_UnmarkGrayObject(mGlobalJSObject);}
|
|
|
|
JSObject*
|
|
GetGlobalJSObjectPreserveColor() const {return mGlobalJSObject;}
|
|
|
|
JSObject*
|
|
GetPrototypeJSObject() const
|
|
{return xpc_UnmarkGrayObject(mPrototypeJSObject);}
|
|
|
|
JSObject*
|
|
GetPrototypeJSObjectPreserveColor() const {return mPrototypeJSObject;}
|
|
|
|
// Getter for the prototype that we use for wrappers that have no
|
|
// helper.
|
|
JSObject*
|
|
GetPrototypeNoHelper(XPCCallContext& ccx);
|
|
|
|
nsIPrincipal*
|
|
GetPrincipal() const
|
|
{return mScriptObjectPrincipal ?
|
|
mScriptObjectPrincipal->GetPrincipal() : nsnull;}
|
|
|
|
void RemoveWrappedNativeProtos();
|
|
|
|
static XPCWrappedNativeScope*
|
|
FindInJSObjectScope(JSContext* cx, JSObject* obj,
|
|
JSBool OKIfNotInitialized = false,
|
|
XPCJSRuntime* runtime = nsnull);
|
|
|
|
static XPCWrappedNativeScope*
|
|
FindInJSObjectScope(XPCCallContext& ccx, JSObject* obj,
|
|
JSBool OKIfNotInitialized = false)
|
|
{
|
|
return FindInJSObjectScope(ccx, obj, OKIfNotInitialized,
|
|
ccx.GetRuntime());
|
|
}
|
|
|
|
static void
|
|
SystemIsBeingShutDown();
|
|
|
|
static void
|
|
TraceJS(JSTracer* trc, XPCJSRuntime* rt);
|
|
|
|
void TraceSelf(JSTracer *trc) {
|
|
JSObject *obj = GetGlobalJSObjectPreserveColor();
|
|
MOZ_ASSERT(obj);
|
|
JS_CALL_OBJECT_TRACER(trc, obj, "XPCWrappedNativeScope::mGlobalJSObject");
|
|
|
|
JSObject *proto = GetPrototypeJSObjectPreserveColor();
|
|
if (proto)
|
|
JS_CALL_OBJECT_TRACER(trc, proto, "XPCWrappedNativeScope::mPrototypeJSObject");
|
|
}
|
|
|
|
static void
|
|
SuspectAllWrappers(XPCJSRuntime* rt, nsCycleCollectionTraversalCallback &cb);
|
|
|
|
static void
|
|
StartFinalizationPhaseOfGC(JSFreeOp *fop, XPCJSRuntime* rt);
|
|
|
|
static void
|
|
FinishedFinalizationPhaseOfGC();
|
|
|
|
static void
|
|
MarkAllWrappedNativesAndProtos();
|
|
|
|
static nsresult
|
|
ClearAllWrappedNativeSecurityPolicies(XPCCallContext& ccx);
|
|
|
|
#ifdef DEBUG
|
|
static void
|
|
ASSERT_NoInterfaceSetsAreMarked();
|
|
#endif
|
|
|
|
static void
|
|
SweepAllWrappedNativeTearOffs();
|
|
|
|
static void
|
|
DebugDumpAllScopes(PRInt16 depth);
|
|
|
|
void
|
|
DebugDump(PRInt16 depth);
|
|
|
|
static size_t
|
|
SizeOfAllScopesIncludingThis(nsMallocSizeOfFun mallocSizeOf);
|
|
|
|
size_t
|
|
SizeOfIncludingThis(nsMallocSizeOfFun mallocSizeOf);
|
|
|
|
JSBool
|
|
IsValid() const {return mRuntime != nsnull;}
|
|
|
|
static JSBool
|
|
IsDyingScope(XPCWrappedNativeScope *scope);
|
|
|
|
void SetComponents(nsXPCComponents* aComponents);
|
|
nsXPCComponents *GetComponents();
|
|
void SetGlobal(XPCCallContext& ccx, JSObject* aGlobal, nsISupports* aNative);
|
|
|
|
static void InitStatics() { gScopes = nsnull; gDyingScopes = nsnull; }
|
|
|
|
XPCContext *GetContext() { return mContext; }
|
|
void ClearContext() { mContext = nsnull; }
|
|
|
|
nsDataHashtable<nsDepCharHashKey, JSObject*>& GetCachedDOMPrototypes()
|
|
{
|
|
return mCachedDOMPrototypes;
|
|
}
|
|
|
|
static XPCWrappedNativeScope *GetNativeScope(JSObject *obj)
|
|
{
|
|
MOZ_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_XPCONNECT_GLOBAL);
|
|
|
|
const js::Value &v = js::GetObjectSlot(obj, JSCLASS_GLOBAL_SLOT_COUNT);
|
|
return v.isUndefined()
|
|
? nsnull
|
|
: static_cast<XPCWrappedNativeScope *>(v.toPrivate());
|
|
}
|
|
void TraceDOMPrototypes(JSTracer *trc);
|
|
|
|
JSBool NewDOMBindingsEnabled()
|
|
{
|
|
return mNewDOMBindingsEnabled;
|
|
}
|
|
|
|
JSBool ExperimentalBindingsEnabled()
|
|
{
|
|
return mExperimentalBindingsEnabled;
|
|
}
|
|
|
|
protected:
|
|
XPCWrappedNativeScope(XPCCallContext& ccx, JSObject* aGlobal, nsISupports* aNative);
|
|
virtual ~XPCWrappedNativeScope();
|
|
|
|
static void KillDyingScopes();
|
|
|
|
XPCWrappedNativeScope(); // not implemented
|
|
|
|
private:
|
|
static XPCWrappedNativeScope* gScopes;
|
|
static XPCWrappedNativeScope* gDyingScopes;
|
|
|
|
XPCJSRuntime* mRuntime;
|
|
Native2WrappedNativeMap* mWrappedNativeMap;
|
|
ClassInfo2WrappedNativeProtoMap* mWrappedNativeProtoMap;
|
|
ClassInfo2WrappedNativeProtoMap* mMainThreadWrappedNativeProtoMap;
|
|
nsRefPtr<nsXPCComponents> mComponents;
|
|
XPCWrappedNativeScope* mNext;
|
|
// The JS global object for this scope. If non-null, this will be the
|
|
// default parent for the XPCWrappedNatives that have us as the scope,
|
|
// unless a PreCreate hook overrides it. Note that this _may_ be null (see
|
|
// constructor).
|
|
js::ObjectPtr mGlobalJSObject;
|
|
|
|
// Cached value of Object.prototype
|
|
js::ObjectPtr mPrototypeJSObject;
|
|
// Prototype to use for wrappers with no helper.
|
|
JSObject* mPrototypeNoHelper;
|
|
|
|
XPCContext* mContext;
|
|
|
|
// The script object principal instance corresponding to our current global
|
|
// JS object.
|
|
// XXXbz what happens if someone calls JS_SetPrivate on mGlobalJSObject.
|
|
// How do we deal? Do we need to? I suspect this isn't worth worrying
|
|
// about, since all of our scope objects are verified as not doing that.
|
|
nsIScriptObjectPrincipal* mScriptObjectPrincipal;
|
|
|
|
nsDataHashtable<nsDepCharHashKey, JSObject*> mCachedDOMPrototypes;
|
|
|
|
JSBool mNewDOMBindingsEnabled;
|
|
JSBool mExperimentalBindingsEnabled;
|
|
};
|
|
|
|
/***************************************************************************/
|
|
// XPCNativeMember represents a single idl declared method, attribute or
|
|
// constant.
|
|
|
|
// Tight. No virtual methods. Can be bitwise copied (until any resolution done).
|
|
|
|
class XPCNativeMember
|
|
{
|
|
public:
|
|
static JSBool GetCallInfo(XPCCallContext& ccx,
|
|
JSObject* funobj,
|
|
XPCNativeInterface** pInterface,
|
|
XPCNativeMember** pMember);
|
|
|
|
jsid GetName() const {return mName;}
|
|
|
|
PRUint16 GetIndex() const {return mIndex;}
|
|
|
|
JSBool GetConstantValue(XPCCallContext& ccx, XPCNativeInterface* iface,
|
|
jsval* pval)
|
|
{NS_ASSERTION(IsConstant(),
|
|
"Only call this if you're sure this is a constant!");
|
|
return Resolve(ccx, iface, nsnull, pval);}
|
|
|
|
JSBool NewFunctionObject(XPCCallContext& ccx, XPCNativeInterface* iface,
|
|
JSObject *parent, jsval* pval);
|
|
|
|
JSBool IsMethod() const
|
|
{return 0 != (mFlags & METHOD);}
|
|
|
|
JSBool IsConstant() const
|
|
{return 0 != (mFlags & CONSTANT);}
|
|
|
|
JSBool IsAttribute() const
|
|
{return 0 != (mFlags & GETTER);}
|
|
|
|
JSBool IsWritableAttribute() const
|
|
{return 0 != (mFlags & SETTER_TOO);}
|
|
|
|
JSBool IsReadOnlyAttribute() const
|
|
{return IsAttribute() && !IsWritableAttribute();}
|
|
|
|
|
|
void SetName(jsid a) {mName = a;}
|
|
|
|
void SetMethod(PRUint16 index)
|
|
{mFlags = METHOD; mIndex = index;}
|
|
|
|
void SetConstant(PRUint16 index)
|
|
{mFlags = CONSTANT; mIndex = index;}
|
|
|
|
void SetReadOnlyAttribute(PRUint16 index)
|
|
{mFlags = GETTER; mIndex = index;}
|
|
|
|
void SetWritableAttribute()
|
|
{NS_ASSERTION(mFlags == GETTER,"bad"); mFlags = GETTER | SETTER_TOO;}
|
|
|
|
/* default ctor - leave random contents */
|
|
XPCNativeMember() {MOZ_COUNT_CTOR(XPCNativeMember);}
|
|
~XPCNativeMember() {MOZ_COUNT_DTOR(XPCNativeMember);}
|
|
|
|
private:
|
|
JSBool Resolve(XPCCallContext& ccx, XPCNativeInterface* iface,
|
|
JSObject *parent, jsval *vp);
|
|
|
|
enum {
|
|
METHOD = 0x01,
|
|
CONSTANT = 0x02,
|
|
GETTER = 0x04,
|
|
SETTER_TOO = 0x08
|
|
};
|
|
|
|
private:
|
|
// our only data...
|
|
jsid mName;
|
|
PRUint16 mIndex;
|
|
PRUint16 mFlags;
|
|
};
|
|
|
|
/***************************************************************************/
|
|
// XPCNativeInterface represents a single idl declared interface. This is
|
|
// primarily the set of XPCNativeMembers.
|
|
|
|
// Tight. No virtual methods.
|
|
|
|
class XPCNativeInterface
|
|
{
|
|
public:
|
|
static XPCNativeInterface* GetNewOrUsed(XPCCallContext& ccx,
|
|
const nsIID* iid);
|
|
static XPCNativeInterface* GetNewOrUsed(XPCCallContext& ccx,
|
|
nsIInterfaceInfo* info);
|
|
static XPCNativeInterface* GetNewOrUsed(XPCCallContext& ccx,
|
|
const char* name);
|
|
static XPCNativeInterface* GetISupports(XPCCallContext& ccx);
|
|
|
|
inline nsIInterfaceInfo* GetInterfaceInfo() const {return mInfo.get();}
|
|
inline jsid GetName() const {return mName;}
|
|
|
|
inline const nsIID* GetIID() const;
|
|
inline const char* GetNameString() const;
|
|
inline XPCNativeMember* FindMember(jsid name) const;
|
|
|
|
inline JSBool HasAncestor(const nsIID* iid) const;
|
|
|
|
PRUint16 GetMemberCount() const {
|
|
return mMemberCount;
|
|
}
|
|
XPCNativeMember* GetMemberAt(PRUint16 i) {
|
|
NS_ASSERTION(i < mMemberCount, "bad index");
|
|
return &mMembers[i];
|
|
}
|
|
|
|
void DebugDump(PRInt16 depth);
|
|
|
|
#define XPC_NATIVE_IFACE_MARK_FLAG ((PRUint16)JS_BIT(15)) // only high bit of 16 is set
|
|
|
|
void Mark() {
|
|
mMarked = 1;
|
|
}
|
|
|
|
void Unmark() {
|
|
mMarked = 0;
|
|
}
|
|
|
|
bool IsMarked() const {
|
|
return mMarked != 0;
|
|
}
|
|
|
|
// NOP. This is just here to make the AutoMarkingPtr code compile.
|
|
inline void TraceJS(JSTracer* trc) {}
|
|
inline void AutoTrace(JSTracer* trc) {}
|
|
|
|
static void DestroyInstance(XPCNativeInterface* inst);
|
|
|
|
size_t SizeOfIncludingThis(nsMallocSizeOfFun mallocSizeOf);
|
|
|
|
protected:
|
|
static XPCNativeInterface* NewInstance(XPCCallContext& ccx,
|
|
nsIInterfaceInfo* aInfo);
|
|
|
|
XPCNativeInterface(); // not implemented
|
|
XPCNativeInterface(nsIInterfaceInfo* aInfo, jsid aName)
|
|
: mInfo(aInfo), mName(aName), mMemberCount(0), mMarked(0)
|
|
{
|
|
MOZ_COUNT_CTOR(XPCNativeInterface);
|
|
}
|
|
~XPCNativeInterface() {
|
|
MOZ_COUNT_DTOR(XPCNativeInterface);
|
|
}
|
|
|
|
void* operator new(size_t, void* p) CPP_THROW_NEW {return p;}
|
|
|
|
XPCNativeInterface(const XPCNativeInterface& r); // not implemented
|
|
XPCNativeInterface& operator= (const XPCNativeInterface& r); // not implemented
|
|
|
|
private:
|
|
nsCOMPtr<nsIInterfaceInfo> mInfo;
|
|
jsid mName;
|
|
PRUint16 mMemberCount : 15;
|
|
PRUint16 mMarked : 1;
|
|
XPCNativeMember mMembers[1]; // always last - object sized for array
|
|
};
|
|
|
|
/***************************************************************************/
|
|
// XPCNativeSetKey is used to key a XPCNativeSet in a NativeSetMap.
|
|
|
|
class XPCNativeSetKey
|
|
{
|
|
public:
|
|
XPCNativeSetKey(XPCNativeSet* BaseSet = nsnull,
|
|
XPCNativeInterface* Addition = nsnull,
|
|
PRUint16 Position = 0)
|
|
: mIsAKey(IS_A_KEY), mPosition(Position), mBaseSet(BaseSet),
|
|
mAddition(Addition) {}
|
|
~XPCNativeSetKey() {}
|
|
|
|
XPCNativeSet* GetBaseSet() const {return mBaseSet;}
|
|
XPCNativeInterface* GetAddition() const {return mAddition;}
|
|
PRUint16 GetPosition() const {return mPosition;}
|
|
|
|
// This is a fun little hack...
|
|
// We build these keys only on the stack. We use them for lookup in
|
|
// NativeSetMap. Becasue we don't want to pay the cost of cloning a key and
|
|
// sticking it into the hashtable, when the XPCNativeSet actually
|
|
// gets added to the table the 'key' in the table is a pointer to the
|
|
// set itself and not this key. Our key compare function expects to get
|
|
// a key and a set. When we do external lookups in the map we pass in one
|
|
// of these keys and our compare function gets passed a key and a set.
|
|
// (see compare_NativeKeyToSet in xpcmaps.cpp). This is all well and good.
|
|
// Except, when the table decides to resize itself. Then it tries to use
|
|
// our compare function with the 'keys' that are in the hashtable (which are
|
|
// really XPCNativeSet objects and not XPCNativeSetKey objects!
|
|
//
|
|
// So, the hack is to have the compare function assume it is getting a
|
|
// XPCNativeSetKey pointer and call this IsAKey method. If that fails then
|
|
// it realises that it really has a XPCNativeSet pointer and deals with that
|
|
// fact. This is safe because we know that both of these classes have no
|
|
// virtual methods and their first data member is a PRUint16. We are
|
|
// confident that XPCNativeSet->mMemberCount will never be 0xffff.
|
|
|
|
JSBool IsAKey() const {return mIsAKey == IS_A_KEY;}
|
|
|
|
enum {IS_A_KEY = 0xffff};
|
|
|
|
// Allow shallow copy
|
|
|
|
private:
|
|
PRUint16 mIsAKey; // must be first data member
|
|
PRUint16 mPosition;
|
|
XPCNativeSet* mBaseSet;
|
|
XPCNativeInterface* mAddition;
|
|
};
|
|
|
|
/***************************************************************************/
|
|
// XPCNativeSet represents an ordered collection of XPCNativeInterface pointers.
|
|
|
|
class XPCNativeSet
|
|
{
|
|
public:
|
|
static XPCNativeSet* GetNewOrUsed(XPCCallContext& ccx, const nsIID* iid);
|
|
static XPCNativeSet* GetNewOrUsed(XPCCallContext& ccx,
|
|
nsIClassInfo* classInfo);
|
|
static XPCNativeSet* GetNewOrUsed(XPCCallContext& ccx,
|
|
XPCNativeSet* otherSet,
|
|
XPCNativeInterface* newInterface,
|
|
PRUint16 position);
|
|
|
|
// This generates a union set.
|
|
//
|
|
// If preserveFirstSetOrder is true, the elements from |firstSet| come first,
|
|
// followed by any non-duplicate items from |secondSet|. If false, the same
|
|
// algorithm is applied; but if we detect that |secondSet| is a superset of
|
|
// |firstSet|, we return |secondSet| without worrying about whether the
|
|
// ordering might differ from |firstSet|.
|
|
static XPCNativeSet* GetNewOrUsed(XPCCallContext& ccx,
|
|
XPCNativeSet* firstSet,
|
|
XPCNativeSet* secondSet,
|
|
bool preserveFirstSetOrder);
|
|
|
|
static void ClearCacheEntryForClassInfo(nsIClassInfo* classInfo);
|
|
|
|
inline JSBool FindMember(jsid name, XPCNativeMember** pMember,
|
|
PRUint16* pInterfaceIndex) const;
|
|
|
|
inline JSBool FindMember(jsid name, XPCNativeMember** pMember,
|
|
XPCNativeInterface** pInterface) const;
|
|
|
|
inline JSBool FindMember(jsid name,
|
|
XPCNativeMember** pMember,
|
|
XPCNativeInterface** pInterface,
|
|
XPCNativeSet* protoSet,
|
|
JSBool* pIsLocal) const;
|
|
|
|
inline JSBool HasInterface(XPCNativeInterface* aInterface) const;
|
|
inline JSBool HasInterfaceWithAncestor(XPCNativeInterface* aInterface) const;
|
|
inline JSBool HasInterfaceWithAncestor(const nsIID* iid) const;
|
|
|
|
inline XPCNativeInterface* FindInterfaceWithIID(const nsIID& iid) const;
|
|
|
|
inline XPCNativeInterface* FindNamedInterface(jsid name) const;
|
|
|
|
PRUint16 GetMemberCount() const {
|
|
return mMemberCount;
|
|
}
|
|
PRUint16 GetInterfaceCount() const {
|
|
return mInterfaceCount;
|
|
}
|
|
XPCNativeInterface **GetInterfaceArray() {
|
|
return mInterfaces;
|
|
}
|
|
|
|
XPCNativeInterface* GetInterfaceAt(PRUint16 i)
|
|
{NS_ASSERTION(i < mInterfaceCount, "bad index"); return mInterfaces[i];}
|
|
|
|
inline JSBool MatchesSetUpToInterface(const XPCNativeSet* other,
|
|
XPCNativeInterface* iface) const;
|
|
|
|
#define XPC_NATIVE_SET_MARK_FLAG ((PRUint16)JS_BIT(15)) // only high bit of 16 is set
|
|
|
|
inline void Mark();
|
|
|
|
// NOP. This is just here to make the AutoMarkingPtr code compile.
|
|
inline void TraceJS(JSTracer* trc) {}
|
|
inline void AutoTrace(JSTracer* trc) {}
|
|
|
|
private:
|
|
void MarkSelfOnly() {
|
|
mMarked = 1;
|
|
}
|
|
|
|
public:
|
|
void Unmark() {
|
|
mMarked = 0;
|
|
}
|
|
bool IsMarked() const {
|
|
return !!mMarked;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
inline void ASSERT_NotMarked();
|
|
#endif
|
|
|
|
void DebugDump(PRInt16 depth);
|
|
|
|
static void DestroyInstance(XPCNativeSet* inst);
|
|
|
|
size_t SizeOfIncludingThis(nsMallocSizeOfFun mallocSizeOf);
|
|
|
|
protected:
|
|
static XPCNativeSet* NewInstance(XPCCallContext& ccx,
|
|
XPCNativeInterface** array,
|
|
PRUint16 count);
|
|
static XPCNativeSet* NewInstanceMutate(XPCNativeSet* otherSet,
|
|
XPCNativeInterface* newInterface,
|
|
PRUint16 position);
|
|
XPCNativeSet()
|
|
: mMemberCount(0), mInterfaceCount(0), mMarked(0)
|
|
{
|
|
MOZ_COUNT_CTOR(XPCNativeSet);
|
|
}
|
|
~XPCNativeSet() {
|
|
MOZ_COUNT_DTOR(XPCNativeSet);
|
|
}
|
|
void* operator new(size_t, void* p) CPP_THROW_NEW {return p;}
|
|
|
|
private:
|
|
PRUint16 mMemberCount;
|
|
PRUint16 mInterfaceCount : 15;
|
|
PRUint16 mMarked : 1;
|
|
XPCNativeInterface* mInterfaces[1]; // always last - object sized for array
|
|
};
|
|
|
|
/***************************************************************************/
|
|
// XPCNativeScriptableFlags is a wrapper class that holds the flags returned
|
|
// from calls to nsIXPCScriptable::GetScriptableFlags(). It has convenience
|
|
// methods to check for particular bitflags. Since we also use this class as
|
|
// a member of the gc'd class XPCNativeScriptableShared, this class holds the
|
|
// bit and exposes the inlined methods to support marking.
|
|
|
|
#define XPC_WN_SJSFLAGS_MARK_FLAG JS_BIT(31) // only high bit of 32 is set
|
|
|
|
class XPCNativeScriptableFlags
|
|
{
|
|
private:
|
|
uint32_t mFlags;
|
|
|
|
public:
|
|
|
|
XPCNativeScriptableFlags(uint32_t flags = 0) : mFlags(flags) {}
|
|
|
|
uint32_t GetFlags() const {return mFlags & ~XPC_WN_SJSFLAGS_MARK_FLAG;}
|
|
void SetFlags(uint32_t flags) {mFlags = flags;}
|
|
|
|
operator uint32_t() const {return GetFlags();}
|
|
|
|
XPCNativeScriptableFlags(const XPCNativeScriptableFlags& r)
|
|
{mFlags = r.GetFlags();}
|
|
|
|
XPCNativeScriptableFlags& operator= (const XPCNativeScriptableFlags& r)
|
|
{mFlags = r.GetFlags(); return *this;}
|
|
|
|
void Mark() {mFlags |= XPC_WN_SJSFLAGS_MARK_FLAG;}
|
|
void Unmark() {mFlags &= ~XPC_WN_SJSFLAGS_MARK_FLAG;}
|
|
JSBool IsMarked() const {return 0 != (mFlags & XPC_WN_SJSFLAGS_MARK_FLAG);}
|
|
|
|
#ifdef GET_IT
|
|
#undef GET_IT
|
|
#endif
|
|
#define GET_IT(f_) const {return 0 != (mFlags & nsIXPCScriptable:: f_ );}
|
|
|
|
JSBool WantPreCreate() GET_IT(WANT_PRECREATE)
|
|
JSBool WantCreate() GET_IT(WANT_CREATE)
|
|
JSBool WantPostCreate() GET_IT(WANT_POSTCREATE)
|
|
JSBool WantAddProperty() GET_IT(WANT_ADDPROPERTY)
|
|
JSBool WantDelProperty() GET_IT(WANT_DELPROPERTY)
|
|
JSBool WantGetProperty() GET_IT(WANT_GETPROPERTY)
|
|
JSBool WantSetProperty() GET_IT(WANT_SETPROPERTY)
|
|
JSBool WantEnumerate() GET_IT(WANT_ENUMERATE)
|
|
JSBool WantNewEnumerate() GET_IT(WANT_NEWENUMERATE)
|
|
JSBool WantNewResolve() GET_IT(WANT_NEWRESOLVE)
|
|
JSBool WantConvert() GET_IT(WANT_CONVERT)
|
|
JSBool WantFinalize() GET_IT(WANT_FINALIZE)
|
|
JSBool WantCheckAccess() GET_IT(WANT_CHECKACCESS)
|
|
JSBool WantCall() GET_IT(WANT_CALL)
|
|
JSBool WantConstruct() GET_IT(WANT_CONSTRUCT)
|
|
JSBool WantHasInstance() GET_IT(WANT_HASINSTANCE)
|
|
JSBool WantEquality() GET_IT(WANT_EQUALITY)
|
|
JSBool WantOuterObject() GET_IT(WANT_OUTER_OBJECT)
|
|
JSBool UseJSStubForAddProperty() GET_IT(USE_JSSTUB_FOR_ADDPROPERTY)
|
|
JSBool UseJSStubForDelProperty() GET_IT(USE_JSSTUB_FOR_DELPROPERTY)
|
|
JSBool UseJSStubForSetProperty() GET_IT(USE_JSSTUB_FOR_SETPROPERTY)
|
|
JSBool DontEnumStaticProps() GET_IT(DONT_ENUM_STATIC_PROPS)
|
|
JSBool DontEnumQueryInterface() GET_IT(DONT_ENUM_QUERY_INTERFACE)
|
|
JSBool DontAskInstanceForScriptable() GET_IT(DONT_ASK_INSTANCE_FOR_SCRIPTABLE)
|
|
JSBool ClassInfoInterfacesOnly() GET_IT(CLASSINFO_INTERFACES_ONLY)
|
|
JSBool AllowPropModsDuringResolve() GET_IT(ALLOW_PROP_MODS_DURING_RESOLVE)
|
|
JSBool AllowPropModsToPrototype() GET_IT(ALLOW_PROP_MODS_TO_PROTOTYPE)
|
|
JSBool IsGlobalObject() GET_IT(IS_GLOBAL_OBJECT)
|
|
JSBool DontReflectInterfaceNames() GET_IT(DONT_REFLECT_INTERFACE_NAMES)
|
|
JSBool UseStubEqualityHook() GET_IT(USE_STUB_EQUALITY_HOOK)
|
|
|
|
#undef GET_IT
|
|
};
|
|
|
|
/***************************************************************************/
|
|
|
|
// XPCNativeScriptableShared is used to hold the JSClass and the
|
|
// associated scriptable flags for XPCWrappedNatives. These are shared across
|
|
// the runtime and are garbage collected by xpconnect. We *used* to just store
|
|
// this inside the XPCNativeScriptableInfo (usually owned by instances of
|
|
// XPCWrappedNativeProto. This had two problems... It was wasteful, and it
|
|
// was a big problem when wrappers are reparented to different scopes (and
|
|
// thus different protos (the DOM does this).
|
|
|
|
// We maintain the invariant that every JSClass for which ext.isWrappedNative
|
|
// is true is a contained in an instance of this struct, and can thus be cast
|
|
// to it.
|
|
struct XPCWrappedNativeJSClass
|
|
{
|
|
js::Class base;
|
|
PRUint32 interfacesBitmap;
|
|
};
|
|
|
|
class XPCNativeScriptableShared
|
|
{
|
|
public:
|
|
const XPCNativeScriptableFlags& GetFlags() const {return mFlags;}
|
|
PRUint32 GetInterfacesBitmap() const
|
|
{return mJSClass.interfacesBitmap;}
|
|
JSClass* GetJSClass()
|
|
{return Jsvalify(&mJSClass.base);}
|
|
JSClass* GetSlimJSClass()
|
|
{if (mCanBeSlim) return GetJSClass(); return nsnull;}
|
|
|
|
XPCNativeScriptableShared(uint32_t aFlags, char* aName,
|
|
PRUint32 interfacesBitmap)
|
|
: mFlags(aFlags),
|
|
mCanBeSlim(false)
|
|
{memset(&mJSClass, 0, sizeof(mJSClass));
|
|
mJSClass.base.name = aName; // take ownership
|
|
mJSClass.interfacesBitmap = interfacesBitmap;
|
|
MOZ_COUNT_CTOR(XPCNativeScriptableShared);}
|
|
|
|
~XPCNativeScriptableShared()
|
|
{if (mJSClass.base.name)nsMemory::Free((void*)mJSClass.base.name);
|
|
MOZ_COUNT_DTOR(XPCNativeScriptableShared);}
|
|
|
|
char* TransferNameOwnership()
|
|
{char* name=(char*)mJSClass.base.name; mJSClass.base.name = nsnull;
|
|
return name;}
|
|
|
|
void PopulateJSClass();
|
|
|
|
void Mark() {mFlags.Mark();}
|
|
void Unmark() {mFlags.Unmark();}
|
|
JSBool IsMarked() const {return mFlags.IsMarked();}
|
|
|
|
private:
|
|
XPCNativeScriptableFlags mFlags;
|
|
XPCWrappedNativeJSClass mJSClass;
|
|
JSBool mCanBeSlim;
|
|
};
|
|
|
|
/***************************************************************************/
|
|
// XPCNativeScriptableInfo is used to hold the nsIXPCScriptable state for a
|
|
// given class or instance.
|
|
|
|
class XPCNativeScriptableInfo
|
|
{
|
|
public:
|
|
static XPCNativeScriptableInfo*
|
|
Construct(XPCCallContext& ccx, const XPCNativeScriptableCreateInfo* sci);
|
|
|
|
nsIXPCScriptable*
|
|
GetCallback() const {return mCallback;}
|
|
|
|
const XPCNativeScriptableFlags&
|
|
GetFlags() const {return mShared->GetFlags();}
|
|
|
|
PRUint32
|
|
GetInterfacesBitmap() const {return mShared->GetInterfacesBitmap();}
|
|
|
|
JSClass*
|
|
GetJSClass() {return mShared->GetJSClass();}
|
|
|
|
JSClass*
|
|
GetSlimJSClass() {return mShared->GetSlimJSClass();}
|
|
|
|
XPCNativeScriptableShared*
|
|
GetScriptableShared() {return mShared;}
|
|
|
|
void
|
|
SetCallback(nsIXPCScriptable* s) {mCallback = s;}
|
|
void
|
|
SetCallback(already_AddRefed<nsIXPCScriptable> s) {mCallback = s;}
|
|
|
|
void
|
|
SetScriptableShared(XPCNativeScriptableShared* shared) {mShared = shared;}
|
|
|
|
void Mark() {
|
|
if (mShared)
|
|
mShared->Mark();
|
|
}
|
|
|
|
void TraceJS(JSTracer *trc) {}
|
|
void AutoTrace(JSTracer *trc) {}
|
|
|
|
protected:
|
|
XPCNativeScriptableInfo(nsIXPCScriptable* scriptable = nsnull,
|
|
XPCNativeScriptableShared* shared = nsnull)
|
|
: mCallback(scriptable), mShared(shared)
|
|
{MOZ_COUNT_CTOR(XPCNativeScriptableInfo);}
|
|
public:
|
|
~XPCNativeScriptableInfo() {MOZ_COUNT_DTOR(XPCNativeScriptableInfo);}
|
|
private:
|
|
|
|
// disable copy ctor and assignment
|
|
XPCNativeScriptableInfo(const XPCNativeScriptableInfo& r); // not implemented
|
|
XPCNativeScriptableInfo& operator= (const XPCNativeScriptableInfo& r); // not implemented
|
|
|
|
private:
|
|
nsCOMPtr<nsIXPCScriptable> mCallback;
|
|
XPCNativeScriptableShared* mShared;
|
|
};
|
|
|
|
/***************************************************************************/
|
|
// XPCNativeScriptableCreateInfo is used in creating new wrapper and protos.
|
|
// it abstracts out the scriptable interface pointer and the flags. After
|
|
// creation these are factored differently using XPCNativeScriptableInfo.
|
|
|
|
class NS_STACK_CLASS XPCNativeScriptableCreateInfo
|
|
{
|
|
public:
|
|
|
|
XPCNativeScriptableCreateInfo(const XPCNativeScriptableInfo& si)
|
|
: mCallback(si.GetCallback()), mFlags(si.GetFlags()),
|
|
mInterfacesBitmap(si.GetInterfacesBitmap()) {}
|
|
|
|
XPCNativeScriptableCreateInfo(already_AddRefed<nsIXPCScriptable> callback,
|
|
XPCNativeScriptableFlags flags,
|
|
PRUint32 interfacesBitmap)
|
|
: mCallback(callback), mFlags(flags),
|
|
mInterfacesBitmap(interfacesBitmap) {}
|
|
|
|
XPCNativeScriptableCreateInfo()
|
|
: mFlags(0), mInterfacesBitmap(0) {}
|
|
|
|
|
|
nsIXPCScriptable*
|
|
GetCallback() const {return mCallback;}
|
|
|
|
const XPCNativeScriptableFlags&
|
|
GetFlags() const {return mFlags;}
|
|
|
|
PRUint32
|
|
GetInterfacesBitmap() const {return mInterfacesBitmap;}
|
|
|
|
void
|
|
SetCallback(already_AddRefed<nsIXPCScriptable> callback)
|
|
{mCallback = callback;}
|
|
|
|
void
|
|
SetFlags(const XPCNativeScriptableFlags& flags) {mFlags = flags;}
|
|
|
|
void
|
|
SetInterfacesBitmap(PRUint32 interfacesBitmap)
|
|
{mInterfacesBitmap = interfacesBitmap;}
|
|
|
|
private:
|
|
nsCOMPtr<nsIXPCScriptable> mCallback;
|
|
XPCNativeScriptableFlags mFlags;
|
|
PRUint32 mInterfacesBitmap;
|
|
};
|
|
|
|
/***********************************************/
|
|
// XPCWrappedNativeProto hold the additional shared wrapper data
|
|
// for XPCWrappedNative whose native objects expose nsIClassInfo.
|
|
|
|
#define UNKNOWN_OFFSETS ((QITableEntry*)1)
|
|
|
|
class XPCWrappedNativeProto
|
|
{
|
|
public:
|
|
static XPCWrappedNativeProto*
|
|
GetNewOrUsed(XPCCallContext& ccx,
|
|
XPCWrappedNativeScope* scope,
|
|
nsIClassInfo* classInfo,
|
|
const XPCNativeScriptableCreateInfo* scriptableCreateInfo,
|
|
QITableEntry* offsets = UNKNOWN_OFFSETS,
|
|
bool callPostCreatePrototype = true);
|
|
|
|
XPCWrappedNativeScope*
|
|
GetScope() const {return mScope;}
|
|
|
|
XPCJSRuntime*
|
|
GetRuntime() const {return mScope->GetRuntime();}
|
|
|
|
JSObject*
|
|
GetJSProtoObject() const {return xpc_UnmarkGrayObject(mJSProtoObject);}
|
|
|
|
nsIClassInfo*
|
|
GetClassInfo() const {return mClassInfo;}
|
|
|
|
XPCNativeSet*
|
|
GetSet() const {return mSet;}
|
|
|
|
XPCNativeScriptableInfo*
|
|
GetScriptableInfo() {return mScriptableInfo;}
|
|
|
|
void**
|
|
GetSecurityInfoAddr() {return &mSecurityInfo;}
|
|
|
|
uint32_t
|
|
GetClassInfoFlags() const {return mClassInfoFlags;}
|
|
|
|
QITableEntry*
|
|
GetOffsets()
|
|
{
|
|
return InitedOffsets() ? mOffsets : nsnull;
|
|
}
|
|
QITableEntry*
|
|
GetOffsetsMasked()
|
|
{
|
|
return mOffsets;
|
|
}
|
|
void
|
|
CacheOffsets(nsISupports* identity)
|
|
{
|
|
static NS_DEFINE_IID(kThisPtrOffsetsSID, NS_THISPTROFFSETS_SID);
|
|
|
|
#ifdef DEBUG
|
|
if (InitedOffsets() && mOffsets) {
|
|
QITableEntry* offsets;
|
|
identity->QueryInterface(kThisPtrOffsetsSID, (void**)&offsets);
|
|
NS_ASSERTION(offsets == mOffsets,
|
|
"We can't deal with objects that have the same "
|
|
"classinfo but different offset tables.");
|
|
}
|
|
#endif
|
|
|
|
if (!InitedOffsets()) {
|
|
if (mClassInfoFlags & nsIClassInfo::CONTENT_NODE) {
|
|
identity->QueryInterface(kThisPtrOffsetsSID, (void**)&mOffsets);
|
|
} else {
|
|
mOffsets = nsnull;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef GET_IT
|
|
#undef GET_IT
|
|
#endif
|
|
#define GET_IT(f_) const {return !!(mClassInfoFlags & nsIClassInfo:: f_ );}
|
|
|
|
JSBool ClassIsSingleton() GET_IT(SINGLETON)
|
|
JSBool ClassIsThreadSafe() GET_IT(THREADSAFE)
|
|
JSBool ClassIsMainThreadOnly() GET_IT(MAIN_THREAD_ONLY)
|
|
JSBool ClassIsDOMObject() GET_IT(DOM_OBJECT)
|
|
JSBool ClassIsPluginObject() GET_IT(PLUGIN_OBJECT)
|
|
|
|
#undef GET_IT
|
|
|
|
XPCLock* GetLock() const
|
|
{return ClassIsThreadSafe() ? GetRuntime()->GetMapLock() : nsnull;}
|
|
|
|
void SetScriptableInfo(XPCNativeScriptableInfo* si)
|
|
{NS_ASSERTION(!mScriptableInfo, "leak here!"); mScriptableInfo = si;}
|
|
|
|
bool CallPostCreatePrototype(XPCCallContext& ccx);
|
|
void JSProtoObjectFinalized(js::FreeOp *fop, JSObject *obj);
|
|
|
|
void SystemIsBeingShutDown();
|
|
|
|
void DebugDump(PRInt16 depth);
|
|
|
|
void TraceSelf(JSTracer *trc) {
|
|
if (mJSProtoObject)
|
|
JS_CALL_OBJECT_TRACER(trc, mJSProtoObject, "XPCWrappedNativeProto::mJSProtoObject");
|
|
}
|
|
|
|
void TraceInside(JSTracer *trc) {
|
|
if (JS_IsGCMarkingTracer(trc)) {
|
|
mSet->Mark();
|
|
if (mScriptableInfo)
|
|
mScriptableInfo->Mark();
|
|
}
|
|
}
|
|
|
|
void TraceJS(JSTracer *trc) {
|
|
TraceSelf(trc);
|
|
TraceInside(trc);
|
|
}
|
|
|
|
void WriteBarrierPre(JSRuntime* rt)
|
|
{
|
|
if (js::IsIncrementalBarrierNeeded(rt) && mJSProtoObject)
|
|
mJSProtoObject.writeBarrierPre(rt);
|
|
}
|
|
|
|
// NOP. This is just here to make the AutoMarkingPtr code compile.
|
|
inline void AutoTrace(JSTracer* trc) {}
|
|
|
|
// Yes, we *do* need to mark the mScriptableInfo in both cases.
|
|
void Mark() const
|
|
{mSet->Mark();
|
|
if (mScriptableInfo) mScriptableInfo->Mark();}
|
|
|
|
#ifdef DEBUG
|
|
void ASSERT_SetNotMarked() const {mSet->ASSERT_NotMarked();}
|
|
#endif
|
|
|
|
~XPCWrappedNativeProto();
|
|
|
|
protected:
|
|
// disable copy ctor and assignment
|
|
XPCWrappedNativeProto(const XPCWrappedNativeProto& r); // not implemented
|
|
XPCWrappedNativeProto& operator= (const XPCWrappedNativeProto& r); // not implemented
|
|
|
|
// hide ctor
|
|
XPCWrappedNativeProto(XPCWrappedNativeScope* Scope,
|
|
nsIClassInfo* ClassInfo,
|
|
PRUint32 ClassInfoFlags,
|
|
XPCNativeSet* Set,
|
|
QITableEntry* offsets);
|
|
|
|
JSBool Init(XPCCallContext& ccx,
|
|
const XPCNativeScriptableCreateInfo* scriptableCreateInfo,
|
|
bool callPostCreatePrototype);
|
|
|
|
private:
|
|
#if defined(DEBUG_xpc_hacker) || defined(DEBUG)
|
|
static PRInt32 gDEBUG_LiveProtoCount;
|
|
#endif
|
|
|
|
private:
|
|
bool
|
|
InitedOffsets()
|
|
{
|
|
return mOffsets != UNKNOWN_OFFSETS;
|
|
}
|
|
|
|
XPCWrappedNativeScope* mScope;
|
|
js::ObjectPtr mJSProtoObject;
|
|
nsCOMPtr<nsIClassInfo> mClassInfo;
|
|
PRUint32 mClassInfoFlags;
|
|
XPCNativeSet* mSet;
|
|
void* mSecurityInfo;
|
|
XPCNativeScriptableInfo* mScriptableInfo;
|
|
QITableEntry* mOffsets;
|
|
};
|
|
|
|
class xpcObjectHelper;
|
|
extern JSBool ConstructSlimWrapper(XPCCallContext &ccx,
|
|
xpcObjectHelper &aHelper,
|
|
XPCWrappedNativeScope* xpcScope,
|
|
jsval *rval);
|
|
extern JSBool MorphSlimWrapper(JSContext *cx, JSObject *obj);
|
|
|
|
/***********************************************/
|
|
// XPCWrappedNativeTearOff represents the info needed to make calls to one
|
|
// interface on the underlying native object of a XPCWrappedNative.
|
|
|
|
class XPCWrappedNativeTearOff
|
|
{
|
|
public:
|
|
JSBool IsAvailable() const {return mInterface == nsnull;}
|
|
JSBool IsReserved() const {return mInterface == (XPCNativeInterface*)1;}
|
|
JSBool IsValid() const {return !IsAvailable() && !IsReserved();}
|
|
void SetReserved() {mInterface = (XPCNativeInterface*)1;}
|
|
|
|
XPCNativeInterface* GetInterface() const {return mInterface;}
|
|
nsISupports* GetNative() const {return mNative;}
|
|
JSObject* GetJSObject();
|
|
JSObject* GetJSObjectPreserveColor() const;
|
|
void SetInterface(XPCNativeInterface* Interface) {mInterface = Interface;}
|
|
void SetNative(nsISupports* Native) {mNative = Native;}
|
|
void SetJSObject(JSObject* JSObj);
|
|
|
|
void JSObjectFinalized() {SetJSObject(nsnull);}
|
|
|
|
XPCWrappedNativeTearOff()
|
|
: mInterface(nsnull), mNative(nsnull), mJSObject(nsnull) {}
|
|
~XPCWrappedNativeTearOff();
|
|
|
|
// NOP. This is just here to make the AutoMarkingPtr code compile.
|
|
inline void TraceJS(JSTracer* trc) {}
|
|
inline void AutoTrace(JSTracer* trc) {}
|
|
|
|
void Mark() {mJSObject = (JSObject*)(intptr_t(mJSObject) | 1);}
|
|
void Unmark() {mJSObject = (JSObject*)(intptr_t(mJSObject) & ~1);}
|
|
bool IsMarked() const {return !!(intptr_t(mJSObject) & 1);}
|
|
|
|
private:
|
|
XPCWrappedNativeTearOff(const XPCWrappedNativeTearOff& r) MOZ_DELETE;
|
|
XPCWrappedNativeTearOff& operator= (const XPCWrappedNativeTearOff& r) MOZ_DELETE;
|
|
|
|
private:
|
|
XPCNativeInterface* mInterface;
|
|
nsISupports* mNative;
|
|
JSObject* mJSObject;
|
|
};
|
|
|
|
/***********************************************/
|
|
// XPCWrappedNativeTearOffChunk is a collections of XPCWrappedNativeTearOff
|
|
// objects. It lets us allocate a set of XPCWrappedNativeTearOff objects and
|
|
// link the sets - rather than only having the option of linking single
|
|
// XPCWrappedNativeTearOff objects.
|
|
//
|
|
// The value of XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK can be tuned at buildtime
|
|
// to balance between the code of allocations of additional chunks and the waste
|
|
// of space for ununsed XPCWrappedNativeTearOff objects.
|
|
|
|
#define XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK 1
|
|
|
|
class XPCWrappedNativeTearOffChunk
|
|
{
|
|
friend class XPCWrappedNative;
|
|
private:
|
|
XPCWrappedNativeTearOffChunk() : mNextChunk(nsnull) {}
|
|
~XPCWrappedNativeTearOffChunk() {delete mNextChunk;}
|
|
|
|
private:
|
|
XPCWrappedNativeTearOff mTearOffs[XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK];
|
|
XPCWrappedNativeTearOffChunk* mNextChunk;
|
|
};
|
|
|
|
void *xpc_GetJSPrivate(JSObject *obj);
|
|
|
|
/***************************************************************************/
|
|
// XPCWrappedNative the wrapper around one instance of a native xpcom object
|
|
// to be used from JavaScript.
|
|
|
|
class XPCWrappedNative : public nsIXPConnectWrappedNative
|
|
{
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSIXPCONNECTJSOBJECTHOLDER
|
|
NS_DECL_NSIXPCONNECTWRAPPEDNATIVE
|
|
// No need to unlink the JS objects, if the XPCWrappedNative will be cycle
|
|
// collected then its mFlatJSObject will be cycle collected too and
|
|
// finalization of the mFlatJSObject will unlink the js objects (see
|
|
// XPC_WN_NoHelper_Finalize and FlatJSObjectFinalized).
|
|
// We also give XPCWrappedNative empty Root/Unroot methods, to avoid
|
|
// root/unrooting the JS objects from addrefing/releasing the
|
|
// XPCWrappedNative during unlinking, which would make the JS objects
|
|
// uncollectable to the JS GC.
|
|
class NS_CYCLE_COLLECTION_INNERCLASS
|
|
: public nsXPCOMCycleCollectionParticipant
|
|
{
|
|
NS_DECL_CYCLE_COLLECTION_CLASS_BODY_NO_UNLINK(XPCWrappedNative,
|
|
XPCWrappedNative)
|
|
static NS_METHOD RootImpl(void *p) { return NS_OK; }
|
|
static NS_METHOD UnlinkImpl(void *p);
|
|
static NS_METHOD UnrootImpl(void *p) { return NS_OK; }
|
|
};
|
|
NS_CYCLE_COLLECTION_PARTICIPANT_INSTANCE
|
|
NS_DECL_CYCLE_COLLECTION_UNMARK_PURPLE_STUB(XPCWrappedNative)
|
|
|
|
nsIPrincipal* GetObjectPrincipal() const;
|
|
|
|
JSBool
|
|
IsValid() const {return nsnull != mFlatJSObject;}
|
|
|
|
#define XPC_SCOPE_WORD(s) (intptr_t(s))
|
|
#define XPC_SCOPE_MASK (intptr_t(0x3))
|
|
#define XPC_SCOPE_TAG (intptr_t(0x1))
|
|
#define XPC_WRAPPER_EXPIRED (intptr_t(0x2))
|
|
|
|
static inline JSBool
|
|
IsTaggedScope(XPCWrappedNativeScope* s)
|
|
{return XPC_SCOPE_WORD(s) & XPC_SCOPE_TAG;}
|
|
|
|
static inline XPCWrappedNativeScope*
|
|
TagScope(XPCWrappedNativeScope* s)
|
|
{NS_ASSERTION(!IsTaggedScope(s), "bad pointer!");
|
|
return (XPCWrappedNativeScope*)(XPC_SCOPE_WORD(s) | XPC_SCOPE_TAG);}
|
|
|
|
static inline XPCWrappedNativeScope*
|
|
UnTagScope(XPCWrappedNativeScope* s)
|
|
{return (XPCWrappedNativeScope*)(XPC_SCOPE_WORD(s) & ~XPC_SCOPE_TAG);}
|
|
|
|
inline JSBool
|
|
IsWrapperExpired() const
|
|
{return XPC_SCOPE_WORD(mMaybeScope) & XPC_WRAPPER_EXPIRED;}
|
|
|
|
JSBool
|
|
HasProto() const {return !IsTaggedScope(mMaybeScope);}
|
|
|
|
XPCWrappedNativeProto*
|
|
GetProto() const
|
|
{return HasProto() ?
|
|
(XPCWrappedNativeProto*)
|
|
(XPC_SCOPE_WORD(mMaybeProto) & ~XPC_SCOPE_MASK) : nsnull;}
|
|
|
|
void SetProto(XPCWrappedNativeProto* p);
|
|
|
|
XPCWrappedNativeScope*
|
|
GetScope() const
|
|
{return GetProto() ? GetProto()->GetScope() :
|
|
(XPCWrappedNativeScope*)
|
|
(XPC_SCOPE_WORD(mMaybeScope) & ~XPC_SCOPE_MASK);}
|
|
|
|
nsISupports*
|
|
GetIdentityObject() const {return mIdentity;}
|
|
|
|
/**
|
|
* This getter clears the gray bit before handing out the JSObject which
|
|
* means that the object is guaranteed to be kept alive past the next CC.
|
|
*/
|
|
JSObject*
|
|
GetFlatJSObject() const
|
|
{if (mFlatJSObject != INVALID_OBJECT)
|
|
xpc_UnmarkGrayObject(mFlatJSObject);
|
|
return mFlatJSObject;}
|
|
|
|
/**
|
|
* This getter does not change the color of the JSObject meaning that the
|
|
* object returned is not guaranteed to be kept alive past the next CC.
|
|
*
|
|
* This should only be called if you are certain that the return value won't
|
|
* be passed into a JS API function and that it won't be stored without
|
|
* being rooted (or otherwise signaling the stored value to the CC).
|
|
*/
|
|
JSObject*
|
|
GetFlatJSObjectPreserveColor() const {return mFlatJSObject;}
|
|
|
|
XPCLock*
|
|
GetLock() const {return IsValid() && HasProto() ?
|
|
GetProto()->GetLock() : nsnull;}
|
|
|
|
XPCNativeSet*
|
|
GetSet() const {XPCAutoLock al(GetLock()); return mSet;}
|
|
|
|
void
|
|
SetSet(XPCNativeSet* set) {XPCAutoLock al(GetLock()); mSet = set;}
|
|
|
|
private:
|
|
inline void
|
|
ExpireWrapper()
|
|
{mMaybeScope = (XPCWrappedNativeScope*)
|
|
(XPC_SCOPE_WORD(mMaybeScope) | XPC_WRAPPER_EXPIRED);}
|
|
|
|
public:
|
|
|
|
XPCNativeScriptableInfo*
|
|
GetScriptableInfo() const {return mScriptableInfo;}
|
|
|
|
nsIXPCScriptable* // call this wrong and you deserve to crash
|
|
GetScriptableCallback() const {return mScriptableInfo->GetCallback();}
|
|
|
|
void**
|
|
GetSecurityInfoAddr() {return HasProto() ?
|
|
GetProto()->GetSecurityInfoAddr() : nsnull;}
|
|
|
|
nsIClassInfo*
|
|
GetClassInfo() const {return IsValid() && HasProto() ?
|
|
GetProto()->GetClassInfo() : nsnull;}
|
|
|
|
JSBool
|
|
HasMutatedSet() const {return IsValid() &&
|
|
(!HasProto() ||
|
|
GetSet() != GetProto()->GetSet());}
|
|
|
|
XPCJSRuntime*
|
|
GetRuntime() const {XPCWrappedNativeScope* scope = GetScope();
|
|
return scope ? scope->GetRuntime() : nsnull;}
|
|
|
|
static nsresult
|
|
WrapNewGlobal(XPCCallContext &ccx, xpcObjectHelper &nativeHelper,
|
|
nsIPrincipal *principal, bool initStandardClasses,
|
|
XPCWrappedNative **wrappedGlobal);
|
|
|
|
static nsresult
|
|
GetNewOrUsed(XPCCallContext& ccx,
|
|
xpcObjectHelper& helper,
|
|
XPCWrappedNativeScope* Scope,
|
|
XPCNativeInterface* Interface,
|
|
XPCWrappedNative** wrapper);
|
|
|
|
static nsresult
|
|
Morph(XPCCallContext& ccx,
|
|
JSObject* existingJSObject,
|
|
XPCNativeInterface* Interface,
|
|
nsWrapperCache *cache,
|
|
XPCWrappedNative** resultWrapper);
|
|
|
|
public:
|
|
static nsresult
|
|
GetUsedOnly(XPCCallContext& ccx,
|
|
nsISupports* Object,
|
|
XPCWrappedNativeScope* Scope,
|
|
XPCNativeInterface* Interface,
|
|
XPCWrappedNative** wrapper);
|
|
|
|
// If pobj2 is not null and *pobj2 is not null after the call then *pobj2
|
|
// points to an object for which IS_SLIM_WRAPPER_OBJECT is true.
|
|
// cx is null when invoked from the marking phase of the GC. In this case
|
|
// fubobj must be null as well.
|
|
static XPCWrappedNative*
|
|
GetWrappedNativeOfJSObject(JSContext* cx, JSObject* obj,
|
|
JSObject* funobj = nsnull,
|
|
JSObject** pobj2 = nsnull,
|
|
XPCWrappedNativeTearOff** pTearOff = nsnull);
|
|
static XPCWrappedNative*
|
|
GetAndMorphWrappedNativeOfJSObject(JSContext* cx, JSObject* obj)
|
|
{
|
|
JSObject *obj2 = nsnull;
|
|
XPCWrappedNative* wrapper =
|
|
GetWrappedNativeOfJSObject(cx, obj, nsnull, &obj2);
|
|
if (wrapper || !obj2)
|
|
return wrapper;
|
|
|
|
NS_ASSERTION(IS_SLIM_WRAPPER(obj2),
|
|
"Hmm, someone changed GetWrappedNativeOfJSObject?");
|
|
SLIM_LOG_WILL_MORPH(cx, obj2);
|
|
return MorphSlimWrapper(cx, obj2) ?
|
|
(XPCWrappedNative*)xpc_GetJSPrivate(obj2) :
|
|
nsnull;
|
|
}
|
|
|
|
static nsresult
|
|
ReparentWrapperIfFound(XPCCallContext& ccx,
|
|
XPCWrappedNativeScope* aOldScope,
|
|
XPCWrappedNativeScope* aNewScope,
|
|
JSObject* aNewParent,
|
|
nsISupports* aCOMObj,
|
|
XPCWrappedNative** aWrapper);
|
|
|
|
// Returns the wrapper corresponding to the parent of our mFlatJSObject.
|
|
//
|
|
// If the parent does not have a WN, or if there is no parent, null is
|
|
// returned.
|
|
XPCWrappedNative *GetParentWrapper();
|
|
|
|
bool IsOrphan();
|
|
nsresult RescueOrphans(XPCCallContext& ccx);
|
|
|
|
void FlatJSObjectFinalized();
|
|
|
|
void SystemIsBeingShutDown();
|
|
|
|
enum CallMode {CALL_METHOD, CALL_GETTER, CALL_SETTER};
|
|
|
|
static JSBool CallMethod(XPCCallContext& ccx,
|
|
CallMode mode = CALL_METHOD);
|
|
|
|
static JSBool GetAttribute(XPCCallContext& ccx)
|
|
{return CallMethod(ccx, CALL_GETTER);}
|
|
|
|
static JSBool SetAttribute(XPCCallContext& ccx)
|
|
{return CallMethod(ccx, CALL_SETTER);}
|
|
|
|
inline JSBool HasInterfaceNoQI(const nsIID& iid);
|
|
|
|
XPCWrappedNativeTearOff* LocateTearOff(XPCCallContext& ccx,
|
|
XPCNativeInterface* aInterface);
|
|
XPCWrappedNativeTearOff* FindTearOff(XPCCallContext& ccx,
|
|
XPCNativeInterface* aInterface,
|
|
JSBool needJSObject = false,
|
|
nsresult* pError = nsnull);
|
|
void Mark() const
|
|
{
|
|
mSet->Mark();
|
|
if (mScriptableInfo) mScriptableInfo->Mark();
|
|
if (HasProto()) GetProto()->Mark();
|
|
}
|
|
|
|
// Yes, we *do* need to mark the mScriptableInfo in both cases.
|
|
inline void TraceInside(JSTracer *trc) {
|
|
if (JS_IsGCMarkingTracer(trc)) {
|
|
mSet->Mark();
|
|
if (mScriptableInfo)
|
|
mScriptableInfo->Mark();
|
|
}
|
|
if (HasProto())
|
|
GetProto()->TraceJS(trc);
|
|
JSObject* wrapper = GetWrapperPreserveColor();
|
|
if (wrapper)
|
|
JS_CALL_OBJECT_TRACER(trc, wrapper, "XPCWrappedNative::mWrapper");
|
|
if (mScriptableInfo &&
|
|
(mScriptableInfo->GetJSClass()->flags & JSCLASS_XPCONNECT_GLOBAL))
|
|
{
|
|
TraceXPCGlobal(trc, mFlatJSObject);
|
|
}
|
|
}
|
|
|
|
void TraceJS(JSTracer *trc) {
|
|
TraceInside(trc);
|
|
}
|
|
|
|
void TraceSelf(JSTracer *trc) {
|
|
// If this got called, we're being kept alive by someone who really
|
|
// needs us alive and whole. Do not let our mFlatJSObject go away.
|
|
// This is the only time we should be tracing our mFlatJSObject,
|
|
// normally somebody else is doing that. Be careful not to trace the
|
|
// bogus INVALID_OBJECT value we can have during init, though.
|
|
if (mFlatJSObject && mFlatJSObject != INVALID_OBJECT) {
|
|
JS_CALL_OBJECT_TRACER(trc, mFlatJSObject,
|
|
"XPCWrappedNative::mFlatJSObject");
|
|
}
|
|
}
|
|
|
|
void AutoTrace(JSTracer *trc) {
|
|
TraceSelf(trc);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
void ASSERT_SetsNotMarked() const
|
|
{mSet->ASSERT_NotMarked();
|
|
if (HasProto()){GetProto()->ASSERT_SetNotMarked();}}
|
|
|
|
int DEBUG_CountOfTearoffChunks() const
|
|
{int i = 0; const XPCWrappedNativeTearOffChunk* to;
|
|
for (to = &mFirstChunk; to; to = to->mNextChunk) {i++;} return i;}
|
|
#endif
|
|
|
|
inline void SweepTearOffs();
|
|
|
|
// Returns a string that shuld be free'd using JS_smprintf_free (or null).
|
|
char* ToString(XPCCallContext& ccx,
|
|
XPCWrappedNativeTearOff* to = nsnull) const;
|
|
|
|
static void GatherProtoScriptableCreateInfo(nsIClassInfo* classInfo,
|
|
XPCNativeScriptableCreateInfo& sciProto);
|
|
|
|
JSBool HasExternalReference() const {return mRefCnt > 1;}
|
|
|
|
JSBool NeedsSOW() { return !!(mWrapperWord & NEEDS_SOW); }
|
|
void SetNeedsSOW() { mWrapperWord |= NEEDS_SOW; }
|
|
JSBool NeedsCOW() { return !!(mWrapperWord & NEEDS_COW); }
|
|
void SetNeedsCOW() { mWrapperWord |= NEEDS_COW; }
|
|
|
|
JSObject* GetWrapperPreserveColor() const
|
|
{return (JSObject*)(mWrapperWord & (size_t)~(size_t)FLAG_MASK);}
|
|
|
|
JSObject* GetWrapper()
|
|
{
|
|
JSObject* wrapper = GetWrapperPreserveColor();
|
|
if (wrapper) {
|
|
xpc_UnmarkGrayObject(wrapper);
|
|
// Call this to unmark mFlatJSObject.
|
|
GetFlatJSObject();
|
|
}
|
|
return wrapper;
|
|
}
|
|
void SetWrapper(JSObject *obj)
|
|
{
|
|
js::IncrementalReferenceBarrier(GetWrapperPreserveColor());
|
|
intptr_t newval = intptr_t(obj) | (mWrapperWord & FLAG_MASK);
|
|
mWrapperWord = newval;
|
|
}
|
|
|
|
// Returns the relevant same-compartment security if applicable, or
|
|
// mFlatJSObject otherwise.
|
|
//
|
|
// This takes care of checking mWrapperWord to see if we already have such
|
|
// a wrapper.
|
|
JSObject *GetSameCompartmentSecurityWrapper(JSContext *cx);
|
|
|
|
void NoteTearoffs(nsCycleCollectionTraversalCallback& cb);
|
|
|
|
QITableEntry* GetOffsets()
|
|
{
|
|
if (!HasProto() || !GetProto()->ClassIsDOMObject())
|
|
return nsnull;
|
|
|
|
XPCWrappedNativeProto* proto = GetProto();
|
|
QITableEntry* offsets = proto->GetOffsets();
|
|
if (!offsets) {
|
|
static NS_DEFINE_IID(kThisPtrOffsetsSID, NS_THISPTROFFSETS_SID);
|
|
mIdentity->QueryInterface(kThisPtrOffsetsSID, (void**)&offsets);
|
|
}
|
|
return offsets;
|
|
}
|
|
|
|
// Make ctor and dtor protected (rather than private) to placate nsCOMPtr.
|
|
protected:
|
|
XPCWrappedNative(); // not implemented
|
|
|
|
// This ctor is used if this object will have a proto.
|
|
XPCWrappedNative(already_AddRefed<nsISupports> aIdentity,
|
|
XPCWrappedNativeProto* aProto);
|
|
|
|
// This ctor is used if this object will NOT have a proto.
|
|
XPCWrappedNative(already_AddRefed<nsISupports> aIdentity,
|
|
XPCWrappedNativeScope* aScope,
|
|
XPCNativeSet* aSet);
|
|
|
|
virtual ~XPCWrappedNative();
|
|
void Destroy();
|
|
|
|
void UpdateScriptableInfo(XPCNativeScriptableInfo *si);
|
|
|
|
private:
|
|
enum {
|
|
NEEDS_SOW = JS_BIT(0),
|
|
NEEDS_COW = JS_BIT(1),
|
|
FLAG_MASK = JS_BITMASK(3)
|
|
};
|
|
|
|
private:
|
|
|
|
JSBool Init(XPCCallContext& ccx, JSObject* parent, const XPCNativeScriptableCreateInfo* sci);
|
|
JSBool Init(XPCCallContext &ccx, JSObject *existingJSObject);
|
|
JSBool FinishInit(XPCCallContext &ccx);
|
|
|
|
JSBool ExtendSet(XPCCallContext& ccx, XPCNativeInterface* aInterface);
|
|
|
|
nsresult InitTearOff(XPCCallContext& ccx,
|
|
XPCWrappedNativeTearOff* aTearOff,
|
|
XPCNativeInterface* aInterface,
|
|
JSBool needJSObject);
|
|
|
|
JSBool InitTearOffJSObject(XPCCallContext& ccx,
|
|
XPCWrappedNativeTearOff* to);
|
|
|
|
public:
|
|
static const XPCNativeScriptableCreateInfo& GatherScriptableCreateInfo(nsISupports* obj,
|
|
nsIClassInfo* classInfo,
|
|
XPCNativeScriptableCreateInfo& sciProto,
|
|
XPCNativeScriptableCreateInfo& sciWrapper);
|
|
|
|
private:
|
|
union
|
|
{
|
|
XPCWrappedNativeScope* mMaybeScope;
|
|
XPCWrappedNativeProto* mMaybeProto;
|
|
};
|
|
XPCNativeSet* mSet;
|
|
JSObject* mFlatJSObject;
|
|
XPCNativeScriptableInfo* mScriptableInfo;
|
|
XPCWrappedNativeTearOffChunk mFirstChunk;
|
|
intptr_t mWrapperWord;
|
|
};
|
|
|
|
/***************************************************************************
|
|
****************************************************************************
|
|
*
|
|
* Core classes for wrapped JSObject for use from native code...
|
|
*
|
|
****************************************************************************
|
|
***************************************************************************/
|
|
|
|
// this interfaces exists so we can refcount nsXPCWrappedJSClass
|
|
// {2453EBA0-A9B8-11d2-BA64-00805F8A5DD7}
|
|
#define NS_IXPCONNECT_WRAPPED_JS_CLASS_IID \
|
|
{ 0x2453eba0, 0xa9b8, 0x11d2, \
|
|
{ 0xba, 0x64, 0x0, 0x80, 0x5f, 0x8a, 0x5d, 0xd7 } }
|
|
|
|
class nsIXPCWrappedJSClass : public nsISupports
|
|
{
|
|
public:
|
|
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IXPCONNECT_WRAPPED_JS_CLASS_IID)
|
|
NS_IMETHOD DebugDump(PRInt16 depth) = 0;
|
|
};
|
|
|
|
NS_DEFINE_STATIC_IID_ACCESSOR(nsIXPCWrappedJSClass,
|
|
NS_IXPCONNECT_WRAPPED_JS_CLASS_IID)
|
|
|
|
/*************************/
|
|
// nsXPCWrappedJSClass represents the sharable factored out common code and
|
|
// data for nsXPCWrappedJS instances for the same interface type.
|
|
|
|
class nsXPCWrappedJSClass : public nsIXPCWrappedJSClass
|
|
{
|
|
// all the interface method declarations...
|
|
NS_DECL_ISUPPORTS
|
|
NS_IMETHOD DebugDump(PRInt16 depth);
|
|
public:
|
|
|
|
static nsresult
|
|
GetNewOrUsed(XPCCallContext& ccx,
|
|
REFNSIID aIID,
|
|
nsXPCWrappedJSClass** clazz);
|
|
|
|
REFNSIID GetIID() const {return mIID;}
|
|
XPCJSRuntime* GetRuntime() const {return mRuntime;}
|
|
nsIInterfaceInfo* GetInterfaceInfo() const {return mInfo;}
|
|
const char* GetInterfaceName();
|
|
|
|
static JSBool IsWrappedJS(nsISupports* aPtr);
|
|
|
|
NS_IMETHOD DelegatedQueryInterface(nsXPCWrappedJS* self, REFNSIID aIID,
|
|
void** aInstancePtr);
|
|
|
|
JSObject* GetRootJSObject(XPCCallContext& ccx, JSObject* aJSObj);
|
|
|
|
NS_IMETHOD CallMethod(nsXPCWrappedJS* wrapper, uint16_t methodIndex,
|
|
const XPTMethodDescriptor* info,
|
|
nsXPTCMiniVariant* params);
|
|
|
|
JSObject* CallQueryInterfaceOnJSObject(XPCCallContext& ccx,
|
|
JSObject* jsobj, REFNSIID aIID);
|
|
|
|
static nsresult BuildPropertyEnumerator(XPCCallContext& ccx,
|
|
JSObject* aJSObj,
|
|
nsISimpleEnumerator** aEnumerate);
|
|
|
|
static nsresult GetNamedPropertyAsVariant(XPCCallContext& ccx,
|
|
JSObject* aJSObj,
|
|
const nsAString& aName,
|
|
nsIVariant** aResult);
|
|
|
|
virtual ~nsXPCWrappedJSClass();
|
|
|
|
static nsresult CheckForException(XPCCallContext & ccx,
|
|
const char * aPropertyName,
|
|
const char * anInterfaceName,
|
|
bool aForceReport);
|
|
private:
|
|
nsXPCWrappedJSClass(); // not implemented
|
|
nsXPCWrappedJSClass(XPCCallContext& ccx, REFNSIID aIID,
|
|
nsIInterfaceInfo* aInfo);
|
|
|
|
JSObject* NewOutObject(JSContext* cx, JSObject* scope);
|
|
|
|
JSBool IsReflectable(uint16_t i) const
|
|
{return (JSBool)(mDescriptors[i/32] & (1 << (i%32)));}
|
|
void SetReflectable(uint16_t i, JSBool b)
|
|
{if (b) mDescriptors[i/32] |= (1 << (i%32));
|
|
else mDescriptors[i/32] &= ~(1 << (i%32));}
|
|
|
|
JSBool GetArraySizeFromParam(JSContext* cx,
|
|
const XPTMethodDescriptor* method,
|
|
const nsXPTParamInfo& param,
|
|
uint16_t methodIndex,
|
|
uint8_t paramIndex,
|
|
nsXPTCMiniVariant* params,
|
|
uint32_t* result);
|
|
|
|
JSBool GetInterfaceTypeFromParam(JSContext* cx,
|
|
const XPTMethodDescriptor* method,
|
|
const nsXPTParamInfo& param,
|
|
uint16_t methodIndex,
|
|
const nsXPTType& type,
|
|
nsXPTCMiniVariant* params,
|
|
nsID* result);
|
|
|
|
void CleanupPointerArray(const nsXPTType& datum_type,
|
|
uint32_t array_count,
|
|
void** arrayp);
|
|
|
|
void CleanupPointerTypeObject(const nsXPTType& type,
|
|
void** pp);
|
|
|
|
private:
|
|
XPCJSRuntime* mRuntime;
|
|
nsIInterfaceInfo* mInfo;
|
|
char* mName;
|
|
nsIID mIID;
|
|
uint32_t* mDescriptors;
|
|
};
|
|
|
|
/*************************/
|
|
// nsXPCWrappedJS is a wrapper for a single JSObject for use from native code.
|
|
// nsXPCWrappedJS objects are chained together to represent the various
|
|
// interface on the single underlying (possibly aggregate) JSObject.
|
|
|
|
class nsXPCWrappedJS : protected nsAutoXPTCStub,
|
|
public nsIXPConnectWrappedJS,
|
|
public nsSupportsWeakReference,
|
|
public nsIPropertyBag,
|
|
public XPCRootSetElem
|
|
{
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSIXPCONNECTJSOBJECTHOLDER
|
|
NS_DECL_NSIXPCONNECTWRAPPEDJS
|
|
NS_DECL_NSISUPPORTSWEAKREFERENCE
|
|
NS_DECL_NSIPROPERTYBAG
|
|
|
|
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXPCWrappedJS, nsIXPConnectWrappedJS)
|
|
NS_DECL_CYCLE_COLLECTION_UNMARK_PURPLE_STUB(nsXPCWrappedJS)
|
|
|
|
NS_IMETHOD CallMethod(PRUint16 methodIndex,
|
|
const XPTMethodDescriptor *info,
|
|
nsXPTCMiniVariant* params);
|
|
|
|
/*
|
|
* This is rarely called directly. Instead one usually calls
|
|
* XPCConvert::JSObject2NativeInterface which will handles cases where the
|
|
* JS object is already a wrapped native or a DOM object.
|
|
*/
|
|
|
|
static nsresult
|
|
GetNewOrUsed(XPCCallContext& ccx,
|
|
JSObject* aJSObj,
|
|
REFNSIID aIID,
|
|
nsISupports* aOuter,
|
|
nsXPCWrappedJS** wrapper);
|
|
|
|
nsISomeInterface* GetXPTCStub() { return mXPTCStub; }
|
|
|
|
/**
|
|
* This getter clears the gray bit before handing out the JSObject which
|
|
* means that the object is guaranteed to be kept alive past the next CC.
|
|
*/
|
|
JSObject* GetJSObject() const {return xpc_UnmarkGrayObject(mJSObj);}
|
|
|
|
/**
|
|
* This getter does not change the color of the JSObject meaning that the
|
|
* object returned is not guaranteed to be kept alive past the next CC.
|
|
*
|
|
* This should only be called if you are certain that the return value won't
|
|
* be passed into a JS API function and that it won't be stored without
|
|
* being rooted (or otherwise signaling the stored value to the CC).
|
|
*/
|
|
JSObject* GetJSObjectPreserveColor() const {return mJSObj;}
|
|
|
|
nsXPCWrappedJSClass* GetClass() const {return mClass;}
|
|
REFNSIID GetIID() const {return GetClass()->GetIID();}
|
|
nsXPCWrappedJS* GetRootWrapper() const {return mRoot;}
|
|
nsXPCWrappedJS* GetNextWrapper() const {return mNext;}
|
|
|
|
nsXPCWrappedJS* Find(REFNSIID aIID);
|
|
nsXPCWrappedJS* FindInherited(REFNSIID aIID);
|
|
|
|
JSBool IsValid() const {return mJSObj != nsnull;}
|
|
void SystemIsBeingShutDown(JSRuntime* rt);
|
|
|
|
// This is used by XPCJSRuntime::GCCallback to find wrappers that no
|
|
// longer root their JSObject and are only still alive because they
|
|
// were being used via nsSupportsWeakReference at the time when their
|
|
// last (outside) reference was released. Wrappers that fit into that
|
|
// category are only deleted when we see that their corresponding JSObject
|
|
// is to be finalized.
|
|
JSBool IsSubjectToFinalization() const {return IsValid() && mRefCnt == 1;}
|
|
|
|
JSBool IsAggregatedToNative() const {return mRoot->mOuter != nsnull;}
|
|
nsISupports* GetAggregatedNativeObject() const {return mRoot->mOuter;}
|
|
|
|
void SetIsMainThreadOnly() {
|
|
MOZ_ASSERT(mMainThread);
|
|
mMainThreadOnly = true;
|
|
}
|
|
bool IsMainThreadOnly() const {return mMainThreadOnly;}
|
|
|
|
void TraceJS(JSTracer* trc);
|
|
static void GetTraceName(JSTracer* trc, char *buf, size_t bufsize);
|
|
|
|
virtual ~nsXPCWrappedJS();
|
|
protected:
|
|
nsXPCWrappedJS(); // not implemented
|
|
nsXPCWrappedJS(XPCCallContext& ccx,
|
|
JSObject* aJSObj,
|
|
nsXPCWrappedJSClass* aClass,
|
|
nsXPCWrappedJS* root,
|
|
nsISupports* aOuter);
|
|
|
|
void Unlink();
|
|
|
|
private:
|
|
JSObject* mJSObj;
|
|
nsXPCWrappedJSClass* mClass;
|
|
nsXPCWrappedJS* mRoot;
|
|
nsXPCWrappedJS* mNext;
|
|
nsISupports* mOuter; // only set in root
|
|
bool mMainThread;
|
|
bool mMainThreadOnly;
|
|
};
|
|
|
|
/***************************************************************************/
|
|
|
|
class XPCJSObjectHolder : public nsIXPConnectJSObjectHolder,
|
|
public XPCRootSetElem
|
|
{
|
|
public:
|
|
// all the interface method declarations...
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSIXPCONNECTJSOBJECTHOLDER
|
|
|
|
// non-interface implementation
|
|
|
|
public:
|
|
static XPCJSObjectHolder* newHolder(XPCCallContext& ccx, JSObject* obj);
|
|
|
|
virtual ~XPCJSObjectHolder();
|
|
|
|
void TraceJS(JSTracer *trc);
|
|
static void GetTraceName(JSTracer* trc, char *buf, size_t bufsize);
|
|
|
|
private:
|
|
XPCJSObjectHolder(XPCCallContext& ccx, JSObject* obj);
|
|
XPCJSObjectHolder(); // not implemented
|
|
|
|
JSObject* mJSObj;
|
|
};
|
|
|
|
/***************************************************************************
|
|
****************************************************************************
|
|
*
|
|
* All manner of utility classes follow...
|
|
*
|
|
****************************************************************************
|
|
***************************************************************************/
|
|
|
|
class xpcProperty : public nsIProperty
|
|
{
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSIPROPERTY
|
|
|
|
xpcProperty(const PRUnichar* aName, PRUint32 aNameLen, nsIVariant* aValue);
|
|
virtual ~xpcProperty() {}
|
|
|
|
private:
|
|
nsString mName;
|
|
nsCOMPtr<nsIVariant> mValue;
|
|
};
|
|
|
|
/***************************************************************************/
|
|
// class here just for static methods
|
|
class XPCConvert
|
|
{
|
|
public:
|
|
static JSBool IsMethodReflectable(const XPTMethodDescriptor& info);
|
|
|
|
/**
|
|
* Convert a native object into a jsval.
|
|
*
|
|
* @param ccx the context for the whole procedure
|
|
* @param d [out] the resulting jsval
|
|
* @param s the native object we're working with
|
|
* @param type the type of object that s is
|
|
* @param iid the interface of s that we want
|
|
* @param scope the default scope to put on the new JSObject's parent
|
|
* chain
|
|
* @param pErr [out] relevant error code, if any.
|
|
*/
|
|
static JSBool NativeData2JS(XPCCallContext& ccx, jsval* d, const void* s,
|
|
const nsXPTType& type, const nsID* iid,
|
|
nsresult* pErr)
|
|
{
|
|
XPCLazyCallContext lccx(ccx);
|
|
return NativeData2JS(lccx, d, s, type, iid, pErr);
|
|
}
|
|
static JSBool NativeData2JS(XPCLazyCallContext& lccx, jsval* d,
|
|
const void* s, const nsXPTType& type,
|
|
const nsID* iid, nsresult* pErr);
|
|
|
|
static JSBool JSData2Native(XPCCallContext& ccx, void* d, jsval s,
|
|
const nsXPTType& type,
|
|
JSBool useAllocator, const nsID* iid,
|
|
nsresult* pErr);
|
|
|
|
/**
|
|
* Convert a native nsISupports into a JSObject.
|
|
*
|
|
* @param ccx the context for the whole procedure
|
|
* @param dest [out] the resulting JSObject
|
|
* @param src the native object we're working with
|
|
* @param iid the interface of src that we want (may be null)
|
|
* @param Interface the interface of src that we want
|
|
* @param cache the wrapper cache for src (may be null, in which case src
|
|
* will be QI'ed to get the cache)
|
|
* @param allowNativeWrapper if true, this method may wrap the resulting
|
|
* JSObject in an XPCNativeWrapper and return that, as needed.
|
|
* @param pErr [out] relevant error code, if any.
|
|
* @param src_is_identity optional performance hint. Set to true only
|
|
* if src is the identity pointer.
|
|
*/
|
|
static JSBool NativeInterface2JSObject(XPCCallContext& ccx,
|
|
jsval* d,
|
|
nsIXPConnectJSObjectHolder** dest,
|
|
xpcObjectHelper& aHelper,
|
|
const nsID* iid,
|
|
XPCNativeInterface** Interface,
|
|
bool allowNativeWrapper,
|
|
nsresult* pErr)
|
|
{
|
|
XPCLazyCallContext lccx(ccx);
|
|
return NativeInterface2JSObject(lccx, d, dest, aHelper, iid, Interface,
|
|
allowNativeWrapper, pErr);
|
|
}
|
|
static JSBool NativeInterface2JSObject(XPCLazyCallContext& lccx,
|
|
jsval* d,
|
|
nsIXPConnectJSObjectHolder** dest,
|
|
xpcObjectHelper& aHelper,
|
|
const nsID* iid,
|
|
XPCNativeInterface** Interface,
|
|
bool allowNativeWrapper,
|
|
nsresult* pErr);
|
|
|
|
static JSBool GetNativeInterfaceFromJSObject(XPCCallContext& ccx,
|
|
void** dest, JSObject* src,
|
|
const nsID* iid,
|
|
nsresult* pErr);
|
|
static JSBool JSObject2NativeInterface(XPCCallContext& ccx,
|
|
void** dest, JSObject* src,
|
|
const nsID* iid,
|
|
nsISupports* aOuter,
|
|
nsresult* pErr);
|
|
static JSBool GetISupportsFromJSObject(JSObject* obj, nsISupports** iface);
|
|
|
|
/**
|
|
* Convert a native array into a jsval.
|
|
*
|
|
* @param ccx the context for the whole procedure
|
|
* @param d [out] the resulting jsval
|
|
* @param s the native array we're working with
|
|
* @param type the type of objects in the array
|
|
* @param iid the interface of each object in the array that we want
|
|
* @param count the number of items in the array
|
|
* @param scope the default scope to put on the new JSObjects' parent chain
|
|
* @param pErr [out] relevant error code, if any.
|
|
*/
|
|
static JSBool NativeArray2JS(XPCLazyCallContext& ccx,
|
|
jsval* d, const void** s,
|
|
const nsXPTType& type, const nsID* iid,
|
|
uint32_t count, nsresult* pErr);
|
|
|
|
static JSBool JSArray2Native(XPCCallContext& ccx, void** d, jsval s,
|
|
uint32_t count, const nsXPTType& type,
|
|
const nsID* iid, nsresult* pErr);
|
|
|
|
static JSBool JSTypedArray2Native(XPCCallContext& ccx,
|
|
void** d,
|
|
JSObject* jsarray,
|
|
uint32_t count,
|
|
const nsXPTType& type,
|
|
nsresult* pErr);
|
|
|
|
static JSBool NativeStringWithSize2JS(JSContext* cx,
|
|
jsval* d, const void* s,
|
|
const nsXPTType& type,
|
|
uint32_t count,
|
|
nsresult* pErr);
|
|
|
|
static JSBool JSStringWithSize2Native(XPCCallContext& ccx, void* d, jsval s,
|
|
uint32_t count, const nsXPTType& type,
|
|
unsigned* pErr);
|
|
|
|
static nsresult JSValToXPCException(XPCCallContext& ccx,
|
|
jsval s,
|
|
const char* ifaceName,
|
|
const char* methodName,
|
|
nsIException** exception);
|
|
|
|
static nsresult JSErrorToXPCException(XPCCallContext& ccx,
|
|
const char* message,
|
|
const char* ifaceName,
|
|
const char* methodName,
|
|
const JSErrorReport* report,
|
|
nsIException** exception);
|
|
|
|
static nsresult ConstructException(nsresult rv, const char* message,
|
|
const char* ifaceName,
|
|
const char* methodName,
|
|
nsISupports* data,
|
|
nsIException** exception,
|
|
JSContext* cx,
|
|
jsval *jsExceptionPtr);
|
|
|
|
private:
|
|
XPCConvert(); // not implemented
|
|
|
|
};
|
|
|
|
/***************************************************************************/
|
|
|
|
// readable string conversions, static methods only
|
|
class XPCStringConvert
|
|
{
|
|
public:
|
|
|
|
// If the string shares the readable's buffer, that buffer will
|
|
// get assigned to *sharedBuffer. Otherwise null will be
|
|
// assigned.
|
|
static jsval ReadableToJSVal(JSContext *cx, const nsAString &readable,
|
|
nsStringBuffer** sharedBuffer);
|
|
|
|
private:
|
|
XPCStringConvert(); // not implemented
|
|
};
|
|
|
|
/***************************************************************************/
|
|
// code for throwing exceptions into JS
|
|
|
|
class XPCThrower
|
|
{
|
|
public:
|
|
static void Throw(nsresult rv, JSContext* cx);
|
|
static void Throw(nsresult rv, XPCCallContext& ccx);
|
|
static void ThrowBadResult(nsresult rv, nsresult result, XPCCallContext& ccx);
|
|
static void ThrowBadParam(nsresult rv, unsigned paramNum, XPCCallContext& ccx);
|
|
static JSBool SetVerbosity(JSBool state)
|
|
{JSBool old = sVerbose; sVerbose = state; return old;}
|
|
|
|
static void BuildAndThrowException(JSContext* cx, nsresult rv, const char* sz);
|
|
static JSBool CheckForPendingException(nsresult result, JSContext *cx);
|
|
|
|
private:
|
|
static void Verbosify(XPCCallContext& ccx,
|
|
char** psz, bool own);
|
|
|
|
static JSBool ThrowExceptionObject(JSContext* cx, nsIException* e);
|
|
|
|
private:
|
|
static JSBool sVerbose;
|
|
};
|
|
|
|
|
|
/***************************************************************************/
|
|
|
|
class XPCJSStack
|
|
{
|
|
public:
|
|
static nsresult
|
|
CreateStack(JSContext* cx, nsIStackFrame** stack);
|
|
|
|
static nsresult
|
|
CreateStackFrameLocation(PRUint32 aLanguage,
|
|
const char* aFilename,
|
|
const char* aFunctionName,
|
|
PRInt32 aLineNumber,
|
|
nsIStackFrame* aCaller,
|
|
nsIStackFrame** stack);
|
|
private:
|
|
XPCJSStack(); // not implemented
|
|
};
|
|
|
|
/***************************************************************************/
|
|
|
|
class nsXPCException :
|
|
public nsIXPCException
|
|
{
|
|
public:
|
|
NS_DEFINE_STATIC_CID_ACCESSOR(NS_XPCEXCEPTION_CID)
|
|
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSIEXCEPTION
|
|
NS_DECL_NSIXPCEXCEPTION
|
|
|
|
static nsresult NewException(const char *aMessage,
|
|
nsresult aResult,
|
|
nsIStackFrame *aLocation,
|
|
nsISupports *aData,
|
|
nsIException** exception);
|
|
|
|
static JSBool NameAndFormatForNSResult(nsresult rv,
|
|
const char** name,
|
|
const char** format);
|
|
|
|
static void* IterateNSResults(nsresult* rv,
|
|
const char** name,
|
|
const char** format,
|
|
void** iterp);
|
|
|
|
static PRUint32 GetNSResultCount();
|
|
|
|
nsXPCException();
|
|
virtual ~nsXPCException();
|
|
|
|
static void InitStatics() { sEverMadeOneFromFactory = false; }
|
|
|
|
protected:
|
|
void Reset();
|
|
private:
|
|
char* mMessage;
|
|
nsresult mResult;
|
|
char* mName;
|
|
nsIStackFrame* mLocation;
|
|
nsISupports* mData;
|
|
char* mFilename;
|
|
int mLineNumber;
|
|
nsIException* mInner;
|
|
bool mInitialized;
|
|
|
|
nsAutoJSValHolder mThrownJSVal;
|
|
|
|
static JSBool sEverMadeOneFromFactory;
|
|
};
|
|
|
|
/***************************************************************************/
|
|
/*
|
|
* nsJSID implements nsIJSID. It is also used by nsJSIID and nsJSCID as a
|
|
* member (as a hidden implementaion detail) to which they delegate many calls.
|
|
*/
|
|
|
|
// Initialization is done on demand, and calling the destructor below is always
|
|
// safe.
|
|
extern void xpc_DestroyJSxIDClassObjects();
|
|
|
|
class nsJSID : public nsIJSID
|
|
{
|
|
public:
|
|
NS_DEFINE_STATIC_CID_ACCESSOR(NS_JS_ID_CID)
|
|
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSIJSID
|
|
|
|
bool InitWithName(const nsID& id, const char *nameString);
|
|
bool SetName(const char* name);
|
|
void SetNameToNoString()
|
|
{NS_ASSERTION(!mName, "name already set"); mName = gNoString;}
|
|
bool NameIsSet() const {return nsnull != mName;}
|
|
const nsID& ID() const {return mID;}
|
|
bool IsValid() const {return !mID.Equals(GetInvalidIID());}
|
|
|
|
static nsJSID* NewID(const char* str);
|
|
static nsJSID* NewID(const nsID& id);
|
|
|
|
nsJSID();
|
|
virtual ~nsJSID();
|
|
protected:
|
|
|
|
void Reset();
|
|
const nsID& GetInvalidIID() const;
|
|
|
|
protected:
|
|
static char gNoString[];
|
|
nsID mID;
|
|
char* mNumber;
|
|
char* mName;
|
|
};
|
|
|
|
// nsJSIID
|
|
|
|
class nsJSIID : public nsIJSIID,
|
|
public nsIXPCScriptable,
|
|
public nsISecurityCheckedComponent
|
|
{
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
|
|
// we manually delagate these to nsJSID
|
|
NS_DECL_NSIJSID
|
|
|
|
// we implement the rest...
|
|
NS_DECL_NSIJSIID
|
|
NS_DECL_NSIXPCSCRIPTABLE
|
|
NS_DECL_NSISECURITYCHECKEDCOMPONENT
|
|
|
|
static nsJSIID* NewID(nsIInterfaceInfo* aInfo);
|
|
|
|
nsJSIID(nsIInterfaceInfo* aInfo);
|
|
nsJSIID(); // not implemented
|
|
virtual ~nsJSIID();
|
|
|
|
private:
|
|
nsCOMPtr<nsIInterfaceInfo> mInfo;
|
|
};
|
|
|
|
// nsJSCID
|
|
|
|
class nsJSCID : public nsIJSCID, public nsIXPCScriptable
|
|
{
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
|
|
// we manually delagate these to nsJSID
|
|
NS_DECL_NSIJSID
|
|
|
|
// we implement the rest...
|
|
NS_DECL_NSIJSCID
|
|
NS_DECL_NSIXPCSCRIPTABLE
|
|
|
|
static nsJSCID* NewID(const char* str);
|
|
|
|
nsJSCID();
|
|
virtual ~nsJSCID();
|
|
|
|
private:
|
|
void ResolveName();
|
|
|
|
private:
|
|
nsJSID mDetails;
|
|
};
|
|
|
|
|
|
/***************************************************************************/
|
|
// XPCJSContextStack is not actually an xpcom object, but xpcom calls are
|
|
// delegated to it as an implementation detail.
|
|
struct XPCJSContextInfo {
|
|
XPCJSContextInfo(JSContext* aCx) :
|
|
cx(aCx),
|
|
savedFrameChain(false),
|
|
suspendDepth(0)
|
|
{}
|
|
JSContext* cx;
|
|
|
|
// Whether the frame chain was saved
|
|
bool savedFrameChain;
|
|
|
|
// Greater than 0 if a request was suspended.
|
|
unsigned suspendDepth;
|
|
};
|
|
|
|
class XPCJSContextStack
|
|
{
|
|
public:
|
|
XPCJSContextStack()
|
|
: mSafeJSContext(NULL)
|
|
, mOwnSafeJSContext(NULL)
|
|
{ }
|
|
|
|
virtual ~XPCJSContextStack();
|
|
|
|
uint32_t Count()
|
|
{
|
|
return mStack.Length();
|
|
}
|
|
|
|
JSContext *Peek()
|
|
{
|
|
return mStack.IsEmpty() ? NULL : mStack[mStack.Length() - 1].cx;
|
|
}
|
|
|
|
JSContext *Pop();
|
|
bool Push(JSContext *cx);
|
|
JSContext *GetSafeJSContext();
|
|
|
|
#ifdef DEBUG
|
|
bool DEBUG_StackHasJSContext(JSContext *cx);
|
|
#endif
|
|
|
|
const InfallibleTArray<XPCJSContextInfo>* GetStack()
|
|
{ return &mStack; }
|
|
|
|
private:
|
|
AutoInfallibleTArray<XPCJSContextInfo, 16> mStack;
|
|
JSContext* mSafeJSContext;
|
|
JSContext* mOwnSafeJSContext;
|
|
};
|
|
|
|
/***************************************************************************/
|
|
|
|
#define NS_XPC_JSCONTEXT_STACK_ITERATOR_CID \
|
|
{ 0x05bae29d, 0x8aef, 0x486d, \
|
|
{ 0x84, 0xaa, 0x53, 0xf4, 0x8f, 0x14, 0x68, 0x11 } }
|
|
|
|
class nsXPCJSContextStackIterator MOZ_FINAL : public nsIJSContextStackIterator
|
|
{
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSIJSCONTEXTSTACKITERATOR
|
|
|
|
private:
|
|
const InfallibleTArray<XPCJSContextInfo> *mStack;
|
|
PRUint32 mPosition;
|
|
};
|
|
|
|
/***************************************************************************/
|
|
#include "nsIScriptSecurityManager.h"
|
|
|
|
class BackstagePass : public nsIScriptObjectPrincipal,
|
|
public nsIXPCScriptable,
|
|
public nsIClassInfo
|
|
{
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSIXPCSCRIPTABLE
|
|
NS_DECL_NSICLASSINFO
|
|
|
|
virtual nsIPrincipal* GetPrincipal() {
|
|
return mPrincipal;
|
|
}
|
|
|
|
BackstagePass(nsIPrincipal *prin) :
|
|
mPrincipal(prin)
|
|
{
|
|
}
|
|
|
|
virtual ~BackstagePass() { }
|
|
|
|
private:
|
|
nsCOMPtr<nsIPrincipal> mPrincipal;
|
|
};
|
|
// 'Components' object
|
|
|
|
class nsXPCComponents : public nsIXPCComponents,
|
|
public nsIXPCScriptable,
|
|
public nsIClassInfo,
|
|
public nsISecurityCheckedComponent
|
|
{
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSIXPCCOMPONENTS
|
|
NS_DECL_NSIXPCSCRIPTABLE
|
|
NS_DECL_NSICLASSINFO
|
|
NS_DECL_NSISECURITYCHECKEDCOMPONENT
|
|
|
|
public:
|
|
static JSBool
|
|
AttachComponentsObject(XPCCallContext& ccx,
|
|
XPCWrappedNativeScope* aScope,
|
|
JSObject* aGlobal);
|
|
|
|
void SystemIsBeingShutDown() {ClearMembers();}
|
|
|
|
virtual ~nsXPCComponents();
|
|
|
|
private:
|
|
nsXPCComponents(XPCWrappedNativeScope* aScope);
|
|
void ClearMembers();
|
|
|
|
private:
|
|
friend class XPCWrappedNativeScope;
|
|
XPCWrappedNativeScope* mScope;
|
|
nsXPCComponents_Interfaces* mInterfaces;
|
|
nsXPCComponents_InterfacesByID* mInterfacesByID;
|
|
nsXPCComponents_Classes* mClasses;
|
|
nsXPCComponents_ClassesByID* mClassesByID;
|
|
nsXPCComponents_Results* mResults;
|
|
nsXPCComponents_ID* mID;
|
|
nsXPCComponents_Exception* mException;
|
|
nsXPCComponents_Constructor* mConstructor;
|
|
nsXPCComponents_Utils* mUtils;
|
|
};
|
|
|
|
|
|
/***************************************************************************/
|
|
|
|
extern JSObject*
|
|
xpc_NewIDObject(JSContext *cx, JSObject* jsobj, const nsID& aID);
|
|
|
|
extern const nsID*
|
|
xpc_JSObjectToID(JSContext *cx, JSObject* obj);
|
|
|
|
extern JSBool
|
|
xpc_JSObjectIsID(JSContext *cx, JSObject* obj);
|
|
|
|
/***************************************************************************/
|
|
// in xpcdebug.cpp
|
|
|
|
extern JSBool
|
|
xpc_DumpJSStack(JSContext* cx, JSBool showArgs, JSBool showLocals,
|
|
JSBool showThisProps);
|
|
|
|
// Return a newly-allocated string containing a representation of the
|
|
// current JS stack. It is the *caller's* responsibility to free this
|
|
// string with JS_smprintf_free().
|
|
extern char*
|
|
xpc_PrintJSStack(JSContext* cx, JSBool showArgs, JSBool showLocals,
|
|
JSBool showThisProps);
|
|
|
|
extern JSBool
|
|
xpc_DumpEvalInJSStackFrame(JSContext* cx, uint32_t frameno, const char* text);
|
|
|
|
extern JSBool
|
|
xpc_DumpJSObject(JSObject* obj);
|
|
|
|
extern JSBool
|
|
xpc_InstallJSDebuggerKeywordHandler(JSRuntime* rt);
|
|
|
|
/***************************************************************************/
|
|
|
|
// Definition of nsScriptError, defined here because we lack a place to put
|
|
// XPCOM objects associated with the JavaScript engine.
|
|
class nsScriptError : public nsIScriptError {
|
|
public:
|
|
nsScriptError();
|
|
|
|
virtual ~nsScriptError();
|
|
|
|
// TODO - do something reasonable on getting null from these babies.
|
|
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSICONSOLEMESSAGE
|
|
NS_DECL_NSISCRIPTERROR
|
|
|
|
private:
|
|
nsString mMessage;
|
|
nsString mSourceName;
|
|
PRUint32 mLineNumber;
|
|
nsString mSourceLine;
|
|
PRUint32 mColumnNumber;
|
|
PRUint32 mFlags;
|
|
nsCString mCategory;
|
|
PRUint64 mOuterWindowID;
|
|
PRUint64 mInnerWindowID;
|
|
PRInt64 mTimeStamp;
|
|
};
|
|
|
|
/******************************************************************************
|
|
* Handles pre/post script processing and the setting/resetting the error
|
|
* reporter
|
|
*/
|
|
class NS_STACK_CLASS AutoScriptEvaluate
|
|
{
|
|
public:
|
|
/**
|
|
* Saves the JSContext as well as initializing our state
|
|
* @param cx The JSContext, this can be null, we don't do anything then
|
|
*/
|
|
AutoScriptEvaluate(JSContext * cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
|
: mJSContext(cx), mState(0), mErrorReporterSet(false),
|
|
mEvaluated(false), mContextHasThread(0) {
|
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
}
|
|
|
|
/**
|
|
* Does the pre script evaluation and sets the error reporter if given
|
|
* This function should only be called once, and will assert if called
|
|
* more than once
|
|
* @param errorReporter the error reporter callback function to set
|
|
*/
|
|
|
|
bool StartEvaluating(JSObject *scope, JSErrorReporter errorReporter = nsnull);
|
|
/**
|
|
* Does the post script evaluation and resets the error reporter
|
|
*/
|
|
~AutoScriptEvaluate();
|
|
private:
|
|
JSContext* mJSContext;
|
|
JSExceptionState* mState;
|
|
bool mErrorReporterSet;
|
|
bool mEvaluated;
|
|
intptr_t mContextHasThread;
|
|
JSAutoEnterCompartment mEnterCompartment;
|
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
|
|
// No copying or assignment allowed
|
|
AutoScriptEvaluate(const AutoScriptEvaluate &) MOZ_DELETE;
|
|
AutoScriptEvaluate & operator =(const AutoScriptEvaluate &) MOZ_DELETE;
|
|
};
|
|
|
|
/***************************************************************************/
|
|
class NS_STACK_CLASS AutoResolveName
|
|
{
|
|
public:
|
|
AutoResolveName(XPCCallContext& ccx, jsid name
|
|
MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
|
|
mOld(XPCJSRuntime::Get()->SetResolveName(name)),
|
|
mCheck(name) {
|
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
|
}
|
|
~AutoResolveName()
|
|
{
|
|
#ifdef DEBUG
|
|
jsid old =
|
|
#endif
|
|
XPCJSRuntime::Get()->SetResolveName(mOld);
|
|
NS_ASSERTION(old == mCheck, "Bad Nesting!");
|
|
}
|
|
|
|
private:
|
|
jsid mOld;
|
|
jsid mCheck;
|
|
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
|
|
};
|
|
|
|
/***************************************************************************/
|
|
class XPCMarkableJSVal
|
|
{
|
|
public:
|
|
XPCMarkableJSVal(jsval val) : mVal(val), mValPtr(&mVal) {}
|
|
XPCMarkableJSVal(jsval *pval) : mVal(JSVAL_VOID), mValPtr(pval) {}
|
|
~XPCMarkableJSVal() {}
|
|
void Mark() {}
|
|
void TraceJS(JSTracer* trc)
|
|
{
|
|
JS_CALL_VALUE_TRACER(trc, *mValPtr, "XPCMarkableJSVal");
|
|
}
|
|
void AutoTrace(JSTracer* trc) {}
|
|
private:
|
|
XPCMarkableJSVal(); // not implemented
|
|
jsval mVal;
|
|
jsval* mValPtr;
|
|
};
|
|
|
|
/***************************************************************************/
|
|
// AutoMarkingPtr is the base class for the various AutoMarking pointer types
|
|
// below. This system allows us to temporarily protect instances of our garbage
|
|
// collected types after they are constructed but before they are safely
|
|
// attached to other rooted objects.
|
|
// This base class has pure virtual support for marking.
|
|
|
|
class AutoMarkingPtr
|
|
{
|
|
public:
|
|
AutoMarkingPtr(XPCCallContext& ccx) {
|
|
mRoot = XPCJSRuntime::Get()->GetAutoRootsAdr();
|
|
mNext = *mRoot;
|
|
*mRoot = this;
|
|
}
|
|
|
|
virtual ~AutoMarkingPtr() {
|
|
if (mRoot) {
|
|
MOZ_ASSERT(*mRoot == this);
|
|
*mRoot = mNext;
|
|
}
|
|
}
|
|
|
|
void TraceJSAll(JSTracer* trc) {
|
|
for (AutoMarkingPtr *cur = this; cur; cur = cur->mNext)
|
|
cur->TraceJS(trc);
|
|
}
|
|
|
|
void MarkAfterJSFinalizeAll() {
|
|
for (AutoMarkingPtr *cur = this; cur; cur = cur->mNext)
|
|
cur->MarkAfterJSFinalize();
|
|
}
|
|
|
|
protected:
|
|
virtual void TraceJS(JSTracer* trc) = 0;
|
|
virtual void MarkAfterJSFinalize() = 0;
|
|
|
|
private:
|
|
AutoMarkingPtr** mRoot;
|
|
AutoMarkingPtr* mNext;
|
|
};
|
|
|
|
template<class T>
|
|
class TypedAutoMarkingPtr : public AutoMarkingPtr
|
|
{
|
|
public:
|
|
TypedAutoMarkingPtr(XPCCallContext& ccx) : AutoMarkingPtr(ccx), mPtr(nsnull) {}
|
|
TypedAutoMarkingPtr(XPCCallContext& ccx, T* ptr) : AutoMarkingPtr(ccx), mPtr(ptr) {}
|
|
|
|
T* get() const { return mPtr; }
|
|
operator T *() const { return mPtr; }
|
|
T* operator->() const { return mPtr; }
|
|
|
|
TypedAutoMarkingPtr<T>& operator =(T* ptr) { mPtr = ptr; return *this; }
|
|
|
|
protected:
|
|
virtual void TraceJS(JSTracer* trc)
|
|
{
|
|
if (mPtr) {
|
|
mPtr->TraceJS(trc);
|
|
mPtr->AutoTrace(trc);
|
|
}
|
|
}
|
|
|
|
virtual void MarkAfterJSFinalize()
|
|
{
|
|
if (mPtr)
|
|
mPtr->Mark();
|
|
}
|
|
|
|
private:
|
|
T* mPtr;
|
|
};
|
|
|
|
typedef TypedAutoMarkingPtr<XPCNativeInterface> AutoMarkingNativeInterfacePtr;
|
|
typedef TypedAutoMarkingPtr<XPCNativeSet> AutoMarkingNativeSetPtr;
|
|
typedef TypedAutoMarkingPtr<XPCWrappedNative> AutoMarkingWrappedNativePtr;
|
|
typedef TypedAutoMarkingPtr<XPCWrappedNativeTearOff> AutoMarkingWrappedNativeTearOffPtr;
|
|
typedef TypedAutoMarkingPtr<XPCWrappedNativeProto> AutoMarkingWrappedNativeProtoPtr;
|
|
typedef TypedAutoMarkingPtr<XPCMarkableJSVal> AutoMarkingJSVal;
|
|
typedef TypedAutoMarkingPtr<XPCNativeScriptableInfo> AutoMarkingNativeScriptableInfoPtr;
|
|
|
|
template<class T>
|
|
class ArrayAutoMarkingPtr : public AutoMarkingPtr
|
|
{
|
|
public:
|
|
ArrayAutoMarkingPtr(XPCCallContext& ccx)
|
|
: AutoMarkingPtr(ccx), mPtr(nsnull), mCount(0) {}
|
|
ArrayAutoMarkingPtr(XPCCallContext& ccx, T** ptr, PRUint32 count, bool clear)
|
|
: AutoMarkingPtr(ccx), mPtr(ptr), mCount(count)
|
|
{
|
|
if (!mPtr) mCount = 0;
|
|
else if (clear) memset(mPtr, 0, mCount*sizeof(T*));
|
|
}
|
|
|
|
T** get() const { return mPtr; }
|
|
operator T **() const { return mPtr; }
|
|
T** operator->() const { return mPtr; }
|
|
|
|
ArrayAutoMarkingPtr<T>& operator =(const ArrayAutoMarkingPtr<T> &other)
|
|
{
|
|
mPtr = other.mPtr;
|
|
mCount = other.mCount;
|
|
return *this;
|
|
}
|
|
|
|
protected:
|
|
virtual void TraceJS(JSTracer* trc)
|
|
{
|
|
for (PRUint32 i = 0; i < mCount; i++) {
|
|
if (mPtr[i]) {
|
|
mPtr[i]->TraceJS(trc);
|
|
mPtr[i]->AutoTrace(trc);
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual void MarkAfterJSFinalize()
|
|
{
|
|
for (PRUint32 i = 0; i < mCount; i++) {
|
|
if (mPtr[i])
|
|
mPtr[i]->Mark();
|
|
}
|
|
}
|
|
|
|
private:
|
|
T** mPtr;
|
|
PRUint32 mCount;
|
|
};
|
|
|
|
typedef ArrayAutoMarkingPtr<XPCNativeInterface> AutoMarkingNativeInterfacePtrArrayPtr;
|
|
|
|
#define AUTO_MARK_JSVAL_HELPER2(tok, line) tok##line
|
|
#define AUTO_MARK_JSVAL_HELPER(tok, line) AUTO_MARK_JSVAL_HELPER2(tok, line)
|
|
|
|
#define AUTO_MARK_JSVAL(ccx, val) \
|
|
XPCMarkableJSVal AUTO_MARK_JSVAL_HELPER(_val_,__LINE__)(val); \
|
|
AutoMarkingJSVal AUTO_MARK_JSVAL_HELPER(_automarker_,__LINE__) \
|
|
(ccx, &AUTO_MARK_JSVAL_HELPER(_val_,__LINE__))
|
|
|
|
/***************************************************************************/
|
|
// Allocates a string that grants all access ("AllAccess")
|
|
|
|
extern char* xpc_CloneAllAccess();
|
|
/***************************************************************************/
|
|
// Returns access if wideName is in list
|
|
|
|
extern char * xpc_CheckAccessList(const PRUnichar* wideName, const char* list[]);
|
|
|
|
/***************************************************************************/
|
|
// in xpcvariant.cpp...
|
|
|
|
// {1809FD50-91E8-11d5-90F9-0010A4E73D9A}
|
|
#define XPCVARIANT_IID \
|
|
{0x1809fd50, 0x91e8, 0x11d5, \
|
|
{ 0x90, 0xf9, 0x0, 0x10, 0xa4, 0xe7, 0x3d, 0x9a } }
|
|
|
|
// {DC524540-487E-4501-9AC7-AAA784B17C1C}
|
|
#define XPCVARIANT_CID \
|
|
{0xdc524540, 0x487e, 0x4501, \
|
|
{ 0x9a, 0xc7, 0xaa, 0xa7, 0x84, 0xb1, 0x7c, 0x1c } }
|
|
|
|
class XPCVariant : public nsIVariant
|
|
{
|
|
public:
|
|
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
|
NS_DECL_NSIVARIANT
|
|
NS_DECL_CYCLE_COLLECTION_CLASS(XPCVariant)
|
|
|
|
// If this class ever implements nsIWritableVariant, take special care with
|
|
// the case when mJSVal is JSVAL_STRING, since we don't own the data in
|
|
// that case.
|
|
|
|
// We #define and iid so that out module local code can use QI to detect
|
|
// if a given nsIVariant is in fact an XPCVariant.
|
|
NS_DECLARE_STATIC_IID_ACCESSOR(XPCVARIANT_IID)
|
|
|
|
static XPCVariant* newVariant(XPCCallContext& ccx, jsval aJSVal);
|
|
|
|
/**
|
|
* This getter clears the gray bit before handing out the jsval if the jsval
|
|
* represents a JSObject. That means that the object is guaranteed to be
|
|
* kept alive past the next CC.
|
|
*/
|
|
jsval GetJSVal() const
|
|
{if (!JSVAL_IS_PRIMITIVE(mJSVal))
|
|
xpc_UnmarkGrayObject(JSVAL_TO_OBJECT(mJSVal));
|
|
return mJSVal;}
|
|
|
|
/**
|
|
* This getter does not change the color of the jsval (if it represents a
|
|
* JSObject) meaning that the value returned is not guaranteed to be kept
|
|
* alive past the next CC.
|
|
*
|
|
* This should only be called if you are certain that the return value won't
|
|
* be passed into a JS API function and that it won't be stored without
|
|
* being rooted (or otherwise signaling the stored value to the CC).
|
|
*/
|
|
jsval GetJSValPreserveColor() const {return mJSVal;}
|
|
|
|
XPCVariant(XPCCallContext& ccx, jsval aJSVal);
|
|
|
|
/**
|
|
* Convert a variant into a jsval.
|
|
*
|
|
* @param ccx the context for the whole procedure
|
|
* @param variant the variant to convert
|
|
* @param scope the default scope to put on the new JSObject's parent chain
|
|
* @param pErr [out] relevant error code, if any.
|
|
* @param pJSVal [out] the resulting jsval.
|
|
*/
|
|
static JSBool VariantDataToJS(XPCLazyCallContext& lccx,
|
|
nsIVariant* variant,
|
|
nsresult* pErr, jsval* pJSVal);
|
|
|
|
bool IsPurple()
|
|
{
|
|
return mRefCnt.IsPurple();
|
|
}
|
|
|
|
void RemovePurple()
|
|
{
|
|
mRefCnt.RemovePurple();
|
|
}
|
|
|
|
void SetCCGeneration(PRUint32 aGen)
|
|
{
|
|
mCCGeneration = aGen;
|
|
}
|
|
|
|
PRUint32 CCGeneration() { return mCCGeneration; }
|
|
protected:
|
|
virtual ~XPCVariant() { }
|
|
|
|
JSBool InitializeData(XPCCallContext& ccx);
|
|
|
|
protected:
|
|
nsDiscriminatedUnion mData;
|
|
jsval mJSVal;
|
|
bool mReturnRawObject : 1;
|
|
PRUint32 mCCGeneration : 31;
|
|
};
|
|
|
|
NS_DEFINE_STATIC_IID_ACCESSOR(XPCVariant, XPCVARIANT_IID)
|
|
|
|
class XPCTraceableVariant: public XPCVariant,
|
|
public XPCRootSetElem
|
|
{
|
|
public:
|
|
XPCTraceableVariant(XPCCallContext& ccx, jsval aJSVal)
|
|
: XPCVariant(ccx, aJSVal)
|
|
{
|
|
ccx.GetRuntime()->AddVariantRoot(this);
|
|
}
|
|
|
|
virtual ~XPCTraceableVariant();
|
|
|
|
void TraceJS(JSTracer* trc);
|
|
static void GetTraceName(JSTracer* trc, char *buf, size_t bufsize);
|
|
};
|
|
|
|
/***************************************************************************/
|
|
|
|
#define PRINCIPALHOLDER_IID \
|
|
{0xbf109f49, 0xf94a, 0x43d8, {0x93, 0xdb, 0xe4, 0x66, 0x49, 0xc5, 0xd9, 0x7d}}
|
|
|
|
class PrincipalHolder : public nsIScriptObjectPrincipal
|
|
{
|
|
public:
|
|
NS_DECLARE_STATIC_IID_ACCESSOR(PRINCIPALHOLDER_IID)
|
|
|
|
PrincipalHolder(nsIPrincipal *holdee)
|
|
: mHoldee(holdee)
|
|
{
|
|
}
|
|
virtual ~PrincipalHolder() { }
|
|
|
|
NS_DECL_ISUPPORTS
|
|
|
|
nsIPrincipal *GetPrincipal();
|
|
|
|
private:
|
|
nsCOMPtr<nsIPrincipal> mHoldee;
|
|
};
|
|
|
|
NS_DEFINE_STATIC_IID_ACCESSOR(PrincipalHolder, PRINCIPALHOLDER_IID)
|
|
|
|
/***************************************************************************/
|
|
// Utilities
|
|
|
|
inline void *
|
|
xpc_GetJSPrivate(JSObject *obj)
|
|
{
|
|
return js::GetObjectPrivate(obj);
|
|
}
|
|
|
|
namespace xpc {
|
|
struct SandboxOptions {
|
|
SandboxOptions()
|
|
: wantXrays(true)
|
|
, wantComponents(true)
|
|
, wantXHRConstructor(false)
|
|
, proto(NULL)
|
|
{ }
|
|
|
|
bool wantXrays;
|
|
bool wantComponents;
|
|
bool wantXHRConstructor;
|
|
JSObject* proto;
|
|
nsCString sandboxName;
|
|
};
|
|
}
|
|
|
|
// Helper for creating a sandbox object to use for evaluating
|
|
// untrusted code completely separated from all other code in the
|
|
// system using xpc_EvalInSandbox(). Takes the JSContext on which to
|
|
// do setup etc on, puts the sandbox object in *vp (which must be
|
|
// rooted by the caller), and uses the principal that's either
|
|
// directly passed in prinOrSop or indirectly as an
|
|
// nsIScriptObjectPrincipal holding the principal. If no principal is
|
|
// reachable through prinOrSop, a new null principal will be created
|
|
// and used.
|
|
nsresult
|
|
xpc_CreateSandboxObject(JSContext * cx, jsval * vp, nsISupports *prinOrSop,
|
|
xpc::SandboxOptions& options);
|
|
// Helper for evaluating scripts in a sandbox object created with
|
|
// xpc_CreateSandboxObject(). The caller is responsible of ensuring
|
|
// that *rval doesn't get collected during the call or usage after the
|
|
// call. This helper will use filename and lineNo for error reporting,
|
|
// and if no filename is provided it will use the codebase from the
|
|
// principal and line number 1 as a fallback. if returnStringOnly is
|
|
// true, then the result in *rval, or the exception in cx->exception
|
|
// will be coerced into strings. If an exception is thrown converting
|
|
// an exception to a string, evalInSandbox will return an NS_ERROR_*
|
|
// result, and cx->exception will be empty.
|
|
nsresult
|
|
xpc_EvalInSandbox(JSContext *cx, JSObject *sandbox, const nsAString& source,
|
|
const char *filename, PRInt32 lineNo,
|
|
JSVersion jsVersion, bool returnStringOnly, jsval *rval);
|
|
|
|
/***************************************************************************/
|
|
// Inlined utilities.
|
|
|
|
inline JSBool
|
|
xpc_ForcePropertyResolve(JSContext* cx, JSObject* obj, jsid id);
|
|
|
|
inline jsid
|
|
GetRTIdByIndex(JSContext *cx, unsigned index);
|
|
|
|
// Wrapper for JS_NewObject to mark the new object as system when parent is
|
|
// also a system object. If uniqueType is specified then a new type object will
|
|
// be created which is used only by the result, so that its property types
|
|
// will be tracked precisely.
|
|
inline JSObject*
|
|
xpc_NewSystemInheritingJSObject(JSContext *cx, JSClass *clasp, JSObject *proto,
|
|
bool uniqueType, JSObject *parent);
|
|
|
|
nsISupports *
|
|
XPC_GetIdentityObject(JSContext *cx, JSObject *obj);
|
|
|
|
namespace xpc {
|
|
|
|
class CompartmentPrivate
|
|
{
|
|
public:
|
|
typedef nsTHashtable<nsPtrHashKey<JSObject> > DOMExpandoMap;
|
|
|
|
CompartmentPrivate(bool wantXrays)
|
|
: wantXrays(wantXrays)
|
|
{
|
|
MOZ_COUNT_CTOR(xpc::CompartmentPrivate);
|
|
}
|
|
|
|
~CompartmentPrivate();
|
|
|
|
bool wantXrays;
|
|
nsAutoPtr<JSObject2JSObjectMap> waiverWrapperMap;
|
|
nsAutoPtr<DOMExpandoMap> domExpandoMap;
|
|
|
|
bool RegisterDOMExpandoObject(JSObject *expando) {
|
|
if (!domExpandoMap) {
|
|
domExpandoMap = new DOMExpandoMap();
|
|
domExpandoMap->Init(8);
|
|
}
|
|
return domExpandoMap->PutEntry(expando, mozilla::fallible_t());
|
|
}
|
|
void RemoveDOMExpandoObject(JSObject *expando) {
|
|
if (domExpandoMap)
|
|
domExpandoMap->RemoveEntry(expando);
|
|
}
|
|
|
|
const nsACString& GetLocation() {
|
|
if (locationURI) {
|
|
if (NS_FAILED(locationURI->GetSpec(location)))
|
|
location = NS_LITERAL_CSTRING("<unknown location>");
|
|
locationURI = nsnull;
|
|
}
|
|
return location;
|
|
}
|
|
void SetLocation(const nsACString& aLocation) {
|
|
if (aLocation.IsEmpty())
|
|
return;
|
|
if (!location.IsEmpty() || locationURI)
|
|
return;
|
|
location = aLocation;
|
|
}
|
|
void SetLocation(nsIURI *aLocationURI) {
|
|
if (!aLocationURI)
|
|
return;
|
|
if (!location.IsEmpty() || locationURI)
|
|
return;
|
|
locationURI = aLocationURI;
|
|
}
|
|
|
|
private:
|
|
nsCString location;
|
|
nsCOMPtr<nsIURI> locationURI;
|
|
};
|
|
|
|
inline CompartmentPrivate*
|
|
GetCompartmentPrivate(JSCompartment *compartment)
|
|
{
|
|
MOZ_ASSERT(compartment);
|
|
void *priv = JS_GetCompartmentPrivate(compartment);
|
|
return static_cast<CompartmentPrivate*>(priv);
|
|
}
|
|
|
|
inline CompartmentPrivate*
|
|
GetCompartmentPrivate(JSObject *object)
|
|
{
|
|
MOZ_ASSERT(object);
|
|
JSCompartment *compartment = js::GetObjectCompartment(object);
|
|
|
|
MOZ_ASSERT(compartment);
|
|
return GetCompartmentPrivate(compartment);
|
|
}
|
|
|
|
}
|
|
|
|
/***************************************************************************/
|
|
// Inlines use the above - include last.
|
|
|
|
#include "XPCInlines.h"
|
|
|
|
/***************************************************************************/
|
|
// Maps have inlines that use the above - include last.
|
|
|
|
#include "XPCMaps.h"
|
|
|
|
/***************************************************************************/
|
|
|
|
#endif /* xpcprivate_h___ */
|