3869 lines
130 KiB
C++
3869 lines
130 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/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_CONTEXT_MAP_LENGTH 8
|
|
#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_NATIVE_WRAPPER_MAP_LENGTH 8
|
|
#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
|
|
|
|
#define INVALID_OBJECT ((JSObject*)1)
|
|
|
|
// 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->ext.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;
|
|
|
|
|
|
static bool ReportAllJSExceptions()
|
|
{
|
|
return gReportAllJSExceptions > 0;
|
|
}
|
|
|
|
protected:
|
|
virtual ~nsXPConnect();
|
|
|
|
nsXPConnect();
|
|
|
|
private:
|
|
// Singleton instance
|
|
static nsXPConnect* gSelf;
|
|
static bool gOnceAliveNowDead;
|
|
|
|
XPCJSRuntime* mRuntime;
|
|
bool mShuttingDown;
|
|
|
|
static uint32_t gReportAllJSExceptions;
|
|
|
|
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 XPCJSContextStack;
|
|
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 : public mozilla::CycleCollectedJSRuntime
|
|
{
|
|
public:
|
|
static XPCJSRuntime* newXPCJSRuntime(nsXPConnect* aXPConnect);
|
|
static XPCJSRuntime* Get() { return nsXPConnect::XPConnect()->GetRuntime(); }
|
|
|
|
XPCJSContextStack* GetJSContextStack() {return mJSContextStack;}
|
|
void DestroyJSContextStack();
|
|
|
|
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 OnJSContextNew(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];
|
|
}
|
|
|
|
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;
|
|
bool CustomContextCallback(JSContext* cx, unsigned operation) override;
|
|
static void GCSliceCallback(JSRuntime* rt,
|
|
JS::GCProgress progress,
|
|
const JS::GCDescription& desc);
|
|
static void FinalizeCallback(JSFreeOp* fop,
|
|
JSFinalizeStatus status,
|
|
bool isCompartmentGC,
|
|
void* data);
|
|
static void WeakPointerZoneGroupCallback(JSRuntime* rt, void* data);
|
|
static void WeakPointerCompartmentCallback(JSRuntime* rt, 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);
|
|
|
|
struct EnvironmentPreparer : public js::ScriptEnvironmentPreparer {
|
|
void invoke(JS::HandleObject scope, Closure& closure) override;
|
|
};
|
|
EnvironmentPreparer mEnvironmentPreparer;
|
|
|
|
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);
|
|
|
|
private:
|
|
XPCJSRuntime() = delete;
|
|
explicit XPCJSRuntime(nsXPConnect* aXPConnect);
|
|
|
|
void ReleaseIncrementally(nsTArray<nsISupports*>& array);
|
|
|
|
static const char* const mStrings[IDX_TOTAL_COUNT];
|
|
jsid mStrIDs[IDX_TOTAL_COUNT];
|
|
JS::Value mStrJSVals[IDX_TOTAL_COUNT];
|
|
|
|
XPCJSContextStack* mJSContextStack;
|
|
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;
|
|
|
|
// If we spend too much time running JS code in an event handler, then we
|
|
// want to show the slow script UI. The timeout T is controlled by prefs. We
|
|
// invoke the interrupt callback once after T/2 seconds and set
|
|
// mSlowScriptSecondHalf to true. After another T/2 seconds, we invoke the
|
|
// interrupt callback again. Since mSlowScriptSecondHalf is now true, it
|
|
// shows the slow script UI. The reason we invoke the callback twice is to
|
|
// ensure that putting the computer to sleep while running a script doesn't
|
|
// cause the UI to be shown. If the laptop goes to sleep during one of the
|
|
// timeout periods, the script still has the other T/2 seconds to complete
|
|
// before the slow script UI is shown.
|
|
bool mSlowScriptSecondHalf;
|
|
|
|
// mSlowScriptCheckpoint is set to the time when:
|
|
// 1. We started processing the current event, or
|
|
// 2. mSlowScriptSecondHalf was set to true
|
|
// (whichever comes later). We use it to determine whether the interrupt
|
|
// callback needs to do anything.
|
|
mozilla::TimeStamp mSlowScriptCheckpoint;
|
|
|
|
friend class Watchdog;
|
|
friend class AutoLockWatchdog;
|
|
friend class XPCIncrementalReleaseRunnable;
|
|
};
|
|
|
|
/***************************************************************************/
|
|
/***************************************************************************/
|
|
// XPCContext is mostly a dumb class to hold JSContext specific data and
|
|
// maps that let us find wrappers created for the given JSContext.
|
|
|
|
// no virtuals
|
|
class XPCContext
|
|
{
|
|
friend class XPCJSRuntime;
|
|
public:
|
|
static XPCContext* GetXPCContext(JSContext* aJSContext)
|
|
{
|
|
MOZ_ASSERT(JS_GetSecondContextPrivate(aJSContext), "should already have XPCContext");
|
|
return static_cast<XPCContext*>(JS_GetSecondContextPrivate(aJSContext));
|
|
}
|
|
|
|
XPCJSRuntime* GetRuntime() const {return mRuntime;}
|
|
JSContext* GetJSContext() const {return mJSContext;}
|
|
|
|
enum LangType {LANG_UNKNOWN, LANG_JS, LANG_NATIVE};
|
|
|
|
LangType GetCallingLangType() const
|
|
{
|
|
return mCallingLangType;
|
|
}
|
|
LangType SetCallingLangType(LangType lt)
|
|
{
|
|
LangType tmp = mCallingLangType;
|
|
mCallingLangType = lt;
|
|
return tmp;
|
|
}
|
|
bool CallerTypeIsJavaScript() const
|
|
{
|
|
return LANG_JS == mCallingLangType;
|
|
}
|
|
bool CallerTypeIsNative() const
|
|
{
|
|
return LANG_NATIVE == mCallingLangType;
|
|
}
|
|
bool CallerTypeIsKnown() const
|
|
{
|
|
return LANG_UNKNOWN != mCallingLangType;
|
|
}
|
|
|
|
nsresult GetException(nsIException** e)
|
|
{
|
|
nsCOMPtr<nsIException> rval = mException;
|
|
rval.forget(e);
|
|
return NS_OK;
|
|
}
|
|
void SetException(nsIException* e)
|
|
{
|
|
mException = e;
|
|
}
|
|
|
|
nsresult GetLastResult() {return mLastResult;}
|
|
void SetLastResult(nsresult rc) {mLastResult = rc;}
|
|
|
|
nsresult GetPendingResult() {return mPendingResult;}
|
|
void SetPendingResult(nsresult rc) {mPendingResult = rc;}
|
|
|
|
void DebugDump(int16_t depth);
|
|
|
|
~XPCContext();
|
|
|
|
private:
|
|
XPCContext(); // no implementation
|
|
XPCContext(XPCJSRuntime* aRuntime, JSContext* aJSContext);
|
|
|
|
static XPCContext* newXPCContext(XPCJSRuntime* aRuntime,
|
|
JSContext* aJSContext);
|
|
private:
|
|
XPCJSRuntime* mRuntime;
|
|
JSContext* mJSContext;
|
|
nsresult mLastResult;
|
|
nsresult mPendingResult;
|
|
nsCOMPtr<nsIException> mException;
|
|
LangType mCallingLangType;
|
|
bool mErrorUnreported;
|
|
};
|
|
|
|
/***************************************************************************/
|
|
|
|
#define NATIVE_CALLER XPCContext::LANG_NATIVE
|
|
#define JS_CALLER XPCContext::LANG_JS
|
|
|
|
// 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 : 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);
|
|
NS_IMETHOD GetLanguage(uint16_t* aResult);
|
|
|
|
enum {NO_ARGS = (unsigned) -1};
|
|
|
|
static JSContext* GetDefaultJSContext();
|
|
|
|
XPCCallContext(XPCContext::LangType callerLanguage,
|
|
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 XPCContext* GetXPCContext() const ;
|
|
inline JSContext* GetJSContext() const ;
|
|
inline bool GetContextPopRequired() const ;
|
|
inline XPCContext::LangType GetCallerLanguage() const ;
|
|
inline XPCContext::LangType GetPrevCallerLanguage() 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); // not implemented
|
|
XPCCallContext& operator= (const XPCCallContext& r); // not implemented
|
|
|
|
private:
|
|
// posible values for mState
|
|
enum State {
|
|
INIT_FAILED,
|
|
SYSTEM_SHUTDOWN,
|
|
HAVE_CONTEXT,
|
|
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;
|
|
|
|
XPCContext* mXPCContext;
|
|
JSContext* mJSContext;
|
|
|
|
XPCContext::LangType mCallerLanguage;
|
|
|
|
// ctor does not necessarily init the following. BEWARE!
|
|
|
|
XPCContext::LangType mPrevCallerLanguage;
|
|
|
|
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.
|
|
|
|
struct XPCWrappedNativeJSClass;
|
|
extern const XPCWrappedNativeJSClass XPC_WN_NoHelper_JSClass;
|
|
extern const js::Class XPC_WN_NoMods_WithCall_Proto_JSClass;
|
|
extern const js::Class XPC_WN_NoMods_NoCall_Proto_JSClass;
|
|
extern const js::Class XPC_WN_ModsAllowed_WithCall_Proto_JSClass;
|
|
extern const js::Class XPC_WN_ModsAllowed_NoCall_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);
|
|
|
|
// Macros to initialize Object or Function like XPC_WN classes
|
|
#define XPC_WN_WithCall_ObjectOps \
|
|
{ \
|
|
nullptr, /* lookupProperty */ \
|
|
nullptr, /* defineProperty */ \
|
|
nullptr, /* hasProperty */ \
|
|
nullptr, /* getProperty */ \
|
|
nullptr, /* setProperty */ \
|
|
nullptr, /* getOwnPropertyDescriptor */ \
|
|
nullptr, /* deleteProperty */ \
|
|
nullptr, nullptr, /* watch/unwatch */ \
|
|
nullptr, /* getElements */ \
|
|
nullptr, /* enumerate */ \
|
|
}
|
|
|
|
#define XPC_WN_NoCall_ObjectOps \
|
|
{ \
|
|
nullptr, /* lookupProperty */ \
|
|
nullptr, /* defineProperty */ \
|
|
nullptr, /* hasProperty */ \
|
|
nullptr, /* getProperty */ \
|
|
nullptr, /* setProperty */ \
|
|
nullptr, /* getOwnPropertyDescriptor */ \
|
|
nullptr, /* deleteProperty */ \
|
|
nullptr, nullptr, /* watch/unwatch */ \
|
|
nullptr, /* getElements */ \
|
|
nullptr, /* enumerate */ \
|
|
}
|
|
|
|
// 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_WithCall_Proto_JSClass ||
|
|
clazz == &XPC_WN_NoMods_NoCall_Proto_JSClass ||
|
|
clazz == &XPC_WN_ModsAllowed_WithCall_Proto_JSClass ||
|
|
clazz == &XPC_WN_ModsAllowed_NoCall_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 : 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();
|
|
mDOMExpandoSet->init(8);
|
|
}
|
|
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;
|
|
|
|
// 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; }
|
|
|
|
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; };
|
|
|
|
protected:
|
|
virtual ~XPCWrappedNativeScope();
|
|
|
|
XPCWrappedNativeScope(); // not implemented
|
|
|
|
private:
|
|
class ClearInterpositionsObserver final : public nsIObserver {
|
|
~ClearInterpositionsObserver() {}
|
|
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSIOBSERVER
|
|
};
|
|
|
|
static XPCWrappedNativeScope* gScopes;
|
|
static XPCWrappedNativeScope* gDyingScopes;
|
|
|
|
static InterpositionMap* gInterpositionMap;
|
|
|
|
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
|
|
{
|
|
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;
|
|
};
|
|
|
|
/***************************************************************************/
|
|
// XPCNativeInterface represents a single idl declared interface. This is
|
|
// primarily the set of XPCNativeMembers.
|
|
|
|
// Tight. No virtual methods.
|
|
|
|
class XPCNativeInterface
|
|
{
|
|
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);
|
|
|
|
#define XPC_NATIVE_IFACE_MARK_FLAG ((uint16_t)JS_BIT(15)) // only high bit of 16 is set
|
|
|
|
void Mark() {
|
|
mMarked = 1;
|
|
}
|
|
|
|
void Unmark() {
|
|
mMarked = 0;
|
|
}
|
|
|
|
bool IsMarked() const {
|
|
return mMarked != 0;
|
|
}
|
|
|
|
// NOP. This is just here to make the AutoMarkingPtr code compile.
|
|
inline void TraceJS(JSTracer* trc) {}
|
|
inline void AutoTrace(JSTracer* trc) {}
|
|
|
|
static void DestroyInstance(XPCNativeInterface* inst);
|
|
|
|
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
|
|
|
|
protected:
|
|
static XPCNativeInterface* NewInstance(nsIInterfaceInfo* aInfo);
|
|
|
|
XPCNativeInterface(); // not implemented
|
|
XPCNativeInterface(nsIInterfaceInfo* aInfo, jsid aName)
|
|
: mInfo(aInfo), mName(aName), mMemberCount(0), mMarked(0)
|
|
{
|
|
MOZ_COUNT_CTOR(XPCNativeInterface);
|
|
}
|
|
~XPCNativeInterface() {
|
|
MOZ_COUNT_DTOR(XPCNativeInterface);
|
|
}
|
|
|
|
void* operator new(size_t, void* p) CPP_THROW_NEW {return p;}
|
|
|
|
XPCNativeInterface(const XPCNativeInterface& r); // not implemented
|
|
XPCNativeInterface& operator= (const XPCNativeInterface& r); // not implemented
|
|
|
|
private:
|
|
nsCOMPtr<nsIInterfaceInfo> mInfo;
|
|
jsid mName;
|
|
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
|
|
{
|
|
public:
|
|
explicit XPCNativeSetKey(XPCNativeSet* BaseSet = nullptr,
|
|
XPCNativeInterface* Addition = nullptr,
|
|
uint16_t Position = 0)
|
|
: mIsAKey(IS_A_KEY), mPosition(Position), mBaseSet(BaseSet),
|
|
mAddition(Addition) {}
|
|
~XPCNativeSetKey() {}
|
|
|
|
XPCNativeSet* GetBaseSet() const {return mBaseSet;}
|
|
XPCNativeInterface* GetAddition() const {return mAddition;}
|
|
uint16_t GetPosition() const {return mPosition;}
|
|
|
|
// This is a fun little hack...
|
|
// We build these keys only on the stack. We use them for lookup in
|
|
// NativeSetMap. Becasue we don't want to pay the cost of cloning a key and
|
|
// sticking it into the hashtable, when the XPCNativeSet actually
|
|
// gets added to the table the 'key' in the table is a pointer to the
|
|
// set itself and not this key. Our key compare function expects to get
|
|
// a key and a set. When we do external lookups in the map we pass in one
|
|
// of these keys and our compare function gets passed a key and a set.
|
|
// (see compare_NativeKeyToSet in xpcmaps.cpp). This is all well and good.
|
|
// Except, when the table decides to resize itself. Then it tries to use
|
|
// our compare function with the 'keys' that are in the hashtable (which are
|
|
// really XPCNativeSet objects and not XPCNativeSetKey objects!
|
|
//
|
|
// So, the hack is to have the compare function assume it is getting a
|
|
// XPCNativeSetKey pointer and call this IsAKey method. If that fails then
|
|
// it realises that it really has a XPCNativeSet pointer and deals with that
|
|
// fact. This is safe because we know that both of these classes have no
|
|
// virtual methods and their first data member is a uint16_t. We are
|
|
// confident that XPCNativeSet->mMemberCount will never be 0xffff.
|
|
|
|
bool IsAKey() const {return mIsAKey == IS_A_KEY;}
|
|
|
|
enum {IS_A_KEY = 0xffff};
|
|
|
|
// Allow shallow copy
|
|
|
|
private:
|
|
uint16_t mIsAKey; // must be first data member
|
|
uint16_t mPosition;
|
|
XPCNativeSet* mBaseSet;
|
|
XPCNativeInterface* mAddition;
|
|
};
|
|
|
|
/***************************************************************************/
|
|
// XPCNativeSet represents an ordered collection of XPCNativeInterface pointers.
|
|
|
|
class XPCNativeSet
|
|
{
|
|
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;
|
|
|
|
#define XPC_NATIVE_SET_MARK_FLAG ((uint16_t)JS_BIT(15)) // only high bit of 16 is set
|
|
|
|
inline void Mark();
|
|
|
|
// NOP. This is just here to make the AutoMarkingPtr code compile.
|
|
inline void TraceJS(JSTracer* trc) {}
|
|
inline void AutoTrace(JSTracer* trc) {}
|
|
|
|
private:
|
|
void MarkSelfOnly() {
|
|
mMarked = 1;
|
|
}
|
|
|
|
public:
|
|
void Unmark() {
|
|
mMarked = 0;
|
|
}
|
|
bool IsMarked() const {
|
|
return !!mMarked;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
inline void ASSERT_NotMarked();
|
|
#endif
|
|
|
|
void DebugDump(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
|
|
{
|
|
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).
|
|
|
|
// We maintain the invariant that every JSClass for which ext.isWrappedNative
|
|
// is true is a contained in an instance of this struct, and can thus be cast
|
|
// to it.
|
|
//
|
|
// XXXbz Do we still need this struct at all?
|
|
struct XPCWrappedNativeJSClass
|
|
{
|
|
js::Class base;
|
|
};
|
|
|
|
class XPCNativeScriptableShared
|
|
{
|
|
public:
|
|
const XPCNativeScriptableFlags& GetFlags() const {return mFlags;}
|
|
const JSClass* GetJSClass()
|
|
{return Jsvalify(&mJSClass.base);}
|
|
|
|
XPCNativeScriptableShared(uint32_t aFlags, char* aName)
|
|
: mFlags(aFlags)
|
|
{memset(&mJSClass, 0, sizeof(mJSClass));
|
|
mJSClass.base.name = aName; // take ownership
|
|
MOZ_COUNT_CTOR(XPCNativeScriptableShared);}
|
|
|
|
~XPCNativeScriptableShared()
|
|
{if (mJSClass.base.name)free((void*)mJSClass.base.name);
|
|
MOZ_COUNT_DTOR(XPCNativeScriptableShared);}
|
|
|
|
char* TransferNameOwnership()
|
|
{char* name=(char*)mJSClass.base.name; mJSClass.base.name = nullptr;
|
|
return name;}
|
|
|
|
void PopulateJSClass();
|
|
|
|
void Mark() {mFlags.Mark();}
|
|
void Unmark() {mFlags.Unmark();}
|
|
bool IsMarked() const {return mFlags.IsMarked();}
|
|
|
|
private:
|
|
XPCNativeScriptableFlags mFlags;
|
|
XPCWrappedNativeJSClass mJSClass;
|
|
};
|
|
|
|
/***************************************************************************/
|
|
// XPCNativeScriptableInfo is used to hold the nsIXPCScriptable state for a
|
|
// given class or instance.
|
|
|
|
class XPCNativeScriptableInfo
|
|
{
|
|
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();}
|
|
|
|
XPCNativeScriptableShared*
|
|
GetScriptableShared() {return mShared;}
|
|
|
|
void
|
|
SetCallback(nsIXPCScriptable* s) {mCallback = s;}
|
|
void
|
|
SetCallback(already_AddRefed<nsIXPCScriptable>&& s) {mCallback = s;}
|
|
|
|
void
|
|
SetScriptableShared(XPCNativeScriptableShared* shared) {mShared = shared;}
|
|
|
|
void Mark() {
|
|
if (mShared)
|
|
mShared->Mark();
|
|
}
|
|
|
|
void TraceJS(JSTracer* trc) {}
|
|
void AutoTrace(JSTracer* trc) {}
|
|
|
|
protected:
|
|
explicit XPCNativeScriptableInfo(nsIXPCScriptable* scriptable = nullptr,
|
|
XPCNativeScriptableShared* shared = nullptr)
|
|
: mCallback(scriptable), mShared(shared)
|
|
{MOZ_COUNT_CTOR(XPCNativeScriptableInfo);}
|
|
public:
|
|
~XPCNativeScriptableInfo() {MOZ_COUNT_DTOR(XPCNativeScriptableInfo);}
|
|
private:
|
|
|
|
// disable copy ctor and assignment
|
|
XPCNativeScriptableInfo(const XPCNativeScriptableInfo& r); // not implemented
|
|
XPCNativeScriptableInfo& operator= (const XPCNativeScriptableInfo& r); // not implemented
|
|
|
|
private:
|
|
nsCOMPtr<nsIXPCScriptable> mCallback;
|
|
XPCNativeScriptableShared* mShared;
|
|
};
|
|
|
|
/***************************************************************************/
|
|
// XPCNativeScriptableCreateInfo is used in creating new wrapper and protos.
|
|
// it abstracts out the scriptable interface pointer and the flags. After
|
|
// creation these are factored differently using XPCNativeScriptableInfo.
|
|
|
|
class MOZ_STACK_CLASS XPCNativeScriptableCreateInfo
|
|
{
|
|
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
|
|
{
|
|
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;}
|
|
|
|
uint32_t
|
|
GetClassInfoFlags() const {return mClassInfoFlags;}
|
|
|
|
#ifdef GET_IT
|
|
#undef GET_IT
|
|
#endif
|
|
#define GET_IT(f_) const {return !!(mClassInfoFlags & nsIClassInfo:: f_ );}
|
|
|
|
bool ClassIsSingleton() GET_IT(SINGLETON)
|
|
bool ClassIsDOMObject() GET_IT(DOM_OBJECT)
|
|
bool ClassIsPluginObject() GET_IT(PLUGIN_OBJECT)
|
|
|
|
#undef GET_IT
|
|
|
|
void SetScriptableInfo(XPCNativeScriptableInfo* si)
|
|
{MOZ_ASSERT(!mScriptableInfo, "leak here!"); mScriptableInfo = si;}
|
|
|
|
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(JSRuntime* rt)
|
|
{
|
|
if (JS::IsIncrementalBarrierNeeded(rt) && mJSProtoObject)
|
|
mJSProtoObject.writeBarrierPre(rt);
|
|
}
|
|
|
|
// NOP. This is just here to make the AutoMarkingPtr code compile.
|
|
inline void AutoTrace(JSTracer* trc) {}
|
|
|
|
// Yes, we *do* need to mark the mScriptableInfo in both cases.
|
|
void Mark() const
|
|
{mSet->Mark();
|
|
if (mScriptableInfo) mScriptableInfo->Mark();}
|
|
|
|
#ifdef DEBUG
|
|
void ASSERT_SetNotMarked() const {mSet->ASSERT_NotMarked();}
|
|
#endif
|
|
|
|
~XPCWrappedNativeProto();
|
|
|
|
protected:
|
|
// disable copy ctor and assignment
|
|
XPCWrappedNativeProto(const XPCWrappedNativeProto& r); // not implemented
|
|
XPCWrappedNativeProto& operator= (const XPCWrappedNativeProto& r); // not implemented
|
|
|
|
// hide ctor
|
|
XPCWrappedNativeProto(XPCWrappedNativeScope* Scope,
|
|
nsIClassInfo* ClassInfo,
|
|
uint32_t ClassInfoFlags,
|
|
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;
|
|
uint32_t mClassInfoFlags;
|
|
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
|
|
{
|
|
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);}
|
|
|
|
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;
|
|
};
|
|
|
|
/***********************************************/
|
|
// XPCWrappedNativeTearOffChunk is a linked list of XPCWrappedNativeTearOff
|
|
// objects. It lets us allocate a set of XPCWrappedNativeTearOff objects and
|
|
// link the sets - rather than only having the option of linking single
|
|
// XPCWrappedNativeTearOff objects.
|
|
|
|
class XPCWrappedNativeTearOffChunk
|
|
{
|
|
friend class XPCWrappedNative;
|
|
private:
|
|
XPCWrappedNativeTearOffChunk() : mNextChunk(nullptr) {}
|
|
~XPCWrappedNativeTearOffChunk() {delete mNextChunk;}
|
|
|
|
private:
|
|
XPCWrappedNativeTearOff mTearOff;
|
|
XPCWrappedNativeTearOffChunk* mNextChunk;
|
|
};
|
|
|
|
/***************************************************************************/
|
|
// 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. Be careful not to trace the
|
|
// bogus INVALID_OBJECT value we can have during init, though.
|
|
if (mFlatJSObject) {
|
|
JS_CallTenuredObjectTracer(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(); // not implemented
|
|
|
|
// This ctor is used if this object will have a proto.
|
|
XPCWrappedNative(already_AddRefed<nsISupports>&& aIdentity,
|
|
XPCWrappedNativeProto* aProto);
|
|
|
|
// This ctor is used if this object will NOT have a proto.
|
|
XPCWrappedNative(already_AddRefed<nsISupports>&& aIdentity,
|
|
XPCWrappedNativeScope* aScope,
|
|
XPCNativeSet* aSet);
|
|
|
|
virtual ~XPCWrappedNative();
|
|
void Destroy();
|
|
|
|
void UpdateScriptableInfo(XPCNativeScriptableInfo* si);
|
|
|
|
private:
|
|
enum {
|
|
// 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;
|
|
XPCWrappedNativeTearOffChunk mFirstChunk;
|
|
};
|
|
|
|
/***************************************************************************
|
|
****************************************************************************
|
|
*
|
|
* 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);
|
|
|
|
static nsresult CheckForException(XPCCallContext & ccx,
|
|
const char * aPropertyName,
|
|
const char * anInterfaceName,
|
|
bool aForceReport);
|
|
private:
|
|
virtual ~nsXPCWrappedJSClass();
|
|
|
|
nsXPCWrappedJSClass(); // not implemented
|
|
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 nsIXPConnectWrappedJS,
|
|
public nsSupportsWeakReference,
|
|
public nsIPropertyBag,
|
|
public XPCRootSetElem
|
|
{
|
|
public:
|
|
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
|
NS_DECL_NSIXPCONNECTJSOBJECTHOLDER
|
|
NS_DECL_NSIXPCONNECTWRAPPEDJS
|
|
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 : 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(); // not implemented
|
|
|
|
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 Interface the interface of src that we want
|
|
* @param cache the wrapper cache for src (may be null, in which case src
|
|
* will be QI'ed to get the cache)
|
|
* @param allowNativeWrapper if true, this method may wrap the resulting
|
|
* JSObject in an XPCNativeWrapper and return that, as needed.
|
|
* @param pErr [out] relevant error code, if any.
|
|
* @param src_is_identity optional performance hint. Set to true only
|
|
* if src is the identity pointer.
|
|
*/
|
|
static bool NativeInterface2JSObject(JS::MutableHandleValue d,
|
|
nsIXPConnectJSObjectHolder** dest,
|
|
xpcObjectHelper& aHelper,
|
|
const nsID* iid,
|
|
XPCNativeInterface** Interface,
|
|
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(); // not implemented
|
|
|
|
};
|
|
|
|
/***************************************************************************/
|
|
// 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(); // not implemented
|
|
|
|
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;
|
|
};
|
|
|
|
|
|
/***************************************************************************/
|
|
// XPCJSContextStack is not actually an xpcom object, but xpcom calls are
|
|
// delegated to it as an implementation detail.
|
|
struct XPCJSContextInfo {
|
|
explicit XPCJSContextInfo(JSContext* aCx) :
|
|
cx(aCx),
|
|
savedFrameChain(false)
|
|
{}
|
|
JSContext* cx;
|
|
|
|
// Whether the frame chain was saved
|
|
bool savedFrameChain;
|
|
};
|
|
|
|
namespace xpc {
|
|
bool PushNullJSContext();
|
|
void PopNullJSContext();
|
|
|
|
} /* namespace xpc */
|
|
|
|
namespace mozilla {
|
|
namespace dom {
|
|
namespace danger {
|
|
class AutoCxPusher;
|
|
} // namespace danger
|
|
} // namespace dom
|
|
} // namespace mozilla
|
|
|
|
class XPCJSContextStack
|
|
{
|
|
public:
|
|
explicit XPCJSContextStack(XPCJSRuntime* aRuntime)
|
|
: mRuntime(aRuntime)
|
|
, mSafeJSContext(nullptr)
|
|
{ }
|
|
|
|
virtual ~XPCJSContextStack();
|
|
|
|
uint32_t Count()
|
|
{
|
|
return mStack.Length();
|
|
}
|
|
|
|
JSContext* Peek()
|
|
{
|
|
return mStack.IsEmpty() ? nullptr : mStack[mStack.Length() - 1].cx;
|
|
}
|
|
|
|
JSContext* InitSafeJSContext();
|
|
JSContext* GetSafeJSContext();
|
|
bool HasJSContext(JSContext* cx);
|
|
|
|
const InfallibleTArray<XPCJSContextInfo>* GetStack()
|
|
{ return &mStack; }
|
|
|
|
private:
|
|
friend class mozilla::dom::danger::AutoCxPusher;
|
|
friend bool xpc::PushNullJSContext();
|
|
friend void xpc::PopNullJSContext();
|
|
|
|
// We make these private so that stack manipulation can only happen
|
|
// through one of the above friends.
|
|
JSContext* Pop();
|
|
bool Push(JSContext* cx);
|
|
|
|
AutoInfallibleTArray<XPCJSContextInfo, 16> mStack;
|
|
XPCJSRuntime* mRuntime;
|
|
JSContext* mSafeJSContext;
|
|
};
|
|
|
|
/***************************************************************************/
|
|
// '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 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()->GetJSContextStack()->GetSafeJSContext();
|
|
}
|
|
|
|
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 Define(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 File : 1;
|
|
bool crypto : 1;
|
|
bool rtcIdentityProvider : 1;
|
|
bool fetch : 1;
|
|
bool caches : 1;
|
|
bool fileReader: 1;
|
|
};
|
|
|
|
// 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);
|
|
|
|
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)
|
|
{ }
|
|
|
|
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;
|
|
|
|
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);
|
|
|
|
// InitGlobalObject enters the compartment of aGlobal, so it doesn't matter what
|
|
// compartment aJSContext is in.
|
|
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;
|
|
|
|
// 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()
|
|
{
|
|
const char* prefName =
|
|
"security.turn_off_all_security_so_that_viruses_can_take_over_this_computer";
|
|
MOZ_RELEASE_ASSERT(mozilla::Preferences::GetBool(prefName));
|
|
}
|
|
|
|
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_LocalizeRuntime(JSRuntime* rt);
|
|
void
|
|
xpc_DelocalizeRuntime(JSRuntime* rt);
|
|
|
|
/***************************************************************************/
|
|
// Inlines use the above - include last.
|
|
|
|
#include "XPCInlines.h"
|
|
|
|
/***************************************************************************/
|
|
// Maps have inlines that use the above - include last.
|
|
|
|
#include "XPCMaps.h"
|
|
|
|
/***************************************************************************/
|
|
|
|
#endif /* xpcprivate_h___ */
|