michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* vim: set ts=8 sts=4 et sw=4 tw=99: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * XPConnect allows JS code to manipulate C++ object and C++ code to manipulate michael@0: * JS objects. JS manipulation of C++ objects tends to be significantly more michael@0: * complex. This comment explains how it is orchestrated by XPConnect. michael@0: * michael@0: * For each C++ object to be manipulated in JS, there is a corresponding JS michael@0: * object. This is called the "flattened JS object". By default, there is an michael@0: * additional C++ object involved of type XPCWrappedNative. The XPCWrappedNative michael@0: * holds pointers to the C++ object and the flat JS object. michael@0: * michael@0: * All XPCWrappedNative objects belong to an XPCWrappedNativeScope. These scopes michael@0: * are essentially in 1:1 correspondence with JS global objects. The michael@0: * XPCWrappedNativeScope has a pointer to the JS global object. The parent of a michael@0: * flattened JS object is, by default, the global JS object corresponding to the michael@0: * wrapper's XPCWrappedNativeScope (the exception to this rule is when a michael@0: * PreCreate hook asks for a different parent; see nsIXPCScriptable below). michael@0: * michael@0: * Some C++ objects (notably DOM objects) have information associated with them michael@0: * that lists the interfaces implemented by these objects. A C++ object exposes michael@0: * this information by implementing nsIClassInfo. If a C++ object implements michael@0: * nsIClassInfo, then JS code can call its methods without needing to use michael@0: * QueryInterface first. Typically, all instances of a C++ class share the same michael@0: * nsIClassInfo instance. (That is, obj->QueryInterface(nsIClassInfo) returns michael@0: * the same result for every obj of a given class.) michael@0: * michael@0: * XPConnect tracks nsIClassInfo information in an XPCWrappedNativeProto object. michael@0: * A given XPCWrappedNativeScope will have one XPCWrappedNativeProto for each michael@0: * nsIClassInfo instance being used. The XPCWrappedNativeProto has an associated michael@0: * JS object, which is used as the prototype of all flattened JS objects created michael@0: * for C++ objects with the given nsIClassInfo. michael@0: * michael@0: * Each XPCWrappedNativeProto has a pointer to its XPCWrappedNativeScope. If an michael@0: * XPCWrappedNative wraps a C++ object with class info, then it points to its michael@0: * XPCWrappedNativeProto. Otherwise it points to its XPCWrappedNativeScope. (The michael@0: * pointers are smooshed together in a tagged union.) Either way it can reach michael@0: * its scope. michael@0: * michael@0: * An XPCWrappedNativeProto keeps track of the set of interfaces implemented by michael@0: * the C++ object in an XPCNativeSet. (The list of interfaces is obtained by michael@0: * calling a method on the nsIClassInfo.) An XPCNativeSet is a collection of michael@0: * XPCNativeInterfaces. Each interface stores the list of members, which can be michael@0: * methods, constants, getters, or setters. michael@0: * michael@0: * An XPCWrappedNative also points to an XPCNativeSet. Initially this starts out michael@0: * the same as the XPCWrappedNativeProto's set. If there is no proto, it starts michael@0: * out as a singleton set containing nsISupports. If JS code QI's new interfaces michael@0: * outside of the existing set, the set will grow. All QueryInterface results michael@0: * are cached in XPCWrappedNativeTearOff objects, which are linked off of the michael@0: * XPCWrappedNative. michael@0: * michael@0: * Besides having class info, a C++ object may be "scriptable" (i.e., implement michael@0: * nsIXPCScriptable). This allows it to implement a more DOM-like interface, michael@0: * besides just exposing XPCOM methods and constants. An nsIXPCScriptable michael@0: * instance has hooks that correspond to all the normal JSClass hooks. Each michael@0: * nsIXPCScriptable instance is mirrored by an XPCNativeScriptableInfo in michael@0: * XPConnect. These can have pointers from XPCWrappedNativeProto and michael@0: * XPCWrappedNative (since C++ objects can have scriptable info without having michael@0: * class info). michael@0: * michael@0: * Most data in an XPCNativeScriptableInfo is shared between instances. The michael@0: * shared data is stored in an XPCNativeScriptableShared object. This type is michael@0: * important because it holds the JSClass of the flattened JS objects with the michael@0: * given scriptable info. michael@0: */ michael@0: michael@0: /* All the XPConnect private declarations - only include locally. */ michael@0: michael@0: #ifndef xpcprivate_h___ michael@0: #define xpcprivate_h___ michael@0: michael@0: #include "mozilla/Alignment.h" michael@0: #include "mozilla/Assertions.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/CycleCollectedJSRuntime.h" michael@0: #include "mozilla/GuardObjects.h" michael@0: #include "mozilla/Maybe.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "mozilla/TimeStamp.h" michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "xpcpublic.h" michael@0: #include "js/TracingAPI.h" michael@0: #include "js/WeakMapPtr.h" michael@0: #include "pldhash.h" michael@0: #include "nscore.h" michael@0: #include "nsXPCOM.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsCycleCollectionParticipant.h" michael@0: #include "nsDebug.h" michael@0: #include "nsISupports.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsIClassInfoImpl.h" michael@0: #include "nsIComponentManager.h" michael@0: #include "nsIComponentRegistrar.h" michael@0: #include "nsISupportsPrimitives.h" michael@0: #include "nsMemory.h" michael@0: #include "nsIXPConnect.h" michael@0: #include "nsIInterfaceInfo.h" michael@0: #include "nsIXPCScriptable.h" michael@0: #include "nsIXPCSecurityManager.h" michael@0: #include "nsIJSRuntimeService.h" michael@0: #include "nsWeakReference.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsXPTCUtils.h" michael@0: #include "xptinfo.h" michael@0: #include "XPCForwards.h" michael@0: #include "XPCLog.h" michael@0: #include "xpccomponents.h" michael@0: #include "xpcexception.h" michael@0: #include "xpcjsid.h" michael@0: #include "prenv.h" michael@0: #include "prclist.h" michael@0: #include "prcvar.h" michael@0: #include "nsString.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsXPIDLString.h" michael@0: #include "nsAutoJSValHolder.h" michael@0: michael@0: #include "MainThreadUtils.h" michael@0: michael@0: #include "nsIConsoleService.h" michael@0: #include "nsIScriptError.h" michael@0: #include "nsIException.h" michael@0: michael@0: #include "nsVariant.h" michael@0: #include "nsIPropertyBag.h" michael@0: #include "nsIProperty.h" michael@0: #include "nsCOMArray.h" michael@0: #include "nsTArray.h" michael@0: #include "nsBaseHashtable.h" michael@0: #include "nsHashKeys.h" michael@0: #include "nsWrapperCache.h" michael@0: #include "nsStringBuffer.h" michael@0: #include "nsDataHashtable.h" michael@0: #include "nsDeque.h" michael@0: michael@0: #include "nsIScriptSecurityManager.h" michael@0: #include "nsNetUtil.h" michael@0: michael@0: #include "nsIPrincipal.h" michael@0: #include "nsJSPrincipals.h" michael@0: #include "nsIScriptObjectPrincipal.h" michael@0: #include "xpcObjectHelper.h" michael@0: #include "nsIThreadInternal.h" michael@0: michael@0: #include "SandboxPrivate.h" michael@0: #include "BackstagePass.h" michael@0: #include "nsCxPusher.h" michael@0: #include "nsAXPCNativeCallContext.h" michael@0: michael@0: #ifdef XP_WIN michael@0: // Nasty MS defines michael@0: #ifdef GetClassInfo michael@0: #undef GetClassInfo michael@0: #endif michael@0: #ifdef GetClassName michael@0: #undef GetClassName michael@0: #endif michael@0: #endif /* XP_WIN */ michael@0: michael@0: #include "nsINode.h" michael@0: michael@0: /***************************************************************************/ michael@0: // default initial sizes for maps (hashtables) michael@0: michael@0: #define XPC_CONTEXT_MAP_SIZE 16 michael@0: #define XPC_JS_MAP_SIZE 64 michael@0: #define XPC_JS_CLASS_MAP_SIZE 64 michael@0: michael@0: #define XPC_NATIVE_MAP_SIZE 64 michael@0: #define XPC_NATIVE_PROTO_MAP_SIZE 16 michael@0: #define XPC_DYING_NATIVE_PROTO_MAP_SIZE 16 michael@0: #define XPC_DETACHED_NATIVE_PROTO_MAP_SIZE 32 michael@0: #define XPC_NATIVE_INTERFACE_MAP_SIZE 64 michael@0: #define XPC_NATIVE_SET_MAP_SIZE 64 michael@0: #define XPC_NATIVE_JSCLASS_MAP_SIZE 32 michael@0: #define XPC_THIS_TRANSLATOR_MAP_SIZE 8 michael@0: #define XPC_NATIVE_WRAPPER_MAP_SIZE 16 michael@0: #define XPC_WRAPPER_MAP_SIZE 16 michael@0: michael@0: /***************************************************************************/ michael@0: // data declarations... michael@0: extern const char XPC_CONTEXT_STACK_CONTRACTID[]; michael@0: extern const char XPC_RUNTIME_CONTRACTID[]; michael@0: extern const char XPC_EXCEPTION_CONTRACTID[]; michael@0: extern const char XPC_CONSOLE_CONTRACTID[]; michael@0: extern const char XPC_SCRIPT_ERROR_CONTRACTID[]; michael@0: extern const char XPC_ID_CONTRACTID[]; michael@0: extern const char XPC_XPCONNECT_CONTRACTID[]; michael@0: michael@0: /***************************************************************************/ michael@0: // Useful macros... michael@0: michael@0: #define XPC_STRING_GETTER_BODY(dest, src) \ michael@0: NS_ENSURE_ARG_POINTER(dest); \ michael@0: char* result; \ michael@0: if (src) \ michael@0: result = (char*) nsMemory::Clone(src, \ michael@0: sizeof(char)*(strlen(src)+1)); \ michael@0: else \ michael@0: result = nullptr; \ michael@0: *dest = result; \ michael@0: return (result || !src) ? NS_OK : NS_ERROR_OUT_OF_MEMORY michael@0: michael@0: michael@0: #define WRAPPER_SLOTS (JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS ) michael@0: michael@0: #define INVALID_OBJECT ((JSObject *)1) michael@0: michael@0: // If IS_WN_CLASS for the JSClass of an object is true, the object is a michael@0: // wrappednative wrapper, holding the XPCWrappedNative in its private slot. michael@0: static inline bool IS_WN_CLASS(const js::Class* clazz) michael@0: { michael@0: return clazz->ext.isWrappedNative; michael@0: } michael@0: michael@0: static inline bool IS_WN_REFLECTOR(JSObject *obj) michael@0: { michael@0: return IS_WN_CLASS(js::GetObjectClass(obj)); michael@0: } michael@0: michael@0: /*************************************************************************** michael@0: **************************************************************************** michael@0: * michael@0: * Core runtime and context classes... michael@0: * michael@0: **************************************************************************** michael@0: ***************************************************************************/ michael@0: michael@0: // We have a general rule internally that getters that return addref'd interface michael@0: // pointer generally do so using an 'out' parm. When interface pointers are michael@0: // returned as function call result values they are not addref'd. Exceptions michael@0: // to this rule are noted explicitly. michael@0: michael@0: inline bool michael@0: AddToCCKind(JSGCTraceKind kind) michael@0: { michael@0: return kind == JSTRACE_OBJECT || kind == JSTRACE_SCRIPT; michael@0: } michael@0: michael@0: class nsXPConnect : public nsIXPConnect, michael@0: public nsIThreadObserver, michael@0: public nsSupportsWeakReference, michael@0: public nsIJSRuntimeService michael@0: { michael@0: public: michael@0: // all the interface method declarations... michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIXPCONNECT michael@0: NS_DECL_NSITHREADOBSERVER michael@0: NS_DECL_NSIJSRUNTIMESERVICE michael@0: michael@0: // non-interface implementation michael@0: public: michael@0: // These get non-addref'd pointers michael@0: static nsXPConnect* XPConnect() michael@0: { michael@0: // Do a release-mode assert that we're not doing anything significant in michael@0: // XPConnect off the main thread. If you're an extension developer hitting michael@0: // this, you need to change your code. See bug 716167. michael@0: if (!MOZ_LIKELY(NS_IsMainThread())) michael@0: MOZ_CRASH(); michael@0: michael@0: return gSelf; michael@0: } michael@0: michael@0: static XPCJSRuntime* GetRuntimeInstance(); michael@0: XPCJSRuntime* GetRuntime() {return mRuntime;} michael@0: michael@0: static bool IsISupportsDescendant(nsIInterfaceInfo* info); michael@0: michael@0: nsIXPCSecurityManager* GetDefaultSecurityManager() const michael@0: { michael@0: // mDefaultSecurityManager is main-thread only. michael@0: if (!NS_IsMainThread()) { michael@0: return nullptr; michael@0: } michael@0: return mDefaultSecurityManager; michael@0: } michael@0: michael@0: // This returns an AddRef'd pointer. It does not do this with an 'out' param michael@0: // only because this form is required by the generic module macro: michael@0: // NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR michael@0: static nsXPConnect* GetSingleton(); michael@0: michael@0: // Called by module code in dll startup michael@0: static void InitStatics(); michael@0: // Called by module code on dll shutdown. michael@0: static void ReleaseXPConnectSingleton(); michael@0: michael@0: virtual ~nsXPConnect(); michael@0: michael@0: bool IsShuttingDown() const {return mShuttingDown;} michael@0: michael@0: nsresult GetInfoForIID(const nsIID * aIID, nsIInterfaceInfo** info); michael@0: nsresult GetInfoForName(const char * name, nsIInterfaceInfo** info); michael@0: michael@0: virtual nsIPrincipal* GetPrincipal(JSObject* obj, michael@0: bool allowShortCircuit) const; michael@0: michael@0: void RecordTraversal(void *p, nsISupports *s); michael@0: virtual char* DebugPrintJSStack(bool showArgs, michael@0: bool showLocals, michael@0: bool showThisProps); michael@0: michael@0: michael@0: static bool ReportAllJSExceptions() michael@0: { michael@0: return gReportAllJSExceptions > 0; michael@0: } michael@0: michael@0: static void CheckForDebugMode(JSRuntime *rt); michael@0: michael@0: protected: michael@0: nsXPConnect(); michael@0: michael@0: private: michael@0: // Singleton instance michael@0: static nsXPConnect* gSelf; michael@0: static bool gOnceAliveNowDead; michael@0: michael@0: XPCJSRuntime* mRuntime; michael@0: nsRefPtr mDefaultSecurityManager; michael@0: bool mShuttingDown; michael@0: michael@0: // nsIThreadInternal doesn't remember which observers it called michael@0: // OnProcessNextEvent on when it gets around to calling AfterProcessNextEvent. michael@0: // So if XPConnect gets initialized mid-event (which can happen), we'll get michael@0: // an 'after' notification without getting an 'on' notification. If we don't michael@0: // watch out for this, we'll do an unmatched |pop| on the context stack. michael@0: uint16_t mEventDepth; michael@0: michael@0: static uint32_t gReportAllJSExceptions; michael@0: michael@0: public: michael@0: static nsIScriptSecurityManager *gScriptSecurityManager; michael@0: }; michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: class XPCRootSetElem michael@0: { michael@0: public: michael@0: XPCRootSetElem() michael@0: { michael@0: #ifdef DEBUG michael@0: mNext = nullptr; michael@0: mSelfp = nullptr; michael@0: #endif michael@0: } michael@0: michael@0: ~XPCRootSetElem() michael@0: { michael@0: MOZ_ASSERT(!mNext, "Must be unlinked"); michael@0: MOZ_ASSERT(!mSelfp, "Must be unlinked"); michael@0: } michael@0: michael@0: inline XPCRootSetElem* GetNextRoot() { return mNext; } michael@0: void AddToRootSet(XPCRootSetElem **listHead); michael@0: void RemoveFromRootSet(); michael@0: michael@0: private: michael@0: XPCRootSetElem *mNext; michael@0: XPCRootSetElem **mSelfp; michael@0: }; michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: // In the current xpconnect system there can only be one XPCJSRuntime. michael@0: // So, xpconnect can only be used on one JSRuntime within the process. michael@0: michael@0: class XPCJSContextStack; michael@0: class WatchdogManager; michael@0: michael@0: enum WatchdogTimestampCategory michael@0: { michael@0: TimestampRuntimeStateChange = 0, michael@0: TimestampWatchdogWakeup, michael@0: TimestampWatchdogHibernateStart, michael@0: TimestampWatchdogHibernateStop, michael@0: TimestampCount michael@0: }; michael@0: michael@0: class AsyncFreeSnowWhite; michael@0: michael@0: class XPCJSRuntime : public mozilla::CycleCollectedJSRuntime michael@0: { michael@0: public: michael@0: static XPCJSRuntime* newXPCJSRuntime(nsXPConnect* aXPConnect); michael@0: static XPCJSRuntime* Get() { return nsXPConnect::XPConnect()->GetRuntime(); } michael@0: michael@0: XPCJSContextStack* GetJSContextStack() {return mJSContextStack;} michael@0: void DestroyJSContextStack(); michael@0: michael@0: XPCCallContext* GetCallContext() const {return mCallContext;} michael@0: XPCCallContext* SetCallContext(XPCCallContext* ccx) michael@0: {XPCCallContext* old = mCallContext; mCallContext = ccx; return old;} michael@0: michael@0: jsid GetResolveName() const {return mResolveName;} michael@0: jsid SetResolveName(jsid name) michael@0: {jsid old = mResolveName; mResolveName = name; return old;} michael@0: michael@0: XPCWrappedNative* GetResolvingWrapper() const {return mResolvingWrapper;} michael@0: XPCWrappedNative* SetResolvingWrapper(XPCWrappedNative* w) michael@0: {XPCWrappedNative* old = mResolvingWrapper; michael@0: mResolvingWrapper = w; return old;} michael@0: michael@0: JSObject2WrappedJSMap* GetWrappedJSMap() const michael@0: {return mWrappedJSMap;} michael@0: michael@0: IID2WrappedJSClassMap* GetWrappedJSClassMap() const michael@0: {return mWrappedJSClassMap;} michael@0: michael@0: IID2NativeInterfaceMap* GetIID2NativeInterfaceMap() const michael@0: {return mIID2NativeInterfaceMap;} michael@0: michael@0: ClassInfo2NativeSetMap* GetClassInfo2NativeSetMap() const michael@0: {return mClassInfo2NativeSetMap;} michael@0: michael@0: NativeSetMap* GetNativeSetMap() const michael@0: {return mNativeSetMap;} michael@0: michael@0: IID2ThisTranslatorMap* GetThisTranslatorMap() const michael@0: {return mThisTranslatorMap;} michael@0: michael@0: XPCNativeScriptableSharedMap* GetNativeScriptableSharedMap() const michael@0: {return mNativeScriptableSharedMap;} michael@0: michael@0: XPCWrappedNativeProtoMap* GetDyingWrappedNativeProtoMap() const michael@0: {return mDyingWrappedNativeProtoMap;} michael@0: michael@0: XPCWrappedNativeProtoMap* GetDetachedWrappedNativeProtoMap() const michael@0: {return mDetachedWrappedNativeProtoMap;} michael@0: michael@0: bool OnJSContextNew(JSContext* cx); michael@0: michael@0: virtual bool michael@0: DescribeCustomObjects(JSObject* aObject, const js::Class* aClasp, michael@0: char (&aName)[72]) const MOZ_OVERRIDE; michael@0: virtual bool michael@0: NoteCustomGCThingXPCOMChildren(const js::Class* aClasp, JSObject* aObj, michael@0: nsCycleCollectionTraversalCallback& aCb) const MOZ_OVERRIDE; michael@0: michael@0: /** michael@0: * Infrastructure for classes that need to defer part of the finalization michael@0: * until after the GC has run, for example for objects that we don't want to michael@0: * destroy during the GC. michael@0: */ michael@0: michael@0: public: michael@0: bool GetDoingFinalization() const {return mDoingFinalization;} michael@0: michael@0: // Mapping of often used strings to jsid atoms that live 'forever'. michael@0: // michael@0: // To add a new string: add to this list and to XPCJSRuntime::mStrings michael@0: // at the top of xpcjsruntime.cpp michael@0: enum { michael@0: IDX_CONSTRUCTOR = 0 , michael@0: IDX_TO_STRING , michael@0: IDX_TO_SOURCE , michael@0: IDX_LAST_RESULT , michael@0: IDX_RETURN_CODE , michael@0: IDX_VALUE , michael@0: IDX_QUERY_INTERFACE , michael@0: IDX_COMPONENTS , michael@0: IDX_WRAPPED_JSOBJECT , michael@0: IDX_OBJECT , michael@0: IDX_FUNCTION , michael@0: IDX_PROTOTYPE , michael@0: IDX_CREATE_INSTANCE , michael@0: IDX_ITEM , michael@0: IDX_PROTO , michael@0: IDX_ITERATOR , michael@0: IDX_EXPOSEDPROPS , michael@0: IDX_EVAL , michael@0: IDX_CONTROLLERS , michael@0: IDX_TOTAL_COUNT // just a count of the above michael@0: }; michael@0: michael@0: JS::HandleId GetStringID(unsigned index) const michael@0: { michael@0: MOZ_ASSERT(index < IDX_TOTAL_COUNT, "index out of range"); michael@0: // fromMarkedLocation() is safe because the string is interned. michael@0: return JS::HandleId::fromMarkedLocation(&mStrIDs[index]); michael@0: } michael@0: JS::HandleValue GetStringJSVal(unsigned index) const michael@0: { michael@0: MOZ_ASSERT(index < IDX_TOTAL_COUNT, "index out of range"); michael@0: // fromMarkedLocation() is safe because the string is interned. michael@0: return JS::HandleValue::fromMarkedLocation(&mStrJSVals[index]); michael@0: } michael@0: const char* GetStringName(unsigned index) const michael@0: { michael@0: MOZ_ASSERT(index < IDX_TOTAL_COUNT, "index out of range"); michael@0: return mStrings[index]; michael@0: } michael@0: michael@0: void TraceNativeBlackRoots(JSTracer* trc) MOZ_OVERRIDE; michael@0: void TraceAdditionalNativeGrayRoots(JSTracer* aTracer) MOZ_OVERRIDE; michael@0: void TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback& cb) MOZ_OVERRIDE; michael@0: void UnmarkSkippableJSHolders(); michael@0: void PrepareForForgetSkippable() MOZ_OVERRIDE; michael@0: void BeginCycleCollectionCallback() MOZ_OVERRIDE; michael@0: void EndCycleCollectionCallback(mozilla::CycleCollectorResults &aResults) MOZ_OVERRIDE; michael@0: void DispatchDeferredDeletion(bool continuation) MOZ_OVERRIDE; michael@0: michael@0: void CustomGCCallback(JSGCStatus status) MOZ_OVERRIDE; michael@0: bool CustomContextCallback(JSContext *cx, unsigned operation) MOZ_OVERRIDE; michael@0: static void GCSliceCallback(JSRuntime *rt, michael@0: JS::GCProgress progress, michael@0: const JS::GCDescription &desc); michael@0: static void FinalizeCallback(JSFreeOp *fop, JSFinalizeStatus status, bool isCompartmentGC); michael@0: michael@0: inline void AddVariantRoot(XPCTraceableVariant* variant); michael@0: inline void AddWrappedJSRoot(nsXPCWrappedJS* wrappedJS); michael@0: inline void AddObjectHolderRoot(XPCJSObjectHolder* holder); michael@0: michael@0: static void SuspectWrappedNative(XPCWrappedNative *wrapper, michael@0: nsCycleCollectionNoteRootCallback &cb); michael@0: michael@0: void DebugDump(int16_t depth); michael@0: michael@0: void SystemIsBeingShutDown(); michael@0: michael@0: bool GCIsRunning() const {return mGCIsRunning;} michael@0: michael@0: ~XPCJSRuntime(); michael@0: michael@0: nsString* NewShortLivedString(); michael@0: void DeleteShortLivedString(nsString *string); michael@0: michael@0: void AddGCCallback(xpcGCCallback cb); michael@0: void RemoveGCCallback(xpcGCCallback cb); michael@0: void AddContextCallback(xpcContextCallback cb); michael@0: void RemoveContextCallback(xpcContextCallback cb); michael@0: michael@0: static JSContext* DefaultJSContextCallback(JSRuntime *rt); michael@0: static void ActivityCallback(void *arg, bool active); michael@0: static void CTypesActivityCallback(JSContext *cx, michael@0: js::CTypesActivityType type); michael@0: static bool InterruptCallback(JSContext *cx); michael@0: static void OutOfMemoryCallback(JSContext *cx); michael@0: michael@0: size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf); michael@0: michael@0: AutoMarkingPtr** GetAutoRootsAdr() {return &mAutoRoots;} michael@0: michael@0: JSObject* GetJunkScope(); michael@0: JSObject* GetCompilationScope(); michael@0: void DeleteSingletonScopes(); michael@0: michael@0: PRTime GetWatchdogTimestamp(WatchdogTimestampCategory aCategory); michael@0: void OnAfterProcessNextEvent() { mSlowScriptCheckpoint = mozilla::TimeStamp(); } michael@0: michael@0: private: michael@0: XPCJSRuntime(); // no implementation michael@0: XPCJSRuntime(nsXPConnect* aXPConnect); michael@0: michael@0: void ReleaseIncrementally(nsTArray &array); michael@0: michael@0: static const char* const mStrings[IDX_TOTAL_COUNT]; michael@0: jsid mStrIDs[IDX_TOTAL_COUNT]; michael@0: jsval mStrJSVals[IDX_TOTAL_COUNT]; michael@0: michael@0: XPCJSContextStack* mJSContextStack; michael@0: XPCCallContext* mCallContext; michael@0: AutoMarkingPtr* mAutoRoots; michael@0: jsid mResolveName; michael@0: XPCWrappedNative* mResolvingWrapper; michael@0: JSObject2WrappedJSMap* mWrappedJSMap; michael@0: IID2WrappedJSClassMap* mWrappedJSClassMap; michael@0: IID2NativeInterfaceMap* mIID2NativeInterfaceMap; michael@0: ClassInfo2NativeSetMap* mClassInfo2NativeSetMap; michael@0: NativeSetMap* mNativeSetMap; michael@0: IID2ThisTranslatorMap* mThisTranslatorMap; michael@0: XPCNativeScriptableSharedMap* mNativeScriptableSharedMap; michael@0: XPCWrappedNativeProtoMap* mDyingWrappedNativeProtoMap; michael@0: XPCWrappedNativeProtoMap* mDetachedWrappedNativeProtoMap; michael@0: bool mGCIsRunning; michael@0: nsTArray mWrappedJSToReleaseArray; michael@0: nsTArray mNativesToReleaseArray; michael@0: bool mDoingFinalization; michael@0: XPCRootSetElem *mVariantRoots; michael@0: XPCRootSetElem *mWrappedJSRoots; michael@0: XPCRootSetElem *mObjectHolderRoots; michael@0: nsTArray extraGCCallbacks; michael@0: nsTArray extraContextCallbacks; michael@0: nsRefPtr mWatchdogManager; michael@0: JS::GCSliceCallback mPrevGCSliceCallback; michael@0: JS::PersistentRootedObject mJunkScope; michael@0: JS::PersistentRootedObject mCompilationScope; michael@0: nsRefPtr mAsyncSnowWhiteFreer; michael@0: michael@0: mozilla::TimeStamp mSlowScriptCheckpoint; michael@0: michael@0: #define XPCCCX_STRING_CACHE_SIZE 2 michael@0: michael@0: mozilla::Maybe mScratchStrings[XPCCCX_STRING_CACHE_SIZE]; michael@0: michael@0: friend class Watchdog; michael@0: friend class AutoLockWatchdog; michael@0: friend class XPCIncrementalReleaseRunnable; michael@0: }; michael@0: michael@0: /***************************************************************************/ michael@0: /***************************************************************************/ michael@0: // XPCContext is mostly a dumb class to hold JSContext specific data and michael@0: // maps that let us find wrappers created for the given JSContext. michael@0: michael@0: // no virtuals michael@0: class XPCContext michael@0: { michael@0: friend class XPCJSRuntime; michael@0: public: michael@0: static XPCContext* GetXPCContext(JSContext* aJSContext) michael@0: { michael@0: MOZ_ASSERT(JS_GetSecondContextPrivate(aJSContext), "should already have XPCContext"); michael@0: return static_cast(JS_GetSecondContextPrivate(aJSContext)); michael@0: } michael@0: michael@0: XPCJSRuntime* GetRuntime() const {return mRuntime;} michael@0: JSContext* GetJSContext() const {return mJSContext;} michael@0: michael@0: enum LangType {LANG_UNKNOWN, LANG_JS, LANG_NATIVE}; michael@0: michael@0: LangType GetCallingLangType() const michael@0: { michael@0: return mCallingLangType; michael@0: } michael@0: LangType SetCallingLangType(LangType lt) michael@0: { michael@0: LangType tmp = mCallingLangType; michael@0: mCallingLangType = lt; michael@0: return tmp; michael@0: } michael@0: bool CallerTypeIsJavaScript() const michael@0: { michael@0: return LANG_JS == mCallingLangType; michael@0: } michael@0: bool CallerTypeIsNative() const michael@0: { michael@0: return LANG_NATIVE == mCallingLangType; michael@0: } michael@0: bool CallerTypeIsKnown() const michael@0: { michael@0: return LANG_UNKNOWN != mCallingLangType; michael@0: } michael@0: michael@0: nsresult GetException(nsIException** e) michael@0: { michael@0: nsCOMPtr rval = mException; michael@0: rval.forget(e); michael@0: return NS_OK; michael@0: } michael@0: void SetException(nsIException* e) michael@0: { michael@0: mException = e; michael@0: } michael@0: michael@0: nsresult GetLastResult() {return mLastResult;} michael@0: void SetLastResult(nsresult rc) {mLastResult = rc;} michael@0: michael@0: nsresult GetPendingResult() {return mPendingResult;} michael@0: void SetPendingResult(nsresult rc) {mPendingResult = rc;} michael@0: michael@0: void DebugDump(int16_t depth); michael@0: void AddScope(PRCList *scope) { PR_INSERT_AFTER(scope, &mScopes); } michael@0: void RemoveScope(PRCList *scope) { PR_REMOVE_LINK(scope); } michael@0: michael@0: void MarkErrorUnreported() { mErrorUnreported = true; } michael@0: void ClearUnreportedError() { mErrorUnreported = false; } michael@0: bool WasErrorReported() { return !mErrorUnreported; } michael@0: michael@0: ~XPCContext(); michael@0: michael@0: private: michael@0: XPCContext(); // no implementation michael@0: XPCContext(XPCJSRuntime* aRuntime, JSContext* aJSContext); michael@0: michael@0: static XPCContext* newXPCContext(XPCJSRuntime* aRuntime, michael@0: JSContext* aJSContext); michael@0: private: michael@0: XPCJSRuntime* mRuntime; michael@0: JSContext* mJSContext; michael@0: nsresult mLastResult; michael@0: nsresult mPendingResult; michael@0: nsCOMPtr mException; michael@0: LangType mCallingLangType; michael@0: bool mErrorUnreported; michael@0: michael@0: // A linked list of scopes to notify when we are destroyed. michael@0: PRCList mScopes; michael@0: }; michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: #define NATIVE_CALLER XPCContext::LANG_NATIVE michael@0: #define JS_CALLER XPCContext::LANG_JS michael@0: michael@0: // No virtuals michael@0: // XPCCallContext is ALWAYS declared as a local variable in some function; michael@0: // i.e. instance lifetime is always controled by some C++ function returning. michael@0: // michael@0: // These things are created frequently in many places. We *intentionally* do michael@0: // not inialialize all members in order to save on construction overhead. michael@0: // Some constructor pass more valid params than others. We init what must be michael@0: // init'd and leave other members undefined. In debug builds the accessors michael@0: // use a CHECK_STATE macro to track whether or not the object is in a valid michael@0: // state to answer the question a caller might be asking. As long as this michael@0: // class is maintained correctly it can do its job without a bunch of added michael@0: // overhead from useless initializations and non-DEBUG error checking. michael@0: // michael@0: // Note that most accessors are inlined. michael@0: michael@0: class MOZ_STACK_CLASS XPCCallContext : public nsAXPCNativeCallContext michael@0: { michael@0: public: michael@0: NS_IMETHOD GetCallee(nsISupports **aResult); michael@0: NS_IMETHOD GetCalleeMethodIndex(uint16_t *aResult); michael@0: NS_IMETHOD GetCalleeWrapper(nsIXPConnectWrappedNative **aResult); michael@0: NS_IMETHOD GetJSContext(JSContext **aResult); michael@0: NS_IMETHOD GetArgc(uint32_t *aResult); michael@0: NS_IMETHOD GetArgvPtr(jsval **aResult); michael@0: NS_IMETHOD GetCalleeInterface(nsIInterfaceInfo **aResult); michael@0: NS_IMETHOD GetCalleeClassInfo(nsIClassInfo **aResult); michael@0: NS_IMETHOD GetPreviousCallContext(nsAXPCNativeCallContext **aResult); michael@0: NS_IMETHOD GetLanguage(uint16_t *aResult); michael@0: michael@0: enum {NO_ARGS = (unsigned) -1}; michael@0: michael@0: static JSContext* GetDefaultJSContext(); michael@0: michael@0: XPCCallContext(XPCContext::LangType callerLanguage, michael@0: JSContext* cx, michael@0: JS::HandleObject obj = JS::NullPtr(), michael@0: JS::HandleObject funobj = JS::NullPtr(), michael@0: JS::HandleId id = JSID_VOIDHANDLE, michael@0: unsigned argc = NO_ARGS, michael@0: jsval *argv = nullptr, michael@0: jsval *rval = nullptr); michael@0: michael@0: virtual ~XPCCallContext(); michael@0: michael@0: inline bool IsValid() const ; michael@0: michael@0: inline XPCJSRuntime* GetRuntime() const ; michael@0: inline XPCContext* GetXPCContext() const ; michael@0: inline JSContext* GetJSContext() const ; michael@0: inline bool GetContextPopRequired() const ; michael@0: inline XPCContext::LangType GetCallerLanguage() const ; michael@0: inline XPCContext::LangType GetPrevCallerLanguage() const ; michael@0: inline XPCCallContext* GetPrevCallContext() const ; michael@0: michael@0: inline JSObject* GetFlattenedJSObject() const ; michael@0: inline nsISupports* GetIdentityObject() const ; michael@0: inline XPCWrappedNative* GetWrapper() const ; michael@0: inline XPCWrappedNativeProto* GetProto() const ; michael@0: michael@0: inline bool CanGetTearOff() const ; michael@0: inline XPCWrappedNativeTearOff* GetTearOff() const ; michael@0: michael@0: inline XPCNativeScriptableInfo* GetScriptableInfo() const ; michael@0: inline bool CanGetSet() const ; michael@0: inline XPCNativeSet* GetSet() const ; michael@0: inline bool CanGetInterface() const ; michael@0: inline XPCNativeInterface* GetInterface() const ; michael@0: inline XPCNativeMember* GetMember() const ; michael@0: inline bool HasInterfaceAndMember() const ; michael@0: inline jsid GetName() const ; michael@0: inline bool GetStaticMemberIsLocal() const ; michael@0: inline unsigned GetArgc() const ; michael@0: inline jsval* GetArgv() const ; michael@0: inline jsval* GetRetVal() const ; michael@0: michael@0: inline uint16_t GetMethodIndex() const ; michael@0: inline void SetMethodIndex(uint16_t index) ; michael@0: michael@0: inline jsid GetResolveName() const; michael@0: inline jsid SetResolveName(JS::HandleId name); michael@0: michael@0: inline XPCWrappedNative* GetResolvingWrapper() const; michael@0: inline XPCWrappedNative* SetResolvingWrapper(XPCWrappedNative* w); michael@0: michael@0: inline void SetRetVal(jsval val); michael@0: michael@0: void SetName(jsid name); michael@0: void SetArgsAndResultPtr(unsigned argc, jsval *argv, jsval *rval); michael@0: void SetCallInfo(XPCNativeInterface* iface, XPCNativeMember* member, michael@0: bool isSetter); michael@0: michael@0: nsresult CanCallNow(); michael@0: michael@0: void SystemIsBeingShutDown(); michael@0: michael@0: operator JSContext*() const {return GetJSContext();} michael@0: michael@0: private: michael@0: michael@0: // no copy ctor or assignment allowed michael@0: XPCCallContext(const XPCCallContext& r); // not implemented michael@0: XPCCallContext& operator= (const XPCCallContext& r); // not implemented michael@0: michael@0: XPCWrappedNative* UnwrapThisIfAllowed(JS::HandleObject obj, JS::HandleObject fun, michael@0: unsigned argc); michael@0: michael@0: private: michael@0: // posible values for mState michael@0: enum State { michael@0: INIT_FAILED, michael@0: SYSTEM_SHUTDOWN, michael@0: HAVE_CONTEXT, michael@0: HAVE_OBJECT, michael@0: HAVE_NAME, michael@0: HAVE_ARGS, michael@0: READY_TO_CALL, michael@0: CALL_DONE michael@0: }; michael@0: michael@0: #ifdef DEBUG michael@0: inline void CHECK_STATE(int s) const {MOZ_ASSERT(mState >= s, "bad state");} michael@0: #else michael@0: #define CHECK_STATE(s) ((void)0) michael@0: #endif michael@0: michael@0: private: michael@0: JSAutoRequest mAr; michael@0: State mState; michael@0: michael@0: nsRefPtr mXPC; michael@0: michael@0: XPCContext* mXPCContext; michael@0: JSContext* mJSContext; michael@0: michael@0: XPCContext::LangType mCallerLanguage; michael@0: michael@0: // ctor does not necessarily init the following. BEWARE! michael@0: michael@0: XPCContext::LangType mPrevCallerLanguage; michael@0: michael@0: XPCCallContext* mPrevCallContext; michael@0: michael@0: JS::RootedObject mFlattenedJSObject; michael@0: XPCWrappedNative* mWrapper; michael@0: XPCWrappedNativeTearOff* mTearOff; michael@0: michael@0: XPCNativeScriptableInfo* mScriptableInfo; michael@0: michael@0: XPCNativeSet* mSet; michael@0: XPCNativeInterface* mInterface; michael@0: XPCNativeMember* mMember; michael@0: michael@0: JS::RootedId mName; michael@0: bool mStaticMemberIsLocal; michael@0: michael@0: unsigned mArgc; michael@0: jsval* mArgv; michael@0: jsval* mRetVal; michael@0: michael@0: uint16_t mMethodIndex; michael@0: }; michael@0: michael@0: /*************************************************************************** michael@0: **************************************************************************** michael@0: * michael@0: * Core classes for wrapped native objects for use from JavaScript... michael@0: * michael@0: **************************************************************************** michael@0: ***************************************************************************/ michael@0: michael@0: // These are the various JSClasses and callbacks whose use that required michael@0: // visibility from more than one .cpp file. michael@0: michael@0: struct XPCWrappedNativeJSClass; michael@0: extern const XPCWrappedNativeJSClass XPC_WN_NoHelper_JSClass; michael@0: extern const js::Class XPC_WN_NoMods_WithCall_Proto_JSClass; michael@0: extern const js::Class XPC_WN_NoMods_NoCall_Proto_JSClass; michael@0: extern const js::Class XPC_WN_ModsAllowed_WithCall_Proto_JSClass; michael@0: extern const js::Class XPC_WN_ModsAllowed_NoCall_Proto_JSClass; michael@0: extern const js::Class XPC_WN_Tearoff_JSClass; michael@0: extern const js::Class XPC_WN_NoHelper_Proto_JSClass; michael@0: michael@0: extern bool michael@0: XPC_WN_CallMethod(JSContext *cx, unsigned argc, jsval *vp); michael@0: michael@0: extern bool michael@0: XPC_WN_GetterSetter(JSContext *cx, unsigned argc, jsval *vp); michael@0: michael@0: extern bool michael@0: XPC_WN_JSOp_Enumerate(JSContext *cx, JS::HandleObject obj, JSIterateOp enum_op, michael@0: JS::MutableHandleValue statep, JS::MutableHandleId idp); michael@0: michael@0: extern JSObject* michael@0: XPC_WN_JSOp_ThisObject(JSContext *cx, JS::HandleObject obj); michael@0: michael@0: // Macros to initialize Object or Function like XPC_WN classes michael@0: #define XPC_WN_WithCall_ObjectOps \ michael@0: { \ michael@0: nullptr, /* lookupGeneric */ \ michael@0: nullptr, /* lookupProperty */ \ michael@0: nullptr, /* lookupElement */ \ michael@0: nullptr, /* defineGeneric */ \ michael@0: nullptr, /* defineProperty */ \ michael@0: nullptr, /* defineElement */ \ michael@0: nullptr, /* getGeneric */ \ michael@0: nullptr, /* getProperty */ \ michael@0: nullptr, /* getElement */ \ michael@0: nullptr, /* setGeneric */ \ michael@0: nullptr, /* setProperty */ \ michael@0: nullptr, /* setElement */ \ michael@0: nullptr, /* getGenericAttributes */ \ michael@0: nullptr, /* setGenericAttributes */ \ michael@0: nullptr, /* deleteProperty */ \ michael@0: nullptr, /* deleteElement */ \ michael@0: nullptr, nullptr, /* watch/unwatch */ \ michael@0: nullptr, /* slice */ \ michael@0: XPC_WN_JSOp_Enumerate, \ michael@0: XPC_WN_JSOp_ThisObject, \ michael@0: } michael@0: michael@0: #define XPC_WN_NoCall_ObjectOps \ michael@0: { \ michael@0: nullptr, /* lookupGeneric */ \ michael@0: nullptr, /* lookupProperty */ \ michael@0: nullptr, /* lookupElement */ \ michael@0: nullptr, /* defineGeneric */ \ michael@0: nullptr, /* defineProperty */ \ michael@0: nullptr, /* defineElement */ \ michael@0: nullptr, /* getGeneric */ \ michael@0: nullptr, /* getProperty */ \ michael@0: nullptr, /* getElement */ \ michael@0: nullptr, /* setGeneric */ \ michael@0: nullptr, /* setProperty */ \ michael@0: nullptr, /* setElement */ \ michael@0: nullptr, /* getGenericAttributes */ \ michael@0: nullptr, /* setGenericAttributes */ \ michael@0: nullptr, /* deleteProperty */ \ michael@0: nullptr, /* deleteElement */ \ michael@0: nullptr, nullptr, /* watch/unwatch */ \ michael@0: nullptr, /* slice */ \ michael@0: XPC_WN_JSOp_Enumerate, \ michael@0: XPC_WN_JSOp_ThisObject, \ michael@0: } michael@0: michael@0: // Maybe this macro should check for class->enumerate == michael@0: // XPC_WN_Shared_Proto_Enumerate or something rather than checking for michael@0: // 4 classes? michael@0: static inline bool IS_PROTO_CLASS(const js::Class *clazz) michael@0: { michael@0: return clazz == &XPC_WN_NoMods_WithCall_Proto_JSClass || michael@0: clazz == &XPC_WN_NoMods_NoCall_Proto_JSClass || michael@0: clazz == &XPC_WN_ModsAllowed_WithCall_Proto_JSClass || michael@0: clazz == &XPC_WN_ModsAllowed_NoCall_Proto_JSClass; michael@0: } michael@0: michael@0: /***************************************************************************/ michael@0: // XPCWrappedNativeScope is one-to-one with a JS global object. michael@0: michael@0: class nsXPCComponentsBase; michael@0: class XPCWrappedNativeScope : public PRCList michael@0: { michael@0: public: michael@0: michael@0: static XPCWrappedNativeScope* michael@0: GetNewOrUsed(JSContext *cx, JS::HandleObject aGlobal); michael@0: michael@0: XPCJSRuntime* michael@0: GetRuntime() const {return XPCJSRuntime::Get();} michael@0: michael@0: Native2WrappedNativeMap* michael@0: GetWrappedNativeMap() const {return mWrappedNativeMap;} michael@0: michael@0: ClassInfo2WrappedNativeProtoMap* michael@0: GetWrappedNativeProtoMap() const {return mWrappedNativeProtoMap;} michael@0: michael@0: nsXPCComponentsBase* michael@0: GetComponents() const {return mComponents;} michael@0: michael@0: // Forces the creation of a privileged |Components| object, even in michael@0: // content scopes. This will crash if used outside of automation. michael@0: void michael@0: ForcePrivilegedComponents(); michael@0: michael@0: bool AttachComponentsObject(JSContext *aCx); michael@0: michael@0: // Returns the JS object reflection of the Components object. michael@0: bool michael@0: GetComponentsJSObject(JS::MutableHandleObject obj); michael@0: michael@0: JSObject* michael@0: GetGlobalJSObject() const { michael@0: JS::ExposeObjectToActiveJS(mGlobalJSObject); michael@0: return mGlobalJSObject; michael@0: } michael@0: michael@0: JSObject* michael@0: GetGlobalJSObjectPreserveColor() const {return mGlobalJSObject;} michael@0: michael@0: nsIPrincipal* michael@0: GetPrincipal() const { michael@0: JSCompartment *c = js::GetObjectCompartment(mGlobalJSObject); michael@0: return nsJSPrincipals::get(JS_GetCompartmentPrincipals(c)); michael@0: } michael@0: michael@0: JSObject* michael@0: GetExpandoChain(JS::HandleObject target); michael@0: michael@0: bool michael@0: SetExpandoChain(JSContext *cx, JS::HandleObject target, JS::HandleObject chain); michael@0: michael@0: void RemoveWrappedNativeProtos(); michael@0: michael@0: static void michael@0: SystemIsBeingShutDown(); michael@0: michael@0: static void michael@0: TraceWrappedNativesInAllScopes(JSTracer* trc, XPCJSRuntime* rt); michael@0: michael@0: void TraceInside(JSTracer *trc) { michael@0: MOZ_ASSERT(mGlobalJSObject); michael@0: mGlobalJSObject.trace(trc, "XPCWrappedNativeScope::mGlobalJSObject"); michael@0: if (mXBLScope) michael@0: mXBLScope.trace(trc, "XPCWrappedNativeScope::mXBLScope"); michael@0: if (mXrayExpandos.initialized()) michael@0: mXrayExpandos.trace(trc); michael@0: } michael@0: michael@0: static void michael@0: SuspectAllWrappers(XPCJSRuntime* rt, nsCycleCollectionNoteRootCallback &cb); michael@0: michael@0: static void michael@0: StartFinalizationPhaseOfGC(JSFreeOp *fop, XPCJSRuntime* rt); michael@0: michael@0: static void michael@0: FinishedFinalizationPhaseOfGC(); michael@0: michael@0: static void michael@0: MarkAllWrappedNativesAndProtos(); michael@0: michael@0: #ifdef DEBUG michael@0: static void michael@0: ASSERT_NoInterfaceSetsAreMarked(); michael@0: #endif michael@0: michael@0: static void michael@0: SweepAllWrappedNativeTearOffs(); michael@0: michael@0: static void michael@0: DebugDumpAllScopes(int16_t depth); michael@0: michael@0: void michael@0: DebugDump(int16_t depth); michael@0: michael@0: struct ScopeSizeInfo { michael@0: ScopeSizeInfo(mozilla::MallocSizeOf mallocSizeOf) michael@0: : mMallocSizeOf(mallocSizeOf), michael@0: mScopeAndMapSize(0), michael@0: mProtoAndIfaceCacheSize(0) michael@0: {} michael@0: michael@0: mozilla::MallocSizeOf mMallocSizeOf; michael@0: size_t mScopeAndMapSize; michael@0: size_t mProtoAndIfaceCacheSize; michael@0: }; michael@0: michael@0: static void michael@0: AddSizeOfAllScopesIncludingThis(ScopeSizeInfo* scopeSizeInfo); michael@0: michael@0: void michael@0: AddSizeOfIncludingThis(ScopeSizeInfo* scopeSizeInfo); michael@0: michael@0: bool michael@0: IsValid() const {return mRuntime != nullptr;} michael@0: michael@0: static bool michael@0: IsDyingScope(XPCWrappedNativeScope *scope); michael@0: michael@0: static void InitStatics() { gScopes = nullptr; gDyingScopes = nullptr; } michael@0: michael@0: XPCContext *GetContext() { return mContext; } michael@0: void ClearContext() { mContext = nullptr; } michael@0: michael@0: typedef js::HashSet, michael@0: js::SystemAllocPolicy> DOMExpandoSet; michael@0: michael@0: bool RegisterDOMExpandoObject(JSObject *expando) { michael@0: // Expandos are proxy objects, and proxies are always tenured. michael@0: JS::AssertGCThingMustBeTenured(expando); michael@0: if (!mDOMExpandoSet) { michael@0: mDOMExpandoSet = new DOMExpandoSet(); michael@0: mDOMExpandoSet->init(8); michael@0: } michael@0: return mDOMExpandoSet->put(expando); michael@0: } michael@0: void RemoveDOMExpandoObject(JSObject *expando) { michael@0: if (mDOMExpandoSet) michael@0: mDOMExpandoSet->remove(expando); michael@0: } michael@0: michael@0: // Gets the appropriate scope object for XBL in this scope. The context michael@0: // must be same-compartment with the global upon entering, and the scope michael@0: // object is wrapped into the compartment of the global. michael@0: JSObject *EnsureXBLScope(JSContext *cx); michael@0: michael@0: XPCWrappedNativeScope(JSContext *cx, JS::HandleObject aGlobal); michael@0: michael@0: nsAutoPtr mWaiverWrapperMap; michael@0: michael@0: bool IsXBLScope() { return mIsXBLScope; } michael@0: bool AllowXBLScope(); michael@0: bool UseXBLScope() { return mUseXBLScope; } michael@0: michael@0: protected: michael@0: virtual ~XPCWrappedNativeScope(); michael@0: michael@0: static void KillDyingScopes(); michael@0: michael@0: XPCWrappedNativeScope(); // not implemented michael@0: michael@0: private: michael@0: static XPCWrappedNativeScope* gScopes; michael@0: static XPCWrappedNativeScope* gDyingScopes; michael@0: michael@0: XPCJSRuntime* mRuntime; michael@0: Native2WrappedNativeMap* mWrappedNativeMap; michael@0: ClassInfo2WrappedNativeProtoMap* mWrappedNativeProtoMap; michael@0: nsRefPtr mComponents; michael@0: XPCWrappedNativeScope* mNext; michael@0: // The JS global object for this scope. If non-null, this will be the michael@0: // default parent for the XPCWrappedNatives that have us as the scope, michael@0: // unless a PreCreate hook overrides it. Note that this _may_ be null (see michael@0: // constructor). michael@0: JS::ObjectPtr mGlobalJSObject; michael@0: michael@0: // XBL Scope. This is is a lazily-created sandbox for non-system scopes. michael@0: // EnsureXBLScope() decides whether it needs to be created or not. michael@0: // This reference is wrapped into the compartment of mGlobalJSObject. michael@0: JS::ObjectPtr mXBLScope; michael@0: michael@0: XPCContext* mContext; michael@0: michael@0: nsAutoPtr mDOMExpandoSet; michael@0: michael@0: JS::WeakMapPtr mXrayExpandos; michael@0: michael@0: bool mIsXBLScope; michael@0: michael@0: // For remote XUL domains, we run all XBL in the content scope for compat michael@0: // reasons (though we sometimes pref this off for automation). We separately michael@0: // track the result of this decision (mAllowXBLScope), from the decision michael@0: // of whether to actually _use_ an XBL scope (mUseXBLScope), which depends michael@0: // on the type of global and whether the compartment is system principal michael@0: // or not. michael@0: // michael@0: // This distinction is useful primarily because, if true, we know that we michael@0: // have no way of distinguishing XBL script from content script for the michael@0: // given scope. In these (unsupported) situations, we just always claim to michael@0: // be XBL. michael@0: bool mAllowXBLScope; michael@0: bool mUseXBLScope; michael@0: }; michael@0: michael@0: /***************************************************************************/ michael@0: // XPCNativeMember represents a single idl declared method, attribute or michael@0: // constant. michael@0: michael@0: // Tight. No virtual methods. Can be bitwise copied (until any resolution done). michael@0: michael@0: class XPCNativeMember michael@0: { michael@0: public: michael@0: static bool GetCallInfo(JSObject* funobj, michael@0: XPCNativeInterface** pInterface, michael@0: XPCNativeMember** pMember); michael@0: michael@0: jsid GetName() const {return mName;} michael@0: michael@0: uint16_t GetIndex() const {return mIndex;} michael@0: michael@0: bool GetConstantValue(XPCCallContext& ccx, XPCNativeInterface* iface, michael@0: jsval* pval) michael@0: {MOZ_ASSERT(IsConstant(), michael@0: "Only call this if you're sure this is a constant!"); michael@0: return Resolve(ccx, iface, JS::NullPtr(), pval);} michael@0: michael@0: bool NewFunctionObject(XPCCallContext& ccx, XPCNativeInterface* iface, michael@0: JS::HandleObject parent, jsval* pval); michael@0: michael@0: bool IsMethod() const michael@0: {return 0 != (mFlags & METHOD);} michael@0: michael@0: bool IsConstant() const michael@0: {return 0 != (mFlags & CONSTANT);} michael@0: michael@0: bool IsAttribute() const michael@0: {return 0 != (mFlags & GETTER);} michael@0: michael@0: bool IsWritableAttribute() const michael@0: {return 0 != (mFlags & SETTER_TOO);} michael@0: michael@0: bool IsReadOnlyAttribute() const michael@0: {return IsAttribute() && !IsWritableAttribute();} michael@0: michael@0: michael@0: void SetName(jsid a) {mName = a;} michael@0: michael@0: void SetMethod(uint16_t index) michael@0: {mFlags = METHOD; mIndex = index;} michael@0: michael@0: void SetConstant(uint16_t index) michael@0: {mFlags = CONSTANT; mIndex = index;} michael@0: michael@0: void SetReadOnlyAttribute(uint16_t index) michael@0: {mFlags = GETTER; mIndex = index;} michael@0: michael@0: void SetWritableAttribute() michael@0: {MOZ_ASSERT(mFlags == GETTER,"bad"); mFlags = GETTER | SETTER_TOO;} michael@0: michael@0: /* default ctor - leave random contents */ michael@0: XPCNativeMember() {MOZ_COUNT_CTOR(XPCNativeMember);} michael@0: ~XPCNativeMember() {MOZ_COUNT_DTOR(XPCNativeMember);} michael@0: michael@0: private: michael@0: bool Resolve(XPCCallContext& ccx, XPCNativeInterface* iface, michael@0: JS::HandleObject parent, jsval *vp); michael@0: michael@0: enum { michael@0: METHOD = 0x01, michael@0: CONSTANT = 0x02, michael@0: GETTER = 0x04, michael@0: SETTER_TOO = 0x08 michael@0: }; michael@0: michael@0: private: michael@0: // our only data... michael@0: jsid mName; michael@0: uint16_t mIndex; michael@0: uint16_t mFlags; michael@0: }; michael@0: michael@0: /***************************************************************************/ michael@0: // XPCNativeInterface represents a single idl declared interface. This is michael@0: // primarily the set of XPCNativeMembers. michael@0: michael@0: // Tight. No virtual methods. michael@0: michael@0: class XPCNativeInterface michael@0: { michael@0: public: michael@0: static XPCNativeInterface* GetNewOrUsed(const nsIID* iid); michael@0: static XPCNativeInterface* GetNewOrUsed(nsIInterfaceInfo* info); michael@0: static XPCNativeInterface* GetNewOrUsed(const char* name); michael@0: static XPCNativeInterface* GetISupports(); michael@0: michael@0: inline nsIInterfaceInfo* GetInterfaceInfo() const {return mInfo.get();} michael@0: inline jsid GetName() const {return mName;} michael@0: michael@0: inline const nsIID* GetIID() const; michael@0: inline const char* GetNameString() const; michael@0: inline XPCNativeMember* FindMember(jsid name) const; michael@0: michael@0: inline bool HasAncestor(const nsIID* iid) const; michael@0: michael@0: uint16_t GetMemberCount() const { michael@0: return mMemberCount; michael@0: } michael@0: XPCNativeMember* GetMemberAt(uint16_t i) { michael@0: MOZ_ASSERT(i < mMemberCount, "bad index"); michael@0: return &mMembers[i]; michael@0: } michael@0: michael@0: void DebugDump(int16_t depth); michael@0: michael@0: #define XPC_NATIVE_IFACE_MARK_FLAG ((uint16_t)JS_BIT(15)) // only high bit of 16 is set michael@0: michael@0: void Mark() { michael@0: mMarked = 1; michael@0: } michael@0: michael@0: void Unmark() { michael@0: mMarked = 0; michael@0: } michael@0: michael@0: bool IsMarked() const { michael@0: return mMarked != 0; michael@0: } michael@0: michael@0: // NOP. This is just here to make the AutoMarkingPtr code compile. michael@0: inline void TraceJS(JSTracer* trc) {} michael@0: inline void AutoTrace(JSTracer* trc) {} michael@0: michael@0: static void DestroyInstance(XPCNativeInterface* inst); michael@0: michael@0: size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf); michael@0: michael@0: protected: michael@0: static XPCNativeInterface* NewInstance(nsIInterfaceInfo* aInfo); michael@0: michael@0: XPCNativeInterface(); // not implemented michael@0: XPCNativeInterface(nsIInterfaceInfo* aInfo, jsid aName) michael@0: : mInfo(aInfo), mName(aName), mMemberCount(0), mMarked(0) michael@0: { michael@0: MOZ_COUNT_CTOR(XPCNativeInterface); michael@0: } michael@0: ~XPCNativeInterface() { michael@0: MOZ_COUNT_DTOR(XPCNativeInterface); michael@0: } michael@0: michael@0: void* operator new(size_t, void* p) CPP_THROW_NEW {return p;} michael@0: michael@0: XPCNativeInterface(const XPCNativeInterface& r); // not implemented michael@0: XPCNativeInterface& operator= (const XPCNativeInterface& r); // not implemented michael@0: michael@0: private: michael@0: nsCOMPtr mInfo; michael@0: jsid mName; michael@0: uint16_t mMemberCount : 15; michael@0: uint16_t mMarked : 1; michael@0: XPCNativeMember mMembers[1]; // always last - object sized for array michael@0: }; michael@0: michael@0: /***************************************************************************/ michael@0: // XPCNativeSetKey is used to key a XPCNativeSet in a NativeSetMap. michael@0: michael@0: class XPCNativeSetKey michael@0: { michael@0: public: michael@0: XPCNativeSetKey(XPCNativeSet* BaseSet = nullptr, michael@0: XPCNativeInterface* Addition = nullptr, michael@0: uint16_t Position = 0) michael@0: : mIsAKey(IS_A_KEY), mPosition(Position), mBaseSet(BaseSet), michael@0: mAddition(Addition) {} michael@0: ~XPCNativeSetKey() {} michael@0: michael@0: XPCNativeSet* GetBaseSet() const {return mBaseSet;} michael@0: XPCNativeInterface* GetAddition() const {return mAddition;} michael@0: uint16_t GetPosition() const {return mPosition;} michael@0: michael@0: // This is a fun little hack... michael@0: // We build these keys only on the stack. We use them for lookup in michael@0: // NativeSetMap. Becasue we don't want to pay the cost of cloning a key and michael@0: // sticking it into the hashtable, when the XPCNativeSet actually michael@0: // gets added to the table the 'key' in the table is a pointer to the michael@0: // set itself and not this key. Our key compare function expects to get michael@0: // a key and a set. When we do external lookups in the map we pass in one michael@0: // of these keys and our compare function gets passed a key and a set. michael@0: // (see compare_NativeKeyToSet in xpcmaps.cpp). This is all well and good. michael@0: // Except, when the table decides to resize itself. Then it tries to use michael@0: // our compare function with the 'keys' that are in the hashtable (which are michael@0: // really XPCNativeSet objects and not XPCNativeSetKey objects! michael@0: // michael@0: // So, the hack is to have the compare function assume it is getting a michael@0: // XPCNativeSetKey pointer and call this IsAKey method. If that fails then michael@0: // it realises that it really has a XPCNativeSet pointer and deals with that michael@0: // fact. This is safe because we know that both of these classes have no michael@0: // virtual methods and their first data member is a uint16_t. We are michael@0: // confident that XPCNativeSet->mMemberCount will never be 0xffff. michael@0: michael@0: bool IsAKey() const {return mIsAKey == IS_A_KEY;} michael@0: michael@0: enum {IS_A_KEY = 0xffff}; michael@0: michael@0: // Allow shallow copy michael@0: michael@0: private: michael@0: uint16_t mIsAKey; // must be first data member michael@0: uint16_t mPosition; michael@0: XPCNativeSet* mBaseSet; michael@0: XPCNativeInterface* mAddition; michael@0: }; michael@0: michael@0: /***************************************************************************/ michael@0: // XPCNativeSet represents an ordered collection of XPCNativeInterface pointers. michael@0: michael@0: class XPCNativeSet michael@0: { michael@0: public: michael@0: static XPCNativeSet* GetNewOrUsed(const nsIID* iid); michael@0: static XPCNativeSet* GetNewOrUsed(nsIClassInfo* classInfo); michael@0: static XPCNativeSet* GetNewOrUsed(XPCNativeSet* otherSet, michael@0: XPCNativeInterface* newInterface, michael@0: uint16_t position); michael@0: michael@0: // This generates a union set. michael@0: // michael@0: // If preserveFirstSetOrder is true, the elements from |firstSet| come first, michael@0: // followed by any non-duplicate items from |secondSet|. If false, the same michael@0: // algorithm is applied; but if we detect that |secondSet| is a superset of michael@0: // |firstSet|, we return |secondSet| without worrying about whether the michael@0: // ordering might differ from |firstSet|. michael@0: static XPCNativeSet* GetNewOrUsed(XPCNativeSet* firstSet, michael@0: XPCNativeSet* secondSet, michael@0: bool preserveFirstSetOrder); michael@0: michael@0: static void ClearCacheEntryForClassInfo(nsIClassInfo* classInfo); michael@0: michael@0: inline bool FindMember(jsid name, XPCNativeMember** pMember, michael@0: uint16_t* pInterfaceIndex) const; michael@0: michael@0: inline bool FindMember(jsid name, XPCNativeMember** pMember, michael@0: XPCNativeInterface** pInterface) const; michael@0: michael@0: inline bool FindMember(jsid name, michael@0: XPCNativeMember** pMember, michael@0: XPCNativeInterface** pInterface, michael@0: XPCNativeSet* protoSet, michael@0: bool* pIsLocal) const; michael@0: michael@0: inline bool HasInterface(XPCNativeInterface* aInterface) const; michael@0: inline bool HasInterfaceWithAncestor(XPCNativeInterface* aInterface) const; michael@0: inline bool HasInterfaceWithAncestor(const nsIID* iid) const; michael@0: michael@0: inline XPCNativeInterface* FindInterfaceWithIID(const nsIID& iid) const; michael@0: michael@0: inline XPCNativeInterface* FindNamedInterface(jsid name) const; michael@0: michael@0: uint16_t GetMemberCount() const { michael@0: return mMemberCount; michael@0: } michael@0: uint16_t GetInterfaceCount() const { michael@0: return mInterfaceCount; michael@0: } michael@0: XPCNativeInterface **GetInterfaceArray() { michael@0: return mInterfaces; michael@0: } michael@0: michael@0: XPCNativeInterface* GetInterfaceAt(uint16_t i) michael@0: {MOZ_ASSERT(i < mInterfaceCount, "bad index"); return mInterfaces[i];} michael@0: michael@0: inline bool MatchesSetUpToInterface(const XPCNativeSet* other, michael@0: XPCNativeInterface* iface) const; michael@0: michael@0: #define XPC_NATIVE_SET_MARK_FLAG ((uint16_t)JS_BIT(15)) // only high bit of 16 is set michael@0: michael@0: inline void Mark(); michael@0: michael@0: // NOP. This is just here to make the AutoMarkingPtr code compile. michael@0: inline void TraceJS(JSTracer* trc) {} michael@0: inline void AutoTrace(JSTracer* trc) {} michael@0: michael@0: private: michael@0: void MarkSelfOnly() { michael@0: mMarked = 1; michael@0: } michael@0: michael@0: public: michael@0: void Unmark() { michael@0: mMarked = 0; michael@0: } michael@0: bool IsMarked() const { michael@0: return !!mMarked; michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: inline void ASSERT_NotMarked(); michael@0: #endif michael@0: michael@0: void DebugDump(int16_t depth); michael@0: michael@0: static void DestroyInstance(XPCNativeSet* inst); michael@0: michael@0: size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf); michael@0: michael@0: protected: michael@0: static XPCNativeSet* NewInstance(XPCNativeInterface** array, michael@0: uint16_t count); michael@0: static XPCNativeSet* NewInstanceMutate(XPCNativeSet* otherSet, michael@0: XPCNativeInterface* newInterface, michael@0: uint16_t position); michael@0: XPCNativeSet() michael@0: : mMemberCount(0), mInterfaceCount(0), mMarked(0) michael@0: { michael@0: MOZ_COUNT_CTOR(XPCNativeSet); michael@0: } michael@0: ~XPCNativeSet() { michael@0: MOZ_COUNT_DTOR(XPCNativeSet); michael@0: } michael@0: void* operator new(size_t, void* p) CPP_THROW_NEW {return p;} michael@0: michael@0: private: michael@0: uint16_t mMemberCount; michael@0: uint16_t mInterfaceCount : 15; michael@0: uint16_t mMarked : 1; michael@0: XPCNativeInterface* mInterfaces[1]; // always last - object sized for array michael@0: }; michael@0: michael@0: /***************************************************************************/ michael@0: // XPCNativeScriptableFlags is a wrapper class that holds the flags returned michael@0: // from calls to nsIXPCScriptable::GetScriptableFlags(). It has convenience michael@0: // methods to check for particular bitflags. Since we also use this class as michael@0: // a member of the gc'd class XPCNativeScriptableShared, this class holds the michael@0: // bit and exposes the inlined methods to support marking. michael@0: michael@0: #define XPC_WN_SJSFLAGS_MARK_FLAG JS_BIT(31) // only high bit of 32 is set michael@0: michael@0: class XPCNativeScriptableFlags michael@0: { michael@0: private: michael@0: uint32_t mFlags; michael@0: michael@0: public: michael@0: michael@0: XPCNativeScriptableFlags(uint32_t flags = 0) : mFlags(flags) {} michael@0: michael@0: uint32_t GetFlags() const {return mFlags & ~XPC_WN_SJSFLAGS_MARK_FLAG;} michael@0: void SetFlags(uint32_t flags) {mFlags = flags;} michael@0: michael@0: operator uint32_t() const {return GetFlags();} michael@0: michael@0: XPCNativeScriptableFlags(const XPCNativeScriptableFlags& r) michael@0: {mFlags = r.GetFlags();} michael@0: michael@0: XPCNativeScriptableFlags& operator= (const XPCNativeScriptableFlags& r) michael@0: {mFlags = r.GetFlags(); return *this;} michael@0: michael@0: void Mark() {mFlags |= XPC_WN_SJSFLAGS_MARK_FLAG;} michael@0: void Unmark() {mFlags &= ~XPC_WN_SJSFLAGS_MARK_FLAG;} michael@0: bool IsMarked() const {return 0 != (mFlags & XPC_WN_SJSFLAGS_MARK_FLAG);} michael@0: michael@0: #ifdef GET_IT michael@0: #undef GET_IT michael@0: #endif michael@0: #define GET_IT(f_) const {return 0 != (mFlags & nsIXPCScriptable:: f_ );} michael@0: michael@0: bool WantPreCreate() GET_IT(WANT_PRECREATE) michael@0: bool WantCreate() GET_IT(WANT_CREATE) michael@0: bool WantPostCreate() GET_IT(WANT_POSTCREATE) michael@0: bool WantAddProperty() GET_IT(WANT_ADDPROPERTY) michael@0: bool WantDelProperty() GET_IT(WANT_DELPROPERTY) michael@0: bool WantGetProperty() GET_IT(WANT_GETPROPERTY) michael@0: bool WantSetProperty() GET_IT(WANT_SETPROPERTY) michael@0: bool WantEnumerate() GET_IT(WANT_ENUMERATE) michael@0: bool WantNewEnumerate() GET_IT(WANT_NEWENUMERATE) michael@0: bool WantNewResolve() GET_IT(WANT_NEWRESOLVE) michael@0: bool WantConvert() GET_IT(WANT_CONVERT) michael@0: bool WantFinalize() GET_IT(WANT_FINALIZE) michael@0: bool WantCall() GET_IT(WANT_CALL) michael@0: bool WantConstruct() GET_IT(WANT_CONSTRUCT) michael@0: bool WantHasInstance() GET_IT(WANT_HASINSTANCE) michael@0: bool WantOuterObject() GET_IT(WANT_OUTER_OBJECT) michael@0: bool UseJSStubForAddProperty() GET_IT(USE_JSSTUB_FOR_ADDPROPERTY) michael@0: bool UseJSStubForDelProperty() GET_IT(USE_JSSTUB_FOR_DELPROPERTY) michael@0: bool UseJSStubForSetProperty() GET_IT(USE_JSSTUB_FOR_SETPROPERTY) michael@0: bool DontEnumStaticProps() GET_IT(DONT_ENUM_STATIC_PROPS) michael@0: bool DontEnumQueryInterface() GET_IT(DONT_ENUM_QUERY_INTERFACE) michael@0: bool DontAskInstanceForScriptable() GET_IT(DONT_ASK_INSTANCE_FOR_SCRIPTABLE) michael@0: bool ClassInfoInterfacesOnly() GET_IT(CLASSINFO_INTERFACES_ONLY) michael@0: bool AllowPropModsDuringResolve() GET_IT(ALLOW_PROP_MODS_DURING_RESOLVE) michael@0: bool AllowPropModsToPrototype() GET_IT(ALLOW_PROP_MODS_TO_PROTOTYPE) michael@0: bool IsGlobalObject() GET_IT(IS_GLOBAL_OBJECT) michael@0: bool DontReflectInterfaceNames() GET_IT(DONT_REFLECT_INTERFACE_NAMES) michael@0: michael@0: #undef GET_IT michael@0: }; michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: // XPCNativeScriptableShared is used to hold the JSClass and the michael@0: // associated scriptable flags for XPCWrappedNatives. These are shared across michael@0: // the runtime and are garbage collected by xpconnect. We *used* to just store michael@0: // this inside the XPCNativeScriptableInfo (usually owned by instances of michael@0: // XPCWrappedNativeProto. This had two problems... It was wasteful, and it michael@0: // was a big problem when wrappers are reparented to different scopes (and michael@0: // thus different protos (the DOM does this). michael@0: michael@0: // We maintain the invariant that every JSClass for which ext.isWrappedNative michael@0: // is true is a contained in an instance of this struct, and can thus be cast michael@0: // to it. michael@0: struct XPCWrappedNativeJSClass michael@0: { michael@0: js::Class base; michael@0: uint32_t interfacesBitmap; michael@0: }; michael@0: michael@0: class XPCNativeScriptableShared michael@0: { michael@0: public: michael@0: const XPCNativeScriptableFlags& GetFlags() const {return mFlags;} michael@0: uint32_t GetInterfacesBitmap() const michael@0: {return mJSClass.interfacesBitmap;} michael@0: const JSClass* GetJSClass() michael@0: {return Jsvalify(&mJSClass.base);} michael@0: michael@0: XPCNativeScriptableShared(uint32_t aFlags, char* aName, michael@0: uint32_t interfacesBitmap) michael@0: : mFlags(aFlags) michael@0: {memset(&mJSClass, 0, sizeof(mJSClass)); michael@0: mJSClass.base.name = aName; // take ownership michael@0: mJSClass.interfacesBitmap = interfacesBitmap; michael@0: MOZ_COUNT_CTOR(XPCNativeScriptableShared);} michael@0: michael@0: ~XPCNativeScriptableShared() michael@0: {if (mJSClass.base.name)nsMemory::Free((void*)mJSClass.base.name); michael@0: MOZ_COUNT_DTOR(XPCNativeScriptableShared);} michael@0: michael@0: char* TransferNameOwnership() michael@0: {char* name=(char*)mJSClass.base.name; mJSClass.base.name = nullptr; michael@0: return name;} michael@0: michael@0: void PopulateJSClass(); michael@0: michael@0: void Mark() {mFlags.Mark();} michael@0: void Unmark() {mFlags.Unmark();} michael@0: bool IsMarked() const {return mFlags.IsMarked();} michael@0: michael@0: private: michael@0: XPCNativeScriptableFlags mFlags; michael@0: XPCWrappedNativeJSClass mJSClass; michael@0: }; michael@0: michael@0: /***************************************************************************/ michael@0: // XPCNativeScriptableInfo is used to hold the nsIXPCScriptable state for a michael@0: // given class or instance. michael@0: michael@0: class XPCNativeScriptableInfo michael@0: { michael@0: public: michael@0: static XPCNativeScriptableInfo* michael@0: Construct(const XPCNativeScriptableCreateInfo* sci); michael@0: michael@0: nsIXPCScriptable* michael@0: GetCallback() const {return mCallback;} michael@0: michael@0: const XPCNativeScriptableFlags& michael@0: GetFlags() const {return mShared->GetFlags();} michael@0: michael@0: uint32_t michael@0: GetInterfacesBitmap() const {return mShared->GetInterfacesBitmap();} michael@0: michael@0: const JSClass* michael@0: GetJSClass() {return mShared->GetJSClass();} michael@0: michael@0: XPCNativeScriptableShared* michael@0: GetScriptableShared() {return mShared;} michael@0: michael@0: void michael@0: SetCallback(nsIXPCScriptable* s) {mCallback = s;} michael@0: void michael@0: SetCallback(already_AddRefed&& s) {mCallback = s;} michael@0: michael@0: void michael@0: SetScriptableShared(XPCNativeScriptableShared* shared) {mShared = shared;} michael@0: michael@0: void Mark() { michael@0: if (mShared) michael@0: mShared->Mark(); michael@0: } michael@0: michael@0: void TraceJS(JSTracer *trc) {} michael@0: void AutoTrace(JSTracer *trc) {} michael@0: michael@0: protected: michael@0: XPCNativeScriptableInfo(nsIXPCScriptable* scriptable = nullptr, michael@0: XPCNativeScriptableShared* shared = nullptr) michael@0: : mCallback(scriptable), mShared(shared) michael@0: {MOZ_COUNT_CTOR(XPCNativeScriptableInfo);} michael@0: public: michael@0: ~XPCNativeScriptableInfo() {MOZ_COUNT_DTOR(XPCNativeScriptableInfo);} michael@0: private: michael@0: michael@0: // disable copy ctor and assignment michael@0: XPCNativeScriptableInfo(const XPCNativeScriptableInfo& r); // not implemented michael@0: XPCNativeScriptableInfo& operator= (const XPCNativeScriptableInfo& r); // not implemented michael@0: michael@0: private: michael@0: nsCOMPtr mCallback; michael@0: XPCNativeScriptableShared* mShared; michael@0: }; michael@0: michael@0: /***************************************************************************/ michael@0: // XPCNativeScriptableCreateInfo is used in creating new wrapper and protos. michael@0: // it abstracts out the scriptable interface pointer and the flags. After michael@0: // creation these are factored differently using XPCNativeScriptableInfo. michael@0: michael@0: class MOZ_STACK_CLASS XPCNativeScriptableCreateInfo michael@0: { michael@0: public: michael@0: michael@0: XPCNativeScriptableCreateInfo(const XPCNativeScriptableInfo& si) michael@0: : mCallback(si.GetCallback()), mFlags(si.GetFlags()), michael@0: mInterfacesBitmap(si.GetInterfacesBitmap()) {} michael@0: michael@0: XPCNativeScriptableCreateInfo(already_AddRefed&& callback, michael@0: XPCNativeScriptableFlags flags, michael@0: uint32_t interfacesBitmap) michael@0: : mCallback(callback), mFlags(flags), michael@0: mInterfacesBitmap(interfacesBitmap) {} michael@0: michael@0: XPCNativeScriptableCreateInfo() michael@0: : mFlags(0), mInterfacesBitmap(0) {} michael@0: michael@0: michael@0: nsIXPCScriptable* michael@0: GetCallback() const {return mCallback;} michael@0: michael@0: const XPCNativeScriptableFlags& michael@0: GetFlags() const {return mFlags;} michael@0: michael@0: uint32_t michael@0: GetInterfacesBitmap() const {return mInterfacesBitmap;} michael@0: michael@0: void michael@0: SetCallback(already_AddRefed&& callback) michael@0: {mCallback = callback;} michael@0: michael@0: void michael@0: SetFlags(const XPCNativeScriptableFlags& flags) {mFlags = flags;} michael@0: michael@0: void michael@0: SetInterfacesBitmap(uint32_t interfacesBitmap) michael@0: {mInterfacesBitmap = interfacesBitmap;} michael@0: michael@0: private: michael@0: nsCOMPtr mCallback; michael@0: XPCNativeScriptableFlags mFlags; michael@0: uint32_t mInterfacesBitmap; michael@0: }; michael@0: michael@0: /***********************************************/ michael@0: // XPCWrappedNativeProto hold the additional shared wrapper data michael@0: // for XPCWrappedNative whose native objects expose nsIClassInfo. michael@0: michael@0: class XPCWrappedNativeProto michael@0: { michael@0: public: michael@0: static XPCWrappedNativeProto* michael@0: GetNewOrUsed(XPCWrappedNativeScope* scope, michael@0: nsIClassInfo* classInfo, michael@0: const XPCNativeScriptableCreateInfo* scriptableCreateInfo, michael@0: bool callPostCreatePrototype = true); michael@0: michael@0: XPCWrappedNativeScope* michael@0: GetScope() const {return mScope;} michael@0: michael@0: XPCJSRuntime* michael@0: GetRuntime() const {return mScope->GetRuntime();} michael@0: michael@0: JSObject* michael@0: GetJSProtoObject() const { michael@0: JS::ExposeObjectToActiveJS(mJSProtoObject); michael@0: return mJSProtoObject; michael@0: } michael@0: michael@0: nsIClassInfo* michael@0: GetClassInfo() const {return mClassInfo;} michael@0: michael@0: XPCNativeSet* michael@0: GetSet() const {return mSet;} michael@0: michael@0: XPCNativeScriptableInfo* michael@0: GetScriptableInfo() {return mScriptableInfo;} michael@0: michael@0: uint32_t michael@0: GetClassInfoFlags() const {return mClassInfoFlags;} michael@0: michael@0: #ifdef GET_IT michael@0: #undef GET_IT michael@0: #endif michael@0: #define GET_IT(f_) const {return !!(mClassInfoFlags & nsIClassInfo:: f_ );} michael@0: michael@0: bool ClassIsSingleton() GET_IT(SINGLETON) michael@0: bool ClassIsDOMObject() GET_IT(DOM_OBJECT) michael@0: bool ClassIsPluginObject() GET_IT(PLUGIN_OBJECT) michael@0: michael@0: #undef GET_IT michael@0: michael@0: void SetScriptableInfo(XPCNativeScriptableInfo* si) michael@0: {MOZ_ASSERT(!mScriptableInfo, "leak here!"); mScriptableInfo = si;} michael@0: michael@0: bool CallPostCreatePrototype(); michael@0: void JSProtoObjectFinalized(js::FreeOp *fop, JSObject *obj); michael@0: michael@0: void SystemIsBeingShutDown(); michael@0: michael@0: void DebugDump(int16_t depth); michael@0: michael@0: void TraceSelf(JSTracer *trc) { michael@0: if (mJSProtoObject) michael@0: mJSProtoObject.trace(trc, "XPCWrappedNativeProto::mJSProtoObject"); michael@0: } michael@0: michael@0: void TraceInside(JSTracer *trc) { michael@0: if (JS_IsGCMarkingTracer(trc)) { michael@0: mSet->Mark(); michael@0: if (mScriptableInfo) michael@0: mScriptableInfo->Mark(); michael@0: } michael@0: michael@0: GetScope()->TraceInside(trc); michael@0: } michael@0: michael@0: void TraceJS(JSTracer *trc) { michael@0: TraceSelf(trc); michael@0: TraceInside(trc); michael@0: } michael@0: michael@0: void WriteBarrierPre(JSRuntime* rt) michael@0: { michael@0: if (JS::IsIncrementalBarrierNeeded(rt) && mJSProtoObject) michael@0: mJSProtoObject.writeBarrierPre(rt); michael@0: } michael@0: michael@0: // NOP. This is just here to make the AutoMarkingPtr code compile. michael@0: inline void AutoTrace(JSTracer* trc) {} michael@0: michael@0: // Yes, we *do* need to mark the mScriptableInfo in both cases. michael@0: void Mark() const michael@0: {mSet->Mark(); michael@0: if (mScriptableInfo) mScriptableInfo->Mark();} michael@0: michael@0: #ifdef DEBUG michael@0: void ASSERT_SetNotMarked() const {mSet->ASSERT_NotMarked();} michael@0: #endif michael@0: michael@0: ~XPCWrappedNativeProto(); michael@0: michael@0: protected: michael@0: // disable copy ctor and assignment michael@0: XPCWrappedNativeProto(const XPCWrappedNativeProto& r); // not implemented michael@0: XPCWrappedNativeProto& operator= (const XPCWrappedNativeProto& r); // not implemented michael@0: michael@0: // hide ctor michael@0: XPCWrappedNativeProto(XPCWrappedNativeScope* Scope, michael@0: nsIClassInfo* ClassInfo, michael@0: uint32_t ClassInfoFlags, michael@0: XPCNativeSet* Set); michael@0: michael@0: bool Init(const XPCNativeScriptableCreateInfo* scriptableCreateInfo, michael@0: bool callPostCreatePrototype); michael@0: michael@0: private: michael@0: #ifdef DEBUG michael@0: static int32_t gDEBUG_LiveProtoCount; michael@0: #endif michael@0: michael@0: private: michael@0: XPCWrappedNativeScope* mScope; michael@0: JS::ObjectPtr mJSProtoObject; michael@0: nsCOMPtr mClassInfo; michael@0: uint32_t mClassInfoFlags; michael@0: XPCNativeSet* mSet; michael@0: XPCNativeScriptableInfo* mScriptableInfo; michael@0: }; michael@0: michael@0: /***********************************************/ michael@0: // XPCWrappedNativeTearOff represents the info needed to make calls to one michael@0: // interface on the underlying native object of a XPCWrappedNative. michael@0: michael@0: class XPCWrappedNativeTearOff michael@0: { michael@0: public: michael@0: bool IsAvailable() const {return mInterface == nullptr;} michael@0: bool IsReserved() const {return mInterface == (XPCNativeInterface*)1;} michael@0: bool IsValid() const {return !IsAvailable() && !IsReserved();} michael@0: void SetReserved() {mInterface = (XPCNativeInterface*)1;} michael@0: michael@0: XPCNativeInterface* GetInterface() const {return mInterface;} michael@0: nsISupports* GetNative() const {return mNative;} michael@0: JSObject* GetJSObject(); michael@0: JSObject* GetJSObjectPreserveColor() const; michael@0: void SetInterface(XPCNativeInterface* Interface) {mInterface = Interface;} michael@0: void SetNative(nsISupports* Native) {mNative = Native;} michael@0: void SetJSObject(JSObject* JSObj); michael@0: michael@0: void JSObjectFinalized() {SetJSObject(nullptr);} michael@0: michael@0: XPCWrappedNativeTearOff() michael@0: : mInterface(nullptr), mNative(nullptr), mJSObject(nullptr) {} michael@0: ~XPCWrappedNativeTearOff(); michael@0: michael@0: // NOP. This is just here to make the AutoMarkingPtr code compile. michael@0: inline void TraceJS(JSTracer* trc) {} michael@0: inline void AutoTrace(JSTracer* trc) {} michael@0: michael@0: void Mark() {mJSObject = (JSObject*)(intptr_t(mJSObject) | 1);} michael@0: void Unmark() {mJSObject = (JSObject*)(intptr_t(mJSObject) & ~1);} michael@0: bool IsMarked() const {return !!(intptr_t(mJSObject) & 1);} michael@0: michael@0: private: michael@0: XPCWrappedNativeTearOff(const XPCWrappedNativeTearOff& r) MOZ_DELETE; michael@0: XPCWrappedNativeTearOff& operator= (const XPCWrappedNativeTearOff& r) MOZ_DELETE; michael@0: michael@0: private: michael@0: XPCNativeInterface* mInterface; michael@0: nsISupports* mNative; michael@0: JSObject* mJSObject; michael@0: }; michael@0: michael@0: /***********************************************/ michael@0: // XPCWrappedNativeTearOffChunk is a collections of XPCWrappedNativeTearOff michael@0: // objects. It lets us allocate a set of XPCWrappedNativeTearOff objects and michael@0: // link the sets - rather than only having the option of linking single michael@0: // XPCWrappedNativeTearOff objects. michael@0: // michael@0: // The value of XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK can be tuned at buildtime michael@0: // to balance between the code of allocations of additional chunks and the waste michael@0: // of space for ununsed XPCWrappedNativeTearOff objects. michael@0: michael@0: #define XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK 1 michael@0: michael@0: class XPCWrappedNativeTearOffChunk michael@0: { michael@0: friend class XPCWrappedNative; michael@0: private: michael@0: XPCWrappedNativeTearOffChunk() : mNextChunk(nullptr) {} michael@0: ~XPCWrappedNativeTearOffChunk() {delete mNextChunk;} michael@0: michael@0: private: michael@0: XPCWrappedNativeTearOff mTearOffs[XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK]; michael@0: XPCWrappedNativeTearOffChunk* mNextChunk; michael@0: }; michael@0: michael@0: void *xpc_GetJSPrivate(JSObject *obj); michael@0: michael@0: /***************************************************************************/ michael@0: // XPCWrappedNative the wrapper around one instance of a native xpcom object michael@0: // to be used from JavaScript. michael@0: michael@0: class XPCWrappedNative : public nsIXPConnectWrappedNative michael@0: { michael@0: public: michael@0: NS_DECL_CYCLE_COLLECTING_ISUPPORTS michael@0: NS_DECL_NSIXPCONNECTJSOBJECTHOLDER michael@0: NS_DECL_NSIXPCONNECTWRAPPEDNATIVE michael@0: michael@0: NS_DECL_CYCLE_COLLECTION_CLASS(XPCWrappedNative) michael@0: michael@0: nsIPrincipal* GetObjectPrincipal() const; michael@0: michael@0: bool michael@0: IsValid() const { return mFlatJSObject.hasFlag(FLAT_JS_OBJECT_VALID); } michael@0: michael@0: #define XPC_SCOPE_WORD(s) (intptr_t(s)) michael@0: #define XPC_SCOPE_MASK (intptr_t(0x3)) michael@0: #define XPC_SCOPE_TAG (intptr_t(0x1)) michael@0: #define XPC_WRAPPER_EXPIRED (intptr_t(0x2)) michael@0: michael@0: static inline bool michael@0: IsTaggedScope(XPCWrappedNativeScope* s) michael@0: {return XPC_SCOPE_WORD(s) & XPC_SCOPE_TAG;} michael@0: michael@0: static inline XPCWrappedNativeScope* michael@0: TagScope(XPCWrappedNativeScope* s) michael@0: {MOZ_ASSERT(!IsTaggedScope(s), "bad pointer!"); michael@0: return (XPCWrappedNativeScope*)(XPC_SCOPE_WORD(s) | XPC_SCOPE_TAG);} michael@0: michael@0: static inline XPCWrappedNativeScope* michael@0: UnTagScope(XPCWrappedNativeScope* s) michael@0: {return (XPCWrappedNativeScope*)(XPC_SCOPE_WORD(s) & ~XPC_SCOPE_TAG);} michael@0: michael@0: inline bool michael@0: IsWrapperExpired() const michael@0: {return XPC_SCOPE_WORD(mMaybeScope) & XPC_WRAPPER_EXPIRED;} michael@0: michael@0: bool michael@0: HasProto() const {return !IsTaggedScope(mMaybeScope);} michael@0: michael@0: XPCWrappedNativeProto* michael@0: GetProto() const michael@0: {return HasProto() ? michael@0: (XPCWrappedNativeProto*) michael@0: (XPC_SCOPE_WORD(mMaybeProto) & ~XPC_SCOPE_MASK) : nullptr;} michael@0: michael@0: void SetProto(XPCWrappedNativeProto* p); michael@0: michael@0: XPCWrappedNativeScope* michael@0: GetScope() const michael@0: {return GetProto() ? GetProto()->GetScope() : michael@0: (XPCWrappedNativeScope*) michael@0: (XPC_SCOPE_WORD(mMaybeScope) & ~XPC_SCOPE_MASK);} michael@0: michael@0: nsISupports* michael@0: GetIdentityObject() const {return mIdentity;} michael@0: michael@0: /** michael@0: * This getter clears the gray bit before handing out the JSObject which michael@0: * means that the object is guaranteed to be kept alive past the next CC. michael@0: */ michael@0: JSObject* michael@0: GetFlatJSObject() const michael@0: { michael@0: JS::ExposeObjectToActiveJS(mFlatJSObject); michael@0: return mFlatJSObject; michael@0: } michael@0: michael@0: /** michael@0: * This getter does not change the color of the JSObject meaning that the michael@0: * object returned is not guaranteed to be kept alive past the next CC. michael@0: * michael@0: * This should only be called if you are certain that the return value won't michael@0: * be passed into a JS API function and that it won't be stored without michael@0: * being rooted (or otherwise signaling the stored value to the CC). michael@0: */ michael@0: JSObject* michael@0: GetFlatJSObjectPreserveColor() const {return mFlatJSObject;} michael@0: michael@0: XPCNativeSet* michael@0: GetSet() const {return mSet;} michael@0: michael@0: void michael@0: SetSet(XPCNativeSet* set) {mSet = set;} michael@0: michael@0: static XPCWrappedNative* Get(JSObject *obj) { michael@0: MOZ_ASSERT(IS_WN_REFLECTOR(obj)); michael@0: return (XPCWrappedNative*)js::GetObjectPrivate(obj); michael@0: } michael@0: michael@0: private: michael@0: inline void michael@0: ExpireWrapper() michael@0: {mMaybeScope = (XPCWrappedNativeScope*) michael@0: (XPC_SCOPE_WORD(mMaybeScope) | XPC_WRAPPER_EXPIRED);} michael@0: michael@0: public: michael@0: michael@0: XPCNativeScriptableInfo* michael@0: GetScriptableInfo() const {return mScriptableInfo;} michael@0: michael@0: nsIXPCScriptable* // call this wrong and you deserve to crash michael@0: GetScriptableCallback() const {return mScriptableInfo->GetCallback();} michael@0: michael@0: nsIClassInfo* michael@0: GetClassInfo() const {return IsValid() && HasProto() ? michael@0: GetProto()->GetClassInfo() : nullptr;} michael@0: michael@0: bool michael@0: HasMutatedSet() const {return IsValid() && michael@0: (!HasProto() || michael@0: GetSet() != GetProto()->GetSet());} michael@0: michael@0: XPCJSRuntime* michael@0: GetRuntime() const {XPCWrappedNativeScope* scope = GetScope(); michael@0: return scope ? scope->GetRuntime() : nullptr;} michael@0: michael@0: static nsresult michael@0: WrapNewGlobal(xpcObjectHelper &nativeHelper, michael@0: nsIPrincipal *principal, bool initStandardClasses, michael@0: JS::CompartmentOptions& aOptions, michael@0: XPCWrappedNative **wrappedGlobal); michael@0: michael@0: static nsresult michael@0: GetNewOrUsed(xpcObjectHelper& helper, michael@0: XPCWrappedNativeScope* Scope, michael@0: XPCNativeInterface* Interface, michael@0: XPCWrappedNative** wrapper); michael@0: michael@0: public: michael@0: static nsresult michael@0: GetUsedOnly(nsISupports* Object, michael@0: XPCWrappedNativeScope* Scope, michael@0: XPCNativeInterface* Interface, michael@0: XPCWrappedNative** wrapper); michael@0: michael@0: static nsresult michael@0: ReparentWrapperIfFound(XPCWrappedNativeScope* aOldScope, michael@0: XPCWrappedNativeScope* aNewScope, michael@0: JS::HandleObject aNewParent, michael@0: nsISupports* aCOMObj); michael@0: michael@0: nsresult RescueOrphans(); michael@0: michael@0: void FlatJSObjectFinalized(); michael@0: michael@0: void SystemIsBeingShutDown(); michael@0: michael@0: enum CallMode {CALL_METHOD, CALL_GETTER, CALL_SETTER}; michael@0: michael@0: static bool CallMethod(XPCCallContext& ccx, michael@0: CallMode mode = CALL_METHOD); michael@0: michael@0: static bool GetAttribute(XPCCallContext& ccx) michael@0: {return CallMethod(ccx, CALL_GETTER);} michael@0: michael@0: static bool SetAttribute(XPCCallContext& ccx) michael@0: {return CallMethod(ccx, CALL_SETTER);} michael@0: michael@0: inline bool HasInterfaceNoQI(const nsIID& iid); michael@0: michael@0: XPCWrappedNativeTearOff* LocateTearOff(XPCNativeInterface* aInterface); michael@0: XPCWrappedNativeTearOff* FindTearOff(XPCNativeInterface* aInterface, michael@0: bool needJSObject = false, michael@0: nsresult* pError = nullptr); michael@0: void Mark() const michael@0: { michael@0: mSet->Mark(); michael@0: if (mScriptableInfo) mScriptableInfo->Mark(); michael@0: if (HasProto()) GetProto()->Mark(); michael@0: } michael@0: michael@0: // Yes, we *do* need to mark the mScriptableInfo in both cases. michael@0: inline void TraceInside(JSTracer *trc) { michael@0: if (JS_IsGCMarkingTracer(trc)) { michael@0: mSet->Mark(); michael@0: if (mScriptableInfo) michael@0: mScriptableInfo->Mark(); michael@0: } michael@0: if (HasProto()) michael@0: GetProto()->TraceSelf(trc); michael@0: else michael@0: GetScope()->TraceInside(trc); michael@0: if (mFlatJSObject && JS_IsGlobalObject(mFlatJSObject)) michael@0: { michael@0: xpc::TraceXPCGlobal(trc, mFlatJSObject); michael@0: } michael@0: } michael@0: michael@0: void TraceJS(JSTracer *trc) { michael@0: TraceInside(trc); michael@0: } michael@0: michael@0: void TraceSelf(JSTracer *trc) { michael@0: // If this got called, we're being kept alive by someone who really michael@0: // needs us alive and whole. Do not let our mFlatJSObject go away. michael@0: // This is the only time we should be tracing our mFlatJSObject, michael@0: // normally somebody else is doing that. Be careful not to trace the michael@0: // bogus INVALID_OBJECT value we can have during init, though. michael@0: if (mFlatJSObject) { michael@0: JS_CallTenuredObjectTracer(trc, &mFlatJSObject, michael@0: "XPCWrappedNative::mFlatJSObject"); michael@0: } michael@0: } michael@0: michael@0: static void Trace(JSTracer *trc, JSObject *obj); michael@0: michael@0: void AutoTrace(JSTracer *trc) { michael@0: TraceSelf(trc); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: void ASSERT_SetsNotMarked() const michael@0: {mSet->ASSERT_NotMarked(); michael@0: if (HasProto()){GetProto()->ASSERT_SetNotMarked();}} michael@0: #endif michael@0: michael@0: inline void SweepTearOffs(); michael@0: michael@0: // Returns a string that shuld be free'd using JS_smprintf_free (or null). michael@0: char* ToString(XPCWrappedNativeTearOff* to = nullptr) const; michael@0: michael@0: static void GatherProtoScriptableCreateInfo(nsIClassInfo* classInfo, michael@0: XPCNativeScriptableCreateInfo& sciProto); michael@0: michael@0: bool HasExternalReference() const {return mRefCnt > 1;} michael@0: michael@0: void NoteTearoffs(nsCycleCollectionTraversalCallback& cb); michael@0: michael@0: // Make ctor and dtor protected (rather than private) to placate nsCOMPtr. michael@0: protected: michael@0: XPCWrappedNative(); // not implemented michael@0: michael@0: // This ctor is used if this object will have a proto. michael@0: XPCWrappedNative(already_AddRefed&& aIdentity, michael@0: XPCWrappedNativeProto* aProto); michael@0: michael@0: // This ctor is used if this object will NOT have a proto. michael@0: XPCWrappedNative(already_AddRefed&& aIdentity, michael@0: XPCWrappedNativeScope* aScope, michael@0: XPCNativeSet* aSet); michael@0: michael@0: virtual ~XPCWrappedNative(); michael@0: void Destroy(); michael@0: michael@0: void UpdateScriptableInfo(XPCNativeScriptableInfo *si); michael@0: michael@0: private: michael@0: enum { michael@0: // Flags bits for mFlatJSObject: michael@0: FLAT_JS_OBJECT_VALID = JS_BIT(0) michael@0: }; michael@0: michael@0: private: michael@0: michael@0: bool Init(JS::HandleObject parent, const XPCNativeScriptableCreateInfo* sci); michael@0: bool FinishInit(); michael@0: michael@0: bool ExtendSet(XPCNativeInterface* aInterface); michael@0: michael@0: nsresult InitTearOff(XPCWrappedNativeTearOff* aTearOff, michael@0: XPCNativeInterface* aInterface, michael@0: bool needJSObject); michael@0: michael@0: bool InitTearOffJSObject(XPCWrappedNativeTearOff* to); michael@0: michael@0: public: michael@0: static const XPCNativeScriptableCreateInfo& GatherScriptableCreateInfo(nsISupports* obj, michael@0: nsIClassInfo* classInfo, michael@0: XPCNativeScriptableCreateInfo& sciProto, michael@0: XPCNativeScriptableCreateInfo& sciWrapper); michael@0: michael@0: private: michael@0: union michael@0: { michael@0: XPCWrappedNativeScope* mMaybeScope; michael@0: XPCWrappedNativeProto* mMaybeProto; michael@0: }; michael@0: XPCNativeSet* mSet; michael@0: JS::TenuredHeap mFlatJSObject; michael@0: XPCNativeScriptableInfo* mScriptableInfo; michael@0: XPCWrappedNativeTearOffChunk mFirstChunk; michael@0: }; michael@0: michael@0: /*************************************************************************** michael@0: **************************************************************************** michael@0: * michael@0: * Core classes for wrapped JSObject for use from native code... michael@0: * michael@0: **************************************************************************** michael@0: ***************************************************************************/ michael@0: michael@0: // this interfaces exists so we can refcount nsXPCWrappedJSClass michael@0: // {2453EBA0-A9B8-11d2-BA64-00805F8A5DD7} michael@0: #define NS_IXPCONNECT_WRAPPED_JS_CLASS_IID \ michael@0: { 0x2453eba0, 0xa9b8, 0x11d2, \ michael@0: { 0xba, 0x64, 0x0, 0x80, 0x5f, 0x8a, 0x5d, 0xd7 } } michael@0: michael@0: class nsIXPCWrappedJSClass : public nsISupports michael@0: { michael@0: public: michael@0: NS_DECLARE_STATIC_IID_ACCESSOR(NS_IXPCONNECT_WRAPPED_JS_CLASS_IID) michael@0: NS_IMETHOD DebugDump(int16_t depth) = 0; michael@0: }; michael@0: michael@0: NS_DEFINE_STATIC_IID_ACCESSOR(nsIXPCWrappedJSClass, michael@0: NS_IXPCONNECT_WRAPPED_JS_CLASS_IID) michael@0: michael@0: /*************************/ michael@0: // nsXPCWrappedJSClass represents the sharable factored out common code and michael@0: // data for nsXPCWrappedJS instances for the same interface type. michael@0: michael@0: class nsXPCWrappedJSClass : public nsIXPCWrappedJSClass michael@0: { michael@0: // all the interface method declarations... michael@0: NS_DECL_ISUPPORTS michael@0: NS_IMETHOD DebugDump(int16_t depth); michael@0: public: michael@0: michael@0: static already_AddRefed michael@0: GetNewOrUsed(JSContext* cx, michael@0: REFNSIID aIID); michael@0: michael@0: REFNSIID GetIID() const {return mIID;} michael@0: XPCJSRuntime* GetRuntime() const {return mRuntime;} michael@0: nsIInterfaceInfo* GetInterfaceInfo() const {return mInfo;} michael@0: const char* GetInterfaceName(); michael@0: michael@0: static bool IsWrappedJS(nsISupports* aPtr); michael@0: michael@0: NS_IMETHOD DelegatedQueryInterface(nsXPCWrappedJS* self, REFNSIID aIID, michael@0: void** aInstancePtr); michael@0: michael@0: JSObject* GetRootJSObject(JSContext* cx, JSObject* aJSObj); michael@0: michael@0: NS_IMETHOD CallMethod(nsXPCWrappedJS* wrapper, uint16_t methodIndex, michael@0: const XPTMethodDescriptor* info, michael@0: nsXPTCMiniVariant* params); michael@0: michael@0: JSObject* CallQueryInterfaceOnJSObject(JSContext* cx, michael@0: JSObject* jsobj, REFNSIID aIID); michael@0: michael@0: static nsresult BuildPropertyEnumerator(XPCCallContext& ccx, michael@0: JSObject* aJSObj, michael@0: nsISimpleEnumerator** aEnumerate); michael@0: michael@0: static nsresult GetNamedPropertyAsVariant(XPCCallContext& ccx, michael@0: JSObject* aJSObj, michael@0: const nsAString& aName, michael@0: nsIVariant** aResult); michael@0: michael@0: virtual ~nsXPCWrappedJSClass(); michael@0: michael@0: static nsresult CheckForException(XPCCallContext & ccx, michael@0: const char * aPropertyName, michael@0: const char * anInterfaceName, michael@0: bool aForceReport); michael@0: private: michael@0: nsXPCWrappedJSClass(); // not implemented michael@0: nsXPCWrappedJSClass(JSContext* cx, REFNSIID aIID, michael@0: nsIInterfaceInfo* aInfo); michael@0: michael@0: bool IsReflectable(uint16_t i) const michael@0: {return (bool)(mDescriptors[i/32] & (1 << (i%32)));} michael@0: void SetReflectable(uint16_t i, bool b) michael@0: {if (b) mDescriptors[i/32] |= (1 << (i%32)); michael@0: else mDescriptors[i/32] &= ~(1 << (i%32));} michael@0: michael@0: bool GetArraySizeFromParam(JSContext* cx, michael@0: const XPTMethodDescriptor* method, michael@0: const nsXPTParamInfo& param, michael@0: uint16_t methodIndex, michael@0: uint8_t paramIndex, michael@0: nsXPTCMiniVariant* params, michael@0: uint32_t* result); michael@0: michael@0: bool GetInterfaceTypeFromParam(JSContext* cx, michael@0: const XPTMethodDescriptor* method, michael@0: const nsXPTParamInfo& param, michael@0: uint16_t methodIndex, michael@0: const nsXPTType& type, michael@0: nsXPTCMiniVariant* params, michael@0: nsID* result); michael@0: michael@0: void CleanupPointerArray(const nsXPTType& datum_type, michael@0: uint32_t array_count, michael@0: void** arrayp); michael@0: michael@0: void CleanupPointerTypeObject(const nsXPTType& type, michael@0: void** pp); michael@0: michael@0: private: michael@0: XPCJSRuntime* mRuntime; michael@0: nsCOMPtr mInfo; michael@0: char* mName; michael@0: nsIID mIID; michael@0: uint32_t* mDescriptors; michael@0: }; michael@0: michael@0: /*************************/ michael@0: // nsXPCWrappedJS is a wrapper for a single JSObject for use from native code. michael@0: // nsXPCWrappedJS objects are chained together to represent the various michael@0: // interface on the single underlying (possibly aggregate) JSObject. michael@0: michael@0: class nsXPCWrappedJS : protected nsAutoXPTCStub, michael@0: public nsIXPConnectWrappedJS, michael@0: public nsSupportsWeakReference, michael@0: public nsIPropertyBag, michael@0: public XPCRootSetElem michael@0: { michael@0: public: michael@0: NS_DECL_CYCLE_COLLECTING_ISUPPORTS michael@0: NS_DECL_NSIXPCONNECTJSOBJECTHOLDER michael@0: NS_DECL_NSIXPCONNECTWRAPPEDJS michael@0: NS_DECL_NSISUPPORTSWEAKREFERENCE michael@0: NS_DECL_NSIPROPERTYBAG michael@0: michael@0: NS_DECL_CYCLE_COLLECTION_SKIPPABLE_CLASS_AMBIGUOUS(nsXPCWrappedJS, nsIXPConnectWrappedJS) michael@0: michael@0: NS_IMETHOD CallMethod(uint16_t methodIndex, michael@0: const XPTMethodDescriptor *info, michael@0: nsXPTCMiniVariant* params); michael@0: michael@0: /* michael@0: * This is rarely called directly. Instead one usually calls michael@0: * XPCConvert::JSObject2NativeInterface which will handles cases where the michael@0: * JS object is already a wrapped native or a DOM object. michael@0: */ michael@0: michael@0: static nsresult michael@0: GetNewOrUsed(JS::HandleObject aJSObj, michael@0: REFNSIID aIID, michael@0: nsXPCWrappedJS** wrapper); michael@0: michael@0: nsISomeInterface* GetXPTCStub() { return mXPTCStub; } michael@0: michael@0: /** michael@0: * This getter does not change the color of the JSObject meaning that the michael@0: * object returned is not guaranteed to be kept alive past the next CC. michael@0: * michael@0: * This should only be called if you are certain that the return value won't michael@0: * be passed into a JS API function and that it won't be stored without michael@0: * being rooted (or otherwise signaling the stored value to the CC). michael@0: */ michael@0: JSObject* GetJSObjectPreserveColor() const {return mJSObj;} michael@0: michael@0: nsXPCWrappedJSClass* GetClass() const {return mClass;} michael@0: REFNSIID GetIID() const {return GetClass()->GetIID();} michael@0: nsXPCWrappedJS* GetRootWrapper() const {return mRoot;} michael@0: nsXPCWrappedJS* GetNextWrapper() const {return mNext;} michael@0: michael@0: nsXPCWrappedJS* Find(REFNSIID aIID); michael@0: nsXPCWrappedJS* FindInherited(REFNSIID aIID); michael@0: nsXPCWrappedJS* FindOrFindInherited(REFNSIID aIID) { michael@0: nsXPCWrappedJS* wrapper = Find(aIID); michael@0: if (wrapper) michael@0: return wrapper; michael@0: return FindInherited(aIID); michael@0: } michael@0: michael@0: bool IsRootWrapper() const {return mRoot == this;} michael@0: bool IsValid() const {return mJSObj != nullptr;} michael@0: void SystemIsBeingShutDown(); michael@0: michael@0: // These two methods are used by JSObject2WrappedJSMap::FindDyingJSObjects michael@0: // to find non-rooting wrappers for dying JS objects. See the top of michael@0: // XPCWrappedJS.cpp for more details. michael@0: bool IsSubjectToFinalization() const {return IsValid() && mRefCnt == 1;} michael@0: bool IsObjectAboutToBeFinalized() {return JS_IsAboutToBeFinalized(&mJSObj);} michael@0: michael@0: bool IsAggregatedToNative() const {return mRoot->mOuter != nullptr;} michael@0: nsISupports* GetAggregatedNativeObject() const {return mRoot->mOuter;} michael@0: void SetAggregatedNativeObject(nsISupports *aNative) { michael@0: MOZ_ASSERT(aNative); michael@0: if (mRoot->mOuter) { michael@0: MOZ_ASSERT(mRoot->mOuter == aNative, michael@0: "Only one aggregated native can be set"); michael@0: return; michael@0: } michael@0: mRoot->mOuter = aNative; michael@0: } michael@0: michael@0: void TraceJS(JSTracer* trc); michael@0: static void GetTraceName(JSTracer* trc, char *buf, size_t bufsize); michael@0: michael@0: virtual ~nsXPCWrappedJS(); michael@0: protected: michael@0: nsXPCWrappedJS(); // not implemented michael@0: nsXPCWrappedJS(JSContext* cx, michael@0: JSObject* aJSObj, michael@0: nsXPCWrappedJSClass* aClass, michael@0: nsXPCWrappedJS* root); michael@0: michael@0: bool CanSkip(); michael@0: void Destroy(); michael@0: void Unlink(); michael@0: michael@0: private: michael@0: JS::Heap mJSObj; michael@0: nsRefPtr mClass; michael@0: nsXPCWrappedJS* mRoot; // If mRoot != this, it is an owning pointer. michael@0: nsXPCWrappedJS* mNext; michael@0: nsCOMPtr mOuter; // only set in root michael@0: }; michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: class XPCJSObjectHolder : public nsIXPConnectJSObjectHolder, michael@0: public XPCRootSetElem michael@0: { michael@0: public: michael@0: // all the interface method declarations... michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIXPCONNECTJSOBJECTHOLDER michael@0: michael@0: // non-interface implementation michael@0: michael@0: public: michael@0: static XPCJSObjectHolder* newHolder(JSObject* obj); michael@0: michael@0: virtual ~XPCJSObjectHolder(); michael@0: michael@0: void TraceJS(JSTracer *trc); michael@0: static void GetTraceName(JSTracer* trc, char *buf, size_t bufsize); michael@0: michael@0: private: michael@0: XPCJSObjectHolder(JSObject* obj); michael@0: XPCJSObjectHolder(); // not implemented michael@0: michael@0: JS::Heap mJSObj; michael@0: }; michael@0: michael@0: /*************************************************************************** michael@0: **************************************************************************** michael@0: * michael@0: * All manner of utility classes follow... michael@0: * michael@0: **************************************************************************** michael@0: ***************************************************************************/ michael@0: michael@0: class xpcProperty : public nsIProperty michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIPROPERTY michael@0: michael@0: xpcProperty(const char16_t* aName, uint32_t aNameLen, nsIVariant* aValue); michael@0: virtual ~xpcProperty() {} michael@0: michael@0: private: michael@0: nsString mName; michael@0: nsCOMPtr mValue; michael@0: }; michael@0: michael@0: /***************************************************************************/ michael@0: // class here just for static methods michael@0: class XPCConvert michael@0: { michael@0: public: michael@0: static bool IsMethodReflectable(const XPTMethodDescriptor& info); michael@0: michael@0: /** michael@0: * Convert a native object into a jsval. michael@0: * michael@0: * @param d [out] the resulting jsval michael@0: * @param s the native object we're working with michael@0: * @param type the type of object that s is michael@0: * @param iid the interface of s that we want michael@0: * @param scope the default scope to put on the new JSObject's parent michael@0: * chain michael@0: * @param pErr [out] relevant error code, if any. michael@0: */ michael@0: michael@0: static bool NativeData2JS(JS::MutableHandleValue d, michael@0: const void* s, const nsXPTType& type, michael@0: const nsID* iid, nsresult* pErr); michael@0: michael@0: static bool JSData2Native(void* d, JS::HandleValue s, michael@0: const nsXPTType& type, michael@0: bool useAllocator, const nsID* iid, michael@0: nsresult* pErr); michael@0: michael@0: /** michael@0: * Convert a native nsISupports into a JSObject. michael@0: * michael@0: * @param dest [out] the resulting JSObject michael@0: * @param src the native object we're working with michael@0: * @param iid the interface of src that we want (may be null) michael@0: * @param Interface the interface of src that we want michael@0: * @param cache the wrapper cache for src (may be null, in which case src michael@0: * will be QI'ed to get the cache) michael@0: * @param allowNativeWrapper if true, this method may wrap the resulting michael@0: * JSObject in an XPCNativeWrapper and return that, as needed. michael@0: * @param pErr [out] relevant error code, if any. michael@0: * @param src_is_identity optional performance hint. Set to true only michael@0: * if src is the identity pointer. michael@0: */ michael@0: static bool NativeInterface2JSObject(JS::MutableHandleValue d, michael@0: nsIXPConnectJSObjectHolder** dest, michael@0: xpcObjectHelper& aHelper, michael@0: const nsID* iid, michael@0: XPCNativeInterface** Interface, michael@0: bool allowNativeWrapper, michael@0: nsresult* pErr); michael@0: michael@0: static bool GetNativeInterfaceFromJSObject(void** dest, JSObject* src, michael@0: const nsID* iid, michael@0: nsresult* pErr); michael@0: static bool JSObject2NativeInterface(void** dest, JS::HandleObject src, michael@0: const nsID* iid, michael@0: nsISupports* aOuter, michael@0: nsresult* pErr); michael@0: static bool GetISupportsFromJSObject(JSObject* obj, nsISupports** iface); michael@0: michael@0: /** michael@0: * Convert a native array into a jsval. michael@0: * michael@0: * @param d [out] the resulting jsval michael@0: * @param s the native array we're working with michael@0: * @param type the type of objects in the array michael@0: * @param iid the interface of each object in the array that we want michael@0: * @param count the number of items in the array michael@0: * @param scope the default scope to put on the new JSObjects' parent chain michael@0: * @param pErr [out] relevant error code, if any. michael@0: */ michael@0: static bool NativeArray2JS(JS::MutableHandleValue d, const void** s, michael@0: const nsXPTType& type, const nsID* iid, michael@0: uint32_t count, nsresult* pErr); michael@0: michael@0: static bool JSArray2Native(void** d, JS::HandleValue s, michael@0: uint32_t count, const nsXPTType& type, michael@0: const nsID* iid, nsresult* pErr); michael@0: michael@0: static bool JSTypedArray2Native(void** d, michael@0: JSObject* jsarray, michael@0: uint32_t count, michael@0: const nsXPTType& type, michael@0: nsresult* pErr); michael@0: michael@0: static bool NativeStringWithSize2JS(JS::MutableHandleValue d, const void* s, michael@0: const nsXPTType& type, michael@0: uint32_t count, michael@0: nsresult* pErr); michael@0: michael@0: static bool JSStringWithSize2Native(void* d, JS::HandleValue s, michael@0: uint32_t count, const nsXPTType& type, michael@0: nsresult* pErr); michael@0: michael@0: static nsresult JSValToXPCException(JS::MutableHandleValue s, michael@0: const char* ifaceName, michael@0: const char* methodName, michael@0: nsIException** exception); michael@0: michael@0: static nsresult JSErrorToXPCException(const char* message, michael@0: const char* ifaceName, michael@0: const char* methodName, michael@0: const JSErrorReport* report, michael@0: nsIException** exception); michael@0: michael@0: static nsresult ConstructException(nsresult rv, const char* message, michael@0: const char* ifaceName, michael@0: const char* methodName, michael@0: nsISupports* data, michael@0: nsIException** exception, michael@0: JSContext* cx, michael@0: jsval *jsExceptionPtr); michael@0: michael@0: private: michael@0: XPCConvert(); // not implemented michael@0: michael@0: }; michael@0: michael@0: /***************************************************************************/ michael@0: // code for throwing exceptions into JS michael@0: michael@0: class nsXPCException; michael@0: michael@0: class XPCThrower michael@0: { michael@0: public: michael@0: static void Throw(nsresult rv, JSContext* cx); michael@0: static void Throw(nsresult rv, XPCCallContext& ccx); michael@0: static void ThrowBadResult(nsresult rv, nsresult result, XPCCallContext& ccx); michael@0: static void ThrowBadParam(nsresult rv, unsigned paramNum, XPCCallContext& ccx); michael@0: static bool SetVerbosity(bool state) michael@0: {bool old = sVerbose; sVerbose = state; return old;} michael@0: michael@0: static bool CheckForPendingException(nsresult result, JSContext *cx); michael@0: michael@0: private: michael@0: static void Verbosify(XPCCallContext& ccx, michael@0: char** psz, bool own); michael@0: michael@0: private: michael@0: static bool sVerbose; michael@0: }; michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: class nsXPCException michael@0: { michael@0: public: michael@0: static bool NameAndFormatForNSResult(nsresult rv, michael@0: const char** name, michael@0: const char** format); michael@0: michael@0: static const void* IterateNSResults(nsresult* rv, michael@0: const char** name, michael@0: const char** format, michael@0: const void** iterp); michael@0: michael@0: static uint32_t GetNSResultCount(); michael@0: }; michael@0: michael@0: /***************************************************************************/ michael@0: /* michael@0: * nsJSID implements nsIJSID. It is also used by nsJSIID and nsJSCID as a michael@0: * member (as a hidden implementaion detail) to which they delegate many calls. michael@0: */ michael@0: michael@0: // Initialization is done on demand, and calling the destructor below is always michael@0: // safe. michael@0: extern void xpc_DestroyJSxIDClassObjects(); michael@0: michael@0: class nsJSID : public nsIJSID michael@0: { michael@0: public: michael@0: NS_DEFINE_STATIC_CID_ACCESSOR(NS_JS_ID_CID) michael@0: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIJSID michael@0: michael@0: bool InitWithName(const nsID& id, const char *nameString); michael@0: bool SetName(const char* name); michael@0: void SetNameToNoString() michael@0: {MOZ_ASSERT(!mName, "name already set"); mName = gNoString;} michael@0: bool NameIsSet() const {return nullptr != mName;} michael@0: const nsID& ID() const {return mID;} michael@0: bool IsValid() const {return !mID.Equals(GetInvalidIID());} michael@0: michael@0: static already_AddRefed NewID(const char* str); michael@0: static already_AddRefed NewID(const nsID& id); michael@0: michael@0: nsJSID(); michael@0: virtual ~nsJSID(); michael@0: protected: michael@0: michael@0: void Reset(); michael@0: const nsID& GetInvalidIID() const; michael@0: michael@0: protected: michael@0: static char gNoString[]; michael@0: nsID mID; michael@0: char* mNumber; michael@0: char* mName; michael@0: }; michael@0: michael@0: // nsJSIID michael@0: michael@0: class nsJSIID : public nsIJSIID, michael@0: public nsIXPCScriptable michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: michael@0: // we manually delagate these to nsJSID michael@0: NS_DECL_NSIJSID michael@0: michael@0: // we implement the rest... michael@0: NS_DECL_NSIJSIID michael@0: NS_DECL_NSIXPCSCRIPTABLE michael@0: michael@0: static already_AddRefed NewID(nsIInterfaceInfo* aInfo); michael@0: michael@0: nsJSIID(nsIInterfaceInfo* aInfo); michael@0: nsJSIID(); // not implemented michael@0: virtual ~nsJSIID(); michael@0: michael@0: private: michael@0: nsCOMPtr mInfo; michael@0: }; michael@0: michael@0: // nsJSCID michael@0: michael@0: class nsJSCID : public nsIJSCID, public nsIXPCScriptable michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: michael@0: // we manually delagate these to nsJSID michael@0: NS_DECL_NSIJSID michael@0: michael@0: // we implement the rest... michael@0: NS_DECL_NSIJSCID michael@0: NS_DECL_NSIXPCSCRIPTABLE michael@0: michael@0: static already_AddRefed NewID(const char* str); michael@0: michael@0: nsJSCID(); michael@0: virtual ~nsJSCID(); michael@0: michael@0: private: michael@0: void ResolveName(); michael@0: michael@0: private: michael@0: nsJSID mDetails; michael@0: }; michael@0: michael@0: michael@0: /***************************************************************************/ michael@0: // XPCJSContextStack is not actually an xpcom object, but xpcom calls are michael@0: // delegated to it as an implementation detail. michael@0: struct XPCJSContextInfo { michael@0: XPCJSContextInfo(JSContext* aCx) : michael@0: cx(aCx), michael@0: savedFrameChain(false) michael@0: {} michael@0: JSContext* cx; michael@0: michael@0: // Whether the frame chain was saved michael@0: bool savedFrameChain; michael@0: }; michael@0: michael@0: namespace xpc { michael@0: michael@0: // These functions are used in a few places where a callback model makes it michael@0: // impossible to push a JSContext using one of our stack-scoped classes. We michael@0: // depend on those stack-scoped classes to maintain nsIScriptContext michael@0: // invariants, so these functions may only be used of the context is not michael@0: // associated with an nsJSContext/nsIScriptContext. michael@0: bool PushJSContextNoScriptContext(JSContext *aCx); michael@0: void PopJSContextNoScriptContext(); michael@0: michael@0: } /* namespace xpc */ michael@0: michael@0: class XPCJSContextStack michael@0: { michael@0: public: michael@0: XPCJSContextStack(XPCJSRuntime *aRuntime) michael@0: : mRuntime(aRuntime) michael@0: , mSafeJSContext(nullptr) michael@0: , mSafeJSContextGlobal(aRuntime->Runtime(), nullptr) michael@0: { } michael@0: michael@0: virtual ~XPCJSContextStack(); michael@0: michael@0: uint32_t Count() michael@0: { michael@0: return mStack.Length(); michael@0: } michael@0: michael@0: JSContext *Peek() michael@0: { michael@0: return mStack.IsEmpty() ? nullptr : mStack[mStack.Length() - 1].cx; michael@0: } michael@0: michael@0: JSContext *InitSafeJSContext(); michael@0: JSContext *GetSafeJSContext(); michael@0: JSObject *GetSafeJSContextGlobal(); michael@0: bool HasJSContext(JSContext *cx); michael@0: michael@0: const InfallibleTArray* GetStack() michael@0: { return &mStack; } michael@0: michael@0: private: michael@0: friend class mozilla::AutoCxPusher; michael@0: friend bool xpc::PushJSContextNoScriptContext(JSContext *aCx);; michael@0: friend void xpc::PopJSContextNoScriptContext(); michael@0: michael@0: // We make these private so that stack manipulation can only happen michael@0: // through one of the above friends. michael@0: JSContext *Pop(); michael@0: bool Push(JSContext *cx); michael@0: michael@0: AutoInfallibleTArray mStack; michael@0: XPCJSRuntime* mRuntime; michael@0: JSContext* mSafeJSContext; michael@0: JS::PersistentRootedObject mSafeJSContextGlobal; michael@0: }; michael@0: michael@0: /***************************************************************************/ michael@0: // 'Components' object implementations. nsXPCComponentsBase has the michael@0: // less-privileged stuff that we're willing to expose to XBL. michael@0: michael@0: class nsXPCComponentsBase : public nsIXPCComponentsBase michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIXPCCOMPONENTSBASE michael@0: michael@0: public: michael@0: void SystemIsBeingShutDown() { ClearMembers(); } michael@0: virtual ~nsXPCComponentsBase(); michael@0: michael@0: XPCWrappedNativeScope *GetScope() { return mScope; } michael@0: michael@0: protected: michael@0: nsXPCComponentsBase(XPCWrappedNativeScope* aScope); michael@0: virtual void ClearMembers(); michael@0: michael@0: XPCWrappedNativeScope* mScope; michael@0: michael@0: // Unprivileged members from nsIXPCComponentsBase. michael@0: nsRefPtr mInterfaces; michael@0: nsRefPtr mInterfacesByID; michael@0: nsRefPtr mResults; michael@0: michael@0: friend class XPCWrappedNativeScope; michael@0: }; michael@0: michael@0: class nsXPCComponents : public nsXPCComponentsBase, michael@0: public nsIXPCComponents michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_FORWARD_NSIXPCCOMPONENTSBASE(nsXPCComponentsBase::) michael@0: NS_DECL_NSIXPCCOMPONENTS michael@0: michael@0: protected: michael@0: nsXPCComponents(XPCWrappedNativeScope* aScope); michael@0: virtual ~nsXPCComponents(); michael@0: virtual void ClearMembers() MOZ_OVERRIDE; michael@0: michael@0: // Privileged members added by nsIXPCComponents. michael@0: nsRefPtr mClasses; michael@0: nsRefPtr mClassesByID; michael@0: nsRefPtr mID; michael@0: nsRefPtr mException; michael@0: nsRefPtr mConstructor; michael@0: nsRefPtr mUtils; michael@0: michael@0: friend class XPCWrappedNativeScope; michael@0: }; michael@0: michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: extern JSObject* michael@0: xpc_NewIDObject(JSContext *cx, JS::HandleObject jsobj, const nsID& aID); michael@0: michael@0: extern const nsID* michael@0: xpc_JSObjectToID(JSContext *cx, JSObject* obj); michael@0: michael@0: extern bool michael@0: xpc_JSObjectIsID(JSContext *cx, JSObject* obj); michael@0: michael@0: /***************************************************************************/ michael@0: // in XPCDebug.cpp michael@0: michael@0: extern bool michael@0: xpc_DumpJSStack(JSContext* cx, bool showArgs, bool showLocals, michael@0: bool showThisProps); michael@0: michael@0: // Return a newly-allocated string containing a representation of the michael@0: // current JS stack. It is the *caller's* responsibility to free this michael@0: // string with JS_smprintf_free(). michael@0: extern char* michael@0: xpc_PrintJSStack(JSContext* cx, bool showArgs, bool showLocals, michael@0: bool showThisProps); michael@0: michael@0: extern bool michael@0: xpc_DumpEvalInJSStackFrame(JSContext* cx, uint32_t frameno, const char* text); michael@0: michael@0: extern bool michael@0: xpc_InstallJSDebuggerKeywordHandler(JSRuntime* rt); michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: // Definition of nsScriptError, defined here because we lack a place to put michael@0: // XPCOM objects associated with the JavaScript engine. michael@0: class nsScriptError : public nsIScriptError { michael@0: public: michael@0: nsScriptError(); michael@0: michael@0: virtual ~nsScriptError(); michael@0: michael@0: // TODO - do something reasonable on getting null from these babies. michael@0: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSICONSOLEMESSAGE michael@0: NS_DECL_NSISCRIPTERROR michael@0: michael@0: private: michael@0: nsString mMessage; michael@0: nsString mSourceName; michael@0: uint32_t mLineNumber; michael@0: nsString mSourceLine; michael@0: uint32_t mColumnNumber; michael@0: uint32_t mFlags; michael@0: nsCString mCategory; michael@0: uint64_t mOuterWindowID; michael@0: uint64_t mInnerWindowID; michael@0: int64_t mTimeStamp; michael@0: bool mIsFromPrivateWindow; michael@0: }; michael@0: michael@0: /****************************************************************************** michael@0: * Handles pre/post script processing and the setting/resetting the error michael@0: * reporter michael@0: */ michael@0: class MOZ_STACK_CLASS AutoScriptEvaluate michael@0: { michael@0: public: michael@0: /** michael@0: * Saves the JSContext as well as initializing our state michael@0: * @param cx The JSContext, this can be null, we don't do anything then michael@0: */ michael@0: AutoScriptEvaluate(JSContext * cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) michael@0: : mJSContext(cx), mErrorReporterSet(false), mEvaluated(false) { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: } michael@0: michael@0: /** michael@0: * Does the pre script evaluation and sets the error reporter if given michael@0: * This function should only be called once, and will assert if called michael@0: * more than once michael@0: * @param errorReporter the error reporter callback function to set michael@0: */ michael@0: michael@0: bool StartEvaluating(JS::HandleObject scope, JSErrorReporter errorReporter = nullptr); michael@0: michael@0: /** michael@0: * Does the post script evaluation and resets the error reporter michael@0: */ michael@0: ~AutoScriptEvaluate(); michael@0: private: michael@0: JSContext* mJSContext; michael@0: mozilla::Maybe mState; michael@0: bool mErrorReporterSet; michael@0: bool mEvaluated; michael@0: mozilla::Maybe mAutoCompartment; michael@0: MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER michael@0: michael@0: // No copying or assignment allowed michael@0: AutoScriptEvaluate(const AutoScriptEvaluate &) MOZ_DELETE; michael@0: AutoScriptEvaluate & operator =(const AutoScriptEvaluate &) MOZ_DELETE; michael@0: }; michael@0: michael@0: /***************************************************************************/ michael@0: class MOZ_STACK_CLASS AutoResolveName michael@0: { michael@0: public: michael@0: AutoResolveName(XPCCallContext& ccx, JS::HandleId name michael@0: MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : michael@0: mOld(ccx, XPCJSRuntime::Get()->SetResolveName(name)) michael@0: #ifdef DEBUG michael@0: ,mCheck(ccx, name) michael@0: #endif michael@0: { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: } michael@0: ~AutoResolveName() michael@0: { michael@0: #ifdef DEBUG michael@0: jsid old = michael@0: #endif michael@0: XPCJSRuntime::Get()->SetResolveName(mOld); michael@0: MOZ_ASSERT(old == mCheck, "Bad Nesting!"); michael@0: } michael@0: michael@0: private: michael@0: JS::RootedId mOld; michael@0: #ifdef DEBUG michael@0: JS::RootedId mCheck; michael@0: #endif michael@0: MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER michael@0: }; michael@0: michael@0: /***************************************************************************/ michael@0: // AutoMarkingPtr is the base class for the various AutoMarking pointer types michael@0: // below. This system allows us to temporarily protect instances of our garbage michael@0: // collected types after they are constructed but before they are safely michael@0: // attached to other rooted objects. michael@0: // This base class has pure virtual support for marking. michael@0: michael@0: class AutoMarkingPtr michael@0: { michael@0: public: michael@0: AutoMarkingPtr(JSContext* cx) { michael@0: mRoot = XPCJSRuntime::Get()->GetAutoRootsAdr(); michael@0: mNext = *mRoot; michael@0: *mRoot = this; michael@0: } michael@0: michael@0: virtual ~AutoMarkingPtr() { michael@0: if (mRoot) { michael@0: MOZ_ASSERT(*mRoot == this); michael@0: *mRoot = mNext; michael@0: } michael@0: } michael@0: michael@0: void TraceJSAll(JSTracer* trc) { michael@0: for (AutoMarkingPtr *cur = this; cur; cur = cur->mNext) michael@0: cur->TraceJS(trc); michael@0: } michael@0: michael@0: void MarkAfterJSFinalizeAll() { michael@0: for (AutoMarkingPtr *cur = this; cur; cur = cur->mNext) michael@0: cur->MarkAfterJSFinalize(); michael@0: } michael@0: michael@0: protected: michael@0: virtual void TraceJS(JSTracer* trc) = 0; michael@0: virtual void MarkAfterJSFinalize() = 0; michael@0: michael@0: private: michael@0: AutoMarkingPtr** mRoot; michael@0: AutoMarkingPtr* mNext; michael@0: }; michael@0: michael@0: template michael@0: class TypedAutoMarkingPtr : public AutoMarkingPtr michael@0: { michael@0: public: michael@0: TypedAutoMarkingPtr(JSContext* cx) : AutoMarkingPtr(cx), mPtr(nullptr) {} michael@0: TypedAutoMarkingPtr(JSContext* cx, T* ptr) : AutoMarkingPtr(cx), mPtr(ptr) {} michael@0: michael@0: T* get() const { return mPtr; } michael@0: operator T *() const { return mPtr; } michael@0: T* operator->() const { return mPtr; } michael@0: michael@0: TypedAutoMarkingPtr& operator =(T* ptr) { mPtr = ptr; return *this; } michael@0: michael@0: protected: michael@0: virtual void TraceJS(JSTracer* trc) michael@0: { michael@0: if (mPtr) { michael@0: mPtr->TraceJS(trc); michael@0: mPtr->AutoTrace(trc); michael@0: } michael@0: } michael@0: michael@0: virtual void MarkAfterJSFinalize() michael@0: { michael@0: if (mPtr) michael@0: mPtr->Mark(); michael@0: } michael@0: michael@0: private: michael@0: T* mPtr; michael@0: }; michael@0: michael@0: typedef TypedAutoMarkingPtr AutoMarkingNativeInterfacePtr; michael@0: typedef TypedAutoMarkingPtr AutoMarkingNativeSetPtr; michael@0: typedef TypedAutoMarkingPtr AutoMarkingWrappedNativePtr; michael@0: typedef TypedAutoMarkingPtr AutoMarkingWrappedNativeTearOffPtr; michael@0: typedef TypedAutoMarkingPtr AutoMarkingWrappedNativeProtoPtr; michael@0: typedef TypedAutoMarkingPtr AutoMarkingNativeScriptableInfoPtr; michael@0: michael@0: template michael@0: class ArrayAutoMarkingPtr : public AutoMarkingPtr michael@0: { michael@0: public: michael@0: ArrayAutoMarkingPtr(JSContext* cx) michael@0: : AutoMarkingPtr(cx), mPtr(nullptr), mCount(0) {} michael@0: ArrayAutoMarkingPtr(JSContext* cx, T** ptr, uint32_t count, bool clear) michael@0: : AutoMarkingPtr(cx), mPtr(ptr), mCount(count) michael@0: { michael@0: if (!mPtr) mCount = 0; michael@0: else if (clear) memset(mPtr, 0, mCount*sizeof(T*)); michael@0: } michael@0: michael@0: T** get() const { return mPtr; } michael@0: operator T **() const { return mPtr; } michael@0: T** operator->() const { return mPtr; } michael@0: michael@0: ArrayAutoMarkingPtr& operator =(const ArrayAutoMarkingPtr &other) michael@0: { michael@0: mPtr = other.mPtr; michael@0: mCount = other.mCount; michael@0: return *this; michael@0: } michael@0: michael@0: protected: michael@0: virtual void TraceJS(JSTracer* trc) michael@0: { michael@0: for (uint32_t i = 0; i < mCount; i++) { michael@0: if (mPtr[i]) { michael@0: mPtr[i]->TraceJS(trc); michael@0: mPtr[i]->AutoTrace(trc); michael@0: } michael@0: } michael@0: } michael@0: michael@0: virtual void MarkAfterJSFinalize() michael@0: { michael@0: for (uint32_t i = 0; i < mCount; i++) { michael@0: if (mPtr[i]) michael@0: mPtr[i]->Mark(); michael@0: } michael@0: } michael@0: michael@0: private: michael@0: T** mPtr; michael@0: uint32_t mCount; michael@0: }; michael@0: michael@0: typedef ArrayAutoMarkingPtr AutoMarkingNativeInterfacePtrArrayPtr; michael@0: michael@0: /***************************************************************************/ michael@0: namespace xpc { michael@0: // Allocates a string that grants all access ("AllAccess") michael@0: char * michael@0: CloneAllAccess(); michael@0: michael@0: // Returns access if wideName is in list michael@0: char * michael@0: CheckAccessList(const char16_t *wideName, const char *const list[]); michael@0: } /* namespace xpc */ michael@0: michael@0: /***************************************************************************/ michael@0: // in xpcvariant.cpp... michael@0: michael@0: // {1809FD50-91E8-11d5-90F9-0010A4E73D9A} michael@0: #define XPCVARIANT_IID \ michael@0: {0x1809fd50, 0x91e8, 0x11d5, \ michael@0: { 0x90, 0xf9, 0x0, 0x10, 0xa4, 0xe7, 0x3d, 0x9a } } michael@0: michael@0: // {DC524540-487E-4501-9AC7-AAA784B17C1C} michael@0: #define XPCVARIANT_CID \ michael@0: {0xdc524540, 0x487e, 0x4501, \ michael@0: { 0x9a, 0xc7, 0xaa, 0xa7, 0x84, 0xb1, 0x7c, 0x1c } } michael@0: michael@0: class XPCVariant : public nsIVariant michael@0: { michael@0: public: michael@0: NS_DECL_CYCLE_COLLECTING_ISUPPORTS michael@0: NS_DECL_NSIVARIANT michael@0: NS_DECL_CYCLE_COLLECTION_CLASS(XPCVariant) michael@0: michael@0: // If this class ever implements nsIWritableVariant, take special care with michael@0: // the case when mJSVal is JSVAL_STRING, since we don't own the data in michael@0: // that case. michael@0: michael@0: // We #define and iid so that out module local code can use QI to detect michael@0: // if a given nsIVariant is in fact an XPCVariant. michael@0: NS_DECLARE_STATIC_IID_ACCESSOR(XPCVARIANT_IID) michael@0: michael@0: static already_AddRefed newVariant(JSContext* cx, jsval aJSVal); michael@0: michael@0: /** michael@0: * This getter clears the gray bit before handing out the jsval if the jsval michael@0: * represents a JSObject. That means that the object is guaranteed to be michael@0: * kept alive past the next CC. michael@0: */ michael@0: jsval GetJSVal() const { michael@0: if (!JSVAL_IS_PRIMITIVE(mJSVal)) michael@0: JS::ExposeObjectToActiveJS(&mJSVal.toObject()); michael@0: return mJSVal; michael@0: } michael@0: michael@0: /** michael@0: * This getter does not change the color of the jsval (if it represents a michael@0: * JSObject) meaning that the value returned is not guaranteed to be kept michael@0: * alive past the next CC. michael@0: * michael@0: * This should only be called if you are certain that the return value won't michael@0: * be passed into a JS API function and that it won't be stored without michael@0: * being rooted (or otherwise signaling the stored value to the CC). michael@0: */ michael@0: jsval GetJSValPreserveColor() const {return mJSVal;} michael@0: michael@0: XPCVariant(JSContext* cx, jsval aJSVal); michael@0: michael@0: /** michael@0: * Convert a variant into a jsval. michael@0: * michael@0: * @param ccx the context for the whole procedure michael@0: * @param variant the variant to convert michael@0: * @param scope the default scope to put on the new JSObject's parent chain michael@0: * @param pErr [out] relevant error code, if any. michael@0: * @param pJSVal [out] the resulting jsval. michael@0: */ michael@0: static bool VariantDataToJS(nsIVariant* variant, michael@0: nsresult* pErr, JS::MutableHandleValue pJSVal); michael@0: michael@0: bool IsPurple() michael@0: { michael@0: return mRefCnt.IsPurple(); michael@0: } michael@0: michael@0: void RemovePurple() michael@0: { michael@0: mRefCnt.RemovePurple(); michael@0: } michael@0: michael@0: void SetCCGeneration(uint32_t aGen) michael@0: { michael@0: mCCGeneration = aGen; michael@0: } michael@0: michael@0: uint32_t CCGeneration() { return mCCGeneration; } michael@0: protected: michael@0: virtual ~XPCVariant() { } michael@0: michael@0: bool InitializeData(JSContext* cx); michael@0: michael@0: protected: michael@0: nsDiscriminatedUnion mData; michael@0: JS::Heap mJSVal; michael@0: bool mReturnRawObject : 1; michael@0: uint32_t mCCGeneration : 31; michael@0: }; michael@0: michael@0: NS_DEFINE_STATIC_IID_ACCESSOR(XPCVariant, XPCVARIANT_IID) michael@0: michael@0: class XPCTraceableVariant: public XPCVariant, michael@0: public XPCRootSetElem michael@0: { michael@0: public: michael@0: XPCTraceableVariant(JSContext* cx, jsval aJSVal) michael@0: : XPCVariant(cx, aJSVal) michael@0: { michael@0: nsXPConnect::GetRuntimeInstance()->AddVariantRoot(this); michael@0: } michael@0: michael@0: virtual ~XPCTraceableVariant(); michael@0: michael@0: void TraceJS(JSTracer* trc); michael@0: static void GetTraceName(JSTracer* trc, char *buf, size_t bufsize); michael@0: }; michael@0: michael@0: /***************************************************************************/ michael@0: // Utilities michael@0: michael@0: inline void * michael@0: xpc_GetJSPrivate(JSObject *obj) michael@0: { michael@0: return js::GetObjectPrivate(obj); michael@0: } michael@0: michael@0: inline JSContext * michael@0: xpc_GetSafeJSContext() michael@0: { michael@0: return XPCJSRuntime::Get()->GetJSContextStack()->GetSafeJSContext(); michael@0: } michael@0: michael@0: namespace xpc { michael@0: michael@0: // JSNatives to expose atob and btoa in various non-DOM XPConnect scopes. michael@0: bool michael@0: Atob(JSContext *cx, unsigned argc, jsval *vp); michael@0: michael@0: bool michael@0: Btoa(JSContext *cx, unsigned argc, jsval *vp); michael@0: michael@0: michael@0: // Helper function that creates a JSFunction that wraps a native function that michael@0: // forwards the call to the original 'callable'. If the 'doclone' argument is michael@0: // set, it also structure clones non-native arguments for extra security. michael@0: bool michael@0: NewFunctionForwarder(JSContext *cx, JS::HandleId id, JS::HandleObject callable, michael@0: bool doclone, JS::MutableHandleValue vp); michael@0: michael@0: bool michael@0: NewFunctionForwarder(JSContext *cx, JS::HandleObject callable, michael@0: bool doclone, JS::MutableHandleValue vp); michael@0: michael@0: // Old fashioned xpc error reporter. Try to use JS_ReportError instead. michael@0: nsresult michael@0: ThrowAndFail(nsresult errNum, JSContext *cx, bool *retval); michael@0: michael@0: struct GlobalProperties { michael@0: GlobalProperties(bool aPromise) { michael@0: mozilla::PodZero(this); michael@0: Promise = true; michael@0: } michael@0: bool Parse(JSContext *cx, JS::HandleObject obj); michael@0: bool Define(JSContext *cx, JS::HandleObject obj); michael@0: bool Promise : 1; michael@0: bool indexedDB : 1; michael@0: bool XMLHttpRequest : 1; michael@0: bool TextDecoder : 1; michael@0: bool TextEncoder : 1; michael@0: bool URL : 1; michael@0: bool atob : 1; michael@0: bool btoa : 1; michael@0: }; michael@0: michael@0: // Infallible. michael@0: already_AddRefed michael@0: NewSandboxConstructor(); michael@0: michael@0: // Returns true if class of 'obj' is SandboxClass. michael@0: bool michael@0: IsSandbox(JSObject *obj); michael@0: michael@0: class MOZ_STACK_CLASS OptionsBase { michael@0: public: michael@0: OptionsBase(JSContext *cx = xpc_GetSafeJSContext(), michael@0: JSObject *options = nullptr) michael@0: : mCx(cx) michael@0: , mObject(cx, options) michael@0: { } michael@0: michael@0: virtual bool Parse() = 0; michael@0: michael@0: protected: michael@0: bool ParseValue(const char *name, JS::MutableHandleValue prop, bool *found = nullptr); michael@0: bool ParseBoolean(const char *name, bool *prop); michael@0: bool ParseObject(const char *name, JS::MutableHandleObject prop); michael@0: bool ParseString(const char *name, nsCString &prop); michael@0: bool ParseString(const char *name, nsString &prop); michael@0: bool ParseId(const char* name, JS::MutableHandleId id); michael@0: michael@0: JSContext *mCx; michael@0: JS::RootedObject mObject; michael@0: }; michael@0: michael@0: class MOZ_STACK_CLASS SandboxOptions : public OptionsBase { michael@0: public: michael@0: SandboxOptions(JSContext *cx = xpc_GetSafeJSContext(), michael@0: JSObject *options = nullptr) michael@0: : OptionsBase(cx, options) michael@0: , wantXrays(true) michael@0: , wantComponents(true) michael@0: , wantExportHelpers(false) michael@0: , proto(cx) michael@0: , sameZoneAs(cx) michael@0: , invisibleToDebugger(false) michael@0: , discardSource(false) michael@0: , globalProperties(true) michael@0: , metadata(cx) michael@0: { } michael@0: michael@0: virtual bool Parse(); michael@0: michael@0: bool wantXrays; michael@0: bool wantComponents; michael@0: bool wantExportHelpers; michael@0: JS::RootedObject proto; michael@0: nsCString sandboxName; michael@0: JS::RootedObject sameZoneAs; michael@0: bool invisibleToDebugger; michael@0: bool discardSource; michael@0: GlobalProperties globalProperties; michael@0: JS::RootedValue metadata; michael@0: michael@0: protected: michael@0: bool ParseGlobalProperties(); michael@0: }; michael@0: michael@0: class MOZ_STACK_CLASS CreateObjectInOptions : public OptionsBase { michael@0: public: michael@0: CreateObjectInOptions(JSContext *cx = xpc_GetSafeJSContext(), michael@0: JSObject* options = nullptr) michael@0: : OptionsBase(cx, options) michael@0: , defineAs(cx, JSID_VOID) michael@0: { } michael@0: michael@0: virtual bool Parse() { return ParseId("defineAs", &defineAs); }; michael@0: michael@0: JS::RootedId defineAs; michael@0: }; michael@0: michael@0: class MOZ_STACK_CLASS ExportOptions : public OptionsBase { michael@0: public: michael@0: ExportOptions(JSContext *cx = xpc_GetSafeJSContext(), michael@0: JSObject* options = nullptr) michael@0: : OptionsBase(cx, options) michael@0: , defineAs(cx, JSID_VOID) michael@0: { } michael@0: michael@0: virtual bool Parse() { return ParseId("defineAs", &defineAs); }; michael@0: michael@0: JS::RootedId defineAs; michael@0: }; michael@0: michael@0: JSObject * michael@0: CreateGlobalObject(JSContext *cx, const JSClass *clasp, nsIPrincipal *principal, michael@0: JS::CompartmentOptions& aOptions); michael@0: michael@0: bool michael@0: InitGlobalObject(JSContext* aJSContext, JS::Handle aGlobal, michael@0: uint32_t aFlags); michael@0: michael@0: // Helper for creating a sandbox object to use for evaluating michael@0: // untrusted code completely separated from all other code in the michael@0: // system using EvalInSandbox(). Takes the JSContext on which to michael@0: // do setup etc on, puts the sandbox object in *vp (which must be michael@0: // rooted by the caller), and uses the principal that's either michael@0: // directly passed in prinOrSop or indirectly as an michael@0: // nsIScriptObjectPrincipal holding the principal. If no principal is michael@0: // reachable through prinOrSop, a new null principal will be created michael@0: // and used. michael@0: nsresult michael@0: CreateSandboxObject(JSContext *cx, JS::MutableHandleValue vp, nsISupports *prinOrSop, michael@0: xpc::SandboxOptions& options); michael@0: // Helper for evaluating scripts in a sandbox object created with michael@0: // CreateSandboxObject(). The caller is responsible of ensuring michael@0: // that *rval doesn't get collected during the call or usage after the michael@0: // call. This helper will use filename and lineNo for error reporting, michael@0: // and if no filename is provided it will use the codebase from the michael@0: // principal and line number 1 as a fallback. if returnStringOnly is michael@0: // true, then the result in *rval, or the exception in cx->exception michael@0: // will be coerced into strings. If an exception is thrown converting michael@0: // an exception to a string, evalInSandbox will return an NS_ERROR_* michael@0: // result, and cx->exception will be empty. michael@0: nsresult michael@0: EvalInSandbox(JSContext *cx, JS::HandleObject sandbox, const nsAString& source, michael@0: const nsACString& filename, int32_t lineNo, michael@0: JSVersion jsVersion, bool returnStringOnly, michael@0: JS::MutableHandleValue rval); michael@0: michael@0: // Helper for retrieving metadata stored in a reserved slot. The metadata michael@0: // is set during the sandbox creation using the "metadata" option. michael@0: nsresult michael@0: GetSandboxMetadata(JSContext *cx, JS::HandleObject sandboxArg, michael@0: JS::MutableHandleValue rval); michael@0: michael@0: nsresult michael@0: SetSandboxMetadata(JSContext *cx, JS::HandleObject sandboxArg, michael@0: JS::HandleValue metadata); michael@0: michael@0: bool michael@0: CreateObjectIn(JSContext *cx, JS::HandleValue vobj, CreateObjectInOptions &options, michael@0: JS::MutableHandleValue rval); michael@0: michael@0: bool michael@0: EvalInWindow(JSContext *cx, const nsAString &source, JS::HandleObject scope, michael@0: JS::MutableHandleValue rval); michael@0: michael@0: bool michael@0: ExportFunction(JSContext *cx, JS::HandleValue vscope, JS::HandleValue vfunction, michael@0: JS::HandleValue voptions, JS::MutableHandleValue rval); michael@0: michael@0: bool michael@0: CloneInto(JSContext *cx, JS::HandleValue vobj, JS::HandleValue vscope, michael@0: JS::HandleValue voptions, JS::MutableHandleValue rval); michael@0: michael@0: } /* namespace xpc */ michael@0: michael@0: michael@0: /***************************************************************************/ michael@0: // Inlined utilities. michael@0: michael@0: inline bool michael@0: xpc_ForcePropertyResolve(JSContext* cx, JS::HandleObject obj, jsid id); michael@0: michael@0: inline jsid michael@0: GetRTIdByIndex(JSContext *cx, unsigned index); michael@0: michael@0: namespace xpc { michael@0: michael@0: class CompartmentPrivate michael@0: { michael@0: public: michael@0: enum LocationHint { michael@0: LocationHintRegular, michael@0: LocationHintAddon michael@0: }; michael@0: michael@0: CompartmentPrivate(JSCompartment *c) michael@0: : wantXrays(false) michael@0: , universalXPConnectEnabled(false) michael@0: , adoptedNode(false) michael@0: , donatedNode(false) michael@0: , scriptability(c) michael@0: , scope(nullptr) michael@0: { michael@0: MOZ_COUNT_CTOR(xpc::CompartmentPrivate); michael@0: } michael@0: michael@0: ~CompartmentPrivate(); michael@0: michael@0: bool wantXrays; michael@0: michael@0: // This is only ever set during mochitest runs when enablePrivilege is called. michael@0: // It's intended as a temporary stopgap measure until we can finish ripping out michael@0: // enablePrivilege. Once set, this value is never unset (i.e., it doesn't follow michael@0: // the old scoping rules of enablePrivilege). Using it is inherently unsafe. michael@0: bool universalXPConnectEnabled; michael@0: michael@0: // for telemetry. See bug 928476. michael@0: bool adoptedNode; michael@0: bool donatedNode; michael@0: michael@0: // The scriptability of this compartment. michael@0: Scriptability scriptability; michael@0: michael@0: // Our XPCWrappedNativeScope. This is non-null if and only if this is an michael@0: // XPConnect compartment. michael@0: XPCWrappedNativeScope *scope; michael@0: michael@0: const nsACString& GetLocation() { michael@0: if (location.IsEmpty() && locationURI) { michael@0: if (NS_FAILED(locationURI->GetSpec(location))) michael@0: location = NS_LITERAL_CSTRING(""); michael@0: } michael@0: return location; michael@0: } michael@0: bool GetLocationURI(nsIURI **aURI) { michael@0: return GetLocationURI(LocationHintRegular, aURI); michael@0: } michael@0: bool GetLocationURI(LocationHint aLocationHint, nsIURI **aURI) { michael@0: if (locationURI) { michael@0: nsCOMPtr rval = locationURI; michael@0: rval.forget(aURI); michael@0: return true; michael@0: } michael@0: return TryParseLocationURI(aLocationHint, aURI); michael@0: } michael@0: void SetLocation(const nsACString& aLocation) { michael@0: if (aLocation.IsEmpty()) michael@0: return; michael@0: if (!location.IsEmpty() || locationURI) michael@0: return; michael@0: location = aLocation; michael@0: } michael@0: void SetLocationURI(nsIURI *aLocationURI) { michael@0: if (!aLocationURI) michael@0: return; michael@0: if (locationURI) michael@0: return; michael@0: locationURI = aLocationURI; michael@0: } michael@0: michael@0: private: michael@0: nsCString location; michael@0: nsCOMPtr locationURI; michael@0: michael@0: bool TryParseLocationURI(LocationHint aType, nsIURI** aURI); michael@0: }; michael@0: michael@0: CompartmentPrivate* michael@0: EnsureCompartmentPrivate(JSObject *obj); michael@0: michael@0: CompartmentPrivate* michael@0: EnsureCompartmentPrivate(JSCompartment *c); michael@0: michael@0: inline CompartmentPrivate* michael@0: GetCompartmentPrivate(JSCompartment *compartment) michael@0: { michael@0: MOZ_ASSERT(compartment); michael@0: void *priv = JS_GetCompartmentPrivate(compartment); michael@0: return static_cast(priv); michael@0: } michael@0: michael@0: inline CompartmentPrivate* michael@0: GetCompartmentPrivate(JSObject *object) michael@0: { michael@0: MOZ_ASSERT(object); michael@0: JSCompartment *compartment = js::GetObjectCompartment(object); michael@0: michael@0: MOZ_ASSERT(compartment); michael@0: return GetCompartmentPrivate(compartment); michael@0: } michael@0: michael@0: bool IsUniversalXPConnectEnabled(JSCompartment *compartment); michael@0: bool IsUniversalXPConnectEnabled(JSContext *cx); michael@0: bool EnableUniversalXPConnect(JSContext *cx); michael@0: michael@0: // This returns null if and only if it is called on an object in a non-XPConnect michael@0: // compartment. michael@0: inline XPCWrappedNativeScope* michael@0: GetObjectScope(JSObject *obj) michael@0: { michael@0: return EnsureCompartmentPrivate(obj)->scope; michael@0: } michael@0: michael@0: // This returns null if a scope doesn't already exist. michael@0: XPCWrappedNativeScope* MaybeGetObjectScope(JSObject *obj); michael@0: michael@0: extern bool gDebugMode; michael@0: extern bool gDesiredDebugMode; michael@0: michael@0: extern const JSClass SafeJSContextGlobalClass; michael@0: michael@0: JSObject* NewOutObject(JSContext* cx, JSObject* scope); michael@0: bool IsOutObject(JSContext* cx, JSObject* obj); michael@0: michael@0: nsresult HasInstance(JSContext *cx, JS::HandleObject objArg, const nsID *iid, bool *bp); michael@0: michael@0: /** michael@0: * Define quick stubs on the given object, @a proto. michael@0: * michael@0: * @param cx michael@0: * A context. Requires request. michael@0: * @param proto michael@0: * The (newly created) prototype object for a DOM class. The JS half michael@0: * of an XPCWrappedNativeProto. michael@0: * @param flags michael@0: * Property flags for the quick stub properties--should be either michael@0: * JSPROP_ENUMERATE or 0. michael@0: * @param interfaceCount michael@0: * The number of interfaces the class implements. michael@0: * @param interfaceArray michael@0: * The interfaces the class implements; interfaceArray and michael@0: * interfaceCount are like what nsIClassInfo.getInterfaces returns. michael@0: */ michael@0: bool michael@0: DOM_DefineQuickStubs(JSContext *cx, JSObject *proto, uint32_t flags, michael@0: uint32_t interfaceCount, const nsIID **interfaceArray); michael@0: michael@0: nsIPrincipal *GetObjectPrincipal(JSObject *obj); michael@0: michael@0: } // namespace xpc michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: extern bool michael@0: DefineStaticJSVals(JSContext *cx); michael@0: } // namespace dom michael@0: } // namespace mozilla michael@0: michael@0: bool michael@0: xpc_LocalizeRuntime(JSRuntime *rt); michael@0: void michael@0: xpc_DelocalizeRuntime(JSRuntime *rt); michael@0: michael@0: /***************************************************************************/ michael@0: // Inlines use the above - include last. michael@0: michael@0: #include "XPCInlines.h" michael@0: michael@0: /***************************************************************************/ michael@0: // Maps have inlines that use the above - include last. michael@0: michael@0: #include "XPCMaps.h" michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: #endif /* xpcprivate_h___ */