gecko-dev/js/xpconnect/src/xpcprivate.h
Andrew McCreight 3e7a8efdae Bug 1289550 - Remove "fun little hack" from XPCNativeSetKey. r=bz
XPCNativeSetKey has a huge comment about this weird hack it does,
where it tags the first 16 bytes with a magic value. The purpose of
this seem to be that PLDHashtable used to require that the Match()
operation handle both the desired "key" type and the actual entry type
(NativeSetMap::Entry in this case), with the latter needed for
resizing. However, that duality in the match operation has not been
needed since bug 374906, which landed in 2007, so this class can be
greatly simplified.

IsAKey() can be replaced with true, which simplifies some hash
operations.
2016-07-27 10:11:40 -07:00

3610 lines
120 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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.
*
* 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.
*
* 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/Alignment.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/Atomics.h"
#include "mozilla/Attributes.h"
#include "mozilla/CycleCollectedJSRuntime.h"
#include "mozilla/GuardObjects.h"
#include "mozilla/Maybe.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Preferences.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/dom/ScriptSettings.h"
#include <math.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "xpcpublic.h"
#include "js/TracingAPI.h"
#include "js/WeakMapPtr.h"
#include "PLDHashTable.h"
#include "nscore.h"
#include "nsXPCOM.h"
#include "nsAutoPtr.h"
#include "nsCycleCollectionParticipant.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 "nsIXPCScriptable.h"
#include "nsIObserver.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 "prenv.h"
#include "prclist.h"
#include "prcvar.h"
#include "nsString.h"
#include "nsReadableUtils.h"
#include "nsXPIDLString.h"
#include "MainThreadUtils.h"
#include "nsIConsoleService.h"
#include "nsIScriptError.h"
#include "nsIException.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 "nsIPrincipal.h"
#include "nsJSPrincipals.h"
#include "nsIScriptObjectPrincipal.h"
#include "xpcObjectHelper.h"
#include "SandboxPrivate.h"
#include "BackstagePass.h"
#include "nsAXPCNativeCallContext.h"
#ifdef XP_WIN
// Nasty MS defines
#ifdef GetClassInfo
#undef GetClassInfo
#endif
#ifdef GetClassName
#undef GetClassName
#endif
#endif /* XP_WIN */
/***************************************************************************/
// default initial sizes for maps (hashtables)
#define XPC_JS_MAP_LENGTH 32
#define XPC_JS_CLASS_MAP_LENGTH 32
#define XPC_NATIVE_MAP_LENGTH 8
#define XPC_NATIVE_PROTO_MAP_LENGTH 8
#define XPC_DYING_NATIVE_PROTO_MAP_LENGTH 8
#define XPC_DETACHED_NATIVE_PROTO_MAP_LENGTH 16
#define XPC_NATIVE_INTERFACE_MAP_LENGTH 32
#define XPC_NATIVE_SET_MAP_LENGTH 32
#define XPC_NATIVE_JSCLASS_MAP_LENGTH 16
#define XPC_THIS_TRANSLATOR_MAP_LENGTH 4
#define XPC_WRAPPER_MAP_LENGTH 8
/***************************************************************************/
// data declarations...
extern const char XPC_CONTEXT_STACK_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[];
/***************************************************************************/
// 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 = nullptr; \
*dest = result; \
return (result || !src) ? NS_OK : NS_ERROR_OUT_OF_MEMORY
#define WRAPPER_FLAGS JSCLASS_HAS_PRIVATE
// If IS_WN_CLASS for the JSClass of an object is true, the object is a
// wrappednative wrapper, holding the XPCWrappedNative in its private slot.
static inline bool IS_WN_CLASS(const js::Class* clazz)
{
return clazz->isWrappedNative();
}
static inline bool IS_WN_REFLECTOR(JSObject* obj)
{
return IS_WN_CLASS(js::GetObjectClass(obj));
}
/***************************************************************************
****************************************************************************
*
* 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.
class nsXPConnect final : public nsIXPConnect
{
public:
// all the interface method declarations...
NS_DECL_ISUPPORTS
NS_DECL_NSIXPCONNECT
// non-interface implementation
public:
// These get non-addref'd pointers
static nsXPConnect* XPConnect()
{
// Do a release-mode assert that we're not doing anything significant in
// XPConnect off the main thread. If you're an extension developer hitting
// this, you need to change your code. See bug 716167.
if (!MOZ_LIKELY(NS_IsMainThread()))
MOZ_CRASH();
return gSelf;
}
static XPCJSRuntime* GetRuntimeInstance();
XPCJSRuntime* GetRuntime() {return mRuntime;}
static bool IsISupportsDescendant(nsIInterfaceInfo* info);
static nsIScriptSecurityManager* SecurityManager()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(gScriptSecurityManager);
return gScriptSecurityManager;
}
static nsIPrincipal* SystemPrincipal()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(gSystemPrincipal);
return gSystemPrincipal;
}
// 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();
// Called by module code on dll shutdown.
static void ReleaseXPConnectSingleton();
bool IsShuttingDown() const {return mShuttingDown;}
nsresult GetInfoForIID(const nsIID * aIID, nsIInterfaceInfo** info);
nsresult GetInfoForName(const char * name, nsIInterfaceInfo** info);
virtual nsIPrincipal* GetPrincipal(JSObject* obj,
bool allowShortCircuit) const override;
void RecordTraversal(void* p, nsISupports* s);
virtual char* DebugPrintJSStack(bool showArgs,
bool showLocals,
bool showThisProps) override;
protected:
virtual ~nsXPConnect();
nsXPConnect();
private:
// Singleton instance
static nsXPConnect* gSelf;
static bool gOnceAliveNowDead;
XPCJSRuntime* mRuntime;
bool mShuttingDown;
public:
static nsIScriptSecurityManager* gScriptSecurityManager;
static nsIPrincipal* gSystemPrincipal;
};
/***************************************************************************/
class XPCRootSetElem
{
public:
XPCRootSetElem()
{
#ifdef DEBUG
mNext = nullptr;
mSelfp = nullptr;
#endif
}
~XPCRootSetElem()
{
MOZ_ASSERT(!mNext, "Must be unlinked");
MOZ_ASSERT(!mSelfp, "Must be unlinked");
}
inline XPCRootSetElem* GetNextRoot() { return mNext; }
void AddToRootSet(XPCRootSetElem** listHead);
void RemoveFromRootSet();
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.
class WatchdogManager;
enum WatchdogTimestampCategory
{
TimestampRuntimeStateChange = 0,
TimestampWatchdogWakeup,
TimestampWatchdogHibernateStart,
TimestampWatchdogHibernateStop,
TimestampCount
};
class AsyncFreeSnowWhite;
template <class StringType>
class ShortLivedStringBuffer
{
public:
StringType* Create()
{
for (uint32_t i = 0; i < ArrayLength(mStrings); ++i) {
if (!mStrings[i]) {
mStrings[i].emplace();
return mStrings[i].ptr();
}
}
// All our internal string wrappers are used, allocate a new string.
return new StringType();
}
void Destroy(StringType* string)
{
for (uint32_t i = 0; i < ArrayLength(mStrings); ++i) {
if (mStrings[i] && mStrings[i].ptr() == string) {
// One of our internal strings is no longer in use, mark
// it as such and free its data.
mStrings[i].reset();
return;
}
}
// We're done with a string that's not one of our internal
// strings, delete it.
delete string;
}
~ShortLivedStringBuffer()
{
#ifdef DEBUG
for (uint32_t i = 0; i < ArrayLength(mStrings); ++i) {
MOZ_ASSERT(!mStrings[i], "Short lived string still in use");
}
#endif
}
private:
mozilla::Maybe<StringType> mStrings[2];
};
class XPCJSRuntime final : public mozilla::CycleCollectedJSRuntime
{
public:
static XPCJSRuntime* newXPCJSRuntime();
static XPCJSRuntime* Get() { return nsXPConnect::XPConnect()->GetRuntime(); }
void RemoveWrappedJS(nsXPCWrappedJS* wrapper);
void AssertInvalidWrappedJSNotInTable(nsXPCWrappedJS* wrapper) const;
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* GetMultiCompartmentWrappedJSMap() 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;}
bool JSContextInitialized(JSContext* cx);
virtual bool
DescribeCustomObjects(JSObject* aObject, const js::Class* aClasp,
char (&aName)[72]) const override;
virtual bool
NoteCustomGCThingXPCOMChildren(const js::Class* aClasp, JSObject* aObj,
nsCycleCollectionTraversalCallback& aCb) const override;
virtual void BeforeProcessTask(bool aMightBlock) override;
virtual void AfterProcessTask(uint32_t aNewRecursionDepth) override;
/**
* Infrastructure for classes that need to defer part of the finalization
* until after the GC has run, for example for objects that we don't want to
* destroy during the GC.
*/
public:
bool 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_EVAL ,
IDX_CONTROLLERS ,
IDX_REALFRAMEELEMENT ,
IDX_LENGTH ,
IDX_NAME ,
IDX_UNDEFINED ,
IDX_EMPTYSTRING ,
IDX_FILENAME ,
IDX_LINENUMBER ,
IDX_COLUMNNUMBER ,
IDX_STACK ,
IDX_MESSAGE ,
IDX_LASTINDEX ,
IDX_TOTAL_COUNT // just a count of the above
};
JS::HandleId GetStringID(unsigned index) const
{
MOZ_ASSERT(index < IDX_TOTAL_COUNT, "index out of range");
// fromMarkedLocation() is safe because the string is interned.
return JS::HandleId::fromMarkedLocation(&mStrIDs[index]);
}
JS::HandleValue GetStringJSVal(unsigned index) const
{
MOZ_ASSERT(index < IDX_TOTAL_COUNT, "index out of range");
// fromMarkedLocation() is safe because the string is interned.
return JS::HandleValue::fromMarkedLocation(&mStrJSVals[index]);
}
const char* GetStringName(unsigned index) const
{
MOZ_ASSERT(index < IDX_TOTAL_COUNT, "index out of range");
return mStrings[index];
}
virtual bool UsefulToMergeZones() const override;
void TraceNativeBlackRoots(JSTracer* trc) override;
void TraceAdditionalNativeGrayRoots(JSTracer* aTracer) override;
void TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback& cb) override;
void UnmarkSkippableJSHolders();
void PrepareForForgetSkippable() override;
void BeginCycleCollectionCallback() override;
void EndCycleCollectionCallback(mozilla::CycleCollectorResults& aResults) override;
void DispatchDeferredDeletion(bool aContinuation, bool aPurge = false) override;
void CustomGCCallback(JSGCStatus status) override;
void CustomOutOfMemoryCallback() override;
void CustomLargeAllocationFailureCallback() override;
static void GCSliceCallback(JSContext* cx,
JS::GCProgress progress,
const JS::GCDescription& desc);
static void FinalizeCallback(JSFreeOp* fop,
JSFinalizeStatus status,
bool isCompartmentGC,
void* data);
static void WeakPointerZoneGroupCallback(JSContext* cx, void* data);
static void WeakPointerCompartmentCallback(JSContext* cx, JSCompartment* comp, void* data);
inline void AddVariantRoot(XPCTraceableVariant* variant);
inline void AddWrappedJSRoot(nsXPCWrappedJS* wrappedJS);
inline void AddObjectHolderRoot(XPCJSObjectHolder* holder);
void DebugDump(int16_t depth);
void SystemIsBeingShutDown();
bool GCIsRunning() const {return mGCIsRunning;}
~XPCJSRuntime();
ShortLivedStringBuffer<nsString> mScratchStrings;
ShortLivedStringBuffer<nsCString> mScratchCStrings;
void AddGCCallback(xpcGCCallback cb);
void RemoveGCCallback(xpcGCCallback cb);
static void ActivityCallback(void* arg, bool active);
static bool InterruptCallback(JSContext* cx);
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
AutoMarkingPtr** GetAutoRootsAdr() {return &mAutoRoots;}
JSObject* UnprivilegedJunkScope() { return mUnprivilegedJunkScope; }
JSObject* PrivilegedJunkScope() { return mPrivilegedJunkScope; }
JSObject* CompilationScope() { return mCompilationScope; }
void InitSingletonScopes();
void DeleteSingletonScopes();
PRTime GetWatchdogTimestamp(WatchdogTimestampCategory aCategory);
nsresult GetPendingResult() { return mPendingResult; }
void SetPendingResult(nsresult rv) { mPendingResult = rv; }
private:
XPCJSRuntime();
nsresult Initialize();
void ReleaseIncrementally(nsTArray<nsISupports*>& array);
static const char* const mStrings[IDX_TOTAL_COUNT];
jsid mStrIDs[IDX_TOTAL_COUNT];
JS::Value mStrJSVals[IDX_TOTAL_COUNT];
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;
bool mGCIsRunning;
nsTArray<nsISupports*> mNativesToReleaseArray;
bool mDoingFinalization;
XPCRootSetElem* mVariantRoots;
XPCRootSetElem* mWrappedJSRoots;
XPCRootSetElem* mObjectHolderRoots;
nsTArray<xpcGCCallback> extraGCCallbacks;
RefPtr<WatchdogManager> mWatchdogManager;
JS::GCSliceCallback mPrevGCSliceCallback;
JS::PersistentRootedObject mUnprivilegedJunkScope;
JS::PersistentRootedObject mPrivilegedJunkScope;
JS::PersistentRootedObject mCompilationScope;
RefPtr<AsyncFreeSnowWhite> mAsyncSnowWhiteFreer;
// mSlowScriptCheckpoint is set to the time when we started processing the
// current event. We use it to determine whether the interrupt callback
// needs to do anything.
mozilla::TimeStamp mSlowScriptCheckpoint;
// Accumulates total time we actually waited for telemetry
mozilla::TimeDuration mSlowScriptActualWait;
bool mTimeoutAccumulated;
// mPendingResult is used to implement Components.returnCode. Only really
// meaningful while calling through XPCWrappedJS.
nsresult mPendingResult;
friend class Watchdog;
friend class AutoLockWatchdog;
friend class XPCIncrementalReleaseRunnable;
};
/***************************************************************************/
// 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 MOZ_STACK_CLASS XPCCallContext final : public nsAXPCNativeCallContext
{
public:
NS_IMETHOD GetCallee(nsISupports** aResult);
NS_IMETHOD GetCalleeMethodIndex(uint16_t* aResult);
NS_IMETHOD GetJSContext(JSContext** aResult);
NS_IMETHOD GetArgc(uint32_t* aResult);
NS_IMETHOD GetArgvPtr(JS::Value** aResult);
NS_IMETHOD GetCalleeInterface(nsIInterfaceInfo** aResult);
NS_IMETHOD GetCalleeClassInfo(nsIClassInfo** aResult);
NS_IMETHOD GetPreviousCallContext(nsAXPCNativeCallContext** aResult);
enum {NO_ARGS = (unsigned) -1};
explicit XPCCallContext(JSContext* cx,
JS::HandleObject obj = nullptr,
JS::HandleObject funobj = nullptr,
JS::HandleId id = JSID_VOIDHANDLE,
unsigned argc = NO_ARGS,
JS::Value* argv = nullptr,
JS::Value* rval = nullptr);
virtual ~XPCCallContext();
inline bool IsValid() const ;
inline XPCJSRuntime* GetRuntime() const ;
inline JSContext* GetJSContext() const ;
inline bool GetContextPopRequired() const ;
inline XPCCallContext* GetPrevCallContext() const ;
inline JSObject* GetFlattenedJSObject() const ;
inline nsISupports* GetIdentityObject() const ;
inline XPCWrappedNative* GetWrapper() const ;
inline XPCWrappedNativeProto* GetProto() const ;
inline bool CanGetTearOff() const ;
inline XPCWrappedNativeTearOff* GetTearOff() const ;
inline XPCNativeScriptableInfo* GetScriptableInfo() const ;
inline bool CanGetSet() const ;
inline XPCNativeSet* GetSet() const ;
inline bool CanGetInterface() const ;
inline XPCNativeInterface* GetInterface() const ;
inline XPCNativeMember* GetMember() const ;
inline bool HasInterfaceAndMember() const ;
inline jsid GetName() const ;
inline bool GetStaticMemberIsLocal() const ;
inline unsigned GetArgc() const ;
inline JS::Value* GetArgv() const ;
inline JS::Value* GetRetVal() const ;
inline uint16_t GetMethodIndex() const ;
inline void SetMethodIndex(uint16_t index) ;
inline jsid GetResolveName() const;
inline jsid SetResolveName(JS::HandleId name);
inline XPCWrappedNative* GetResolvingWrapper() const;
inline XPCWrappedNative* SetResolvingWrapper(XPCWrappedNative* w);
inline void SetRetVal(JS::Value val);
void SetName(jsid name);
void SetArgsAndResultPtr(unsigned argc, JS::Value* argv, JS::Value* rval);
void SetCallInfo(XPCNativeInterface* iface, XPCNativeMember* member,
bool isSetter);
nsresult CanCallNow();
void SystemIsBeingShutDown();
operator JSContext*() const {return GetJSContext();}
private:
// no copy ctor or assignment allowed
XPCCallContext(const XPCCallContext& r) = delete;
XPCCallContext& operator= (const XPCCallContext& r) = delete;
private:
// posible values for mState
enum State {
INIT_FAILED,
SYSTEM_SHUTDOWN,
HAVE_RUNTIME,
HAVE_OBJECT,
HAVE_NAME,
HAVE_ARGS,
READY_TO_CALL,
CALL_DONE
};
#ifdef DEBUG
inline void CHECK_STATE(int s) const {MOZ_ASSERT(mState >= s, "bad state");}
#else
#define CHECK_STATE(s) ((void)0)
#endif
private:
JSAutoRequest mAr;
State mState;
RefPtr<nsXPConnect> mXPC;
XPCJSRuntime* mXPCJSRuntime;
JSContext* mJSContext;
// ctor does not necessarily init the following. BEWARE!
XPCCallContext* mPrevCallContext;
XPCWrappedNative* mWrapper;
XPCWrappedNativeTearOff* mTearOff;
XPCNativeScriptableInfo* mScriptableInfo;
XPCNativeSet* mSet;
XPCNativeInterface* mInterface;
XPCNativeMember* mMember;
JS::RootedId mName;
bool mStaticMemberIsLocal;
unsigned mArgc;
JS::Value* mArgv;
JS::Value* mRetVal;
uint16_t mMethodIndex;
};
/***************************************************************************
****************************************************************************
*
* 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.
extern const js::Class XPC_WN_NoHelper_JSClass;
extern const js::Class XPC_WN_NoMods_Proto_JSClass;
extern const js::Class XPC_WN_ModsAllowed_Proto_JSClass;
extern const js::Class XPC_WN_Tearoff_JSClass;
#define XPC_WN_TEAROFF_RESERVED_SLOTS 1
#define XPC_WN_TEAROFF_FLAT_OBJECT_SLOT 0
extern const js::Class XPC_WN_NoHelper_Proto_JSClass;
extern bool
XPC_WN_CallMethod(JSContext* cx, unsigned argc, JS::Value* vp);
extern bool
XPC_WN_GetterSetter(JSContext* cx, unsigned argc, JS::Value* vp);
// 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(const js::Class* clazz)
{
return clazz == &XPC_WN_NoMods_Proto_JSClass ||
clazz == &XPC_WN_ModsAllowed_Proto_JSClass;
}
typedef js::HashSet<size_t,
js::DefaultHasher<size_t>,
js::SystemAllocPolicy> InterpositionWhitelist;
struct InterpositionWhitelistPair {
nsIAddonInterposition* interposition;
InterpositionWhitelist whitelist;
};
typedef nsTArray<InterpositionWhitelistPair> InterpositionWhitelistArray;
/***************************************************************************/
// XPCWrappedNativeScope is one-to-one with a JS global object.
class nsIAddonInterposition;
class nsXPCComponentsBase;
class XPCWrappedNativeScope final : public PRCList
{
public:
XPCJSRuntime*
GetRuntime() const {return XPCJSRuntime::Get();}
Native2WrappedNativeMap*
GetWrappedNativeMap() const {return mWrappedNativeMap;}
ClassInfo2WrappedNativeProtoMap*
GetWrappedNativeProtoMap() const {return mWrappedNativeProtoMap;}
nsXPCComponentsBase*
GetComponents() const {return mComponents;}
// Forces the creation of a privileged |Components| object, even in
// content scopes. This will crash if used outside of automation.
void
ForcePrivilegedComponents();
bool AttachComponentsObject(JSContext* aCx);
// Returns the JS object reflection of the Components object.
bool
GetComponentsJSObject(JS::MutableHandleObject obj);
JSObject*
GetGlobalJSObject() const {
JS::ExposeObjectToActiveJS(mGlobalJSObject);
return mGlobalJSObject;
}
JSObject*
GetGlobalJSObjectPreserveColor() const {return mGlobalJSObject;}
nsIPrincipal*
GetPrincipal() const {
JSCompartment* c = js::GetObjectCompartment(mGlobalJSObject);
return nsJSPrincipals::get(JS_GetCompartmentPrincipals(c));
}
JSObject*
GetExpandoChain(JS::HandleObject target);
bool
SetExpandoChain(JSContext* cx, JS::HandleObject target, JS::HandleObject chain);
static void
SystemIsBeingShutDown();
static void
TraceWrappedNativesInAllScopes(JSTracer* trc, XPCJSRuntime* rt);
void TraceSelf(JSTracer* trc) {
MOZ_ASSERT(mGlobalJSObject);
mGlobalJSObject.trace(trc, "XPCWrappedNativeScope::mGlobalJSObject");
}
void TraceInside(JSTracer* trc) {
if (mContentXBLScope)
mContentXBLScope.trace(trc, "XPCWrappedNativeScope::mXBLScope");
for (size_t i = 0; i < mAddonScopes.Length(); i++)
mAddonScopes[i].trace(trc, "XPCWrappedNativeScope::mAddonScopes");
if (mXrayExpandos.initialized())
mXrayExpandos.trace(trc);
}
static void
SuspectAllWrappers(XPCJSRuntime* rt, nsCycleCollectionNoteRootCallback& cb);
static void
MarkAllWrappedNativesAndProtos();
#ifdef DEBUG
static void
ASSERT_NoInterfaceSetsAreMarked();
#endif
static void
SweepAllWrappedNativeTearOffs();
static void
UpdateWeakPointersAfterGC(XPCJSRuntime* rt);
static void
KillDyingScopes();
static void
DebugDumpAllScopes(int16_t depth);
void
DebugDump(int16_t depth);
struct ScopeSizeInfo {
explicit ScopeSizeInfo(mozilla::MallocSizeOf mallocSizeOf)
: mMallocSizeOf(mallocSizeOf),
mScopeAndMapSize(0),
mProtoAndIfaceCacheSize(0)
{}
mozilla::MallocSizeOf mMallocSizeOf;
size_t mScopeAndMapSize;
size_t mProtoAndIfaceCacheSize;
};
static void
AddSizeOfAllScopesIncludingThis(ScopeSizeInfo* scopeSizeInfo);
void
AddSizeOfIncludingThis(ScopeSizeInfo* scopeSizeInfo);
bool
IsValid() const {return mRuntime != nullptr;}
static bool
IsDyingScope(XPCWrappedNativeScope* scope);
typedef js::HashSet<JS::Heap<JSObject*>,
js::MovableCellHasher<JS::Heap<JSObject*>>,
js::SystemAllocPolicy> DOMExpandoSet;
bool RegisterDOMExpandoObject(JSObject* expando) {
// Expandos are proxy objects, and proxies are always tenured.
JS::AssertGCThingMustBeTenured(expando);
if (!mDOMExpandoSet) {
mDOMExpandoSet = new DOMExpandoSet();
if (!mDOMExpandoSet->init(8))
return false;
}
return mDOMExpandoSet->put(JS::Heap<JSObject*>(expando));
}
void RemoveDOMExpandoObject(JSObject* expando) {
if (mDOMExpandoSet) {
DOMExpandoSet::Ptr p = mDOMExpandoSet->lookup(JS::Heap<JSObject*>(expando));
MOZ_ASSERT(p.found());
mDOMExpandoSet->remove(p);
}
}
typedef js::HashMap<JSAddonId*,
nsCOMPtr<nsIAddonInterposition>,
js::PointerHasher<JSAddonId*, 3>,
js::SystemAllocPolicy> InterpositionMap;
typedef js::HashSet<JSAddonId*,
js::PointerHasher<JSAddonId*, 3>,
js::SystemAllocPolicy> AddonSet;
// Gets the appropriate scope object for XBL in this scope. The context
// must be same-compartment with the global upon entering, and the scope
// object is wrapped into the compartment of the global.
JSObject* EnsureContentXBLScope(JSContext* cx);
JSObject* EnsureAddonScope(JSContext* cx, JSAddonId* addonId);
XPCWrappedNativeScope(JSContext* cx, JS::HandleObject aGlobal);
nsAutoPtr<JSObject2JSObjectMap> mWaiverWrapperMap;
bool IsContentXBLScope() { return mIsContentXBLScope; }
bool AllowContentXBLScope();
bool UseContentXBLScope() { return mUseContentXBLScope; }
void ClearContentXBLScope() { mContentXBLScope = nullptr; }
bool IsAddonScope() { return mIsAddonScope; }
bool HasInterposition() { return mInterposition; }
nsCOMPtr<nsIAddonInterposition> GetInterposition();
static bool SetAddonInterposition(JSContext* cx,
JSAddonId* addonId,
nsIAddonInterposition* interp);
static InterpositionWhitelist* GetInterpositionWhitelist(nsIAddonInterposition* interposition);
static bool UpdateInterpositionWhitelist(JSContext* cx,
nsIAddonInterposition* interposition);
void SetAddonCallInterposition() { mHasCallInterpositions = true; }
bool HasCallInterposition() { return mHasCallInterpositions; };
static bool AllowCPOWsInAddon(JSContext* cx, JSAddonId* addonId, bool allow);
protected:
virtual ~XPCWrappedNativeScope();
XPCWrappedNativeScope() = delete;
private:
class ClearInterpositionsObserver final : public nsIObserver {
~ClearInterpositionsObserver() {}
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
};
static XPCWrappedNativeScope* gScopes;
static XPCWrappedNativeScope* gDyingScopes;
static bool gShutdownObserverInitialized;
static InterpositionMap* gInterpositionMap;
static AddonSet* gAllowCPOWAddonSet;
static InterpositionWhitelistArray* gInterpositionWhitelists;
XPCJSRuntime* mRuntime;
Native2WrappedNativeMap* mWrappedNativeMap;
ClassInfo2WrappedNativeProtoMap* mWrappedNativeProtoMap;
RefPtr<nsXPCComponentsBase> 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;
// XBL Scope. This is is a lazily-created sandbox for non-system scopes.
// EnsureContentXBLScope() decides whether it needs to be created or not.
// This reference is wrapped into the compartment of mGlobalJSObject.
JS::ObjectPtr mContentXBLScope;
// Lazily created sandboxes for addon code.
nsTArray<JS::ObjectPtr> mAddonScopes;
// This is a service that will be use to interpose on some property accesses on
// objects from other scope, for add-on compatibility reasons.
nsCOMPtr<nsIAddonInterposition> mInterposition;
// If this flag is set, we intercept function calls on vanilla JS function objects
// from this scope if the caller scope has mInterposition set.
bool mHasCallInterpositions;
nsAutoPtr<DOMExpandoSet> mDOMExpandoSet;
JS::WeakMapPtr<JSObject*, JSObject*> mXrayExpandos;
bool mIsContentXBLScope;
bool mIsAddonScope;
// For remote XUL domains, we run all XBL in the content scope for compat
// reasons (though we sometimes pref this off for automation). We separately
// track the result of this decision (mAllowContentXBLScope), from the decision
// of whether to actually _use_ an XBL scope (mUseContentXBLScope), which depends
// on the type of global and whether the compartment is system principal
// or not.
//
// This distinction is useful primarily because, if true, we know that we
// have no way of distinguishing XBL script from content script for the
// given scope. In these (unsupported) situations, we just always claim to
// be XBL.
bool mAllowContentXBLScope;
bool mUseContentXBLScope;
};
/***************************************************************************/
// Slots we use for our functions
#define XPC_FUNCTION_NATIVE_MEMBER_SLOT 0
#define XPC_FUNCTION_PARENT_OBJECT_SLOT 1
/***************************************************************************/
// XPCNativeMember represents a single idl declared method, attribute or
// constant.
// Tight. No virtual methods. Can be bitwise copied (until any resolution done).
class XPCNativeMember final
{
public:
static bool GetCallInfo(JSObject* funobj,
XPCNativeInterface** pInterface,
XPCNativeMember** pMember);
jsid GetName() const {return mName;}
uint16_t GetIndex() const {return mIndex;}
bool GetConstantValue(XPCCallContext& ccx, XPCNativeInterface* iface,
JS::Value* pval)
{MOZ_ASSERT(IsConstant(),
"Only call this if you're sure this is a constant!");
return Resolve(ccx, iface, nullptr, pval);}
bool NewFunctionObject(XPCCallContext& ccx, XPCNativeInterface* iface,
JS::HandleObject parent, JS::Value* pval);
bool IsMethod() const
{return 0 != (mFlags & METHOD);}
bool IsConstant() const
{return 0 != (mFlags & CONSTANT);}
bool IsAttribute() const
{return 0 != (mFlags & GETTER);}
bool IsWritableAttribute() const
{return 0 != (mFlags & SETTER_TOO);}
bool IsReadOnlyAttribute() const
{return IsAttribute() && !IsWritableAttribute();}
void SetName(jsid a) {mName = a;}
void SetMethod(uint16_t index)
{mFlags = METHOD; mIndex = index;}
void SetConstant(uint16_t index)
{mFlags = CONSTANT; mIndex = index;}
void SetReadOnlyAttribute(uint16_t index)
{mFlags = GETTER; mIndex = index;}
void SetWritableAttribute()
{MOZ_ASSERT(mFlags == GETTER,"bad"); mFlags = GETTER | SETTER_TOO;}
static uint16_t GetMaxIndexInInterface()
{return (1<<12) - 1;}
inline XPCNativeInterface* GetInterface() const;
void SetIndexInInterface(uint16_t index)
{mIndexInInterface = index;}
/* default ctor - leave random contents */
XPCNativeMember() {MOZ_COUNT_CTOR(XPCNativeMember);}
~XPCNativeMember() {MOZ_COUNT_DTOR(XPCNativeMember);}
private:
bool Resolve(XPCCallContext& ccx, XPCNativeInterface* iface,
JS::HandleObject parent, JS::Value* vp);
enum {
METHOD = 0x01,
CONSTANT = 0x02,
GETTER = 0x04,
SETTER_TOO = 0x08
// If you add a flag here, you may need to make mFlags wider and either
// make mIndexInInterface narrower (and adjust
// XPCNativeInterface::NewInstance accordingly) or make this object
// bigger.
};
private:
// our only data...
jsid mName;
uint16_t mIndex;
// mFlags needs to be wide enogh to hold the flags in the above enum.
uint16_t mFlags : 4;
// mIndexInInterface is the index of this in our XPCNativeInterface's
// mMembers. In theory our XPCNativeInterface could have as many as 2^15-1
// members (since mMemberCount is 15-bit) but in practice we prevent
// creation of XPCNativeInterfaces which have more than 2^12 members.
// If the width of this field changes, update GetMaxIndexInInterface.
uint16_t mIndexInInterface : 12;
} JS_HAZ_NON_GC_POINTER; // Only stores a pinned string
/***************************************************************************/
// XPCNativeInterface represents a single idl declared interface. This is
// primarily the set of XPCNativeMembers.
// Tight. No virtual methods.
class XPCNativeInterface final
{
public:
static XPCNativeInterface* GetNewOrUsed(const nsIID* iid);
static XPCNativeInterface* GetNewOrUsed(nsIInterfaceInfo* info);
static XPCNativeInterface* GetNewOrUsed(const char* name);
static XPCNativeInterface* GetISupports();
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 bool HasAncestor(const nsIID* iid) const;
static inline size_t OffsetOfMembers();
uint16_t GetMemberCount() const {
return mMemberCount;
}
XPCNativeMember* GetMemberAt(uint16_t i) {
MOZ_ASSERT(i < mMemberCount, "bad index");
return &mMembers[i];
}
void DebugDump(int16_t depth);
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(mozilla::MallocSizeOf mallocSizeOf);
protected:
static XPCNativeInterface* NewInstance(nsIInterfaceInfo* aInfo);
XPCNativeInterface() = delete;
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) = delete;
XPCNativeInterface& operator= (const XPCNativeInterface& r) = delete;
private:
nsCOMPtr<nsIInterfaceInfo> mInfo;
jsid mName;
uint16_t mMemberCount : 15;
uint16_t mMarked : 1;
XPCNativeMember mMembers[1]; // always last - object sized for array
};
/***************************************************************************/
// XPCNativeSetKey is used to key a XPCNativeSet in a NativeSetMap.
class XPCNativeSetKey final
{
public:
explicit XPCNativeSetKey(XPCNativeSet* BaseSet = nullptr,
XPCNativeInterface* Addition = nullptr,
uint16_t Position = 0)
: mBaseSet(BaseSet), mAddition(Addition), mPosition(Position) {}
~XPCNativeSetKey() {}
XPCNativeSet* GetBaseSet() const {return mBaseSet;}
XPCNativeInterface* GetAddition() const {return mAddition;}
uint16_t GetPosition() const {return mPosition;}
// Allow shallow copy
private:
XPCNativeSet* mBaseSet;
XPCNativeInterface* mAddition;
uint16_t mPosition;
};
/***************************************************************************/
// XPCNativeSet represents an ordered collection of XPCNativeInterface pointers.
class XPCNativeSet final
{
public:
static XPCNativeSet* GetNewOrUsed(const nsIID* iid);
static XPCNativeSet* GetNewOrUsed(nsIClassInfo* classInfo);
static XPCNativeSet* GetNewOrUsed(XPCNativeSet* otherSet,
XPCNativeInterface* newInterface,
uint16_t 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(XPCNativeSet* firstSet,
XPCNativeSet* secondSet,
bool preserveFirstSetOrder);
static void ClearCacheEntryForClassInfo(nsIClassInfo* classInfo);
inline bool FindMember(jsid name, XPCNativeMember** pMember,
uint16_t* pInterfaceIndex) const;
inline bool FindMember(jsid name, XPCNativeMember** pMember,
XPCNativeInterface** pInterface) const;
inline bool FindMember(jsid name,
XPCNativeMember** pMember,
XPCNativeInterface** pInterface,
XPCNativeSet* protoSet,
bool* pIsLocal) const;
inline bool HasInterface(XPCNativeInterface* aInterface) const;
inline bool HasInterfaceWithAncestor(XPCNativeInterface* aInterface) const;
inline bool HasInterfaceWithAncestor(const nsIID* iid) const;
inline XPCNativeInterface* FindInterfaceWithIID(const nsIID& iid) const;
inline XPCNativeInterface* FindNamedInterface(jsid name) const;
uint16_t GetMemberCount() const {
return mMemberCount;
}
uint16_t GetInterfaceCount() const {
return mInterfaceCount;
}
XPCNativeInterface** GetInterfaceArray() {
return mInterfaces;
}
XPCNativeInterface* GetInterfaceAt(uint16_t i)
{MOZ_ASSERT(i < mInterfaceCount, "bad index"); return mInterfaces[i];}
inline bool MatchesSetUpToInterface(const XPCNativeSet* other,
XPCNativeInterface* iface) const;
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(int16_t depth);
static void DestroyInstance(XPCNativeSet* inst);
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
protected:
static XPCNativeSet* NewInstance(XPCNativeInterface** array,
uint16_t count);
static XPCNativeSet* NewInstanceMutate(XPCNativeSet* otherSet,
XPCNativeInterface* newInterface,
uint16_t 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:
uint16_t mMemberCount;
uint16_t mInterfaceCount : 15;
uint16_t 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 final
{
private:
uint32_t mFlags;
public:
explicit 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;}
bool 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_ );}
bool WantPreCreate() GET_IT(WANT_PRECREATE)
bool WantAddProperty() GET_IT(WANT_ADDPROPERTY)
bool WantGetProperty() GET_IT(WANT_GETPROPERTY)
bool WantSetProperty() GET_IT(WANT_SETPROPERTY)
bool WantEnumerate() GET_IT(WANT_ENUMERATE)
bool WantNewEnumerate() GET_IT(WANT_NEWENUMERATE)
bool WantResolve() GET_IT(WANT_RESOLVE)
bool WantFinalize() GET_IT(WANT_FINALIZE)
bool WantCall() GET_IT(WANT_CALL)
bool WantConstruct() GET_IT(WANT_CONSTRUCT)
bool WantHasInstance() GET_IT(WANT_HASINSTANCE)
bool UseJSStubForAddProperty() GET_IT(USE_JSSTUB_FOR_ADDPROPERTY)
bool UseJSStubForDelProperty() GET_IT(USE_JSSTUB_FOR_DELPROPERTY)
bool UseJSStubForSetProperty() GET_IT(USE_JSSTUB_FOR_SETPROPERTY)
bool DontEnumQueryInterface() GET_IT(DONT_ENUM_QUERY_INTERFACE)
bool DontAskInstanceForScriptable() GET_IT(DONT_ASK_INSTANCE_FOR_SCRIPTABLE)
bool ClassInfoInterfacesOnly() GET_IT(CLASSINFO_INTERFACES_ONLY)
bool AllowPropModsDuringResolve() GET_IT(ALLOW_PROP_MODS_DURING_RESOLVE)
bool AllowPropModsToPrototype() GET_IT(ALLOW_PROP_MODS_TO_PROTOTYPE)
bool IsGlobalObject() GET_IT(IS_GLOBAL_OBJECT)
bool DontReflectInterfaceNames() GET_IT(DONT_REFLECT_INTERFACE_NAMES)
#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).
class XPCNativeScriptableShared final
{
public:
const XPCNativeScriptableFlags& GetFlags() const { return mFlags; }
const JSClass* GetJSClass() { return Jsvalify(&mJSClass); }
XPCNativeScriptableShared(uint32_t aFlags, char* aName, bool aPopulate);
~XPCNativeScriptableShared() {
free((void*)mJSClass.name);
free((void*)mJSClass.cOps);
MOZ_COUNT_DTOR(XPCNativeScriptableShared);
}
char* TransferNameOwnership() {
char* name = (char*)mJSClass.name;
mJSClass.name = nullptr;
return name;
}
void Mark() { mFlags.Mark(); }
void Unmark() { mFlags.Unmark(); }
bool IsMarked() const { return mFlags.IsMarked(); }
private:
XPCNativeScriptableFlags mFlags;
// This is an unusual js::Class instance: its name and cOps members are
// heap-allocated, unlike all other instances for which they are statically
// allocated. So we must free them in the destructor.
js::Class mJSClass;
};
/***************************************************************************/
// XPCNativeScriptableInfo is used to hold the nsIXPCScriptable state for a
// given class or instance.
class XPCNativeScriptableInfo final
{
public:
static XPCNativeScriptableInfo*
Construct(const XPCNativeScriptableCreateInfo* sci);
nsIXPCScriptable*
GetCallback() const {return mCallback;}
const XPCNativeScriptableFlags&
GetFlags() const {return mShared->GetFlags();}
const JSClass*
GetJSClass() {return mShared->GetJSClass();}
void
SetScriptableShared(XPCNativeScriptableShared* shared) {mShared = shared;}
void Mark() {
if (mShared)
mShared->Mark();
}
void TraceJS(JSTracer* trc) {}
void AutoTrace(JSTracer* trc) {}
protected:
explicit XPCNativeScriptableInfo(nsIXPCScriptable* scriptable)
: mCallback(scriptable), mShared(nullptr)
{MOZ_COUNT_CTOR(XPCNativeScriptableInfo);}
public:
~XPCNativeScriptableInfo() {MOZ_COUNT_DTOR(XPCNativeScriptableInfo);}
private:
// disable copy ctor and assignment
XPCNativeScriptableInfo(const XPCNativeScriptableInfo& r) = delete;
XPCNativeScriptableInfo& operator= (const XPCNativeScriptableInfo& r) = delete;
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 MOZ_STACK_CLASS XPCNativeScriptableCreateInfo final
{
public:
explicit XPCNativeScriptableCreateInfo(const XPCNativeScriptableInfo& si)
: mCallback(si.GetCallback()), mFlags(si.GetFlags()) {}
XPCNativeScriptableCreateInfo(already_AddRefed<nsIXPCScriptable>&& callback,
XPCNativeScriptableFlags flags)
: mCallback(callback), mFlags(flags) {}
XPCNativeScriptableCreateInfo()
: mFlags(0) {}
nsIXPCScriptable*
GetCallback() const {return mCallback;}
const XPCNativeScriptableFlags&
GetFlags() const {return mFlags;}
void
SetCallback(already_AddRefed<nsIXPCScriptable>&& callback)
{mCallback = callback;}
void
SetFlags(const XPCNativeScriptableFlags& flags) {mFlags = flags;}
private:
nsCOMPtr<nsIXPCScriptable> mCallback;
XPCNativeScriptableFlags mFlags;
};
/***********************************************/
// XPCWrappedNativeProto hold the additional shared wrapper data
// for XPCWrappedNative whose native objects expose nsIClassInfo.
class XPCWrappedNativeProto final
{
public:
static XPCWrappedNativeProto*
GetNewOrUsed(XPCWrappedNativeScope* scope,
nsIClassInfo* classInfo,
const XPCNativeScriptableCreateInfo* scriptableCreateInfo,
bool callPostCreatePrototype = true);
XPCWrappedNativeScope*
GetScope() const {return mScope;}
XPCJSRuntime*
GetRuntime() const {return mScope->GetRuntime();}
JSObject*
GetJSProtoObject() const {
JS::ExposeObjectToActiveJS(mJSProtoObject);
return mJSProtoObject;
}
nsIClassInfo*
GetClassInfo() const {return mClassInfo;}
XPCNativeSet*
GetSet() const {return mSet;}
XPCNativeScriptableInfo*
GetScriptableInfo() {return mScriptableInfo;}
bool CallPostCreatePrototype();
void JSProtoObjectFinalized(js::FreeOp* fop, JSObject* obj);
void JSProtoObjectMoved(JSObject* obj, const JSObject* old);
void SystemIsBeingShutDown();
void DebugDump(int16_t depth);
void TraceSelf(JSTracer* trc) {
if (mJSProtoObject)
mJSProtoObject.trace(trc, "XPCWrappedNativeProto::mJSProtoObject");
}
void TraceInside(JSTracer* trc) {
if (trc->isMarkingTracer()) {
mSet->Mark();
if (mScriptableInfo)
mScriptableInfo->Mark();
}
GetScope()->TraceSelf(trc);
}
void TraceJS(JSTracer* trc) {
TraceSelf(trc);
TraceInside(trc);
}
void WriteBarrierPre(JSContext* cx)
{
if (JS::IsIncrementalBarrierNeeded(cx) && mJSProtoObject)
mJSProtoObject.writeBarrierPre(cx);
}
// 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) = delete;
XPCWrappedNativeProto& operator= (const XPCWrappedNativeProto& r) = delete;
// hide ctor
XPCWrappedNativeProto(XPCWrappedNativeScope* Scope,
nsIClassInfo* ClassInfo,
XPCNativeSet* Set);
bool Init(const XPCNativeScriptableCreateInfo* scriptableCreateInfo,
bool callPostCreatePrototype);
private:
#ifdef DEBUG
static int32_t gDEBUG_LiveProtoCount;
#endif
private:
XPCWrappedNativeScope* mScope;
JS::ObjectPtr mJSProtoObject;
nsCOMPtr<nsIClassInfo> mClassInfo;
XPCNativeSet* mSet;
XPCNativeScriptableInfo* mScriptableInfo;
};
/***********************************************/
// XPCWrappedNativeTearOff represents the info needed to make calls to one
// interface on the underlying native object of a XPCWrappedNative.
class XPCWrappedNativeTearOff final
{
public:
bool IsAvailable() const {return mInterface == nullptr;}
bool IsReserved() const {return mInterface == (XPCNativeInterface*)1;}
bool 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;}
already_AddRefed<nsISupports> TakeNative() { return mNative.forget(); }
void SetJSObject(JSObject* JSObj);
void JSObjectFinalized() {SetJSObject(nullptr);}
void JSObjectMoved(JSObject* obj, const JSObject* old);
XPCWrappedNativeTearOff()
: mInterface(nullptr), mJSObject(nullptr)
{
MOZ_COUNT_CTOR(XPCWrappedNativeTearOff);
}
~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.setFlags(1);}
void Unmark() {mJSObject.unsetFlags(1);}
bool IsMarked() const {return mJSObject.hasFlag(1);}
XPCWrappedNativeTearOff* AddTearOff()
{
MOZ_ASSERT(!mNextTearOff);
mNextTearOff = mozilla::MakeUnique<XPCWrappedNativeTearOff>();
return mNextTearOff.get();
}
XPCWrappedNativeTearOff* GetNextTearOff() {return mNextTearOff.get();}
private:
XPCWrappedNativeTearOff(const XPCWrappedNativeTearOff& r) = delete;
XPCWrappedNativeTearOff& operator= (const XPCWrappedNativeTearOff& r) = delete;
private:
XPCNativeInterface* mInterface;
// mNative is an nsRefPtr not an nsCOMPtr because it may not be the canonical
// nsISupports pointer.
RefPtr<nsISupports> mNative;
JS::TenuredHeap<JSObject*> mJSObject;
mozilla::UniquePtr<XPCWrappedNativeTearOff> mNextTearOff;
};
/***************************************************************************/
// XPCWrappedNative the wrapper around one instance of a native xpcom object
// to be used from JavaScript.
class XPCWrappedNative final : public nsIXPConnectWrappedNative
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_NSIXPCONNECTJSOBJECTHOLDER
NS_DECL_NSIXPCONNECTWRAPPEDNATIVE
NS_DECL_CYCLE_COLLECTION_CLASS(XPCWrappedNative)
nsIPrincipal* GetObjectPrincipal() const;
bool
IsValid() const { return mFlatJSObject.hasFlag(FLAT_JS_OBJECT_VALID); }
#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 bool
IsTaggedScope(XPCWrappedNativeScope* s)
{return XPC_SCOPE_WORD(s) & XPC_SCOPE_TAG;}
static inline XPCWrappedNativeScope*
TagScope(XPCWrappedNativeScope* s)
{MOZ_ASSERT(!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 bool
IsWrapperExpired() const
{return XPC_SCOPE_WORD(mMaybeScope) & XPC_WRAPPER_EXPIRED;}
bool
HasProto() const {return !IsTaggedScope(mMaybeScope);}
XPCWrappedNativeProto*
GetProto() const
{return HasProto() ?
(XPCWrappedNativeProto*)
(XPC_SCOPE_WORD(mMaybeProto) & ~XPC_SCOPE_MASK) : nullptr;}
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
{
JS::ExposeObjectToActiveJS(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;}
XPCNativeSet*
GetSet() const {return mSet;}
void
SetSet(XPCNativeSet* set) {mSet = set;}
static XPCWrappedNative* Get(JSObject* obj) {
MOZ_ASSERT(IS_WN_REFLECTOR(obj));
return (XPCWrappedNative*)js::GetObjectPrivate(obj);
}
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();}
nsIClassInfo*
GetClassInfo() const {return IsValid() && HasProto() ?
GetProto()->GetClassInfo() : nullptr;}
bool
HasMutatedSet() const {return IsValid() &&
(!HasProto() ||
GetSet() != GetProto()->GetSet());}
XPCJSRuntime*
GetRuntime() const {XPCWrappedNativeScope* scope = GetScope();
return scope ? scope->GetRuntime() : nullptr;}
static nsresult
WrapNewGlobal(xpcObjectHelper& nativeHelper,
nsIPrincipal* principal, bool initStandardClasses,
JS::CompartmentOptions& aOptions,
XPCWrappedNative** wrappedGlobal);
static nsresult
GetNewOrUsed(xpcObjectHelper& helper,
XPCWrappedNativeScope* Scope,
XPCNativeInterface* Interface,
XPCWrappedNative** wrapper);
static nsresult
GetUsedOnly(nsISupports* Object,
XPCWrappedNativeScope* Scope,
XPCNativeInterface* Interface,
XPCWrappedNative** wrapper);
void FlatJSObjectFinalized();
void FlatJSObjectMoved(JSObject* obj, const JSObject* old);
void SystemIsBeingShutDown();
enum CallMode {CALL_METHOD, CALL_GETTER, CALL_SETTER};
static bool CallMethod(XPCCallContext& ccx,
CallMode mode = CALL_METHOD);
static bool GetAttribute(XPCCallContext& ccx)
{return CallMethod(ccx, CALL_GETTER);}
static bool SetAttribute(XPCCallContext& ccx)
{return CallMethod(ccx, CALL_SETTER);}
inline bool HasInterfaceNoQI(const nsIID& iid);
XPCWrappedNativeTearOff* FindTearOff(XPCNativeInterface* aInterface,
bool needJSObject = false,
nsresult* pError = nullptr);
XPCWrappedNativeTearOff* FindTearOff(const nsIID& iid);
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 (trc->isMarkingTracer()) {
mSet->Mark();
if (mScriptableInfo)
mScriptableInfo->Mark();
}
if (HasProto())
GetProto()->TraceSelf(trc);
else
GetScope()->TraceSelf(trc);
if (mFlatJSObject && JS_IsGlobalObject(mFlatJSObject))
{
xpc::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.
JS::TraceEdge(trc, &mFlatJSObject, "XPCWrappedNative::mFlatJSObject");
}
static void Trace(JSTracer* trc, JSObject* obj);
void AutoTrace(JSTracer* trc) {
TraceSelf(trc);
}
#ifdef DEBUG
void ASSERT_SetsNotMarked() const
{mSet->ASSERT_NotMarked();
if (HasProto()){GetProto()->ASSERT_SetNotMarked();}}
#endif
inline void SweepTearOffs();
// Returns a string that shuld be free'd using JS_smprintf_free (or null).
char* ToString(XPCWrappedNativeTearOff* to = nullptr) const;
static void GatherProtoScriptableCreateInfo(nsIClassInfo* classInfo,
XPCNativeScriptableCreateInfo& sciProto);
bool HasExternalReference() const {return mRefCnt > 1;}
void Suspect(nsCycleCollectionNoteRootCallback& cb);
void NoteTearoffs(nsCycleCollectionTraversalCallback& cb);
// Make ctor and dtor protected (rather than private) to placate nsCOMPtr.
protected:
XPCWrappedNative() = delete;
// 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 {
// Flags bits for mFlatJSObject:
FLAT_JS_OBJECT_VALID = JS_BIT(0)
};
bool Init(const XPCNativeScriptableCreateInfo* sci);
bool FinishInit();
bool ExtendSet(XPCNativeInterface* aInterface);
nsresult InitTearOff(XPCWrappedNativeTearOff* aTearOff,
XPCNativeInterface* aInterface,
bool needJSObject);
bool InitTearOffJSObject(XPCWrappedNativeTearOff* to);
public:
static const XPCNativeScriptableCreateInfo& GatherScriptableCreateInfo(nsISupports* obj,
nsIClassInfo* classInfo,
XPCNativeScriptableCreateInfo& sciProto,
XPCNativeScriptableCreateInfo& sciWrapper);
private:
union
{
XPCWrappedNativeScope* mMaybeScope;
XPCWrappedNativeProto* mMaybeProto;
};
XPCNativeSet* mSet;
JS::TenuredHeap<JSObject*> mFlatJSObject;
XPCNativeScriptableInfo* mScriptableInfo;
XPCWrappedNativeTearOff mFirstTearOff;
};
/***************************************************************************
****************************************************************************
*
* 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(int16_t 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 final : public nsIXPCWrappedJSClass
{
// all the interface method declarations...
NS_DECL_ISUPPORTS
NS_IMETHOD DebugDump(int16_t depth) override;
public:
static already_AddRefed<nsXPCWrappedJSClass>
GetNewOrUsed(JSContext* cx,
REFNSIID aIID,
bool allowNonScriptable = false);
REFNSIID GetIID() const {return mIID;}
XPCJSRuntime* GetRuntime() const {return mRuntime;}
nsIInterfaceInfo* GetInterfaceInfo() const {return mInfo;}
const char* GetInterfaceName();
static bool IsWrappedJS(nsISupports* aPtr);
NS_IMETHOD DelegatedQueryInterface(nsXPCWrappedJS* self, REFNSIID aIID,
void** aInstancePtr);
JSObject* GetRootJSObject(JSContext* cx, JSObject* aJSObj);
NS_IMETHOD CallMethod(nsXPCWrappedJS* wrapper, uint16_t methodIndex,
const XPTMethodDescriptor* info,
nsXPTCMiniVariant* params);
JSObject* CallQueryInterfaceOnJSObject(JSContext* cx,
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);
private:
// aSyntheticException, if not null, is the exception we should be using.
// If null, look for an exception on the JSContext hanging off the
// XPCCallContext.
static nsresult CheckForException(XPCCallContext & ccx,
mozilla::dom::AutoEntryScript& aes,
const char * aPropertyName,
const char * anInterfaceName,
nsIException* aSyntheticException = nullptr);
virtual ~nsXPCWrappedJSClass();
nsXPCWrappedJSClass() = delete;
nsXPCWrappedJSClass(JSContext* cx, REFNSIID aIID,
nsIInterfaceInfo* aInfo);
bool IsReflectable(uint16_t i) const
{return (bool)(mDescriptors[i/32] & (1 << (i%32)));}
void SetReflectable(uint16_t i, bool b)
{if (b) mDescriptors[i/32] |= (1 << (i%32));
else mDescriptors[i/32] &= ~(1 << (i%32));}
bool GetArraySizeFromParam(JSContext* cx,
const XPTMethodDescriptor* method,
const nsXPTParamInfo& param,
uint16_t methodIndex,
uint8_t paramIndex,
nsXPTCMiniVariant* params,
uint32_t* result) const;
bool GetInterfaceTypeFromParam(JSContext* cx,
const XPTMethodDescriptor* method,
const nsXPTParamInfo& param,
uint16_t methodIndex,
const nsXPTType& type,
nsXPTCMiniVariant* params,
nsID* result) const;
static void CleanupPointerArray(const nsXPTType& datum_type,
uint32_t array_count,
void** arrayp);
static void CleanupPointerTypeObject(const nsXPTType& type,
void** pp);
void CleanupOutparams(JSContext* cx, uint16_t methodIndex, const nsXPTMethodInfo* info,
nsXPTCMiniVariant* nativeParams, bool inOutOnly, uint8_t n) const;
private:
XPCJSRuntime* mRuntime;
nsCOMPtr<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 final : protected nsAutoXPTCStub,
public nsIXPConnectWrappedJSUnmarkGray,
public nsSupportsWeakReference,
public nsIPropertyBag,
public XPCRootSetElem
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_NSIXPCONNECTJSOBJECTHOLDER
NS_DECL_NSIXPCONNECTWRAPPEDJS
NS_DECL_NSIXPCONNECTWRAPPEDJSUNMARKGRAY
NS_DECL_NSISUPPORTSWEAKREFERENCE
NS_DECL_NSIPROPERTYBAG
NS_DECL_CYCLE_COLLECTION_SKIPPABLE_CLASS_AMBIGUOUS(nsXPCWrappedJS, nsIXPConnectWrappedJS)
NS_IMETHOD CallMethod(uint16_t methodIndex,
const XPTMethodDescriptor* info,
nsXPTCMiniVariant* params) override;
/*
* 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(JS::HandleObject aJSObj,
REFNSIID aIID,
nsXPCWrappedJS** wrapper);
nsISomeInterface* GetXPTCStub() { return mXPTCStub; }
/**
* 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;}
// Returns true if the wrapper chain contains references to multiple
// compartments. If the wrapper chain contains references to multiple
// compartments, then it must be registered on the XPCJSRuntime. Otherwise,
// it should be registered in the CompartmentPrivate for the compartment of
// the root's JS object. This will only return correct results when called
// on the root wrapper and will assert if not called on a root wrapper.
bool IsMultiCompartment() const;
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);
nsXPCWrappedJS* FindOrFindInherited(REFNSIID aIID) {
nsXPCWrappedJS* wrapper = Find(aIID);
if (wrapper)
return wrapper;
return FindInherited(aIID);
}
bool IsRootWrapper() const {return mRoot == this;}
bool IsValid() const {return mJSObj != nullptr;}
void SystemIsBeingShutDown();
// These two methods are used by JSObject2WrappedJSMap::FindDyingJSObjects
// to find non-rooting wrappers for dying JS objects. See the top of
// XPCWrappedJS.cpp for more details.
bool IsSubjectToFinalization() const {return IsValid() && mRefCnt == 1;}
void UpdateObjectPointerAfterGC() {JS_UpdateWeakPointerAfterGC(&mJSObj);}
bool IsAggregatedToNative() const {return mRoot->mOuter != nullptr;}
nsISupports* GetAggregatedNativeObject() const {return mRoot->mOuter;}
void SetAggregatedNativeObject(nsISupports* aNative) {
MOZ_ASSERT(aNative);
if (mRoot->mOuter) {
MOZ_ASSERT(mRoot->mOuter == aNative,
"Only one aggregated native can be set");
return;
}
mRoot->mOuter = aNative;
}
void TraceJS(JSTracer* trc);
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
virtual ~nsXPCWrappedJS();
protected:
nsXPCWrappedJS() = delete;
nsXPCWrappedJS(JSContext* cx,
JSObject* aJSObj,
nsXPCWrappedJSClass* aClass,
nsXPCWrappedJS* root,
nsresult* rv);
bool CanSkip();
void Destroy();
void Unlink();
private:
JS::Heap<JSObject*> mJSObj;
RefPtr<nsXPCWrappedJSClass> mClass;
nsXPCWrappedJS* mRoot; // If mRoot != this, it is an owning pointer.
nsXPCWrappedJS* mNext;
nsCOMPtr<nsISupports> mOuter; // only set in root
};
/***************************************************************************/
class XPCJSObjectHolder final : public nsIXPConnectJSObjectHolder,
public XPCRootSetElem
{
public:
// all the interface method declarations...
NS_DECL_ISUPPORTS
NS_DECL_NSIXPCONNECTJSOBJECTHOLDER
// non-interface implementation
public:
void TraceJS(JSTracer* trc);
explicit XPCJSObjectHolder(JSObject* obj);
private:
virtual ~XPCJSObjectHolder();
XPCJSObjectHolder() = delete;
JS::Heap<JSObject*> mJSObj;
};
/***************************************************************************
****************************************************************************
*
* All manner of utility classes follow...
*
****************************************************************************
***************************************************************************/
class xpcProperty : public nsIProperty
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPROPERTY
xpcProperty(const char16_t* aName, uint32_t aNameLen, nsIVariant* aValue);
private:
virtual ~xpcProperty() {}
nsString mName;
nsCOMPtr<nsIVariant> mValue;
};
/***************************************************************************/
// class here just for static methods
class XPCConvert
{
public:
static bool IsMethodReflectable(const XPTMethodDescriptor& info);
/**
* Convert a native object into a JS::Value.
*
* @param d [out] the resulting JS::Value
* @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 bool NativeData2JS(JS::MutableHandleValue d,
const void* s, const nsXPTType& type,
const nsID* iid, nsresult* pErr);
static bool JSData2Native(void* d, JS::HandleValue s,
const nsXPTType& type,
const nsID* iid,
nsresult* pErr);
/**
* Convert a native nsISupports into a JSObject.
*
* @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 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 bool NativeInterface2JSObject(JS::MutableHandleValue d,
nsIXPConnectJSObjectHolder** dest,
xpcObjectHelper& aHelper,
const nsID* iid,
bool allowNativeWrapper,
nsresult* pErr);
static bool GetNativeInterfaceFromJSObject(void** dest, JSObject* src,
const nsID* iid,
nsresult* pErr);
static bool JSObject2NativeInterface(void** dest, JS::HandleObject src,
const nsID* iid,
nsISupports* aOuter,
nsresult* pErr);
// Note - This return the XPCWrappedNative, rather than the native itself,
// for the WN case. You probably want UnwrapReflectorToISupports.
static bool GetISupportsFromJSObject(JSObject* obj, nsISupports** iface);
/**
* Convert a native array into a JS::Value.
*
* @param d [out] the resulting JS::Value
* @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 bool NativeArray2JS(JS::MutableHandleValue d, const void** s,
const nsXPTType& type, const nsID* iid,
uint32_t count, nsresult* pErr);
static bool JSArray2Native(void** d, JS::HandleValue s,
uint32_t count, const nsXPTType& type,
const nsID* iid, nsresult* pErr);
static bool JSTypedArray2Native(void** d,
JSObject* jsarray,
uint32_t count,
const nsXPTType& type,
nsresult* pErr);
static bool NativeStringWithSize2JS(JS::MutableHandleValue d, const void* s,
const nsXPTType& type,
uint32_t count,
nsresult* pErr);
static bool JSStringWithSize2Native(void* d, JS::HandleValue s,
uint32_t count, const nsXPTType& type,
nsresult* pErr);
static nsresult JSValToXPCException(JS::MutableHandleValue s,
const char* ifaceName,
const char* methodName,
nsIException** exception);
static nsresult JSErrorToXPCException(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,
JS::Value* jsExceptionPtr);
private:
XPCConvert() = delete;
};
/***************************************************************************/
// code for throwing exceptions into JS
class nsXPCException;
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 bool SetVerbosity(bool state)
{bool old = sVerbose; sVerbose = state; return old;}
static bool CheckForPendingException(nsresult result, JSContext* cx);
private:
static void Verbosify(XPCCallContext& ccx,
char** psz, bool own);
private:
static bool sVerbose;
};
/***************************************************************************/
class nsXPCException
{
public:
static bool NameAndFormatForNSResult(nsresult rv,
const char** name,
const char** format);
static const void* IterateNSResults(nsresult* rv,
const char** name,
const char** format,
const void** iterp);
static uint32_t GetNSResultCount();
};
/***************************************************************************/
/*
* 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 final : 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()
{MOZ_ASSERT(!mName, "name already set"); mName = const_cast<char*>(gNoString);}
bool NameIsSet() const {return nullptr != mName;}
const nsID& ID() const {return mID;}
bool IsValid() const {return !mID.Equals(GetInvalidIID());}
static already_AddRefed<nsJSID> NewID(const char* str);
static already_AddRefed<nsJSID> NewID(const nsID& id);
nsJSID();
void Reset();
const nsID& GetInvalidIID() const;
protected:
virtual ~nsJSID();
static const char gNoString[];
nsID mID;
char* mNumber;
char* mName;
};
// nsJSIID
class nsJSIID : public nsIJSIID,
public nsIXPCScriptable
{
public:
NS_DECL_ISUPPORTS
// we manually delagate these to nsJSID
NS_DECL_NSIJSID
// we implement the rest...
NS_DECL_NSIJSIID
NS_DECL_NSIXPCSCRIPTABLE
static already_AddRefed<nsJSIID> NewID(nsIInterfaceInfo* aInfo);
explicit nsJSIID(nsIInterfaceInfo* aInfo);
nsJSIID() = delete;
private:
virtual ~nsJSIID();
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 already_AddRefed<nsJSCID> NewID(const char* str);
nsJSCID();
private:
virtual ~nsJSCID();
void ResolveName();
private:
RefPtr<nsJSID> mDetails;
};
/***************************************************************************/
// 'Components' object implementations. nsXPCComponentsBase has the
// less-privileged stuff that we're willing to expose to XBL.
class nsXPCComponentsBase : public nsIXPCComponentsBase
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIXPCCOMPONENTSBASE
public:
void SystemIsBeingShutDown() { ClearMembers(); }
XPCWrappedNativeScope* GetScope() { return mScope; }
protected:
virtual ~nsXPCComponentsBase();
explicit nsXPCComponentsBase(XPCWrappedNativeScope* aScope);
virtual void ClearMembers();
XPCWrappedNativeScope* mScope;
// Unprivileged members from nsIXPCComponentsBase.
RefPtr<nsXPCComponents_Interfaces> mInterfaces;
RefPtr<nsXPCComponents_InterfacesByID> mInterfacesByID;
RefPtr<nsXPCComponents_Results> mResults;
friend class XPCWrappedNativeScope;
};
class nsXPCComponents : public nsXPCComponentsBase,
public nsIXPCComponents
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_FORWARD_NSIXPCCOMPONENTSBASE(nsXPCComponentsBase::)
NS_DECL_NSIXPCCOMPONENTS
protected:
explicit nsXPCComponents(XPCWrappedNativeScope* aScope);
virtual ~nsXPCComponents();
virtual void ClearMembers() override;
// Privileged members added by nsIXPCComponents.
RefPtr<nsXPCComponents_Classes> mClasses;
RefPtr<nsXPCComponents_ClassesByID> mClassesByID;
RefPtr<nsXPCComponents_ID> mID;
RefPtr<nsXPCComponents_Exception> mException;
RefPtr<nsXPCComponents_Constructor> mConstructor;
RefPtr<nsXPCComponents_Utils> mUtils;
friend class XPCWrappedNativeScope;
};
/***************************************************************************/
extern JSObject*
xpc_NewIDObject(JSContext* cx, JS::HandleObject jsobj, const nsID& aID);
extern const nsID*
xpc_JSObjectToID(JSContext* cx, JSObject* obj);
extern bool
xpc_JSObjectIsID(JSContext* cx, JSObject* obj);
/***************************************************************************/
// in XPCDebug.cpp
extern bool
xpc_DumpJSStack(bool showArgs, bool showLocals, bool 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, bool showArgs, bool showLocals,
bool showThisProps);
/***************************************************************************/
// Definition of nsScriptError, defined here because we lack a place to put
// XPCOM objects associated with the JavaScript engine.
class nsScriptErrorBase : public nsIScriptError {
public:
nsScriptErrorBase();
// TODO - do something reasonable on getting null from these babies.
NS_DECL_NSICONSOLEMESSAGE
NS_DECL_NSISCRIPTERROR
protected:
virtual ~nsScriptErrorBase();
void
InitializeOnMainThread();
nsString mMessage;
nsString mMessageName;
nsString mSourceName;
uint32_t mLineNumber;
nsString mSourceLine;
uint32_t mColumnNumber;
uint32_t mFlags;
nsCString mCategory;
// mOuterWindowID is set on the main thread from InitializeOnMainThread().
uint64_t mOuterWindowID;
uint64_t mInnerWindowID;
int64_t mTimeStamp;
// mInitializedOnMainThread and mIsFromPrivateWindow are set on the main
// thread from InitializeOnMainThread().
mozilla::Atomic<bool> mInitializedOnMainThread;
bool mIsFromPrivateWindow;
};
class nsScriptError final : public nsScriptErrorBase {
public:
nsScriptError() {}
NS_DECL_THREADSAFE_ISUPPORTS
private:
virtual ~nsScriptError() {}
};
class nsScriptErrorWithStack : public nsScriptErrorBase {
public:
explicit nsScriptErrorWithStack(JS::HandleObject);
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsScriptErrorWithStack)
NS_IMETHOD Init(const nsAString& message,
const nsAString& sourceName,
const nsAString& sourceLine,
uint32_t lineNumber,
uint32_t columnNumber,
uint32_t flags,
const char* category) override;
NS_IMETHOD GetStack(JS::MutableHandleValue) override;
NS_IMETHOD ToString(nsACString& aResult) override;
private:
virtual ~nsScriptErrorWithStack();
// Complete stackframe where the error happened.
// Must be SavedFrame object.
JS::Heap<JSObject*> mStack;
};
/******************************************************************************
* Handles pre/post script processing.
*/
class MOZ_RAII 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
*/
explicit AutoScriptEvaluate(JSContext * cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: mJSContext(cx), mEvaluated(false) {
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
/**
* Does the pre script evaluation.
* This function should only be called once, and will assert if called
* more than once
*/
bool StartEvaluating(JS::HandleObject scope);
/**
* Does the post script evaluation.
*/
~AutoScriptEvaluate();
private:
JSContext* mJSContext;
mozilla::Maybe<JS::AutoSaveExceptionState> mState;
bool mEvaluated;
mozilla::Maybe<JSAutoCompartment> mAutoCompartment;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
// No copying or assignment allowed
AutoScriptEvaluate(const AutoScriptEvaluate&) = delete;
AutoScriptEvaluate & operator =(const AutoScriptEvaluate&) = delete;
};
/***************************************************************************/
class MOZ_RAII AutoResolveName
{
public:
AutoResolveName(XPCCallContext& ccx, JS::HandleId name
MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
mOld(ccx, XPCJSRuntime::Get()->SetResolveName(name))
#ifdef DEBUG
,mCheck(ccx, name)
#endif
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
~AutoResolveName()
{
#ifdef DEBUG
jsid old =
#endif
XPCJSRuntime::Get()->SetResolveName(mOld);
MOZ_ASSERT(old == mCheck, "Bad Nesting!");
}
private:
JS::RootedId mOld;
#ifdef DEBUG
JS::RootedId mCheck;
#endif
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
/***************************************************************************/
// 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:
explicit AutoMarkingPtr(JSContext* cx) {
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:
explicit TypedAutoMarkingPtr(JSContext* cx) : AutoMarkingPtr(cx), mPtr(nullptr) {}
TypedAutoMarkingPtr(JSContext* cx, T* ptr) : AutoMarkingPtr(cx), 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<XPCNativeScriptableInfo> AutoMarkingNativeScriptableInfoPtr;
template<class T>
class ArrayAutoMarkingPtr : public AutoMarkingPtr
{
public:
explicit ArrayAutoMarkingPtr(JSContext* cx)
: AutoMarkingPtr(cx), mPtr(nullptr), mCount(0) {}
ArrayAutoMarkingPtr(JSContext* cx, T** ptr, uint32_t count, bool clear)
: AutoMarkingPtr(cx), 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 (uint32_t i = 0; i < mCount; i++) {
if (mPtr[i]) {
mPtr[i]->TraceJS(trc);
mPtr[i]->AutoTrace(trc);
}
}
}
virtual void MarkAfterJSFinalize()
{
for (uint32_t i = 0; i < mCount; i++) {
if (mPtr[i])
mPtr[i]->Mark();
}
}
private:
T** mPtr;
uint32_t mCount;
};
typedef ArrayAutoMarkingPtr<XPCNativeInterface> AutoMarkingNativeInterfacePtrArrayPtr;
/***************************************************************************/
namespace xpc {
// Allocates a string that grants all access ("AllAccess")
char*
CloneAllAccess();
// Returns access if wideName is in list
char*
CheckAccessList(const char16_t* wideName, const char* const list[]);
} /* namespace xpc */
/***************************************************************************/
// 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 already_AddRefed<XPCVariant> newVariant(JSContext* cx, JS::Value aJSVal);
/**
* This getter clears the gray bit before handing out the Value if the Value
* represents a JSObject. That means that the object is guaranteed to be
* kept alive past the next CC.
*/
JS::Value GetJSVal() const {
if (!mJSVal.isPrimitive())
JS::ExposeObjectToActiveJS(&mJSVal.toObject());
return mJSVal;
}
/**
* This getter does not change the color of the Value (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).
*/
JS::Value GetJSValPreserveColor() const {return mJSVal;}
XPCVariant(JSContext* cx, JS::Value aJSVal);
/**
* Convert a variant into a JS::Value.
*
* @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 bool VariantDataToJS(nsIVariant* variant,
nsresult* pErr, JS::MutableHandleValue pJSVal);
bool IsPurple()
{
return mRefCnt.IsPurple();
}
void RemovePurple()
{
mRefCnt.RemovePurple();
}
void SetCCGeneration(uint32_t aGen)
{
mCCGeneration = aGen;
}
uint32_t CCGeneration() { return mCCGeneration; }
protected:
virtual ~XPCVariant() { }
bool InitializeData(JSContext* cx);
protected:
nsDiscriminatedUnion mData;
JS::Heap<JS::Value> mJSVal;
bool mReturnRawObject : 1;
uint32_t mCCGeneration : 31;
};
NS_DEFINE_STATIC_IID_ACCESSOR(XPCVariant, XPCVARIANT_IID)
class XPCTraceableVariant: public XPCVariant,
public XPCRootSetElem
{
public:
XPCTraceableVariant(JSContext* cx, JS::Value aJSVal)
: XPCVariant(cx, aJSVal)
{
nsXPConnect::GetRuntimeInstance()->AddVariantRoot(this);
}
virtual ~XPCTraceableVariant();
void TraceJS(JSTracer* trc);
};
/***************************************************************************/
// Utilities
inline void*
xpc_GetJSPrivate(JSObject* obj)
{
return js::GetObjectPrivate(obj);
}
inline JSContext*
xpc_GetSafeJSContext()
{
return XPCJSRuntime::Get()->Context();
}
namespace xpc {
JSAddonId*
NewAddonId(JSContext* cx, const nsACString& id);
// JSNatives to expose atob and btoa in various non-DOM XPConnect scopes.
bool
Atob(JSContext* cx, unsigned argc, JS::Value* vp);
bool
Btoa(JSContext* cx, unsigned argc, JS::Value* vp);
// Helper function that creates a JSFunction that wraps a native function that
// forwards the call to the original 'callable'.
class FunctionForwarderOptions;
bool
NewFunctionForwarder(JSContext* cx, JS::HandleId id, JS::HandleObject callable,
FunctionForwarderOptions& options, JS::MutableHandleValue vp);
// Old fashioned xpc error reporter. Try to use JS_ReportError instead.
nsresult
ThrowAndFail(nsresult errNum, JSContext* cx, bool* retval);
struct GlobalProperties {
GlobalProperties() {
mozilla::PodZero(this);
}
bool Parse(JSContext* cx, JS::HandleObject obj);
bool DefineInXPCComponents(JSContext* cx, JS::HandleObject obj);
bool DefineInSandbox(JSContext* cx, JS::HandleObject obj);
bool CSS : 1;
bool indexedDB : 1;
bool XMLHttpRequest : 1;
bool TextDecoder : 1;
bool TextEncoder : 1;
bool URL : 1;
bool URLSearchParams : 1;
bool atob : 1;
bool btoa : 1;
bool Blob : 1;
bool Directory : 1;
bool File : 1;
bool crypto : 1;
bool rtcIdentityProvider : 1;
bool fetch : 1;
bool caches : 1;
bool fileReader: 1;
private:
bool Define(JSContext* cx, JS::HandleObject obj);
};
// Infallible.
already_AddRefed<nsIXPCComponents_utils_Sandbox>
NewSandboxConstructor();
// Returns true if class of 'obj' is SandboxClass.
bool
IsSandbox(JSObject* obj);
class MOZ_STACK_CLASS OptionsBase {
public:
explicit OptionsBase(JSContext* cx = xpc_GetSafeJSContext(),
JSObject* options = nullptr)
: mCx(cx)
, mObject(cx, options)
{ }
virtual bool Parse() = 0;
protected:
bool ParseValue(const char* name, JS::MutableHandleValue prop, bool* found = nullptr);
bool ParseBoolean(const char* name, bool* prop);
bool ParseObject(const char* name, JS::MutableHandleObject prop);
bool ParseJSString(const char* name, JS::MutableHandleString prop);
bool ParseString(const char* name, nsCString& prop);
bool ParseString(const char* name, nsString& prop);
bool ParseId(const char* name, JS::MutableHandleId id);
bool ParseUInt32(const char* name, uint32_t* prop);
JSContext* mCx;
JS::RootedObject mObject;
};
class MOZ_STACK_CLASS SandboxOptions : public OptionsBase {
public:
explicit SandboxOptions(JSContext* cx = xpc_GetSafeJSContext(),
JSObject* options = nullptr)
: OptionsBase(cx, options)
, wantXrays(true)
, allowWaivers(true)
, wantComponents(true)
, wantExportHelpers(false)
, isWebExtensionContentScript(false)
, waiveInterposition(false)
, proto(cx)
, addonId(cx)
, writeToGlobalPrototype(false)
, sameZoneAs(cx)
, freshZone(false)
, invisibleToDebugger(false)
, discardSource(false)
, metadata(cx)
, userContextId(0)
{ }
virtual bool Parse();
bool wantXrays;
bool allowWaivers;
bool wantComponents;
bool wantExportHelpers;
bool isWebExtensionContentScript;
bool waiveInterposition;
JS::RootedObject proto;
nsCString sandboxName;
JS::RootedString addonId;
bool writeToGlobalPrototype;
JS::RootedObject sameZoneAs;
bool freshZone;
bool invisibleToDebugger;
bool discardSource;
GlobalProperties globalProperties;
JS::RootedValue metadata;
uint32_t userContextId;
protected:
bool ParseGlobalProperties();
};
class MOZ_STACK_CLASS CreateObjectInOptions : public OptionsBase {
public:
explicit CreateObjectInOptions(JSContext* cx = xpc_GetSafeJSContext(),
JSObject* options = nullptr)
: OptionsBase(cx, options)
, defineAs(cx, JSID_VOID)
{ }
virtual bool Parse() { return ParseId("defineAs", &defineAs); }
JS::RootedId defineAs;
};
class MOZ_STACK_CLASS ExportFunctionOptions : public OptionsBase {
public:
explicit ExportFunctionOptions(JSContext* cx = xpc_GetSafeJSContext(),
JSObject* options = nullptr)
: OptionsBase(cx, options)
, defineAs(cx, JSID_VOID)
, allowCrossOriginArguments(false)
{ }
virtual bool Parse() {
return ParseId("defineAs", &defineAs) &&
ParseBoolean("allowCrossOriginArguments", &allowCrossOriginArguments);
}
JS::RootedId defineAs;
bool allowCrossOriginArguments;
};
class MOZ_STACK_CLASS FunctionForwarderOptions : public OptionsBase {
public:
explicit FunctionForwarderOptions(JSContext* cx = xpc_GetSafeJSContext(),
JSObject* options = nullptr)
: OptionsBase(cx, options)
, allowCrossOriginArguments(false)
{ }
JSObject* ToJSObject(JSContext* cx) {
JS::RootedObject obj(cx, JS_NewObjectWithGivenProto(cx, nullptr, nullptr));
if (!obj)
return nullptr;
JS::RootedValue val(cx);
unsigned attrs = JSPROP_READONLY | JSPROP_PERMANENT;
val = JS::BooleanValue(allowCrossOriginArguments);
if (!JS_DefineProperty(cx, obj, "allowCrossOriginArguments", val, attrs))
return nullptr;
return obj;
}
virtual bool Parse() {
return ParseBoolean("allowCrossOriginArguments", &allowCrossOriginArguments);
}
bool allowCrossOriginArguments;
};
class MOZ_STACK_CLASS StackScopedCloneOptions : public OptionsBase {
public:
explicit StackScopedCloneOptions(JSContext* cx = xpc_GetSafeJSContext(),
JSObject* options = nullptr)
: OptionsBase(cx, options)
, wrapReflectors(false)
, cloneFunctions(false)
, deepFreeze(false)
{ }
virtual bool Parse() {
return ParseBoolean("wrapReflectors", &wrapReflectors) &&
ParseBoolean("cloneFunctions", &cloneFunctions) &&
ParseBoolean("deepFreeze", &deepFreeze);
}
// When a reflector is encountered, wrap it rather than aborting the clone.
bool wrapReflectors;
// When a function is encountered, clone it (exportFunction-style) rather than
// aborting the clone.
bool cloneFunctions;
// If true, the resulting object is deep-frozen after being cloned.
bool deepFreeze;
};
JSObject*
CreateGlobalObject(JSContext* cx, const JSClass* clasp, nsIPrincipal* principal,
JS::CompartmentOptions& aOptions);
// Modify the provided compartment options, consistent with |aPrincipal| and
// with globally-cached values of various preferences.
//
// Call this function *before* |aOptions| is used to create the corresponding
// global object, as not all of the options it sets can be modified on an
// existing global object. (The type system should make this obvious, because
// you can't get a *mutable* JS::CompartmentOptions& from an existing global
// object.)
void
InitGlobalObjectOptions(JS::CompartmentOptions& aOptions,
nsIPrincipal* aPrincipal);
// Finish initializing an already-created, not-yet-exposed-to-script global
// object. This will attach a Components object (if necessary) and call
// |JS_FireOnNewGlobalObject| (if necessary).
//
// If you must modify compartment options, see InitGlobalObjectOptions above.
bool
InitGlobalObject(JSContext* aJSContext, JS::Handle<JSObject*> aGlobal,
uint32_t aFlags);
// Helper for creating a sandbox object to use for evaluating
// untrusted code completely separated from all other code in the
// system using 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
CreateSandboxObject(JSContext* cx, JS::MutableHandleValue vp, nsISupports* prinOrSop,
xpc::SandboxOptions& options);
// Helper for evaluating scripts in a sandbox object created with
// 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.
nsresult
EvalInSandbox(JSContext* cx, JS::HandleObject sandbox, const nsAString& source,
const nsACString& filename, int32_t lineNo,
JSVersion jsVersion, JS::MutableHandleValue rval);
nsresult
GetSandboxAddonId(JSContext* cx, JS::HandleObject sandboxArg,
JS::MutableHandleValue rval);
// Helper for retrieving metadata stored in a reserved slot. The metadata
// is set during the sandbox creation using the "metadata" option.
nsresult
GetSandboxMetadata(JSContext* cx, JS::HandleObject sandboxArg,
JS::MutableHandleValue rval);
nsresult
SetSandboxMetadata(JSContext* cx, JS::HandleObject sandboxArg,
JS::HandleValue metadata);
bool
CreateObjectIn(JSContext* cx, JS::HandleValue vobj, CreateObjectInOptions& options,
JS::MutableHandleValue rval);
bool
EvalInWindow(JSContext* cx, const nsAString& source, JS::HandleObject scope,
JS::MutableHandleValue rval);
bool
ExportFunction(JSContext* cx, JS::HandleValue vscope, JS::HandleValue vfunction,
JS::HandleValue voptions, JS::MutableHandleValue rval);
bool
CloneInto(JSContext* cx, JS::HandleValue vobj, JS::HandleValue vscope,
JS::HandleValue voptions, JS::MutableHandleValue rval);
bool
StackScopedClone(JSContext* cx, StackScopedCloneOptions& options, JS::MutableHandleValue val);
} /* namespace xpc */
/***************************************************************************/
// Inlined utilities.
inline bool
xpc_ForcePropertyResolve(JSContext* cx, JS::HandleObject obj, jsid id);
inline jsid
GetRTIdByIndex(JSContext* cx, unsigned index);
namespace xpc {
enum WrapperDenialType {
WrapperDenialForXray = 0,
WrapperDenialForCOW,
WrapperDenialTypeCount
};
bool ReportWrapperDenial(JSContext* cx, JS::HandleId id, WrapperDenialType type, const char* reason);
class CompartmentPrivate
{
public:
enum LocationHint {
LocationHintRegular,
LocationHintAddon
};
explicit CompartmentPrivate(JSCompartment* c);
~CompartmentPrivate();
static CompartmentPrivate* Get(JSCompartment* compartment)
{
MOZ_ASSERT(compartment);
void* priv = JS_GetCompartmentPrivate(compartment);
return static_cast<CompartmentPrivate*>(priv);
}
static CompartmentPrivate* Get(JSObject* object)
{
JSCompartment* compartment = js::GetObjectCompartment(object);
return Get(compartment);
}
// Controls whether this compartment gets Xrays to same-origin. This behavior
// is deprecated, but is still the default for sandboxes for compatibity
// reasons.
bool wantXrays;
// Controls whether this compartment is allowed to waive Xrays to content
// that it subsumes. This should generally be true, except in cases where we
// want to prevent code from depending on Xray Waivers (which might make it
// more portable to other browser architectures).
bool allowWaivers;
// This flag is intended for a very specific use, internal to Gecko. It may
// go away or change behavior at any time. It should not be added to any
// documentation and it should not be used without consulting the XPConnect
// module owner.
bool writeToGlobalPrototype;
// When writeToGlobalPrototype is true, we use this flag to temporarily
// disable the writeToGlobalPrototype behavior (when resolving standard
// classes, for example).
bool skipWriteToGlobalPrototype;
// This scope corresponds to a WebExtension content script, and receives
// various bits of special compatibility behavior.
bool isWebExtensionContentScript;
// Even if an add-on needs interposition, it does not necessary need it
// for every scope. If this flag is set we waive interposition for this
// scope.
bool waiveInterposition;
// If CPOWs are disabled for browser code via the
// dom.ipc.cpows.forbid-unsafe-from-browser preferences, then only
// add-ons can use CPOWs. This flag allows a non-addon scope
// to opt into CPOWs. It's necessary for the implementation of
// RemoteAddonsParent.jsm.
bool allowCPOWs;
// This is only ever set during mochitest runs when enablePrivilege is called.
// It's intended as a temporary stopgap measure until we can finish ripping out
// enablePrivilege. Once set, this value is never unset (i.e., it doesn't follow
// the old scoping rules of enablePrivilege).
//
// Using it in production is inherently unsafe.
bool universalXPConnectEnabled;
// This is only ever set during mochitest runs when enablePrivilege is called.
// It allows the SpecialPowers scope to waive the normal chrome security
// wrappers and expose properties directly to content. This lets us avoid a
// bunch of overhead and complexity in our SpecialPowers automation glue.
//
// Using it in production is inherently unsafe.
bool forcePermissiveCOWs;
// Whether we've emitted a warning about a property that was filtered out
// by a security wrapper. See XrayWrapper.cpp.
bool wrapperDenialWarnings[WrapperDenialTypeCount];
// The scriptability of this compartment.
Scriptability scriptability;
// Our XPCWrappedNativeScope. This is non-null if and only if this is an
// XPConnect compartment.
XPCWrappedNativeScope* scope;
const nsACString& GetLocation() {
if (location.IsEmpty() && locationURI) {
nsCOMPtr<nsIXPConnectWrappedJS> jsLocationURI =
do_QueryInterface(locationURI);
if (jsLocationURI) {
// We cannot call into JS-implemented nsIURI objects, because
// we are iterating over the JS heap at this point.
location =
NS_LITERAL_CSTRING("<JS-implemented nsIURI location>");
} else if (NS_FAILED(locationURI->GetSpec(location))) {
location = NS_LITERAL_CSTRING("<unknown location>");
}
}
return location;
}
bool GetLocationURI(nsIURI** aURI) {
return GetLocationURI(LocationHintRegular, aURI);
}
bool GetLocationURI(LocationHint aLocationHint, nsIURI** aURI) {
if (locationURI) {
nsCOMPtr<nsIURI> rval = locationURI;
rval.forget(aURI);
return true;
}
return TryParseLocationURI(aLocationHint, aURI);
}
void SetLocation(const nsACString& aLocation) {
if (aLocation.IsEmpty())
return;
if (!location.IsEmpty() || locationURI)
return;
location = aLocation;
}
void SetLocationURI(nsIURI* aLocationURI) {
if (!aLocationURI)
return;
if (locationURI)
return;
locationURI = aLocationURI;
}
JSObject2WrappedJSMap* GetWrappedJSMap() const { return mWrappedJSMap; }
void UpdateWeakPointersAfterGC(XPCJSRuntime* runtime);
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
private:
nsCString location;
nsCOMPtr<nsIURI> locationURI;
JSObject2WrappedJSMap* mWrappedJSMap;
bool TryParseLocationURI(LocationHint aType, nsIURI** aURI);
};
bool IsUniversalXPConnectEnabled(JSCompartment* compartment);
bool IsUniversalXPConnectEnabled(JSContext* cx);
bool EnableUniversalXPConnect(JSContext* cx);
inline void
CrashIfNotInAutomation()
{
MOZ_RELEASE_ASSERT(IsInAutomation());
}
inline XPCWrappedNativeScope*
ObjectScope(JSObject* obj)
{
return CompartmentPrivate::Get(obj)->scope;
}
JSObject* NewOutObject(JSContext* cx);
bool IsOutObject(JSContext* cx, JSObject* obj);
nsresult HasInstance(JSContext* cx, JS::HandleObject objArg, const nsID* iid, bool* bp);
nsIPrincipal* GetObjectPrincipal(JSObject* obj);
} // namespace xpc
namespace mozilla {
namespace dom {
extern bool
DefineStaticJSVals(JSContext* cx);
} // namespace dom
} // namespace mozilla
bool
xpc_LocalizeContext(JSContext* cx);
void
xpc_DelocalizeContext(JSContext* cx);
/***************************************************************************/
// Inlines use the above - include last.
#include "XPCInlines.h"
/***************************************************************************/
// Maps have inlines that use the above - include last.
#include "XPCMaps.h"
/***************************************************************************/
#endif /* xpcprivate_h___ */