/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* vim: set ts=8 sts=4 et sw=4 tw=99: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * XPConnect allows JS code to manipulate C++ object and C++ code to manipulate * JS objects. JS manipulation of C++ objects tends to be significantly more * complex. This comment explains how it is orchestrated by XPConnect. * * For each C++ object to be manipulated in JS, there is a corresponding JS * object. This is called the "flattened JS object". By default, there is an * additional C++ object involved of type XPCWrappedNative. The XPCWrappedNative * holds pointers to the C++ object and the flat JS object. * * All XPCWrappedNative objects belong to an XPCWrappedNativeScope. These scopes * are essentially in 1:1 correspondence with JS global objects. The * XPCWrappedNativeScope has a pointer to the JS global object. The parent of a * flattened JS object is, by default, the global JS object corresponding to the * wrapper's XPCWrappedNativeScope (the exception to this rule is when a * PreCreate hook asks for a different parent; see nsIXPCScriptable below). * * Some C++ objects (notably DOM objects) have information associated with them * that lists the interfaces implemented by these objects. A C++ object exposes * this information by implementing nsIClassInfo. If a C++ object implements * nsIClassInfo, then JS code can call its methods without needing to use * QueryInterface first. Typically, all instances of a C++ class share the same * nsIClassInfo instance. (That is, obj->QueryInterface(nsIClassInfo) returns * the same result for every obj of a given class.) * * XPConnect tracks nsIClassInfo information in an XPCWrappedNativeProto object. * A given XPCWrappedNativeScope will have one XPCWrappedNativeProto for each * nsIClassInfo instance being used. The XPCWrappedNativeProto has an associated * JS object, which is used as the prototype of all flattened JS objects created * for C++ objects with the given nsIClassInfo. * * Each XPCWrappedNativeProto has a pointer to its XPCWrappedNativeScope. If an * XPCWrappedNative wraps a C++ object with class info, then it points to its * XPCWrappedNativeProto. Otherwise it points to its XPCWrappedNativeScope. (The * pointers are smooshed together in a tagged union.) Either way it can reach * its scope. * * An XPCWrappedNativeProto keeps track of the set of interfaces implemented by * the C++ object in an XPCNativeSet. (The list of interfaces is obtained by * calling a method on the nsIClassInfo.) An XPCNativeSet is a collection of * XPCNativeInterfaces. Each interface stores the list of members, which can be * methods, constants, getters, or setters. * * An XPCWrappedNative also points to an XPCNativeSet. Initially this starts out * the same as the XPCWrappedNativeProto's set. If there is no proto, it starts * out as a singleton set containing nsISupports. If JS code QI's new interfaces * outside of the existing set, the set will grow. All QueryInterface results * are cached in XPCWrappedNativeTearOff objects, which are linked off of the * XPCWrappedNative. * * Besides having class info, a C++ object may be "scriptable" (i.e., implement * nsIXPCScriptable). This allows it to implement a more DOM-like interface, * besides just exposing XPCOM methods and constants. An nsIXPCScriptable * instance has hooks that correspond to all the normal JSClass hooks. Each * nsIXPCScriptable instance is mirrored by an XPCNativeScriptableInfo in * XPConnect. These can have pointers from XPCWrappedNativeProto and * XPCWrappedNative (since C++ objects can have scriptable info without having * class info). * * Most data in an XPCNativeScriptableInfo is shared between instances. The * shared data is stored in an XPCNativeScriptableShared object. This type is * important because it holds the JSClass of the flattened JS objects with the * given scriptable info. */ /* All the XPConnect private declarations - only include locally. */ #ifndef xpcprivate_h___ #define xpcprivate_h___ #include "mozilla/Alignment.h" #include "mozilla/ArrayUtils.h" #include "mozilla/Assertions.h" #include "mozilla/Atomics.h" #include "mozilla/Attributes.h" #include "mozilla/CycleCollectedJSRuntime.h" #include "mozilla/GuardObjects.h" #include "mozilla/Maybe.h" #include "mozilla/MemoryReporting.h" #include "mozilla/Preferences.h" #include "mozilla/TimeStamp.h" #include "mozilla/dom/ScriptSettings.h" #include #include #include #include #include "xpcpublic.h" #include "js/TracingAPI.h" #include "js/WeakMapPtr.h" #include "PLDHashTable.h" #include "nscore.h" #include "nsXPCOM.h" #include "nsAutoPtr.h" #include "nsCycleCollectionParticipant.h" #include "nsDebug.h" #include "nsISupports.h" #include "nsIServiceManager.h" #include "nsIClassInfoImpl.h" #include "nsIComponentManager.h" #include "nsIComponentRegistrar.h" #include "nsISupportsPrimitives.h" #include "nsMemory.h" #include "nsIXPConnect.h" #include "nsIInterfaceInfo.h" #include "nsIXPCScriptable.h" #include "nsIObserver.h" #include "nsWeakReference.h" #include "nsCOMPtr.h" #include "nsXPTCUtils.h" #include "xptinfo.h" #include "XPCForwards.h" #include "XPCLog.h" #include "xpccomponents.h" #include "xpcexception.h" #include "xpcjsid.h" #include "prenv.h" #include "prclist.h" #include "prcvar.h" #include "nsString.h" #include "nsReadableUtils.h" #include "nsXPIDLString.h" #include "MainThreadUtils.h" #include "nsIConsoleService.h" #include "nsIScriptError.h" #include "nsIException.h" #include "nsVariant.h" #include "nsIPropertyBag.h" #include "nsIProperty.h" #include "nsCOMArray.h" #include "nsTArray.h" #include "nsBaseHashtable.h" #include "nsHashKeys.h" #include "nsWrapperCache.h" #include "nsStringBuffer.h" #include "nsDataHashtable.h" #include "nsDeque.h" #include "nsIScriptSecurityManager.h" #include "nsIPrincipal.h" #include "nsJSPrincipals.h" #include "nsIScriptObjectPrincipal.h" #include "xpcObjectHelper.h" #include "SandboxPrivate.h" #include "BackstagePass.h" #include "nsAXPCNativeCallContext.h" #ifdef XP_WIN // Nasty MS defines #ifdef GetClassInfo #undef GetClassInfo #endif #ifdef GetClassName #undef GetClassName #endif #endif /* XP_WIN */ /***************************************************************************/ // default initial sizes for maps (hashtables) #define XPC_CONTEXT_MAP_LENGTH 8 #define XPC_JS_MAP_LENGTH 32 #define XPC_JS_CLASS_MAP_LENGTH 32 #define XPC_NATIVE_MAP_LENGTH 8 #define XPC_NATIVE_PROTO_MAP_LENGTH 8 #define XPC_DYING_NATIVE_PROTO_MAP_LENGTH 8 #define XPC_DETACHED_NATIVE_PROTO_MAP_LENGTH 16 #define XPC_NATIVE_INTERFACE_MAP_LENGTH 32 #define XPC_NATIVE_SET_MAP_LENGTH 32 #define XPC_NATIVE_JSCLASS_MAP_LENGTH 16 #define XPC_THIS_TRANSLATOR_MAP_LENGTH 4 #define XPC_NATIVE_WRAPPER_MAP_LENGTH 8 #define XPC_WRAPPER_MAP_LENGTH 8 /***************************************************************************/ // data declarations... extern const char XPC_CONTEXT_STACK_CONTRACTID[]; extern const char XPC_EXCEPTION_CONTRACTID[]; extern const char XPC_CONSOLE_CONTRACTID[]; extern const char XPC_SCRIPT_ERROR_CONTRACTID[]; extern const char XPC_ID_CONTRACTID[]; extern const char XPC_XPCONNECT_CONTRACTID[]; /***************************************************************************/ // Useful macros... #define XPC_STRING_GETTER_BODY(dest, src) \ NS_ENSURE_ARG_POINTER(dest); \ char* result; \ if (src) \ result = (char*) nsMemory::Clone(src, \ sizeof(char)*(strlen(src)+1)); \ else \ result = nullptr; \ *dest = result; \ return (result || !src) ? NS_OK : NS_ERROR_OUT_OF_MEMORY #define WRAPPER_FLAGS JSCLASS_HAS_PRIVATE #define INVALID_OBJECT ((JSObject*)1) // If IS_WN_CLASS for the JSClass of an object is true, the object is a // wrappednative wrapper, holding the XPCWrappedNative in its private slot. static inline bool IS_WN_CLASS(const js::Class* clazz) { return clazz->ext.isWrappedNative; } static inline bool IS_WN_REFLECTOR(JSObject* obj) { return IS_WN_CLASS(js::GetObjectClass(obj)); } /*************************************************************************** **************************************************************************** * * Core runtime and context classes... * **************************************************************************** ***************************************************************************/ // We have a general rule internally that getters that return addref'd interface // pointer generally do so using an 'out' parm. When interface pointers are // returned as function call result values they are not addref'd. Exceptions // to this rule are noted explicitly. class nsXPConnect final : public nsIXPConnect { public: // all the interface method declarations... NS_DECL_ISUPPORTS NS_DECL_NSIXPCONNECT // non-interface implementation public: // These get non-addref'd pointers static nsXPConnect* XPConnect() { // Do a release-mode assert that we're not doing anything significant in // XPConnect off the main thread. If you're an extension developer hitting // this, you need to change your code. See bug 716167. if (!MOZ_LIKELY(NS_IsMainThread())) MOZ_CRASH(); return gSelf; } static XPCJSRuntime* GetRuntimeInstance(); XPCJSRuntime* GetRuntime() {return mRuntime;} static bool IsISupportsDescendant(nsIInterfaceInfo* info); static nsIScriptSecurityManager* SecurityManager() { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(gScriptSecurityManager); return gScriptSecurityManager; } static nsIPrincipal* SystemPrincipal() { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(gSystemPrincipal); return gSystemPrincipal; } // This returns an AddRef'd pointer. It does not do this with an 'out' param // only because this form is required by the generic module macro: // NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR static nsXPConnect* GetSingleton(); // Called by module code in dll startup static void InitStatics(); // Called by module code on dll shutdown. static void ReleaseXPConnectSingleton(); bool IsShuttingDown() const {return mShuttingDown;} nsresult GetInfoForIID(const nsIID * aIID, nsIInterfaceInfo** info); nsresult GetInfoForName(const char * name, nsIInterfaceInfo** info); virtual nsIPrincipal* GetPrincipal(JSObject* obj, bool allowShortCircuit) const override; void RecordTraversal(void* p, nsISupports* s); virtual char* DebugPrintJSStack(bool showArgs, bool showLocals, bool showThisProps) override; static bool ReportAllJSExceptions() { return gReportAllJSExceptions > 0; } protected: virtual ~nsXPConnect(); nsXPConnect(); private: // Singleton instance static nsXPConnect* gSelf; static bool gOnceAliveNowDead; XPCJSRuntime* mRuntime; bool mShuttingDown; static uint32_t gReportAllJSExceptions; public: static nsIScriptSecurityManager* gScriptSecurityManager; static nsIPrincipal* gSystemPrincipal; }; /***************************************************************************/ class XPCRootSetElem { public: XPCRootSetElem() { #ifdef DEBUG mNext = nullptr; mSelfp = nullptr; #endif } ~XPCRootSetElem() { MOZ_ASSERT(!mNext, "Must be unlinked"); MOZ_ASSERT(!mSelfp, "Must be unlinked"); } inline XPCRootSetElem* GetNextRoot() { return mNext; } void AddToRootSet(XPCRootSetElem** listHead); void RemoveFromRootSet(); private: XPCRootSetElem* mNext; XPCRootSetElem** mSelfp; }; /***************************************************************************/ // In the current xpconnect system there can only be one XPCJSRuntime. // So, xpconnect can only be used on one JSRuntime within the process. class XPCJSContextStack; class WatchdogManager; enum WatchdogTimestampCategory { TimestampRuntimeStateChange = 0, TimestampWatchdogWakeup, TimestampWatchdogHibernateStart, TimestampWatchdogHibernateStop, TimestampCount }; class AsyncFreeSnowWhite; template class ShortLivedStringBuffer { public: StringType* Create() { for (uint32_t i = 0; i < ArrayLength(mStrings); ++i) { if (!mStrings[i]) { mStrings[i].emplace(); return mStrings[i].ptr(); } } // All our internal string wrappers are used, allocate a new string. return new StringType(); } void Destroy(StringType* string) { for (uint32_t i = 0; i < ArrayLength(mStrings); ++i) { if (mStrings[i] && mStrings[i].ptr() == string) { // One of our internal strings is no longer in use, mark // it as such and free its data. mStrings[i].reset(); return; } } // We're done with a string that's not one of our internal // strings, delete it. delete string; } ~ShortLivedStringBuffer() { #ifdef DEBUG for (uint32_t i = 0; i < ArrayLength(mStrings); ++i) { MOZ_ASSERT(!mStrings[i], "Short lived string still in use"); } #endif } private: mozilla::Maybe mStrings[2]; }; class XPCJSRuntime : public mozilla::CycleCollectedJSRuntime { public: static XPCJSRuntime* newXPCJSRuntime(nsXPConnect* aXPConnect); static XPCJSRuntime* Get() { return nsXPConnect::XPConnect()->GetRuntime(); } XPCJSContextStack* GetJSContextStack() {return mJSContextStack;} void DestroyJSContextStack(); void RemoveWrappedJS(nsXPCWrappedJS* wrapper); void AssertInvalidWrappedJSNotInTable(nsXPCWrappedJS* wrapper) const; XPCCallContext* GetCallContext() const {return mCallContext;} XPCCallContext* SetCallContext(XPCCallContext* ccx) {XPCCallContext* old = mCallContext; mCallContext = ccx; return old;} jsid GetResolveName() const {return mResolveName;} jsid SetResolveName(jsid name) {jsid old = mResolveName; mResolveName = name; return old;} XPCWrappedNative* GetResolvingWrapper() const {return mResolvingWrapper;} XPCWrappedNative* SetResolvingWrapper(XPCWrappedNative* w) {XPCWrappedNative* old = mResolvingWrapper; mResolvingWrapper = w; return old;} JSObject2WrappedJSMap* GetMultiCompartmentWrappedJSMap() const {return mWrappedJSMap;} IID2WrappedJSClassMap* GetWrappedJSClassMap() const {return mWrappedJSClassMap;} IID2NativeInterfaceMap* GetIID2NativeInterfaceMap() const {return mIID2NativeInterfaceMap;} ClassInfo2NativeSetMap* GetClassInfo2NativeSetMap() const {return mClassInfo2NativeSetMap;} NativeSetMap* GetNativeSetMap() const {return mNativeSetMap;} IID2ThisTranslatorMap* GetThisTranslatorMap() const {return mThisTranslatorMap;} XPCNativeScriptableSharedMap* GetNativeScriptableSharedMap() const {return mNativeScriptableSharedMap;} XPCWrappedNativeProtoMap* GetDyingWrappedNativeProtoMap() const {return mDyingWrappedNativeProtoMap;} XPCWrappedNativeProtoMap* GetDetachedWrappedNativeProtoMap() const {return mDetachedWrappedNativeProtoMap;} bool OnJSContextNew(JSContext* cx); virtual bool DescribeCustomObjects(JSObject* aObject, const js::Class* aClasp, char (&aName)[72]) const override; virtual bool NoteCustomGCThingXPCOMChildren(const js::Class* aClasp, JSObject* aObj, nsCycleCollectionTraversalCallback& aCb) const override; virtual void BeforeProcessTask(bool aMightBlock) override; virtual void AfterProcessTask(uint32_t aNewRecursionDepth) override; /** * Infrastructure for classes that need to defer part of the finalization * until after the GC has run, for example for objects that we don't want to * destroy during the GC. */ public: bool GetDoingFinalization() const {return mDoingFinalization;} // Mapping of often used strings to jsid atoms that live 'forever'. // // To add a new string: add to this list and to XPCJSRuntime::mStrings // at the top of XPCJSRuntime.cpp enum { IDX_CONSTRUCTOR = 0 , IDX_TO_STRING , IDX_TO_SOURCE , IDX_LAST_RESULT , IDX_RETURN_CODE , IDX_VALUE , IDX_QUERY_INTERFACE , IDX_COMPONENTS , IDX_WRAPPED_JSOBJECT , IDX_OBJECT , IDX_FUNCTION , IDX_PROTOTYPE , IDX_CREATE_INSTANCE , IDX_ITEM , IDX_PROTO , IDX_ITERATOR , IDX_EXPOSEDPROPS , IDX_EVAL , IDX_CONTROLLERS , IDX_REALFRAMEELEMENT , IDX_LENGTH , IDX_NAME , IDX_UNDEFINED , IDX_EMPTYSTRING , IDX_FILENAME , IDX_LINENUMBER , IDX_COLUMNNUMBER , IDX_STACK , IDX_MESSAGE , IDX_LASTINDEX , IDX_TOTAL_COUNT // just a count of the above }; JS::HandleId GetStringID(unsigned index) const { MOZ_ASSERT(index < IDX_TOTAL_COUNT, "index out of range"); // fromMarkedLocation() is safe because the string is interned. return JS::HandleId::fromMarkedLocation(&mStrIDs[index]); } JS::HandleValue GetStringJSVal(unsigned index) const { MOZ_ASSERT(index < IDX_TOTAL_COUNT, "index out of range"); // fromMarkedLocation() is safe because the string is interned. return JS::HandleValue::fromMarkedLocation(&mStrJSVals[index]); } const char* GetStringName(unsigned index) const { MOZ_ASSERT(index < IDX_TOTAL_COUNT, "index out of range"); return mStrings[index]; } void TraceNativeBlackRoots(JSTracer* trc) override; void TraceAdditionalNativeGrayRoots(JSTracer* aTracer) override; void TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback& cb) override; void UnmarkSkippableJSHolders(); void PrepareForForgetSkippable() override; void BeginCycleCollectionCallback() override; void EndCycleCollectionCallback(mozilla::CycleCollectorResults& aResults) override; void DispatchDeferredDeletion(bool aContinuation, bool aPurge = false) override; void CustomGCCallback(JSGCStatus status) override; void CustomOutOfMemoryCallback() override; void CustomLargeAllocationFailureCallback() override; bool CustomContextCallback(JSContext* cx, unsigned operation) override; static void GCSliceCallback(JSRuntime* rt, JS::GCProgress progress, const JS::GCDescription& desc); static void FinalizeCallback(JSFreeOp* fop, JSFinalizeStatus status, bool isCompartmentGC, void* data); static void WeakPointerZoneGroupCallback(JSRuntime* rt, void* data); static void WeakPointerCompartmentCallback(JSRuntime* rt, JSCompartment* comp, void* data); inline void AddVariantRoot(XPCTraceableVariant* variant); inline void AddWrappedJSRoot(nsXPCWrappedJS* wrappedJS); inline void AddObjectHolderRoot(XPCJSObjectHolder* holder); void DebugDump(int16_t depth); void SystemIsBeingShutDown(); bool GCIsRunning() const {return mGCIsRunning;} ~XPCJSRuntime(); ShortLivedStringBuffer mScratchStrings; ShortLivedStringBuffer mScratchCStrings; void AddGCCallback(xpcGCCallback cb); void RemoveGCCallback(xpcGCCallback cb); struct EnvironmentPreparer : public js::ScriptEnvironmentPreparer { void invoke(JS::HandleObject scope, Closure& closure) override; }; EnvironmentPreparer mEnvironmentPreparer; static void ActivityCallback(void* arg, bool active); static bool InterruptCallback(JSContext* cx); size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf); AutoMarkingPtr** GetAutoRootsAdr() {return &mAutoRoots;} JSObject* UnprivilegedJunkScope() { return mUnprivilegedJunkScope; } JSObject* PrivilegedJunkScope() { return mPrivilegedJunkScope; } JSObject* CompilationScope() { return mCompilationScope; } void InitSingletonScopes(); void DeleteSingletonScopes(); PRTime GetWatchdogTimestamp(WatchdogTimestampCategory aCategory); private: XPCJSRuntime() = delete; explicit XPCJSRuntime(nsXPConnect* aXPConnect); void ReleaseIncrementally(nsTArray& array); static const char* const mStrings[IDX_TOTAL_COUNT]; jsid mStrIDs[IDX_TOTAL_COUNT]; JS::Value mStrJSVals[IDX_TOTAL_COUNT]; XPCJSContextStack* mJSContextStack; XPCCallContext* mCallContext; AutoMarkingPtr* mAutoRoots; jsid mResolveName; XPCWrappedNative* mResolvingWrapper; JSObject2WrappedJSMap* mWrappedJSMap; IID2WrappedJSClassMap* mWrappedJSClassMap; IID2NativeInterfaceMap* mIID2NativeInterfaceMap; ClassInfo2NativeSetMap* mClassInfo2NativeSetMap; NativeSetMap* mNativeSetMap; IID2ThisTranslatorMap* mThisTranslatorMap; XPCNativeScriptableSharedMap* mNativeScriptableSharedMap; XPCWrappedNativeProtoMap* mDyingWrappedNativeProtoMap; XPCWrappedNativeProtoMap* mDetachedWrappedNativeProtoMap; bool mGCIsRunning; nsTArray mNativesToReleaseArray; bool mDoingFinalization; XPCRootSetElem* mVariantRoots; XPCRootSetElem* mWrappedJSRoots; XPCRootSetElem* mObjectHolderRoots; nsTArray extraGCCallbacks; RefPtr mWatchdogManager; JS::GCSliceCallback mPrevGCSliceCallback; JS::PersistentRootedObject mUnprivilegedJunkScope; JS::PersistentRootedObject mPrivilegedJunkScope; JS::PersistentRootedObject mCompilationScope; RefPtr mAsyncSnowWhiteFreer; // If we spend too much time running JS code in an event handler, then we // want to show the slow script UI. The timeout T is controlled by prefs. We // invoke the interrupt callback once after T/2 seconds and set // mSlowScriptSecondHalf to true. After another T/2 seconds, we invoke the // interrupt callback again. Since mSlowScriptSecondHalf is now true, it // shows the slow script UI. The reason we invoke the callback twice is to // ensure that putting the computer to sleep while running a script doesn't // cause the UI to be shown. If the laptop goes to sleep during one of the // timeout periods, the script still has the other T/2 seconds to complete // before the slow script UI is shown. bool mSlowScriptSecondHalf; // mSlowScriptCheckpoint is set to the time when: // 1. We started processing the current event, or // 2. mSlowScriptSecondHalf was set to true // (whichever comes later). We use it to determine whether the interrupt // callback needs to do anything. mozilla::TimeStamp mSlowScriptCheckpoint; friend class Watchdog; friend class AutoLockWatchdog; friend class XPCIncrementalReleaseRunnable; }; /***************************************************************************/ /***************************************************************************/ // XPCContext is mostly a dumb class to hold JSContext specific data and // maps that let us find wrappers created for the given JSContext. // no virtuals class XPCContext { friend class XPCJSRuntime; public: static XPCContext* GetXPCContext(JSContext* aJSContext) { MOZ_ASSERT(JS_GetSecondContextPrivate(aJSContext), "should already have XPCContext"); return static_cast(JS_GetSecondContextPrivate(aJSContext)); } XPCJSRuntime* GetRuntime() const {return mRuntime;} JSContext* GetJSContext() const {return mJSContext;} enum LangType {LANG_UNKNOWN, LANG_JS, LANG_NATIVE}; LangType GetCallingLangType() const { return mCallingLangType; } LangType SetCallingLangType(LangType lt) { LangType tmp = mCallingLangType; mCallingLangType = lt; return tmp; } bool CallerTypeIsJavaScript() const { return LANG_JS == mCallingLangType; } bool CallerTypeIsNative() const { return LANG_NATIVE == mCallingLangType; } bool CallerTypeIsKnown() const { return LANG_UNKNOWN != mCallingLangType; } nsresult GetException(nsIException** e) { nsCOMPtr rval = mException; rval.forget(e); return NS_OK; } void SetException(nsIException* e) { mException = e; } nsresult GetLastResult() {return mLastResult;} void SetLastResult(nsresult rc) {mLastResult = rc;} nsresult GetPendingResult() {return mPendingResult;} void SetPendingResult(nsresult rc) {mPendingResult = rc;} void DebugDump(int16_t depth); ~XPCContext(); private: XPCContext(); // no implementation XPCContext(XPCJSRuntime* aRuntime, JSContext* aJSContext); static XPCContext* newXPCContext(XPCJSRuntime* aRuntime, JSContext* aJSContext); private: XPCJSRuntime* mRuntime; JSContext* mJSContext; nsresult mLastResult; nsresult mPendingResult; nsCOMPtr mException; LangType mCallingLangType; bool mErrorUnreported; }; /***************************************************************************/ #define NATIVE_CALLER XPCContext::LANG_NATIVE #define JS_CALLER XPCContext::LANG_JS // No virtuals // XPCCallContext is ALWAYS declared as a local variable in some function; // i.e. instance lifetime is always controled by some C++ function returning. // // These things are created frequently in many places. We *intentionally* do // not inialialize all members in order to save on construction overhead. // Some constructor pass more valid params than others. We init what must be // init'd and leave other members undefined. In debug builds the accessors // use a CHECK_STATE macro to track whether or not the object is in a valid // state to answer the question a caller might be asking. As long as this // class is maintained correctly it can do its job without a bunch of added // overhead from useless initializations and non-DEBUG error checking. // // Note that most accessors are inlined. class MOZ_STACK_CLASS XPCCallContext : public nsAXPCNativeCallContext { public: NS_IMETHOD GetCallee(nsISupports** aResult); NS_IMETHOD GetCalleeMethodIndex(uint16_t* aResult); NS_IMETHOD GetJSContext(JSContext** aResult); NS_IMETHOD GetArgc(uint32_t* aResult); NS_IMETHOD GetArgvPtr(JS::Value** aResult); NS_IMETHOD GetCalleeInterface(nsIInterfaceInfo** aResult); NS_IMETHOD GetCalleeClassInfo(nsIClassInfo** aResult); NS_IMETHOD GetPreviousCallContext(nsAXPCNativeCallContext** aResult); NS_IMETHOD GetLanguage(uint16_t* aResult); enum {NO_ARGS = (unsigned) -1}; static JSContext* GetDefaultJSContext(); XPCCallContext(XPCContext::LangType callerLanguage, JSContext* cx, JS::HandleObject obj = nullptr, JS::HandleObject funobj = nullptr, JS::HandleId id = JSID_VOIDHANDLE, unsigned argc = NO_ARGS, JS::Value* argv = nullptr, JS::Value* rval = nullptr); virtual ~XPCCallContext(); inline bool IsValid() const ; inline XPCJSRuntime* GetRuntime() const ; inline XPCContext* GetXPCContext() const ; inline JSContext* GetJSContext() const ; inline bool GetContextPopRequired() const ; inline XPCContext::LangType GetCallerLanguage() const ; inline XPCContext::LangType GetPrevCallerLanguage() const ; inline XPCCallContext* GetPrevCallContext() const ; inline JSObject* GetFlattenedJSObject() const ; inline nsISupports* GetIdentityObject() const ; inline XPCWrappedNative* GetWrapper() const ; inline XPCWrappedNativeProto* GetProto() const ; inline bool CanGetTearOff() const ; inline XPCWrappedNativeTearOff* GetTearOff() const ; inline XPCNativeScriptableInfo* GetScriptableInfo() const ; inline bool CanGetSet() const ; inline XPCNativeSet* GetSet() const ; inline bool CanGetInterface() const ; inline XPCNativeInterface* GetInterface() const ; inline XPCNativeMember* GetMember() const ; inline bool HasInterfaceAndMember() const ; inline jsid GetName() const ; inline bool GetStaticMemberIsLocal() const ; inline unsigned GetArgc() const ; inline JS::Value* GetArgv() const ; inline JS::Value* GetRetVal() const ; inline uint16_t GetMethodIndex() const ; inline void SetMethodIndex(uint16_t index) ; inline jsid GetResolveName() const; inline jsid SetResolveName(JS::HandleId name); inline XPCWrappedNative* GetResolvingWrapper() const; inline XPCWrappedNative* SetResolvingWrapper(XPCWrappedNative* w); inline void SetRetVal(JS::Value val); void SetName(jsid name); void SetArgsAndResultPtr(unsigned argc, JS::Value* argv, JS::Value* rval); void SetCallInfo(XPCNativeInterface* iface, XPCNativeMember* member, bool isSetter); nsresult CanCallNow(); void SystemIsBeingShutDown(); operator JSContext*() const {return GetJSContext();} private: // no copy ctor or assignment allowed XPCCallContext(const XPCCallContext& r); // not implemented XPCCallContext& operator= (const XPCCallContext& r); // not implemented private: // posible values for mState enum State { INIT_FAILED, SYSTEM_SHUTDOWN, HAVE_CONTEXT, HAVE_OBJECT, HAVE_NAME, HAVE_ARGS, READY_TO_CALL, CALL_DONE }; #ifdef DEBUG inline void CHECK_STATE(int s) const {MOZ_ASSERT(mState >= s, "bad state");} #else #define CHECK_STATE(s) ((void)0) #endif private: JSAutoRequest mAr; State mState; RefPtr mXPC; XPCContext* mXPCContext; JSContext* mJSContext; XPCContext::LangType mCallerLanguage; // ctor does not necessarily init the following. BEWARE! XPCContext::LangType mPrevCallerLanguage; XPCCallContext* mPrevCallContext; XPCWrappedNative* mWrapper; XPCWrappedNativeTearOff* mTearOff; XPCNativeScriptableInfo* mScriptableInfo; XPCNativeSet* mSet; XPCNativeInterface* mInterface; XPCNativeMember* mMember; JS::RootedId mName; bool mStaticMemberIsLocal; unsigned mArgc; JS::Value* mArgv; JS::Value* mRetVal; uint16_t mMethodIndex; }; /*************************************************************************** **************************************************************************** * * Core classes for wrapped native objects for use from JavaScript... * **************************************************************************** ***************************************************************************/ // These are the various JSClasses and callbacks whose use that required // visibility from more than one .cpp file. struct XPCWrappedNativeJSClass; extern const XPCWrappedNativeJSClass XPC_WN_NoHelper_JSClass; extern const js::Class XPC_WN_NoMods_WithCall_Proto_JSClass; extern const js::Class XPC_WN_NoMods_NoCall_Proto_JSClass; extern const js::Class XPC_WN_ModsAllowed_WithCall_Proto_JSClass; extern const js::Class XPC_WN_ModsAllowed_NoCall_Proto_JSClass; extern const js::Class XPC_WN_Tearoff_JSClass; #define XPC_WN_TEAROFF_RESERVED_SLOTS 1 #define XPC_WN_TEAROFF_FLAT_OBJECT_SLOT 0 extern const js::Class XPC_WN_NoHelper_Proto_JSClass; extern bool XPC_WN_CallMethod(JSContext* cx, unsigned argc, JS::Value* vp); extern bool XPC_WN_GetterSetter(JSContext* cx, unsigned argc, JS::Value* vp); // Macros to initialize Object or Function like XPC_WN classes #define XPC_WN_WithCall_ObjectOps \ { \ nullptr, /* lookupProperty */ \ nullptr, /* defineProperty */ \ nullptr, /* hasProperty */ \ nullptr, /* getProperty */ \ nullptr, /* setProperty */ \ nullptr, /* getOwnPropertyDescriptor */ \ nullptr, /* deleteProperty */ \ nullptr, nullptr, /* watch/unwatch */ \ nullptr, /* getElements */ \ nullptr, /* enumerate */ \ } #define XPC_WN_NoCall_ObjectOps \ { \ nullptr, /* lookupProperty */ \ nullptr, /* defineProperty */ \ nullptr, /* hasProperty */ \ nullptr, /* getProperty */ \ nullptr, /* setProperty */ \ nullptr, /* getOwnPropertyDescriptor */ \ nullptr, /* deleteProperty */ \ nullptr, nullptr, /* watch/unwatch */ \ nullptr, /* getElements */ \ nullptr, /* enumerate */ \ } // Maybe this macro should check for class->enumerate == // XPC_WN_Shared_Proto_Enumerate or something rather than checking for // 4 classes? static inline bool IS_PROTO_CLASS(const js::Class* clazz) { return clazz == &XPC_WN_NoMods_WithCall_Proto_JSClass || clazz == &XPC_WN_NoMods_NoCall_Proto_JSClass || clazz == &XPC_WN_ModsAllowed_WithCall_Proto_JSClass || clazz == &XPC_WN_ModsAllowed_NoCall_Proto_JSClass; } typedef js::HashSet, js::SystemAllocPolicy> InterpositionWhitelist; struct InterpositionWhitelistPair { nsIAddonInterposition* interposition; InterpositionWhitelist whitelist; }; typedef nsTArray InterpositionWhitelistArray; /***************************************************************************/ // XPCWrappedNativeScope is one-to-one with a JS global object. class nsIAddonInterposition; class nsXPCComponentsBase; class XPCWrappedNativeScope : public PRCList { public: XPCJSRuntime* GetRuntime() const {return XPCJSRuntime::Get();} Native2WrappedNativeMap* GetWrappedNativeMap() const {return mWrappedNativeMap;} ClassInfo2WrappedNativeProtoMap* GetWrappedNativeProtoMap() const {return mWrappedNativeProtoMap;} nsXPCComponentsBase* GetComponents() const {return mComponents;} // Forces the creation of a privileged |Components| object, even in // content scopes. This will crash if used outside of automation. void ForcePrivilegedComponents(); bool AttachComponentsObject(JSContext* aCx); // Returns the JS object reflection of the Components object. bool GetComponentsJSObject(JS::MutableHandleObject obj); JSObject* GetGlobalJSObject() const { JS::ExposeObjectToActiveJS(mGlobalJSObject); return mGlobalJSObject; } JSObject* GetGlobalJSObjectPreserveColor() const {return mGlobalJSObject;} nsIPrincipal* GetPrincipal() const { JSCompartment* c = js::GetObjectCompartment(mGlobalJSObject); return nsJSPrincipals::get(JS_GetCompartmentPrincipals(c)); } JSObject* GetExpandoChain(JS::HandleObject target); bool SetExpandoChain(JSContext* cx, JS::HandleObject target, JS::HandleObject chain); static void SystemIsBeingShutDown(); static void TraceWrappedNativesInAllScopes(JSTracer* trc, XPCJSRuntime* rt); void TraceSelf(JSTracer* trc) { MOZ_ASSERT(mGlobalJSObject); mGlobalJSObject.trace(trc, "XPCWrappedNativeScope::mGlobalJSObject"); } void TraceInside(JSTracer* trc) { if (mContentXBLScope) mContentXBLScope.trace(trc, "XPCWrappedNativeScope::mXBLScope"); for (size_t i = 0; i < mAddonScopes.Length(); i++) mAddonScopes[i].trace(trc, "XPCWrappedNativeScope::mAddonScopes"); if (mXrayExpandos.initialized()) mXrayExpandos.trace(trc); } static void SuspectAllWrappers(XPCJSRuntime* rt, nsCycleCollectionNoteRootCallback& cb); static void MarkAllWrappedNativesAndProtos(); #ifdef DEBUG static void ASSERT_NoInterfaceSetsAreMarked(); #endif static void SweepAllWrappedNativeTearOffs(); static void UpdateWeakPointersAfterGC(XPCJSRuntime* rt); static void KillDyingScopes(); static void DebugDumpAllScopes(int16_t depth); void DebugDump(int16_t depth); struct ScopeSizeInfo { explicit ScopeSizeInfo(mozilla::MallocSizeOf mallocSizeOf) : mMallocSizeOf(mallocSizeOf), mScopeAndMapSize(0), mProtoAndIfaceCacheSize(0) {} mozilla::MallocSizeOf mMallocSizeOf; size_t mScopeAndMapSize; size_t mProtoAndIfaceCacheSize; }; static void AddSizeOfAllScopesIncludingThis(ScopeSizeInfo* scopeSizeInfo); void AddSizeOfIncludingThis(ScopeSizeInfo* scopeSizeInfo); bool IsValid() const {return mRuntime != nullptr;} static bool IsDyingScope(XPCWrappedNativeScope* scope); typedef js::HashSet, js::MovableCellHasher>, js::SystemAllocPolicy> DOMExpandoSet; bool RegisterDOMExpandoObject(JSObject* expando) { // Expandos are proxy objects, and proxies are always tenured. JS::AssertGCThingMustBeTenured(expando); if (!mDOMExpandoSet) { mDOMExpandoSet = new DOMExpandoSet(); mDOMExpandoSet->init(8); } return mDOMExpandoSet->put(JS::Heap(expando)); } void RemoveDOMExpandoObject(JSObject* expando) { if (mDOMExpandoSet) { DOMExpandoSet::Ptr p = mDOMExpandoSet->lookup(JS::Heap(expando)); MOZ_ASSERT(p.found()); mDOMExpandoSet->remove(p); } } typedef js::HashMap, js::PointerHasher, js::SystemAllocPolicy> InterpositionMap; // Gets the appropriate scope object for XBL in this scope. The context // must be same-compartment with the global upon entering, and the scope // object is wrapped into the compartment of the global. JSObject* EnsureContentXBLScope(JSContext* cx); JSObject* EnsureAddonScope(JSContext* cx, JSAddonId* addonId); XPCWrappedNativeScope(JSContext* cx, JS::HandleObject aGlobal); nsAutoPtr mWaiverWrapperMap; bool IsContentXBLScope() { return mIsContentXBLScope; } bool AllowContentXBLScope(); bool UseContentXBLScope() { return mUseContentXBLScope; } bool IsAddonScope() { return mIsAddonScope; } bool HasInterposition() { return mInterposition; } nsCOMPtr GetInterposition(); static bool SetAddonInterposition(JSContext* cx, JSAddonId* addonId, nsIAddonInterposition* interp); static InterpositionWhitelist* GetInterpositionWhitelist(nsIAddonInterposition* interposition); static bool UpdateInterpositionWhitelist(JSContext* cx, nsIAddonInterposition* interposition); void SetAddonCallInterposition() { mHasCallInterpositions = true; } bool HasCallInterposition() { return mHasCallInterpositions; }; protected: virtual ~XPCWrappedNativeScope(); XPCWrappedNativeScope(); // not implemented private: class ClearInterpositionsObserver final : public nsIObserver { ~ClearInterpositionsObserver() {} public: NS_DECL_ISUPPORTS NS_DECL_NSIOBSERVER }; static XPCWrappedNativeScope* gScopes; static XPCWrappedNativeScope* gDyingScopes; static InterpositionMap* gInterpositionMap; static InterpositionWhitelistArray* gInterpositionWhitelists; XPCJSRuntime* mRuntime; Native2WrappedNativeMap* mWrappedNativeMap; ClassInfo2WrappedNativeProtoMap* mWrappedNativeProtoMap; RefPtr mComponents; XPCWrappedNativeScope* mNext; // The JS global object for this scope. If non-null, this will be the // default parent for the XPCWrappedNatives that have us as the scope, // unless a PreCreate hook overrides it. Note that this _may_ be null (see // constructor). JS::ObjectPtr mGlobalJSObject; // XBL Scope. This is is a lazily-created sandbox for non-system scopes. // EnsureContentXBLScope() decides whether it needs to be created or not. // This reference is wrapped into the compartment of mGlobalJSObject. JS::ObjectPtr mContentXBLScope; // Lazily created sandboxes for addon code. nsTArray mAddonScopes; // This is a service that will be use to interpose on some property accesses on // objects from other scope, for add-on compatibility reasons. nsCOMPtr mInterposition; // If this flag is set, we intercept function calls on vanilla JS function objects // from this scope if the caller scope has mInterposition set. bool mHasCallInterpositions; nsAutoPtr mDOMExpandoSet; JS::WeakMapPtr mXrayExpandos; bool mIsContentXBLScope; bool mIsAddonScope; // For remote XUL domains, we run all XBL in the content scope for compat // reasons (though we sometimes pref this off for automation). We separately // track the result of this decision (mAllowContentXBLScope), from the decision // of whether to actually _use_ an XBL scope (mUseContentXBLScope), which depends // on the type of global and whether the compartment is system principal // or not. // // This distinction is useful primarily because, if true, we know that we // have no way of distinguishing XBL script from content script for the // given scope. In these (unsupported) situations, we just always claim to // be XBL. bool mAllowContentXBLScope; bool mUseContentXBLScope; }; /***************************************************************************/ // Slots we use for our functions #define XPC_FUNCTION_NATIVE_MEMBER_SLOT 0 #define XPC_FUNCTION_PARENT_OBJECT_SLOT 1 /***************************************************************************/ // XPCNativeMember represents a single idl declared method, attribute or // constant. // Tight. No virtual methods. Can be bitwise copied (until any resolution done). class XPCNativeMember { public: static bool GetCallInfo(JSObject* funobj, XPCNativeInterface** pInterface, XPCNativeMember** pMember); jsid GetName() const {return mName;} uint16_t GetIndex() const {return mIndex;} bool GetConstantValue(XPCCallContext& ccx, XPCNativeInterface* iface, JS::Value* pval) {MOZ_ASSERT(IsConstant(), "Only call this if you're sure this is a constant!"); return Resolve(ccx, iface, nullptr, pval);} bool NewFunctionObject(XPCCallContext& ccx, XPCNativeInterface* iface, JS::HandleObject parent, JS::Value* pval); bool IsMethod() const {return 0 != (mFlags & METHOD);} bool IsConstant() const {return 0 != (mFlags & CONSTANT);} bool IsAttribute() const {return 0 != (mFlags & GETTER);} bool IsWritableAttribute() const {return 0 != (mFlags & SETTER_TOO);} bool IsReadOnlyAttribute() const {return IsAttribute() && !IsWritableAttribute();} void SetName(jsid a) {mName = a;} void SetMethod(uint16_t index) {mFlags = METHOD; mIndex = index;} void SetConstant(uint16_t index) {mFlags = CONSTANT; mIndex = index;} void SetReadOnlyAttribute(uint16_t index) {mFlags = GETTER; mIndex = index;} void SetWritableAttribute() {MOZ_ASSERT(mFlags == GETTER,"bad"); mFlags = GETTER | SETTER_TOO;} static uint16_t GetMaxIndexInInterface() {return (1<<12) - 1;} inline XPCNativeInterface* GetInterface() const; void SetIndexInInterface(uint16_t index) {mIndexInInterface = index;} /* default ctor - leave random contents */ XPCNativeMember() {MOZ_COUNT_CTOR(XPCNativeMember);} ~XPCNativeMember() {MOZ_COUNT_DTOR(XPCNativeMember);} private: bool Resolve(XPCCallContext& ccx, XPCNativeInterface* iface, JS::HandleObject parent, JS::Value* vp); enum { METHOD = 0x01, CONSTANT = 0x02, GETTER = 0x04, SETTER_TOO = 0x08 // If you add a flag here, you may need to make mFlags wider and either // make mIndexInInterface narrower (and adjust // XPCNativeInterface::NewInstance accordingly) or make this object // bigger. }; private: // our only data... jsid mName; uint16_t mIndex; // mFlags needs to be wide enogh to hold the flags in the above enum. uint16_t mFlags : 4; // mIndexInInterface is the index of this in our XPCNativeInterface's // mMembers. In theory our XPCNativeInterface could have as many as 2^15-1 // members (since mMemberCount is 15-bit) but in practice we prevent // creation of XPCNativeInterfaces which have more than 2^12 members. // If the width of this field changes, update GetMaxIndexInInterface. uint16_t mIndexInInterface : 12; }; /***************************************************************************/ // XPCNativeInterface represents a single idl declared interface. This is // primarily the set of XPCNativeMembers. // Tight. No virtual methods. class XPCNativeInterface { public: static XPCNativeInterface* GetNewOrUsed(const nsIID* iid); static XPCNativeInterface* GetNewOrUsed(nsIInterfaceInfo* info); static XPCNativeInterface* GetNewOrUsed(const char* name); static XPCNativeInterface* GetISupports(); inline nsIInterfaceInfo* GetInterfaceInfo() const {return mInfo.get();} inline jsid GetName() const {return mName;} inline const nsIID* GetIID() const; inline const char* GetNameString() const; inline XPCNativeMember* FindMember(jsid name) const; inline bool HasAncestor(const nsIID* iid) const; static inline size_t OffsetOfMembers(); uint16_t GetMemberCount() const { return mMemberCount; } XPCNativeMember* GetMemberAt(uint16_t i) { MOZ_ASSERT(i < mMemberCount, "bad index"); return &mMembers[i]; } void DebugDump(int16_t depth); #define XPC_NATIVE_IFACE_MARK_FLAG ((uint16_t)JS_BIT(15)) // only high bit of 16 is set void Mark() { mMarked = 1; } void Unmark() { mMarked = 0; } bool IsMarked() const { return mMarked != 0; } // NOP. This is just here to make the AutoMarkingPtr code compile. inline void TraceJS(JSTracer* trc) {} inline void AutoTrace(JSTracer* trc) {} static void DestroyInstance(XPCNativeInterface* inst); size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf); protected: static XPCNativeInterface* NewInstance(nsIInterfaceInfo* aInfo); XPCNativeInterface(); // not implemented XPCNativeInterface(nsIInterfaceInfo* aInfo, jsid aName) : mInfo(aInfo), mName(aName), mMemberCount(0), mMarked(0) { MOZ_COUNT_CTOR(XPCNativeInterface); } ~XPCNativeInterface() { MOZ_COUNT_DTOR(XPCNativeInterface); } void* operator new(size_t, void* p) CPP_THROW_NEW {return p;} XPCNativeInterface(const XPCNativeInterface& r); // not implemented XPCNativeInterface& operator= (const XPCNativeInterface& r); // not implemented private: nsCOMPtr mInfo; jsid mName; uint16_t mMemberCount : 15; uint16_t mMarked : 1; XPCNativeMember mMembers[1]; // always last - object sized for array }; /***************************************************************************/ // XPCNativeSetKey is used to key a XPCNativeSet in a NativeSetMap. class XPCNativeSetKey { public: explicit XPCNativeSetKey(XPCNativeSet* BaseSet = nullptr, XPCNativeInterface* Addition = nullptr, uint16_t Position = 0) : mIsAKey(IS_A_KEY), mPosition(Position), mBaseSet(BaseSet), mAddition(Addition) {} ~XPCNativeSetKey() {} XPCNativeSet* GetBaseSet() const {return mBaseSet;} XPCNativeInterface* GetAddition() const {return mAddition;} uint16_t GetPosition() const {return mPosition;} // This is a fun little hack... // We build these keys only on the stack. We use them for lookup in // NativeSetMap. Becasue we don't want to pay the cost of cloning a key and // sticking it into the hashtable, when the XPCNativeSet actually // gets added to the table the 'key' in the table is a pointer to the // set itself and not this key. Our key compare function expects to get // a key and a set. When we do external lookups in the map we pass in one // of these keys and our compare function gets passed a key and a set. // (see compare_NativeKeyToSet in xpcmaps.cpp). This is all well and good. // Except, when the table decides to resize itself. Then it tries to use // our compare function with the 'keys' that are in the hashtable (which are // really XPCNativeSet objects and not XPCNativeSetKey objects! // // So, the hack is to have the compare function assume it is getting a // XPCNativeSetKey pointer and call this IsAKey method. If that fails then // it realises that it really has a XPCNativeSet pointer and deals with that // fact. This is safe because we know that both of these classes have no // virtual methods and their first data member is a uint16_t. We are // confident that XPCNativeSet->mMemberCount will never be 0xffff. bool IsAKey() const {return mIsAKey == IS_A_KEY;} enum {IS_A_KEY = 0xffff}; // Allow shallow copy private: uint16_t mIsAKey; // must be first data member uint16_t mPosition; XPCNativeSet* mBaseSet; XPCNativeInterface* mAddition; }; /***************************************************************************/ // XPCNativeSet represents an ordered collection of XPCNativeInterface pointers. class XPCNativeSet { public: static XPCNativeSet* GetNewOrUsed(const nsIID* iid); static XPCNativeSet* GetNewOrUsed(nsIClassInfo* classInfo); static XPCNativeSet* GetNewOrUsed(XPCNativeSet* otherSet, XPCNativeInterface* newInterface, uint16_t position); // This generates a union set. // // If preserveFirstSetOrder is true, the elements from |firstSet| come first, // followed by any non-duplicate items from |secondSet|. If false, the same // algorithm is applied; but if we detect that |secondSet| is a superset of // |firstSet|, we return |secondSet| without worrying about whether the // ordering might differ from |firstSet|. static XPCNativeSet* GetNewOrUsed(XPCNativeSet* firstSet, XPCNativeSet* secondSet, bool preserveFirstSetOrder); static void ClearCacheEntryForClassInfo(nsIClassInfo* classInfo); inline bool FindMember(jsid name, XPCNativeMember** pMember, uint16_t* pInterfaceIndex) const; inline bool FindMember(jsid name, XPCNativeMember** pMember, XPCNativeInterface** pInterface) const; inline bool FindMember(jsid name, XPCNativeMember** pMember, XPCNativeInterface** pInterface, XPCNativeSet* protoSet, bool* pIsLocal) const; inline bool HasInterface(XPCNativeInterface* aInterface) const; inline bool HasInterfaceWithAncestor(XPCNativeInterface* aInterface) const; inline bool HasInterfaceWithAncestor(const nsIID* iid) const; inline XPCNativeInterface* FindInterfaceWithIID(const nsIID& iid) const; inline XPCNativeInterface* FindNamedInterface(jsid name) const; uint16_t GetMemberCount() const { return mMemberCount; } uint16_t GetInterfaceCount() const { return mInterfaceCount; } XPCNativeInterface** GetInterfaceArray() { return mInterfaces; } XPCNativeInterface* GetInterfaceAt(uint16_t i) {MOZ_ASSERT(i < mInterfaceCount, "bad index"); return mInterfaces[i];} inline bool MatchesSetUpToInterface(const XPCNativeSet* other, XPCNativeInterface* iface) const; #define XPC_NATIVE_SET_MARK_FLAG ((uint16_t)JS_BIT(15)) // only high bit of 16 is set inline void Mark(); // NOP. This is just here to make the AutoMarkingPtr code compile. inline void TraceJS(JSTracer* trc) {} inline void AutoTrace(JSTracer* trc) {} private: void MarkSelfOnly() { mMarked = 1; } public: void Unmark() { mMarked = 0; } bool IsMarked() const { return !!mMarked; } #ifdef DEBUG inline void ASSERT_NotMarked(); #endif void DebugDump(int16_t depth); static void DestroyInstance(XPCNativeSet* inst); size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf); protected: static XPCNativeSet* NewInstance(XPCNativeInterface** array, uint16_t count); static XPCNativeSet* NewInstanceMutate(XPCNativeSet* otherSet, XPCNativeInterface* newInterface, uint16_t position); XPCNativeSet() : mMemberCount(0), mInterfaceCount(0), mMarked(0) { MOZ_COUNT_CTOR(XPCNativeSet); } ~XPCNativeSet() { MOZ_COUNT_DTOR(XPCNativeSet); } void* operator new(size_t, void* p) CPP_THROW_NEW {return p;} private: uint16_t mMemberCount; uint16_t mInterfaceCount : 15; uint16_t mMarked : 1; XPCNativeInterface* mInterfaces[1]; // always last - object sized for array }; /***************************************************************************/ // XPCNativeScriptableFlags is a wrapper class that holds the flags returned // from calls to nsIXPCScriptable::GetScriptableFlags(). It has convenience // methods to check for particular bitflags. Since we also use this class as // a member of the gc'd class XPCNativeScriptableShared, this class holds the // bit and exposes the inlined methods to support marking. #define XPC_WN_SJSFLAGS_MARK_FLAG JS_BIT(31) // only high bit of 32 is set class XPCNativeScriptableFlags { private: uint32_t mFlags; public: explicit XPCNativeScriptableFlags(uint32_t flags = 0) : mFlags(flags) {} uint32_t GetFlags() const {return mFlags & ~XPC_WN_SJSFLAGS_MARK_FLAG;} void SetFlags(uint32_t flags) {mFlags = flags;} operator uint32_t() const {return GetFlags();} XPCNativeScriptableFlags(const XPCNativeScriptableFlags& r) {mFlags = r.GetFlags();} XPCNativeScriptableFlags& operator= (const XPCNativeScriptableFlags& r) {mFlags = r.GetFlags(); return *this;} void Mark() {mFlags |= XPC_WN_SJSFLAGS_MARK_FLAG;} void Unmark() {mFlags &= ~XPC_WN_SJSFLAGS_MARK_FLAG;} bool IsMarked() const {return 0 != (mFlags & XPC_WN_SJSFLAGS_MARK_FLAG);} #ifdef GET_IT #undef GET_IT #endif #define GET_IT(f_) const {return 0 != (mFlags & nsIXPCScriptable:: f_ );} bool WantPreCreate() GET_IT(WANT_PRECREATE) bool WantAddProperty() GET_IT(WANT_ADDPROPERTY) bool WantGetProperty() GET_IT(WANT_GETPROPERTY) bool WantSetProperty() GET_IT(WANT_SETPROPERTY) bool WantEnumerate() GET_IT(WANT_ENUMERATE) bool WantNewEnumerate() GET_IT(WANT_NEWENUMERATE) bool WantResolve() GET_IT(WANT_RESOLVE) bool WantFinalize() GET_IT(WANT_FINALIZE) bool WantCall() GET_IT(WANT_CALL) bool WantConstruct() GET_IT(WANT_CONSTRUCT) bool WantHasInstance() GET_IT(WANT_HASINSTANCE) bool UseJSStubForAddProperty() GET_IT(USE_JSSTUB_FOR_ADDPROPERTY) bool UseJSStubForDelProperty() GET_IT(USE_JSSTUB_FOR_DELPROPERTY) bool UseJSStubForSetProperty() GET_IT(USE_JSSTUB_FOR_SETPROPERTY) bool DontEnumQueryInterface() GET_IT(DONT_ENUM_QUERY_INTERFACE) bool DontAskInstanceForScriptable() GET_IT(DONT_ASK_INSTANCE_FOR_SCRIPTABLE) bool ClassInfoInterfacesOnly() GET_IT(CLASSINFO_INTERFACES_ONLY) bool AllowPropModsDuringResolve() GET_IT(ALLOW_PROP_MODS_DURING_RESOLVE) bool AllowPropModsToPrototype() GET_IT(ALLOW_PROP_MODS_TO_PROTOTYPE) bool IsGlobalObject() GET_IT(IS_GLOBAL_OBJECT) bool DontReflectInterfaceNames() GET_IT(DONT_REFLECT_INTERFACE_NAMES) #undef GET_IT }; /***************************************************************************/ // XPCNativeScriptableShared is used to hold the JSClass and the // associated scriptable flags for XPCWrappedNatives. These are shared across // the runtime and are garbage collected by xpconnect. We *used* to just store // this inside the XPCNativeScriptableInfo (usually owned by instances of // XPCWrappedNativeProto. This had two problems... It was wasteful, and it // was a big problem when wrappers are reparented to different scopes (and // thus different protos (the DOM does this). // We maintain the invariant that every JSClass for which ext.isWrappedNative // is true is a contained in an instance of this struct, and can thus be cast // to it. // // XXXbz Do we still need this struct at all? struct XPCWrappedNativeJSClass { js::Class base; }; class XPCNativeScriptableShared { public: const XPCNativeScriptableFlags& GetFlags() const {return mFlags;} const JSClass* GetJSClass() {return Jsvalify(&mJSClass.base);} XPCNativeScriptableShared(uint32_t aFlags, char* aName) : mFlags(aFlags) {memset(&mJSClass, 0, sizeof(mJSClass)); mJSClass.base.name = aName; // take ownership MOZ_COUNT_CTOR(XPCNativeScriptableShared);} ~XPCNativeScriptableShared() {if (mJSClass.base.name)free((void*)mJSClass.base.name); MOZ_COUNT_DTOR(XPCNativeScriptableShared);} char* TransferNameOwnership() {char* name=(char*)mJSClass.base.name; mJSClass.base.name = nullptr; return name;} void PopulateJSClass(); void Mark() {mFlags.Mark();} void Unmark() {mFlags.Unmark();} bool IsMarked() const {return mFlags.IsMarked();} private: XPCNativeScriptableFlags mFlags; XPCWrappedNativeJSClass mJSClass; }; /***************************************************************************/ // XPCNativeScriptableInfo is used to hold the nsIXPCScriptable state for a // given class or instance. class XPCNativeScriptableInfo { public: static XPCNativeScriptableInfo* Construct(const XPCNativeScriptableCreateInfo* sci); nsIXPCScriptable* GetCallback() const {return mCallback;} const XPCNativeScriptableFlags& GetFlags() const {return mShared->GetFlags();} const JSClass* GetJSClass() {return mShared->GetJSClass();} XPCNativeScriptableShared* GetScriptableShared() {return mShared;} void SetCallback(nsIXPCScriptable* s) {mCallback = s;} void SetCallback(already_AddRefed&& s) {mCallback = s;} void SetScriptableShared(XPCNativeScriptableShared* shared) {mShared = shared;} void Mark() { if (mShared) mShared->Mark(); } void TraceJS(JSTracer* trc) {} void AutoTrace(JSTracer* trc) {} protected: explicit XPCNativeScriptableInfo(nsIXPCScriptable* scriptable = nullptr, XPCNativeScriptableShared* shared = nullptr) : mCallback(scriptable), mShared(shared) {MOZ_COUNT_CTOR(XPCNativeScriptableInfo);} public: ~XPCNativeScriptableInfo() {MOZ_COUNT_DTOR(XPCNativeScriptableInfo);} private: // disable copy ctor and assignment XPCNativeScriptableInfo(const XPCNativeScriptableInfo& r); // not implemented XPCNativeScriptableInfo& operator= (const XPCNativeScriptableInfo& r); // not implemented private: nsCOMPtr mCallback; XPCNativeScriptableShared* mShared; }; /***************************************************************************/ // XPCNativeScriptableCreateInfo is used in creating new wrapper and protos. // it abstracts out the scriptable interface pointer and the flags. After // creation these are factored differently using XPCNativeScriptableInfo. class MOZ_STACK_CLASS XPCNativeScriptableCreateInfo { public: explicit XPCNativeScriptableCreateInfo(const XPCNativeScriptableInfo& si) : mCallback(si.GetCallback()), mFlags(si.GetFlags()) {} XPCNativeScriptableCreateInfo(already_AddRefed&& callback, XPCNativeScriptableFlags flags) : mCallback(callback), mFlags(flags) {} XPCNativeScriptableCreateInfo() : mFlags(0) {} nsIXPCScriptable* GetCallback() const {return mCallback;} const XPCNativeScriptableFlags& GetFlags() const {return mFlags;} void SetCallback(already_AddRefed&& callback) {mCallback = callback;} void SetFlags(const XPCNativeScriptableFlags& flags) {mFlags = flags;} private: nsCOMPtr mCallback; XPCNativeScriptableFlags mFlags; }; /***********************************************/ // XPCWrappedNativeProto hold the additional shared wrapper data // for XPCWrappedNative whose native objects expose nsIClassInfo. class XPCWrappedNativeProto { public: static XPCWrappedNativeProto* GetNewOrUsed(XPCWrappedNativeScope* scope, nsIClassInfo* classInfo, const XPCNativeScriptableCreateInfo* scriptableCreateInfo, bool callPostCreatePrototype = true); XPCWrappedNativeScope* GetScope() const {return mScope;} XPCJSRuntime* GetRuntime() const {return mScope->GetRuntime();} JSObject* GetJSProtoObject() const { JS::ExposeObjectToActiveJS(mJSProtoObject); return mJSProtoObject; } nsIClassInfo* GetClassInfo() const {return mClassInfo;} XPCNativeSet* GetSet() const {return mSet;} XPCNativeScriptableInfo* GetScriptableInfo() {return mScriptableInfo;} uint32_t GetClassInfoFlags() const {return mClassInfoFlags;} #ifdef GET_IT #undef GET_IT #endif #define GET_IT(f_) const {return !!(mClassInfoFlags & nsIClassInfo:: f_ );} bool ClassIsSingleton() GET_IT(SINGLETON) bool ClassIsDOMObject() GET_IT(DOM_OBJECT) bool ClassIsPluginObject() GET_IT(PLUGIN_OBJECT) #undef GET_IT void SetScriptableInfo(XPCNativeScriptableInfo* si) {MOZ_ASSERT(!mScriptableInfo, "leak here!"); mScriptableInfo = si;} bool CallPostCreatePrototype(); void JSProtoObjectFinalized(js::FreeOp* fop, JSObject* obj); void JSProtoObjectMoved(JSObject* obj, const JSObject* old); void SystemIsBeingShutDown(); void DebugDump(int16_t depth); void TraceSelf(JSTracer* trc) { if (mJSProtoObject) mJSProtoObject.trace(trc, "XPCWrappedNativeProto::mJSProtoObject"); } void TraceInside(JSTracer* trc) { if (trc->isMarkingTracer()) { mSet->Mark(); if (mScriptableInfo) mScriptableInfo->Mark(); } GetScope()->TraceSelf(trc); } void TraceJS(JSTracer* trc) { TraceSelf(trc); TraceInside(trc); } void WriteBarrierPre(JSRuntime* rt) { if (JS::IsIncrementalBarrierNeeded(rt) && mJSProtoObject) mJSProtoObject.writeBarrierPre(rt); } // NOP. This is just here to make the AutoMarkingPtr code compile. inline void AutoTrace(JSTracer* trc) {} // Yes, we *do* need to mark the mScriptableInfo in both cases. void Mark() const {mSet->Mark(); if (mScriptableInfo) mScriptableInfo->Mark();} #ifdef DEBUG void ASSERT_SetNotMarked() const {mSet->ASSERT_NotMarked();} #endif ~XPCWrappedNativeProto(); protected: // disable copy ctor and assignment XPCWrappedNativeProto(const XPCWrappedNativeProto& r); // not implemented XPCWrappedNativeProto& operator= (const XPCWrappedNativeProto& r); // not implemented // hide ctor XPCWrappedNativeProto(XPCWrappedNativeScope* Scope, nsIClassInfo* ClassInfo, uint32_t ClassInfoFlags, XPCNativeSet* Set); bool Init(const XPCNativeScriptableCreateInfo* scriptableCreateInfo, bool callPostCreatePrototype); private: #ifdef DEBUG static int32_t gDEBUG_LiveProtoCount; #endif private: XPCWrappedNativeScope* mScope; JS::ObjectPtr mJSProtoObject; nsCOMPtr mClassInfo; uint32_t mClassInfoFlags; XPCNativeSet* mSet; XPCNativeScriptableInfo* mScriptableInfo; }; /***********************************************/ // XPCWrappedNativeTearOff represents the info needed to make calls to one // interface on the underlying native object of a XPCWrappedNative. class XPCWrappedNativeTearOff { public: bool IsAvailable() const {return mInterface == nullptr;} bool IsReserved() const {return mInterface == (XPCNativeInterface*)1;} bool IsValid() const {return !IsAvailable() && !IsReserved();} void SetReserved() {mInterface = (XPCNativeInterface*)1;} XPCNativeInterface* GetInterface() const {return mInterface;} nsISupports* GetNative() const {return mNative;} JSObject* GetJSObject(); JSObject* GetJSObjectPreserveColor() const; void SetInterface(XPCNativeInterface* Interface) {mInterface = Interface;} void SetNative(nsISupports* Native) {mNative = Native;} already_AddRefed TakeNative() { return mNative.forget(); } void SetJSObject(JSObject* JSObj); void JSObjectFinalized() {SetJSObject(nullptr);} void JSObjectMoved(JSObject* obj, const JSObject* old); XPCWrappedNativeTearOff() : mInterface(nullptr), mJSObject(nullptr) { MOZ_COUNT_CTOR(XPCWrappedNativeTearOff); } ~XPCWrappedNativeTearOff(); // NOP. This is just here to make the AutoMarkingPtr code compile. inline void TraceJS(JSTracer* trc) {} inline void AutoTrace(JSTracer* trc) {} void Mark() {mJSObject.setFlags(1);} void Unmark() {mJSObject.unsetFlags(1);} bool IsMarked() const {return mJSObject.hasFlag(1);} private: XPCWrappedNativeTearOff(const XPCWrappedNativeTearOff& r) = delete; XPCWrappedNativeTearOff& operator= (const XPCWrappedNativeTearOff& r) = delete; private: XPCNativeInterface* mInterface; // mNative is an nsRefPtr not an nsCOMPtr because it may not be the canonical // nsISupports pointer. RefPtr mNative; JS::TenuredHeap mJSObject; }; /***********************************************/ // XPCWrappedNativeTearOffChunk is a linked list of XPCWrappedNativeTearOff // objects. It lets us allocate a set of XPCWrappedNativeTearOff objects and // link the sets - rather than only having the option of linking single // XPCWrappedNativeTearOff objects. class XPCWrappedNativeTearOffChunk { friend class XPCWrappedNative; private: XPCWrappedNativeTearOffChunk() : mNextChunk(nullptr) {} ~XPCWrappedNativeTearOffChunk() {delete mNextChunk;} private: XPCWrappedNativeTearOff mTearOff; XPCWrappedNativeTearOffChunk* mNextChunk; }; /***************************************************************************/ // XPCWrappedNative the wrapper around one instance of a native xpcom object // to be used from JavaScript. class XPCWrappedNative final : public nsIXPConnectWrappedNative { public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_NSIXPCONNECTJSOBJECTHOLDER NS_DECL_NSIXPCONNECTWRAPPEDNATIVE NS_DECL_CYCLE_COLLECTION_CLASS(XPCWrappedNative) nsIPrincipal* GetObjectPrincipal() const; bool IsValid() const { return mFlatJSObject.hasFlag(FLAT_JS_OBJECT_VALID); } #define XPC_SCOPE_WORD(s) (intptr_t(s)) #define XPC_SCOPE_MASK (intptr_t(0x3)) #define XPC_SCOPE_TAG (intptr_t(0x1)) #define XPC_WRAPPER_EXPIRED (intptr_t(0x2)) static inline bool IsTaggedScope(XPCWrappedNativeScope* s) {return XPC_SCOPE_WORD(s) & XPC_SCOPE_TAG;} static inline XPCWrappedNativeScope* TagScope(XPCWrappedNativeScope* s) {MOZ_ASSERT(!IsTaggedScope(s), "bad pointer!"); return (XPCWrappedNativeScope*)(XPC_SCOPE_WORD(s) | XPC_SCOPE_TAG);} static inline XPCWrappedNativeScope* UnTagScope(XPCWrappedNativeScope* s) {return (XPCWrappedNativeScope*)(XPC_SCOPE_WORD(s) & ~XPC_SCOPE_TAG);} inline bool IsWrapperExpired() const {return XPC_SCOPE_WORD(mMaybeScope) & XPC_WRAPPER_EXPIRED;} bool HasProto() const {return !IsTaggedScope(mMaybeScope);} XPCWrappedNativeProto* GetProto() const {return HasProto() ? (XPCWrappedNativeProto*) (XPC_SCOPE_WORD(mMaybeProto) & ~XPC_SCOPE_MASK) : nullptr;} void SetProto(XPCWrappedNativeProto* p); XPCWrappedNativeScope* GetScope() const {return GetProto() ? GetProto()->GetScope() : (XPCWrappedNativeScope*) (XPC_SCOPE_WORD(mMaybeScope) & ~XPC_SCOPE_MASK);} nsISupports* GetIdentityObject() const {return mIdentity;} /** * This getter clears the gray bit before handing out the JSObject which * means that the object is guaranteed to be kept alive past the next CC. */ JSObject* GetFlatJSObject() const { JS::ExposeObjectToActiveJS(mFlatJSObject); return mFlatJSObject; } /** * This getter does not change the color of the JSObject meaning that the * object returned is not guaranteed to be kept alive past the next CC. * * This should only be called if you are certain that the return value won't * be passed into a JS API function and that it won't be stored without * being rooted (or otherwise signaling the stored value to the CC). */ JSObject* GetFlatJSObjectPreserveColor() const {return mFlatJSObject;} XPCNativeSet* GetSet() const {return mSet;} void SetSet(XPCNativeSet* set) {mSet = set;} static XPCWrappedNative* Get(JSObject* obj) { MOZ_ASSERT(IS_WN_REFLECTOR(obj)); return (XPCWrappedNative*)js::GetObjectPrivate(obj); } private: inline void ExpireWrapper() {mMaybeScope = (XPCWrappedNativeScope*) (XPC_SCOPE_WORD(mMaybeScope) | XPC_WRAPPER_EXPIRED);} public: XPCNativeScriptableInfo* GetScriptableInfo() const {return mScriptableInfo;} nsIXPCScriptable* // call this wrong and you deserve to crash GetScriptableCallback() const {return mScriptableInfo->GetCallback();} nsIClassInfo* GetClassInfo() const {return IsValid() && HasProto() ? GetProto()->GetClassInfo() : nullptr;} bool HasMutatedSet() const {return IsValid() && (!HasProto() || GetSet() != GetProto()->GetSet());} XPCJSRuntime* GetRuntime() const {XPCWrappedNativeScope* scope = GetScope(); return scope ? scope->GetRuntime() : nullptr;} static nsresult WrapNewGlobal(xpcObjectHelper& nativeHelper, nsIPrincipal* principal, bool initStandardClasses, JS::CompartmentOptions& aOptions, XPCWrappedNative** wrappedGlobal); static nsresult GetNewOrUsed(xpcObjectHelper& helper, XPCWrappedNativeScope* Scope, XPCNativeInterface* Interface, XPCWrappedNative** wrapper); static nsresult GetUsedOnly(nsISupports* Object, XPCWrappedNativeScope* Scope, XPCNativeInterface* Interface, XPCWrappedNative** wrapper); void FlatJSObjectFinalized(); void FlatJSObjectMoved(JSObject* obj, const JSObject* old); void SystemIsBeingShutDown(); enum CallMode {CALL_METHOD, CALL_GETTER, CALL_SETTER}; static bool CallMethod(XPCCallContext& ccx, CallMode mode = CALL_METHOD); static bool GetAttribute(XPCCallContext& ccx) {return CallMethod(ccx, CALL_GETTER);} static bool SetAttribute(XPCCallContext& ccx) {return CallMethod(ccx, CALL_SETTER);} inline bool HasInterfaceNoQI(const nsIID& iid); XPCWrappedNativeTearOff* FindTearOff(XPCNativeInterface* aInterface, bool needJSObject = false, nsresult* pError = nullptr); XPCWrappedNativeTearOff* FindTearOff(const nsIID& iid); void Mark() const { mSet->Mark(); if (mScriptableInfo) mScriptableInfo->Mark(); if (HasProto()) GetProto()->Mark(); } // Yes, we *do* need to mark the mScriptableInfo in both cases. inline void TraceInside(JSTracer* trc) { if (trc->isMarkingTracer()) { mSet->Mark(); if (mScriptableInfo) mScriptableInfo->Mark(); } if (HasProto()) GetProto()->TraceSelf(trc); else GetScope()->TraceSelf(trc); if (mFlatJSObject && JS_IsGlobalObject(mFlatJSObject)) { xpc::TraceXPCGlobal(trc, mFlatJSObject); } } void TraceJS(JSTracer* trc) { TraceInside(trc); } void TraceSelf(JSTracer* trc) { // If this got called, we're being kept alive by someone who really // needs us alive and whole. Do not let our mFlatJSObject go away. // This is the only time we should be tracing our mFlatJSObject, // normally somebody else is doing that. Be careful not to trace the // bogus INVALID_OBJECT value we can have during init, though. if (mFlatJSObject) { JS_CallTenuredObjectTracer(trc, &mFlatJSObject, "XPCWrappedNative::mFlatJSObject"); } } static void Trace(JSTracer* trc, JSObject* obj); void AutoTrace(JSTracer* trc) { TraceSelf(trc); } #ifdef DEBUG void ASSERT_SetsNotMarked() const {mSet->ASSERT_NotMarked(); if (HasProto()){GetProto()->ASSERT_SetNotMarked();}} #endif inline void SweepTearOffs(); // Returns a string that shuld be free'd using JS_smprintf_free (or null). char* ToString(XPCWrappedNativeTearOff* to = nullptr) const; static void GatherProtoScriptableCreateInfo(nsIClassInfo* classInfo, XPCNativeScriptableCreateInfo& sciProto); bool HasExternalReference() const {return mRefCnt > 1;} void Suspect(nsCycleCollectionNoteRootCallback& cb); void NoteTearoffs(nsCycleCollectionTraversalCallback& cb); // Make ctor and dtor protected (rather than private) to placate nsCOMPtr. protected: XPCWrappedNative(); // not implemented // This ctor is used if this object will have a proto. XPCWrappedNative(already_AddRefed&& aIdentity, XPCWrappedNativeProto* aProto); // This ctor is used if this object will NOT have a proto. XPCWrappedNative(already_AddRefed&& aIdentity, XPCWrappedNativeScope* aScope, XPCNativeSet* aSet); virtual ~XPCWrappedNative(); void Destroy(); void UpdateScriptableInfo(XPCNativeScriptableInfo* si); private: enum { // Flags bits for mFlatJSObject: FLAT_JS_OBJECT_VALID = JS_BIT(0) }; bool Init(const XPCNativeScriptableCreateInfo* sci); bool FinishInit(); bool ExtendSet(XPCNativeInterface* aInterface); nsresult InitTearOff(XPCWrappedNativeTearOff* aTearOff, XPCNativeInterface* aInterface, bool needJSObject); bool InitTearOffJSObject(XPCWrappedNativeTearOff* to); public: static const XPCNativeScriptableCreateInfo& GatherScriptableCreateInfo(nsISupports* obj, nsIClassInfo* classInfo, XPCNativeScriptableCreateInfo& sciProto, XPCNativeScriptableCreateInfo& sciWrapper); private: union { XPCWrappedNativeScope* mMaybeScope; XPCWrappedNativeProto* mMaybeProto; }; XPCNativeSet* mSet; JS::TenuredHeap mFlatJSObject; XPCNativeScriptableInfo* mScriptableInfo; XPCWrappedNativeTearOffChunk mFirstChunk; }; /*************************************************************************** **************************************************************************** * * Core classes for wrapped JSObject for use from native code... * **************************************************************************** ***************************************************************************/ // this interfaces exists so we can refcount nsXPCWrappedJSClass // {2453EBA0-A9B8-11d2-BA64-00805F8A5DD7} #define NS_IXPCONNECT_WRAPPED_JS_CLASS_IID \ { 0x2453eba0, 0xa9b8, 0x11d2, \ { 0xba, 0x64, 0x0, 0x80, 0x5f, 0x8a, 0x5d, 0xd7 } } class nsIXPCWrappedJSClass : public nsISupports { public: NS_DECLARE_STATIC_IID_ACCESSOR(NS_IXPCONNECT_WRAPPED_JS_CLASS_IID) NS_IMETHOD DebugDump(int16_t depth) = 0; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsIXPCWrappedJSClass, NS_IXPCONNECT_WRAPPED_JS_CLASS_IID) /*************************/ // nsXPCWrappedJSClass represents the sharable factored out common code and // data for nsXPCWrappedJS instances for the same interface type. class nsXPCWrappedJSClass final : public nsIXPCWrappedJSClass { // all the interface method declarations... NS_DECL_ISUPPORTS NS_IMETHOD DebugDump(int16_t depth) override; public: static already_AddRefed GetNewOrUsed(JSContext* cx, REFNSIID aIID, bool allowNonScriptable = false); REFNSIID GetIID() const {return mIID;} XPCJSRuntime* GetRuntime() const {return mRuntime;} nsIInterfaceInfo* GetInterfaceInfo() const {return mInfo;} const char* GetInterfaceName(); static bool IsWrappedJS(nsISupports* aPtr); NS_IMETHOD DelegatedQueryInterface(nsXPCWrappedJS* self, REFNSIID aIID, void** aInstancePtr); JSObject* GetRootJSObject(JSContext* cx, JSObject* aJSObj); NS_IMETHOD CallMethod(nsXPCWrappedJS* wrapper, uint16_t methodIndex, const XPTMethodDescriptor* info, nsXPTCMiniVariant* params); JSObject* CallQueryInterfaceOnJSObject(JSContext* cx, JSObject* jsobj, REFNSIID aIID); static nsresult BuildPropertyEnumerator(XPCCallContext& ccx, JSObject* aJSObj, nsISimpleEnumerator** aEnumerate); static nsresult GetNamedPropertyAsVariant(XPCCallContext& ccx, JSObject* aJSObj, const nsAString& aName, nsIVariant** aResult); static nsresult CheckForException(XPCCallContext & ccx, const char * aPropertyName, const char * anInterfaceName, bool aForceReport); private: virtual ~nsXPCWrappedJSClass(); nsXPCWrappedJSClass(); // not implemented nsXPCWrappedJSClass(JSContext* cx, REFNSIID aIID, nsIInterfaceInfo* aInfo); bool IsReflectable(uint16_t i) const {return (bool)(mDescriptors[i/32] & (1 << (i%32)));} void SetReflectable(uint16_t i, bool b) {if (b) mDescriptors[i/32] |= (1 << (i%32)); else mDescriptors[i/32] &= ~(1 << (i%32));} bool GetArraySizeFromParam(JSContext* cx, const XPTMethodDescriptor* method, const nsXPTParamInfo& param, uint16_t methodIndex, uint8_t paramIndex, nsXPTCMiniVariant* params, uint32_t* result) const; bool GetInterfaceTypeFromParam(JSContext* cx, const XPTMethodDescriptor* method, const nsXPTParamInfo& param, uint16_t methodIndex, const nsXPTType& type, nsXPTCMiniVariant* params, nsID* result) const; static void CleanupPointerArray(const nsXPTType& datum_type, uint32_t array_count, void** arrayp); static void CleanupPointerTypeObject(const nsXPTType& type, void** pp); void CleanupOutparams(JSContext* cx, uint16_t methodIndex, const nsXPTMethodInfo* info, nsXPTCMiniVariant* nativeParams, bool inOutOnly, uint8_t n) const; private: XPCJSRuntime* mRuntime; nsCOMPtr mInfo; char* mName; nsIID mIID; uint32_t* mDescriptors; }; /*************************/ // nsXPCWrappedJS is a wrapper for a single JSObject for use from native code. // nsXPCWrappedJS objects are chained together to represent the various // interface on the single underlying (possibly aggregate) JSObject. class nsXPCWrappedJS final : protected nsAutoXPTCStub, public nsIXPConnectWrappedJS, public nsSupportsWeakReference, public nsIPropertyBag, public XPCRootSetElem { public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_NSIXPCONNECTJSOBJECTHOLDER NS_DECL_NSIXPCONNECTWRAPPEDJS NS_DECL_NSISUPPORTSWEAKREFERENCE NS_DECL_NSIPROPERTYBAG NS_DECL_CYCLE_COLLECTION_SKIPPABLE_CLASS_AMBIGUOUS(nsXPCWrappedJS, nsIXPConnectWrappedJS) NS_IMETHOD CallMethod(uint16_t methodIndex, const XPTMethodDescriptor* info, nsXPTCMiniVariant* params) override; /* * This is rarely called directly. Instead one usually calls * XPCConvert::JSObject2NativeInterface which will handles cases where the * JS object is already a wrapped native or a DOM object. */ static nsresult GetNewOrUsed(JS::HandleObject aJSObj, REFNSIID aIID, nsXPCWrappedJS** wrapper); nsISomeInterface* GetXPTCStub() { return mXPTCStub; } /** * This getter does not change the color of the JSObject meaning that the * object returned is not guaranteed to be kept alive past the next CC. * * This should only be called if you are certain that the return value won't * be passed into a JS API function and that it won't be stored without * being rooted (or otherwise signaling the stored value to the CC). */ JSObject* GetJSObjectPreserveColor() const {return mJSObj;} // Returns true if the wrapper chain contains references to multiple // compartments. If the wrapper chain contains references to multiple // compartments, then it must be registered on the XPCJSRuntime. Otherwise, // it should be registered in the CompartmentPrivate for the compartment of // the root's JS object. This will only return correct results when called // on the root wrapper and will assert if not called on a root wrapper. bool IsMultiCompartment() const; nsXPCWrappedJSClass* GetClass() const {return mClass;} REFNSIID GetIID() const {return GetClass()->GetIID();} nsXPCWrappedJS* GetRootWrapper() const {return mRoot;} nsXPCWrappedJS* GetNextWrapper() const {return mNext;} nsXPCWrappedJS* Find(REFNSIID aIID); nsXPCWrappedJS* FindInherited(REFNSIID aIID); nsXPCWrappedJS* FindOrFindInherited(REFNSIID aIID) { nsXPCWrappedJS* wrapper = Find(aIID); if (wrapper) return wrapper; return FindInherited(aIID); } bool IsRootWrapper() const {return mRoot == this;} bool IsValid() const {return mJSObj != nullptr;} void SystemIsBeingShutDown(); // These two methods are used by JSObject2WrappedJSMap::FindDyingJSObjects // to find non-rooting wrappers for dying JS objects. See the top of // XPCWrappedJS.cpp for more details. bool IsSubjectToFinalization() const {return IsValid() && mRefCnt == 1;} void UpdateObjectPointerAfterGC() {JS_UpdateWeakPointerAfterGC(&mJSObj);} bool IsAggregatedToNative() const {return mRoot->mOuter != nullptr;} nsISupports* GetAggregatedNativeObject() const {return mRoot->mOuter;} void SetAggregatedNativeObject(nsISupports* aNative) { MOZ_ASSERT(aNative); if (mRoot->mOuter) { MOZ_ASSERT(mRoot->mOuter == aNative, "Only one aggregated native can be set"); return; } mRoot->mOuter = aNative; } void TraceJS(JSTracer* trc); size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; virtual ~nsXPCWrappedJS(); protected: nsXPCWrappedJS() = delete; nsXPCWrappedJS(JSContext* cx, JSObject* aJSObj, nsXPCWrappedJSClass* aClass, nsXPCWrappedJS* root, nsresult* rv); bool CanSkip(); void Destroy(); void Unlink(); private: JS::Heap mJSObj; RefPtr mClass; nsXPCWrappedJS* mRoot; // If mRoot != this, it is an owning pointer. nsXPCWrappedJS* mNext; nsCOMPtr mOuter; // only set in root }; /***************************************************************************/ class XPCJSObjectHolder : public nsIXPConnectJSObjectHolder, public XPCRootSetElem { public: // all the interface method declarations... NS_DECL_ISUPPORTS NS_DECL_NSIXPCONNECTJSOBJECTHOLDER // non-interface implementation public: void TraceJS(JSTracer* trc); explicit XPCJSObjectHolder(JSObject* obj); private: virtual ~XPCJSObjectHolder(); XPCJSObjectHolder(); // not implemented JS::Heap mJSObj; }; /*************************************************************************** **************************************************************************** * * All manner of utility classes follow... * **************************************************************************** ***************************************************************************/ class xpcProperty : public nsIProperty { public: NS_DECL_ISUPPORTS NS_DECL_NSIPROPERTY xpcProperty(const char16_t* aName, uint32_t aNameLen, nsIVariant* aValue); private: virtual ~xpcProperty() {} nsString mName; nsCOMPtr mValue; }; /***************************************************************************/ // class here just for static methods class XPCConvert { public: static bool IsMethodReflectable(const XPTMethodDescriptor& info); /** * Convert a native object into a JS::Value. * * @param d [out] the resulting JS::Value * @param s the native object we're working with * @param type the type of object that s is * @param iid the interface of s that we want * @param scope the default scope to put on the new JSObject's parent * chain * @param pErr [out] relevant error code, if any. */ static bool NativeData2JS(JS::MutableHandleValue d, const void* s, const nsXPTType& type, const nsID* iid, nsresult* pErr); static bool JSData2Native(void* d, JS::HandleValue s, const nsXPTType& type, const nsID* iid, nsresult* pErr); /** * Convert a native nsISupports into a JSObject. * * @param dest [out] the resulting JSObject * @param src the native object we're working with * @param iid the interface of src that we want (may be null) * @param Interface the interface of src that we want * @param cache the wrapper cache for src (may be null, in which case src * will be QI'ed to get the cache) * @param allowNativeWrapper if true, this method may wrap the resulting * JSObject in an XPCNativeWrapper and return that, as needed. * @param pErr [out] relevant error code, if any. * @param src_is_identity optional performance hint. Set to true only * if src is the identity pointer. */ static bool NativeInterface2JSObject(JS::MutableHandleValue d, nsIXPConnectJSObjectHolder** dest, xpcObjectHelper& aHelper, const nsID* iid, XPCNativeInterface** Interface, bool allowNativeWrapper, nsresult* pErr); static bool GetNativeInterfaceFromJSObject(void** dest, JSObject* src, const nsID* iid, nsresult* pErr); static bool JSObject2NativeInterface(void** dest, JS::HandleObject src, const nsID* iid, nsISupports* aOuter, nsresult* pErr); // Note - This return the XPCWrappedNative, rather than the native itself, // for the WN case. You probably want UnwrapReflectorToISupports. static bool GetISupportsFromJSObject(JSObject* obj, nsISupports** iface); /** * Convert a native array into a JS::Value. * * @param d [out] the resulting JS::Value * @param s the native array we're working with * @param type the type of objects in the array * @param iid the interface of each object in the array that we want * @param count the number of items in the array * @param scope the default scope to put on the new JSObjects' parent chain * @param pErr [out] relevant error code, if any. */ static bool NativeArray2JS(JS::MutableHandleValue d, const void** s, const nsXPTType& type, const nsID* iid, uint32_t count, nsresult* pErr); static bool JSArray2Native(void** d, JS::HandleValue s, uint32_t count, const nsXPTType& type, const nsID* iid, nsresult* pErr); static bool JSTypedArray2Native(void** d, JSObject* jsarray, uint32_t count, const nsXPTType& type, nsresult* pErr); static bool NativeStringWithSize2JS(JS::MutableHandleValue d, const void* s, const nsXPTType& type, uint32_t count, nsresult* pErr); static bool JSStringWithSize2Native(void* d, JS::HandleValue s, uint32_t count, const nsXPTType& type, nsresult* pErr); static nsresult JSValToXPCException(JS::MutableHandleValue s, const char* ifaceName, const char* methodName, nsIException** exception); static nsresult JSErrorToXPCException(const char* message, const char* ifaceName, const char* methodName, const JSErrorReport* report, nsIException** exception); static nsresult ConstructException(nsresult rv, const char* message, const char* ifaceName, const char* methodName, nsISupports* data, nsIException** exception, JSContext* cx, JS::Value* jsExceptionPtr); private: XPCConvert(); // not implemented }; /***************************************************************************/ // code for throwing exceptions into JS class nsXPCException; class XPCThrower { public: static void Throw(nsresult rv, JSContext* cx); static void Throw(nsresult rv, XPCCallContext& ccx); static void ThrowBadResult(nsresult rv, nsresult result, XPCCallContext& ccx); static void ThrowBadParam(nsresult rv, unsigned paramNum, XPCCallContext& ccx); static bool SetVerbosity(bool state) {bool old = sVerbose; sVerbose = state; return old;} static bool CheckForPendingException(nsresult result, JSContext* cx); private: static void Verbosify(XPCCallContext& ccx, char** psz, bool own); private: static bool sVerbose; }; /***************************************************************************/ class nsXPCException { public: static bool NameAndFormatForNSResult(nsresult rv, const char** name, const char** format); static const void* IterateNSResults(nsresult* rv, const char** name, const char** format, const void** iterp); static uint32_t GetNSResultCount(); }; /***************************************************************************/ /* * nsJSID implements nsIJSID. It is also used by nsJSIID and nsJSCID as a * member (as a hidden implementaion detail) to which they delegate many calls. */ // Initialization is done on demand, and calling the destructor below is always // safe. extern void xpc_DestroyJSxIDClassObjects(); class nsJSID final : public nsIJSID { public: NS_DEFINE_STATIC_CID_ACCESSOR(NS_JS_ID_CID) NS_DECL_ISUPPORTS NS_DECL_NSIJSID bool InitWithName(const nsID& id, const char* nameString); bool SetName(const char* name); void SetNameToNoString() {MOZ_ASSERT(!mName, "name already set"); mName = const_cast(gNoString);} bool NameIsSet() const {return nullptr != mName;} const nsID& ID() const {return mID;} bool IsValid() const {return !mID.Equals(GetInvalidIID());} static already_AddRefed NewID(const char* str); static already_AddRefed NewID(const nsID& id); nsJSID(); void Reset(); const nsID& GetInvalidIID() const; protected: virtual ~nsJSID(); static const char gNoString[]; nsID mID; char* mNumber; char* mName; }; // nsJSIID class nsJSIID : public nsIJSIID, public nsIXPCScriptable { public: NS_DECL_ISUPPORTS // we manually delagate these to nsJSID NS_DECL_NSIJSID // we implement the rest... NS_DECL_NSIJSIID NS_DECL_NSIXPCSCRIPTABLE static already_AddRefed NewID(nsIInterfaceInfo* aInfo); explicit nsJSIID(nsIInterfaceInfo* aInfo); nsJSIID(); // not implemented private: virtual ~nsJSIID(); nsCOMPtr mInfo; }; // nsJSCID class nsJSCID : public nsIJSCID, public nsIXPCScriptable { public: NS_DECL_ISUPPORTS // we manually delagate these to nsJSID NS_DECL_NSIJSID // we implement the rest... NS_DECL_NSIJSCID NS_DECL_NSIXPCSCRIPTABLE static already_AddRefed NewID(const char* str); nsJSCID(); private: virtual ~nsJSCID(); void ResolveName(); private: RefPtr mDetails; }; /***************************************************************************/ // XPCJSContextStack is not actually an xpcom object, but xpcom calls are // delegated to it as an implementation detail. struct XPCJSContextInfo { explicit XPCJSContextInfo(JSContext* aCx) : cx(aCx), savedFrameChain(false) {} JSContext* cx; // Whether the frame chain was saved bool savedFrameChain; }; namespace xpc { bool PushNullJSContext(); void PopNullJSContext(); } /* namespace xpc */ namespace mozilla { namespace dom { namespace danger { class AutoCxPusher; } // namespace danger } // namespace dom } // namespace mozilla class XPCJSContextStack { public: explicit XPCJSContextStack(XPCJSRuntime* aRuntime) : mRuntime(aRuntime) , mSafeJSContext(nullptr) { } virtual ~XPCJSContextStack(); uint32_t Count() { return mStack.Length(); } JSContext* Peek() { return mStack.IsEmpty() ? nullptr : mStack[mStack.Length() - 1].cx; } JSContext* InitSafeJSContext(); JSContext* GetSafeJSContext(); bool HasJSContext(JSContext* cx); const InfallibleTArray* GetStack() { return &mStack; } private: friend class mozilla::dom::danger::AutoCxPusher; friend bool xpc::PushNullJSContext(); friend void xpc::PopNullJSContext(); // We make these private so that stack manipulation can only happen // through one of the above friends. JSContext* Pop(); bool Push(JSContext* cx); AutoInfallibleTArray mStack; XPCJSRuntime* mRuntime; JSContext* mSafeJSContext; }; /***************************************************************************/ // 'Components' object implementations. nsXPCComponentsBase has the // less-privileged stuff that we're willing to expose to XBL. class nsXPCComponentsBase : public nsIXPCComponentsBase { public: NS_DECL_ISUPPORTS NS_DECL_NSIXPCCOMPONENTSBASE public: void SystemIsBeingShutDown() { ClearMembers(); } XPCWrappedNativeScope* GetScope() { return mScope; } protected: virtual ~nsXPCComponentsBase(); explicit nsXPCComponentsBase(XPCWrappedNativeScope* aScope); virtual void ClearMembers(); XPCWrappedNativeScope* mScope; // Unprivileged members from nsIXPCComponentsBase. RefPtr mInterfaces; RefPtr mInterfacesByID; RefPtr mResults; friend class XPCWrappedNativeScope; }; class nsXPCComponents : public nsXPCComponentsBase, public nsIXPCComponents { public: NS_DECL_ISUPPORTS_INHERITED NS_FORWARD_NSIXPCCOMPONENTSBASE(nsXPCComponentsBase::) NS_DECL_NSIXPCCOMPONENTS protected: explicit nsXPCComponents(XPCWrappedNativeScope* aScope); virtual ~nsXPCComponents(); virtual void ClearMembers() override; // Privileged members added by nsIXPCComponents. RefPtr mClasses; RefPtr mClassesByID; RefPtr mID; RefPtr mException; RefPtr mConstructor; RefPtr mUtils; friend class XPCWrappedNativeScope; }; /***************************************************************************/ extern JSObject* xpc_NewIDObject(JSContext* cx, JS::HandleObject jsobj, const nsID& aID); extern const nsID* xpc_JSObjectToID(JSContext* cx, JSObject* obj); extern bool xpc_JSObjectIsID(JSContext* cx, JSObject* obj); /***************************************************************************/ // in XPCDebug.cpp extern bool xpc_DumpJSStack(bool showArgs, bool showLocals, bool showThisProps); // Return a newly-allocated string containing a representation of the // current JS stack. It is the *caller's* responsibility to free this // string with JS_smprintf_free(). extern char* xpc_PrintJSStack(JSContext* cx, bool showArgs, bool showLocals, bool showThisProps); /***************************************************************************/ // Definition of nsScriptError, defined here because we lack a place to put // XPCOM objects associated with the JavaScript engine. class nsScriptErrorBase : public nsIScriptError { public: nsScriptErrorBase(); // TODO - do something reasonable on getting null from these babies. NS_DECL_NSICONSOLEMESSAGE NS_DECL_NSISCRIPTERROR protected: virtual ~nsScriptErrorBase(); void InitializeOnMainThread(); nsString mMessage; nsString mSourceName; uint32_t mLineNumber; nsString mSourceLine; uint32_t mColumnNumber; uint32_t mFlags; nsCString mCategory; // mOuterWindowID is set on the main thread from InitializeOnMainThread(). uint64_t mOuterWindowID; uint64_t mInnerWindowID; int64_t mTimeStamp; // mInitializedOnMainThread and mIsFromPrivateWindow are set on the main // thread from InitializeOnMainThread(). mozilla::Atomic mInitializedOnMainThread; bool mIsFromPrivateWindow; }; class nsScriptError final : public nsScriptErrorBase { public: nsScriptError() {} NS_DECL_THREADSAFE_ISUPPORTS private: virtual ~nsScriptError() {} }; class nsScriptErrorWithStack : public nsScriptErrorBase { public: explicit nsScriptErrorWithStack(JS::HandleObject); NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsScriptErrorWithStack) NS_IMETHOD Init(const nsAString& message, const nsAString& sourceName, const nsAString& sourceLine, uint32_t lineNumber, uint32_t columnNumber, uint32_t flags, const char* category) override; NS_IMETHOD GetStack(JS::MutableHandleValue) override; NS_IMETHOD ToString(nsACString& aResult) override; private: virtual ~nsScriptErrorWithStack(); // Complete stackframe where the error happened. // Must be SavedFrame object. JS::Heap mStack; }; /****************************************************************************** * Handles pre/post script processing. */ class MOZ_RAII AutoScriptEvaluate { public: /** * Saves the JSContext as well as initializing our state * @param cx The JSContext, this can be null, we don't do anything then */ explicit AutoScriptEvaluate(JSContext * cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : mJSContext(cx), mEvaluated(false) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; } /** * Does the pre script evaluation. * This function should only be called once, and will assert if called * more than once */ bool StartEvaluating(JS::HandleObject scope); /** * Does the post script evaluation. */ ~AutoScriptEvaluate(); private: JSContext* mJSContext; mozilla::Maybe mState; bool mEvaluated; mozilla::Maybe mAutoCompartment; MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER // No copying or assignment allowed AutoScriptEvaluate(const AutoScriptEvaluate&) = delete; AutoScriptEvaluate & operator =(const AutoScriptEvaluate&) = delete; }; /***************************************************************************/ class MOZ_RAII AutoResolveName { public: AutoResolveName(XPCCallContext& ccx, JS::HandleId name MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : mOld(ccx, XPCJSRuntime::Get()->SetResolveName(name)) #ifdef DEBUG ,mCheck(ccx, name) #endif { MOZ_GUARD_OBJECT_NOTIFIER_INIT; } ~AutoResolveName() { #ifdef DEBUG jsid old = #endif XPCJSRuntime::Get()->SetResolveName(mOld); MOZ_ASSERT(old == mCheck, "Bad Nesting!"); } private: JS::RootedId mOld; #ifdef DEBUG JS::RootedId mCheck; #endif MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; /***************************************************************************/ // AutoMarkingPtr is the base class for the various AutoMarking pointer types // below. This system allows us to temporarily protect instances of our garbage // collected types after they are constructed but before they are safely // attached to other rooted objects. // This base class has pure virtual support for marking. class AutoMarkingPtr { public: explicit AutoMarkingPtr(JSContext* cx) { mRoot = XPCJSRuntime::Get()->GetAutoRootsAdr(); mNext = *mRoot; *mRoot = this; } virtual ~AutoMarkingPtr() { if (mRoot) { MOZ_ASSERT(*mRoot == this); *mRoot = mNext; } } void TraceJSAll(JSTracer* trc) { for (AutoMarkingPtr* cur = this; cur; cur = cur->mNext) cur->TraceJS(trc); } void MarkAfterJSFinalizeAll() { for (AutoMarkingPtr* cur = this; cur; cur = cur->mNext) cur->MarkAfterJSFinalize(); } protected: virtual void TraceJS(JSTracer* trc) = 0; virtual void MarkAfterJSFinalize() = 0; private: AutoMarkingPtr** mRoot; AutoMarkingPtr* mNext; }; template class TypedAutoMarkingPtr : public AutoMarkingPtr { public: explicit TypedAutoMarkingPtr(JSContext* cx) : AutoMarkingPtr(cx), mPtr(nullptr) {} TypedAutoMarkingPtr(JSContext* cx, T* ptr) : AutoMarkingPtr(cx), mPtr(ptr) {} T* get() const { return mPtr; } operator T*() const { return mPtr; } T* operator->() const { return mPtr; } TypedAutoMarkingPtr& operator =(T* ptr) { mPtr = ptr; return *this; } protected: virtual void TraceJS(JSTracer* trc) { if (mPtr) { mPtr->TraceJS(trc); mPtr->AutoTrace(trc); } } virtual void MarkAfterJSFinalize() { if (mPtr) mPtr->Mark(); } private: T* mPtr; }; typedef TypedAutoMarkingPtr AutoMarkingNativeInterfacePtr; typedef TypedAutoMarkingPtr AutoMarkingNativeSetPtr; typedef TypedAutoMarkingPtr AutoMarkingWrappedNativePtr; typedef TypedAutoMarkingPtr AutoMarkingWrappedNativeTearOffPtr; typedef TypedAutoMarkingPtr AutoMarkingWrappedNativeProtoPtr; typedef TypedAutoMarkingPtr AutoMarkingNativeScriptableInfoPtr; template class ArrayAutoMarkingPtr : public AutoMarkingPtr { public: explicit ArrayAutoMarkingPtr(JSContext* cx) : AutoMarkingPtr(cx), mPtr(nullptr), mCount(0) {} ArrayAutoMarkingPtr(JSContext* cx, T** ptr, uint32_t count, bool clear) : AutoMarkingPtr(cx), mPtr(ptr), mCount(count) { if (!mPtr) mCount = 0; else if (clear) memset(mPtr, 0, mCount*sizeof(T*)); } T** get() const { return mPtr; } operator T**() const { return mPtr; } T** operator->() const { return mPtr; } ArrayAutoMarkingPtr& operator =(const ArrayAutoMarkingPtr& other) { mPtr = other.mPtr; mCount = other.mCount; return *this; } protected: virtual void TraceJS(JSTracer* trc) { for (uint32_t i = 0; i < mCount; i++) { if (mPtr[i]) { mPtr[i]->TraceJS(trc); mPtr[i]->AutoTrace(trc); } } } virtual void MarkAfterJSFinalize() { for (uint32_t i = 0; i < mCount; i++) { if (mPtr[i]) mPtr[i]->Mark(); } } private: T** mPtr; uint32_t mCount; }; typedef ArrayAutoMarkingPtr AutoMarkingNativeInterfacePtrArrayPtr; /***************************************************************************/ namespace xpc { // Allocates a string that grants all access ("AllAccess") char* CloneAllAccess(); // Returns access if wideName is in list char* CheckAccessList(const char16_t* wideName, const char* const list[]); } /* namespace xpc */ /***************************************************************************/ // in xpcvariant.cpp... // {1809FD50-91E8-11d5-90F9-0010A4E73D9A} #define XPCVARIANT_IID \ {0x1809fd50, 0x91e8, 0x11d5, \ { 0x90, 0xf9, 0x0, 0x10, 0xa4, 0xe7, 0x3d, 0x9a } } // {DC524540-487E-4501-9AC7-AAA784B17C1C} #define XPCVARIANT_CID \ {0xdc524540, 0x487e, 0x4501, \ { 0x9a, 0xc7, 0xaa, 0xa7, 0x84, 0xb1, 0x7c, 0x1c } } class XPCVariant : public nsIVariant { public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_NSIVARIANT NS_DECL_CYCLE_COLLECTION_CLASS(XPCVariant) // If this class ever implements nsIWritableVariant, take special care with // the case when mJSVal is JSVAL_STRING, since we don't own the data in // that case. // We #define and iid so that out module local code can use QI to detect // if a given nsIVariant is in fact an XPCVariant. NS_DECLARE_STATIC_IID_ACCESSOR(XPCVARIANT_IID) static already_AddRefed newVariant(JSContext* cx, JS::Value aJSVal); /** * This getter clears the gray bit before handing out the Value if the Value * represents a JSObject. That means that the object is guaranteed to be * kept alive past the next CC. */ JS::Value GetJSVal() const { if (!mJSVal.isPrimitive()) JS::ExposeObjectToActiveJS(&mJSVal.toObject()); return mJSVal; } /** * This getter does not change the color of the Value (if it represents a * JSObject) meaning that the value returned is not guaranteed to be kept * alive past the next CC. * * This should only be called if you are certain that the return value won't * be passed into a JS API function and that it won't be stored without * being rooted (or otherwise signaling the stored value to the CC). */ JS::Value GetJSValPreserveColor() const {return mJSVal;} XPCVariant(JSContext* cx, JS::Value aJSVal); /** * Convert a variant into a JS::Value. * * @param ccx the context for the whole procedure * @param variant the variant to convert * @param scope the default scope to put on the new JSObject's parent chain * @param pErr [out] relevant error code, if any. * @param pJSVal [out] the resulting jsval. */ static bool VariantDataToJS(nsIVariant* variant, nsresult* pErr, JS::MutableHandleValue pJSVal); bool IsPurple() { return mRefCnt.IsPurple(); } void RemovePurple() { mRefCnt.RemovePurple(); } void SetCCGeneration(uint32_t aGen) { mCCGeneration = aGen; } uint32_t CCGeneration() { return mCCGeneration; } protected: virtual ~XPCVariant() { } bool InitializeData(JSContext* cx); protected: nsDiscriminatedUnion mData; JS::Heap mJSVal; bool mReturnRawObject : 1; uint32_t mCCGeneration : 31; }; NS_DEFINE_STATIC_IID_ACCESSOR(XPCVariant, XPCVARIANT_IID) class XPCTraceableVariant: public XPCVariant, public XPCRootSetElem { public: XPCTraceableVariant(JSContext* cx, JS::Value aJSVal) : XPCVariant(cx, aJSVal) { nsXPConnect::GetRuntimeInstance()->AddVariantRoot(this); } virtual ~XPCTraceableVariant(); void TraceJS(JSTracer* trc); }; /***************************************************************************/ // Utilities inline void* xpc_GetJSPrivate(JSObject* obj) { return js::GetObjectPrivate(obj); } inline JSContext* xpc_GetSafeJSContext() { return XPCJSRuntime::Get()->GetJSContextStack()->GetSafeJSContext(); } namespace xpc { JSAddonId* NewAddonId(JSContext* cx, const nsACString& id); // JSNatives to expose atob and btoa in various non-DOM XPConnect scopes. bool Atob(JSContext* cx, unsigned argc, JS::Value* vp); bool Btoa(JSContext* cx, unsigned argc, JS::Value* vp); // Helper function that creates a JSFunction that wraps a native function that // forwards the call to the original 'callable'. class FunctionForwarderOptions; bool NewFunctionForwarder(JSContext* cx, JS::HandleId id, JS::HandleObject callable, FunctionForwarderOptions& options, JS::MutableHandleValue vp); // Old fashioned xpc error reporter. Try to use JS_ReportError instead. nsresult ThrowAndFail(nsresult errNum, JSContext* cx, bool* retval); struct GlobalProperties { GlobalProperties() { mozilla::PodZero(this); } bool Parse(JSContext* cx, JS::HandleObject obj); bool Define(JSContext* cx, JS::HandleObject obj); bool CSS : 1; bool indexedDB : 1; bool XMLHttpRequest : 1; bool TextDecoder : 1; bool TextEncoder : 1; bool URL : 1; bool URLSearchParams : 1; bool atob : 1; bool btoa : 1; bool Blob : 1; bool File : 1; bool crypto : 1; bool rtcIdentityProvider : 1; bool fetch : 1; bool caches : 1; bool fileReader: 1; }; // Infallible. already_AddRefed NewSandboxConstructor(); // Returns true if class of 'obj' is SandboxClass. bool IsSandbox(JSObject* obj); class MOZ_STACK_CLASS OptionsBase { public: explicit OptionsBase(JSContext* cx = xpc_GetSafeJSContext(), JSObject* options = nullptr) : mCx(cx) , mObject(cx, options) { } virtual bool Parse() = 0; protected: bool ParseValue(const char* name, JS::MutableHandleValue prop, bool* found = nullptr); bool ParseBoolean(const char* name, bool* prop); bool ParseObject(const char* name, JS::MutableHandleObject prop); bool ParseJSString(const char* name, JS::MutableHandleString prop); bool ParseString(const char* name, nsCString& prop); bool ParseString(const char* name, nsString& prop); bool ParseId(const char* name, JS::MutableHandleId id); JSContext* mCx; JS::RootedObject mObject; }; class MOZ_STACK_CLASS SandboxOptions : public OptionsBase { public: explicit SandboxOptions(JSContext* cx = xpc_GetSafeJSContext(), JSObject* options = nullptr) : OptionsBase(cx, options) , wantXrays(true) , allowWaivers(true) , wantComponents(true) , wantExportHelpers(false) , isWebExtensionContentScript(false) , waiveInterposition(false) , proto(cx) , addonId(cx) , writeToGlobalPrototype(false) , sameZoneAs(cx) , freshZone(false) , invisibleToDebugger(false) , discardSource(false) , metadata(cx) { } virtual bool Parse(); bool wantXrays; bool allowWaivers; bool wantComponents; bool wantExportHelpers; bool isWebExtensionContentScript; bool waiveInterposition; JS::RootedObject proto; nsCString sandboxName; JS::RootedString addonId; bool writeToGlobalPrototype; JS::RootedObject sameZoneAs; bool freshZone; bool invisibleToDebugger; bool discardSource; GlobalProperties globalProperties; JS::RootedValue metadata; protected: bool ParseGlobalProperties(); }; class MOZ_STACK_CLASS CreateObjectInOptions : public OptionsBase { public: explicit CreateObjectInOptions(JSContext* cx = xpc_GetSafeJSContext(), JSObject* options = nullptr) : OptionsBase(cx, options) , defineAs(cx, JSID_VOID) { } virtual bool Parse() { return ParseId("defineAs", &defineAs); } JS::RootedId defineAs; }; class MOZ_STACK_CLASS ExportFunctionOptions : public OptionsBase { public: explicit ExportFunctionOptions(JSContext* cx = xpc_GetSafeJSContext(), JSObject* options = nullptr) : OptionsBase(cx, options) , defineAs(cx, JSID_VOID) , allowCrossOriginArguments(false) { } virtual bool Parse() { return ParseId("defineAs", &defineAs) && ParseBoolean("allowCrossOriginArguments", &allowCrossOriginArguments); } JS::RootedId defineAs; bool allowCrossOriginArguments; }; class MOZ_STACK_CLASS FunctionForwarderOptions : public OptionsBase { public: explicit FunctionForwarderOptions(JSContext* cx = xpc_GetSafeJSContext(), JSObject* options = nullptr) : OptionsBase(cx, options) , allowCrossOriginArguments(false) { } JSObject* ToJSObject(JSContext* cx) { JS::RootedObject obj(cx, JS_NewObjectWithGivenProto(cx, nullptr, nullptr)); if (!obj) return nullptr; JS::RootedValue val(cx); unsigned attrs = JSPROP_READONLY | JSPROP_PERMANENT; val = JS::BooleanValue(allowCrossOriginArguments); if (!JS_DefineProperty(cx, obj, "allowCrossOriginArguments", val, attrs)) return nullptr; return obj; } virtual bool Parse() { return ParseBoolean("allowCrossOriginArguments", &allowCrossOriginArguments); } bool allowCrossOriginArguments; }; class MOZ_STACK_CLASS StackScopedCloneOptions : public OptionsBase { public: explicit StackScopedCloneOptions(JSContext* cx = xpc_GetSafeJSContext(), JSObject* options = nullptr) : OptionsBase(cx, options) , wrapReflectors(false) , cloneFunctions(false) , deepFreeze(false) { } virtual bool Parse() { return ParseBoolean("wrapReflectors", &wrapReflectors) && ParseBoolean("cloneFunctions", &cloneFunctions) && ParseBoolean("deepFreeze", &deepFreeze); } // When a reflector is encountered, wrap it rather than aborting the clone. bool wrapReflectors; // When a function is encountered, clone it (exportFunction-style) rather than // aborting the clone. bool cloneFunctions; // If true, the resulting object is deep-frozen after being cloned. bool deepFreeze; }; JSObject* CreateGlobalObject(JSContext* cx, const JSClass* clasp, nsIPrincipal* principal, JS::CompartmentOptions& aOptions); // InitGlobalObject enters the compartment of aGlobal, so it doesn't matter what // compartment aJSContext is in. bool InitGlobalObject(JSContext* aJSContext, JS::Handle aGlobal, uint32_t aFlags); // Helper for creating a sandbox object to use for evaluating // untrusted code completely separated from all other code in the // system using EvalInSandbox(). Takes the JSContext on which to // do setup etc on, puts the sandbox object in *vp (which must be // rooted by the caller), and uses the principal that's either // directly passed in prinOrSop or indirectly as an // nsIScriptObjectPrincipal holding the principal. If no principal is // reachable through prinOrSop, a new null principal will be created // and used. nsresult CreateSandboxObject(JSContext* cx, JS::MutableHandleValue vp, nsISupports* prinOrSop, xpc::SandboxOptions& options); // Helper for evaluating scripts in a sandbox object created with // CreateSandboxObject(). The caller is responsible of ensuring // that *rval doesn't get collected during the call or usage after the // call. This helper will use filename and lineNo for error reporting, // and if no filename is provided it will use the codebase from the // principal and line number 1 as a fallback. nsresult EvalInSandbox(JSContext* cx, JS::HandleObject sandbox, const nsAString& source, const nsACString& filename, int32_t lineNo, JSVersion jsVersion, JS::MutableHandleValue rval); nsresult GetSandboxAddonId(JSContext* cx, JS::HandleObject sandboxArg, JS::MutableHandleValue rval); // Helper for retrieving metadata stored in a reserved slot. The metadata // is set during the sandbox creation using the "metadata" option. nsresult GetSandboxMetadata(JSContext* cx, JS::HandleObject sandboxArg, JS::MutableHandleValue rval); nsresult SetSandboxMetadata(JSContext* cx, JS::HandleObject sandboxArg, JS::HandleValue metadata); bool CreateObjectIn(JSContext* cx, JS::HandleValue vobj, CreateObjectInOptions& options, JS::MutableHandleValue rval); bool EvalInWindow(JSContext* cx, const nsAString& source, JS::HandleObject scope, JS::MutableHandleValue rval); bool ExportFunction(JSContext* cx, JS::HandleValue vscope, JS::HandleValue vfunction, JS::HandleValue voptions, JS::MutableHandleValue rval); bool CloneInto(JSContext* cx, JS::HandleValue vobj, JS::HandleValue vscope, JS::HandleValue voptions, JS::MutableHandleValue rval); bool StackScopedClone(JSContext* cx, StackScopedCloneOptions& options, JS::MutableHandleValue val); } /* namespace xpc */ /***************************************************************************/ // Inlined utilities. inline bool xpc_ForcePropertyResolve(JSContext* cx, JS::HandleObject obj, jsid id); inline jsid GetRTIdByIndex(JSContext* cx, unsigned index); namespace xpc { enum WrapperDenialType { WrapperDenialForXray = 0, WrapperDenialForCOW, WrapperDenialTypeCount }; bool ReportWrapperDenial(JSContext* cx, JS::HandleId id, WrapperDenialType type, const char* reason); class CompartmentPrivate { public: enum LocationHint { LocationHintRegular, LocationHintAddon }; explicit CompartmentPrivate(JSCompartment* c); ~CompartmentPrivate(); static CompartmentPrivate* Get(JSCompartment* compartment) { MOZ_ASSERT(compartment); void* priv = JS_GetCompartmentPrivate(compartment); return static_cast(priv); } static CompartmentPrivate* Get(JSObject* object) { JSCompartment* compartment = js::GetObjectCompartment(object); return Get(compartment); } // Controls whether this compartment gets Xrays to same-origin. This behavior // is deprecated, but is still the default for sandboxes for compatibity // reasons. bool wantXrays; // Controls whether this compartment is allowed to waive Xrays to content // that it subsumes. This should generally be true, except in cases where we // want to prevent code from depending on Xray Waivers (which might make it // more portable to other browser architectures). bool allowWaivers; // This flag is intended for a very specific use, internal to Gecko. It may // go away or change behavior at any time. It should not be added to any // documentation and it should not be used without consulting the XPConnect // module owner. bool writeToGlobalPrototype; // When writeToGlobalPrototype is true, we use this flag to temporarily // disable the writeToGlobalPrototype behavior (when resolving standard // classes, for example). bool skipWriteToGlobalPrototype; // This scope corresponds to a WebExtension content script, and receives // various bits of special compatibility behavior. bool isWebExtensionContentScript; // Even if an add-on needs interposition, it does not necessary need it // for every scope. If this flag is set we waive interposition for this // scope. bool waiveInterposition; // This is only ever set during mochitest runs when enablePrivilege is called. // It's intended as a temporary stopgap measure until we can finish ripping out // enablePrivilege. Once set, this value is never unset (i.e., it doesn't follow // the old scoping rules of enablePrivilege). // // Using it in production is inherently unsafe. bool universalXPConnectEnabled; // This is only ever set during mochitest runs when enablePrivilege is called. // It allows the SpecialPowers scope to waive the normal chrome security // wrappers and expose properties directly to content. This lets us avoid a // bunch of overhead and complexity in our SpecialPowers automation glue. // // Using it in production is inherently unsafe. bool forcePermissiveCOWs; // Whether we've emitted a warning about a property that was filtered out // by a security wrapper. See XrayWrapper.cpp. bool wrapperDenialWarnings[WrapperDenialTypeCount]; // The scriptability of this compartment. Scriptability scriptability; // Our XPCWrappedNativeScope. This is non-null if and only if this is an // XPConnect compartment. XPCWrappedNativeScope* scope; const nsACString& GetLocation() { if (location.IsEmpty() && locationURI) { nsCOMPtr jsLocationURI = do_QueryInterface(locationURI); if (jsLocationURI) { // We cannot call into JS-implemented nsIURI objects, because // we are iterating over the JS heap at this point. location = NS_LITERAL_CSTRING(""); } else if (NS_FAILED(locationURI->GetSpec(location))) { location = NS_LITERAL_CSTRING(""); } } return location; } bool GetLocationURI(nsIURI** aURI) { return GetLocationURI(LocationHintRegular, aURI); } bool GetLocationURI(LocationHint aLocationHint, nsIURI** aURI) { if (locationURI) { nsCOMPtr rval = locationURI; rval.forget(aURI); return true; } return TryParseLocationURI(aLocationHint, aURI); } void SetLocation(const nsACString& aLocation) { if (aLocation.IsEmpty()) return; if (!location.IsEmpty() || locationURI) return; location = aLocation; } void SetLocationURI(nsIURI* aLocationURI) { if (!aLocationURI) return; if (locationURI) return; locationURI = aLocationURI; } JSObject2WrappedJSMap* GetWrappedJSMap() const { return mWrappedJSMap; } void UpdateWeakPointersAfterGC(XPCJSRuntime* runtime); size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf); private: nsCString location; nsCOMPtr locationURI; JSObject2WrappedJSMap* mWrappedJSMap; bool TryParseLocationURI(LocationHint aType, nsIURI** aURI); }; bool IsUniversalXPConnectEnabled(JSCompartment* compartment); bool IsUniversalXPConnectEnabled(JSContext* cx); bool EnableUniversalXPConnect(JSContext* cx); inline void CrashIfNotInAutomation() { const char* prefName = "security.turn_off_all_security_so_that_viruses_can_take_over_this_computer"; MOZ_RELEASE_ASSERT(mozilla::Preferences::GetBool(prefName)); } inline XPCWrappedNativeScope* ObjectScope(JSObject* obj) { return CompartmentPrivate::Get(obj)->scope; } JSObject* NewOutObject(JSContext* cx); bool IsOutObject(JSContext* cx, JSObject* obj); nsresult HasInstance(JSContext* cx, JS::HandleObject objArg, const nsID* iid, bool* bp); nsIPrincipal* GetObjectPrincipal(JSObject* obj); } // namespace xpc namespace mozilla { namespace dom { extern bool DefineStaticJSVals(JSContext* cx); } // namespace dom } // namespace mozilla bool xpc_LocalizeRuntime(JSRuntime* rt); void xpc_DelocalizeRuntime(JSRuntime* rt); /***************************************************************************/ // Inlines use the above - include last. #include "XPCInlines.h" /***************************************************************************/ // Maps have inlines that use the above - include last. #include "XPCMaps.h" /***************************************************************************/ #endif /* xpcprivate_h___ */