From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- js/xpconnect/src/xpcprivate.h | 2842 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2842 insertions(+) create mode 100644 js/xpconnect/src/xpcprivate.h (limited to 'js/xpconnect/src/xpcprivate.h') diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h new file mode 100644 index 0000000000..2c629e5bc2 --- /dev/null +++ b/js/xpconnect/src/xpcprivate.h @@ -0,0 +1,2842 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 compartments. The + * XPCWrappedNativeScope has a pointer to the JS compartment. The global of a + * flattened JS object is one of the globals in this compartment (the exception + * to this rule is when a PreCreate hook asks for a different global; 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 can have pointers from XPCWrappedNativeProto and + * XPCWrappedNative (since C++ objects can have scriptable info without having + * class 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/BasePrincipal.h" +#include "mozilla/CycleCollectedJSContext.h" +#include "mozilla/CycleCollectedJSRuntime.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/DefineEnum.h" +#include "mozilla/HashFunctions.h" +#include "mozilla/LinkedList.h" +#include "mozilla/Maybe.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/mozalloc.h" +#include "mozilla/Preferences.h" +#include "mozilla/TimeStamp.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/Vector.h" + +#include "mozilla/dom/ScriptSettings.h" + +#include +#include +#include +#include + +#include "xpcpublic.h" +#include "js/HashTable.h" +#include "js/GCHashTable.h" +#include "js/Object.h" // JS::GetClass, JS::GetCompartment +#include "js/PropertyAndElement.h" // JS_DefineProperty +#include "js/TracingAPI.h" +#include "js/WeakMapPtr.h" +#include "nscore.h" +#include "nsXPCOM.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 "nsISimpleEnumerator.h" +#include "nsIXPConnect.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 "prenv.h" +#include "prcvar.h" +#include "nsString.h" +#include "nsReadableUtils.h" + +#include "MainThreadUtils.h" + +#include "nsIConsoleService.h" + +#include "nsVariant.h" +#include "nsCOMArray.h" +#include "nsTArray.h" +#include "nsBaseHashtable.h" +#include "nsHashKeys.h" +#include "nsWrapperCache.h" +#include "nsStringBuffer.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" + +#ifdef XP_WIN +// Nasty MS defines +# ifdef GetClassInfo +# undef GetClassInfo +# endif +# ifdef GetClassName +# undef GetClassName +# endif +#endif /* XP_WIN */ + +namespace mozilla { +namespace dom { +class AutoEntryScript; +class Exception; +} // namespace dom +} // namespace mozilla + +/***************************************************************************/ +// data declarations... +extern const char XPC_EXCEPTION_CONTRACTID[]; +extern const char XPC_CONSOLE_CONTRACTID[]; +extern const char XPC_SCRIPT_ERROR_CONTRACTID[]; +extern const char XPC_XPCONNECT_CONTRACTID[]; + +/***************************************************************************/ +// Helper function. + +namespace xpc { + +inline bool IsWrappedNativeReflector(JSObject* obj) { + return JS::GetClass(obj)->isWrappedNative(); +} + +} // namespace xpc + +/*************************************************************************** +**************************************************************************** +* +* 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 + + // non-interface implementation + public: + static XPCJSRuntime* GetRuntimeInstance(); + XPCJSContext* GetContext() { return mContext; } + + static nsIScriptSecurityManager* SecurityManager() { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(gScriptSecurityManager); + return gScriptSecurityManager; + } + + static nsIPrincipal* SystemPrincipal() { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(gSystemPrincipal); + return gSystemPrincipal; + } + + // Called by module code in dll startup + static void InitStatics(); + // Called by module code on dll shutdown. + static void ReleaseXPConnectSingleton(); + + static void InitJSContext(); + + void RecordTraversal(void* p, nsISupports* s); + + protected: + virtual ~nsXPConnect(); + + nsXPConnect(); + + private: + // Singleton instance + static nsXPConnect* gSelf; + static bool gOnceAliveNowDead; + + XPCJSContext* mContext = nullptr; + XPCJSRuntime* mRuntime = nullptr; + + friend class nsIXPConnect; + + public: + static nsIScriptSecurityManager* gScriptSecurityManager; + static nsIPrincipal* gSystemPrincipal; +}; + +/***************************************************************************/ + +// In the current xpconnect system there can only be one XPCJSContext. +// So, xpconnect can only be used on one JSContext within the process. + +class WatchdogManager; + +// clang-format off +MOZ_DEFINE_ENUM(WatchdogTimestampCategory, ( + TimestampWatchdogWakeup, + TimestampWatchdogHibernateStart, + TimestampWatchdogHibernateStop, + TimestampContextStateChange +)); +// clang-format on + +class AsyncFreeSnowWhite; +class XPCWrappedNativeScope; + +using XPCWrappedNativeScopeList = mozilla::LinkedList; + +class XPCJSContext final : public mozilla::CycleCollectedJSContext, + public mozilla::LinkedListElement { + public: + static XPCJSContext* NewXPCJSContext(); + static XPCJSContext* Get(); + + XPCJSRuntime* Runtime() const; + + virtual mozilla::CycleCollectedJSRuntime* CreateRuntime( + JSContext* aCx) override; + + 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; + } + + bool JSContextInitialized(JSContext* cx); + + virtual void BeforeProcessTask(bool aMightBlock) override; + virtual void AfterProcessTask(uint32_t aNewRecursionDepth) override; + + // Relay to the CCGCScheduler instead of queuing up an idle runnable + // (as is done for workers in CycleCollectedJSContext). + virtual void MaybePokeGC() override; + + ~XPCJSContext(); + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf); + + bool IsSystemCaller() const override; + + AutoMarkingPtr** GetAutoRootsAdr() { return &mAutoRoots; } + + nsresult GetPendingResult() { return mPendingResult; } + void SetPendingResult(nsresult rv) { mPendingResult = rv; } + + PRTime GetWatchdogTimestamp(WatchdogTimestampCategory aCategory); + + static bool RecordScriptActivity(bool aActive); + + bool SetHasScriptActivity(bool aActive) { + bool oldValue = mHasScriptActivity; + mHasScriptActivity = aActive; + return oldValue; + } + + static bool InterruptCallback(JSContext* cx); + + // 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_VALUE, + IDX_QUERY_INTERFACE, + IDX_COMPONENTS, + IDX_CC, + IDX_CI, + IDX_CR, + IDX_CU, + IDX_SERVICES, + IDX_WRAPPED_JSOBJECT, + IDX_PROTOTYPE, + IDX_EVAL, + IDX_CONTROLLERS, + IDX_CONTROLLERS_CLASS, + IDX_LENGTH, + IDX_NAME, + IDX_UNDEFINED, + IDX_EMPTYSTRING, + IDX_FILENAME, + IDX_LINENUMBER, + IDX_COLUMNNUMBER, + IDX_STACK, + IDX_MESSAGE, + IDX_CAUSE, + IDX_ERRORS, + IDX_LASTINDEX, + IDX_THEN, + IDX_ISINSTANCE, + IDX_INFINITY, + IDX_NAN, + IDX_CLASS_ID, + IDX_INTERFACE_ID, + IDX_INITIALIZER, + IDX_PRINT, + IDX_FETCH, + IDX_CRYPTO, + IDX_INDEXEDDB, + IDX_STRUCTUREDCLONE, + IDX_TOTAL_COUNT // just a count of the above + }; + + inline JS::HandleId GetStringID(unsigned index) const; + inline const char* GetStringName(unsigned index) const; + + private: + XPCJSContext(); + + MOZ_IS_CLASS_INIT + nsresult Initialize(); + + XPCCallContext* mCallContext; + AutoMarkingPtr* mAutoRoots; + jsid mResolveName; + XPCWrappedNative* mResolvingWrapper; + WatchdogManager* mWatchdogManager; + + // Number of XPCJSContexts currently alive. + static uint32_t sInstanceCount; + static mozilla::StaticAutoPtr sWatchdogInstance; + static WatchdogManager* GetWatchdogManager(); + + // 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; + // Accumulates total time we actually waited for telemetry + mozilla::TimeDuration mSlowScriptActualWait; + bool mTimeoutAccumulated; + bool mExecutedChromeScript; + + bool mHasScriptActivity; + + // mPendingResult is used to implement Components.returnCode. Only really + // meaningful while calling through XPCWrappedJS. + nsresult mPendingResult; + + // These members must be accessed via WatchdogManager. + enum { CONTEXT_ACTIVE, CONTEXT_INACTIVE } mActive; + PRTime mLastStateChange; + + friend class XPCJSRuntime; + friend class Watchdog; + friend class WatchdogManager; + friend class AutoLockWatchdog; +}; + +class XPCJSRuntime final : public mozilla::CycleCollectedJSRuntime { + public: + static XPCJSRuntime* Get(); + + void RemoveWrappedJS(nsXPCWrappedJS* wrapper); + void AssertInvalidWrappedJSNotInTable(nsXPCWrappedJS* wrapper) const; + + JSObject2WrappedJSMap* GetMultiCompartmentWrappedJSMap() const { + return mWrappedJSMap.get(); + } + + IID2NativeInterfaceMap* GetIID2NativeInterfaceMap() const { + return mIID2NativeInterfaceMap.get(); + } + + ClassInfo2NativeSetMap* GetClassInfo2NativeSetMap() const { + return mClassInfo2NativeSetMap.get(); + } + + NativeSetMap* GetNativeSetMap() const { return mNativeSetMap.get(); } + + using WrappedNativeProtoVector = + mozilla::Vector, 0, + InfallibleAllocPolicy>; + WrappedNativeProtoVector& GetDyingWrappedNativeProtos() { + return mDyingWrappedNativeProtos; + } + + XPCWrappedNativeScopeList& GetWrappedNativeScopes() { + return mWrappedNativeScopes; + } + + bool InitializeStrings(JSContext* cx); + + virtual bool DescribeCustomObjects(JSObject* aObject, const JSClass* aClasp, + char (&aName)[72]) const override; + virtual bool NoteCustomGCThingXPCOMChildren( + const JSClass* aClasp, JSObject* aObj, + nsCycleCollectionTraversalCallback& aCb) const 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; } + + JS::HandleId GetStringID(unsigned index) const { + MOZ_ASSERT(index < XPCJSContext::IDX_TOTAL_COUNT, "index out of range"); + // fromMarkedLocation() is safe because the string is interned. + return JS::HandleId::fromMarkedLocation(&mStrIDs[index]); + } + const char* GetStringName(unsigned index) const { + MOZ_ASSERT(index < XPCJSContext::IDX_TOTAL_COUNT, "index out of range"); + return mStrings[index]; + } + + virtual bool UsefulToMergeZones() const override; + void TraceNativeBlackRoots(JSTracer* trc) override; + void TraceAdditionalNativeGrayRoots(JSTracer* aTracer) override; + void TraverseAdditionalNativeRoots( + nsCycleCollectionNoteRootCallback& cb) override; + void UnmarkSkippableJSHolders(); + void PrepareForForgetSkippable() override; + void BeginCycleCollectionCallback(mozilla::CCReason aReason) override; + void EndCycleCollectionCallback( + mozilla::CycleCollectorResults& aResults) override; + void DispatchDeferredDeletion(bool aContinuation, + bool aPurge = false) override; + + void CustomGCCallback(JSGCStatus status) override; + void CustomOutOfMemoryCallback() override; + void OnLargeAllocationFailure(); + static void GCSliceCallback(JSContext* cx, JS::GCProgress progress, + const JS::GCDescription& desc); + static void DoCycleCollectionCallback(JSContext* cx); + static void FinalizeCallback(JS::GCContext* gcx, JSFinalizeStatus status, + void* data); + static void WeakPointerZonesCallback(JSTracer* trc, void* data); + static void WeakPointerCompartmentCallback(JSTracer* trc, + JS::Compartment* comp, void* data); + + inline void AddSubjectToFinalizationWJS(nsXPCWrappedJS* wrappedJS); + + void DebugDump(int16_t depth); + + bool GCIsRunning() const { return mGCIsRunning; } + + ~XPCJSRuntime(); + + void AddGCCallback(xpcGCCallback cb); + void RemoveGCCallback(xpcGCCallback cb); + + JSObject* GetUAWidgetScope(JSContext* cx, nsIPrincipal* principal); + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf); + + /** + * The unprivileged junk scope is an unprivileged sandbox global used for + * convenience by certain operations which need an unprivileged global but + * don't have one immediately handy. It should generally be avoided when + * possible. + * + * The scope is created lazily when it is needed, and held weakly so that it + * is destroyed when there are no longer any remaining external references to + * it. This means that under low memory conditions, when the scope does not + * already exist, we may not be able to create one. In these circumstances, + * the infallible version of this API will abort, and the fallible version + * will return null. Callers should therefore prefer the fallible version when + * on a codepath which can already return failure, but may use the infallible + * one otherwise. + */ + JSObject* UnprivilegedJunkScope(); + JSObject* UnprivilegedJunkScope(const mozilla::fallible_t&); + + bool IsUnprivilegedJunkScope(JSObject*); + JSObject* LoaderGlobal(); + + void DeleteSingletonScopes(); + + private: + explicit XPCJSRuntime(JSContext* aCx); + + MOZ_IS_CLASS_INIT + void Initialize(JSContext* cx); + void Shutdown(JSContext* cx) override; + + static const char* const mStrings[XPCJSContext::IDX_TOTAL_COUNT]; + jsid mStrIDs[XPCJSContext::IDX_TOTAL_COUNT]; + + struct Hasher { + using Key = RefPtr; + using Lookup = Key; + static uint32_t hash(const Lookup& l) { return l->GetOriginNoSuffixHash(); } + static bool match(const Key& k, const Lookup& l) { + return k->FastEquals(l); + } + }; + + struct MapEntryGCPolicy { + static bool traceWeak(JSTracer* trc, + RefPtr* /* unused */, + JS::Heap* value) { + return JS::GCPolicy>::traceWeak(trc, value); + } + }; + + typedef JS::GCHashMap, JS::Heap, + Hasher, js::SystemAllocPolicy, MapEntryGCPolicy> + Principal2JSObjectMap; + + mozilla::UniquePtr mWrappedJSMap; + mozilla::UniquePtr mIID2NativeInterfaceMap; + mozilla::UniquePtr mClassInfo2NativeSetMap; + mozilla::UniquePtr mNativeSetMap; + Principal2JSObjectMap mUAWidgetScopeMap; + XPCWrappedNativeScopeList mWrappedNativeScopes; + WrappedNativeProtoVector mDyingWrappedNativeProtos; + bool mGCIsRunning; + nsTArray mNativesToReleaseArray; + bool mDoingFinalization; + mozilla::LinkedList mSubjectToFinalizationWJS; + nsTArray extraGCCallbacks; + JS::GCSliceCallback mPrevGCSliceCallback; + JS::DoCycleCollectionCallback mPrevDoCycleCollectionCallback; + mozilla::WeakPtr mUnprivilegedJunkScope; + JS::PersistentRootedObject mLoaderGlobal; + RefPtr mAsyncSnowWhiteFreer; + + friend class XPCJSContext; + friend class XPCIncrementalReleaseRunnable; +}; + +inline JS::HandleId XPCJSContext::GetStringID(unsigned index) const { + return Runtime()->GetStringID(index); +} + +inline const char* XPCJSContext::GetStringName(unsigned index) const { + return Runtime()->GetStringName(index); +} + +/***************************************************************************/ + +// No virtuals +// XPCCallContext is ALWAYS declared as a local variable in some function; +// i.e. instance lifetime is always controled by some C++ function returning. +// +// These things are created frequently in many places. We *intentionally* do +// not inialialize all members in order to save on construction overhead. +// Some constructor pass more valid params than others. We init what must be +// init'd and leave other members undefined. In debug builds the accessors +// use a CHECK_STATE macro to track whether or not the object is in a valid +// state to answer the question a caller might be asking. As long as this +// class is maintained correctly it can do its job without a bunch of added +// overhead from useless initializations and non-DEBUG error checking. +// +// Note that most accessors are inlined. + +class MOZ_STACK_CLASS XPCCallContext final { + public: + enum : unsigned { NO_ARGS = (unsigned)-1 }; + + explicit XPCCallContext(JSContext* cx, JS::HandleObject obj = nullptr, + JS::HandleObject funobj = nullptr, + JS::HandleId id = JS::VoidHandlePropertyKey, + unsigned argc = NO_ARGS, JS::Value* argv = nullptr, + JS::Value* rval = nullptr); + + virtual ~XPCCallContext(); + + inline bool IsValid() const; + + inline XPCJSContext* GetContext() const; + inline JSContext* GetJSContext() const; + inline bool GetContextPopRequired() const; + inline XPCCallContext* GetPrevCallContext() const; + + inline JSObject* GetFlattenedJSObject() const; + inline XPCWrappedNative* GetWrapper() const; + + inline bool CanGetTearOff() const; + inline XPCWrappedNativeTearOff* GetTearOff() const; + + inline nsIXPCScriptable* GetScriptable() const; + inline XPCNativeSet* GetSet() const; + inline bool CanGetInterface() const; + inline XPCNativeInterface* GetInterface() const; + inline XPCNativeMember* GetMember() const; + inline bool HasInterfaceAndMember() const; + inline bool GetStaticMemberIsLocal() const; + inline unsigned GetArgc() const; + inline JS::Value* GetArgv() const; + + inline uint16_t GetMethodIndex() const; + + inline jsid GetResolveName() const; + inline jsid SetResolveName(JS::HandleId name); + + inline XPCWrappedNative* GetResolvingWrapper() const; + inline XPCWrappedNative* SetResolvingWrapper(XPCWrappedNative* w); + + inline void SetRetVal(const JS::Value& val); + + void SetName(jsid name); + void SetArgsAndResultPtr(unsigned argc, JS::Value* argv, JS::Value* rval); + void SetCallInfo(XPCNativeInterface* iface, XPCNativeMember* member, + bool isSetter); + + nsresult CanCallNow(); + + void SystemIsBeingShutDown(); + + operator JSContext*() const { return GetJSContext(); } + + private: + // no copy ctor or assignment allowed + XPCCallContext(const XPCCallContext& r) = delete; + XPCCallContext& operator=(const XPCCallContext& r) = delete; + + private: + // posible values for mState + enum State { + INIT_FAILED, + SYSTEM_SHUTDOWN, + HAVE_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: + State mState; + + nsCOMPtr mXPC; + + XPCJSContext* mXPCJSContext; + JSContext* mJSContext; + + // ctor does not necessarily init the following. BEWARE! + + XPCCallContext* mPrevCallContext; + + XPCWrappedNative* mWrapper; + XPCWrappedNativeTearOff* mTearOff; + + nsCOMPtr mScriptable; + + RefPtr mSet; + RefPtr mInterface; + XPCNativeMember* mMember; + + JS::RootedId mName; + bool mStaticMemberIsLocal; + + unsigned mArgc; + JS::Value* mArgv; + JS::Value* mRetVal; + + uint16_t mMethodIndex; +}; + +/*************************************************************************** +**************************************************************************** +* +* Core classes for wrapped native objects for use from JavaScript... +* +**************************************************************************** +***************************************************************************/ + +// These are the various JSClasses and callbacks whose use that required +// visibility from more than one .cpp file. + +extern const JSClass XPC_WN_NoHelper_JSClass; +extern const JSClass XPC_WN_Proto_JSClass; +extern const JSClass XPC_WN_Tearoff_JSClass; +extern const JSClass 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); + +/***************************************************************************/ +// XPCWrappedNativeScope is one-to-one with a JS compartment. + +class XPCWrappedNativeScope final + : public mozilla::LinkedListElement { + public: + XPCJSRuntime* GetRuntime() const { return XPCJSRuntime::Get(); } + + Native2WrappedNativeMap* GetWrappedNativeMap() const { + return mWrappedNativeMap.get(); + } + + ClassInfo2WrappedNativeProtoMap* GetWrappedNativeProtoMap() const { + return mWrappedNativeProtoMap.get(); + } + + nsXPCComponents* GetComponents() const { return mComponents; } + + bool AttachComponentsObject(JSContext* aCx); + + bool AttachJSServices(JSContext* aCx); + + // Returns the JS object reflection of the Components object. + bool GetComponentsJSObject(JSContext* cx, JS::MutableHandleObject obj); + + JSObject* GetExpandoChain(JS::HandleObject target); + + JSObject* DetachExpandoChain(JS::HandleObject target); + + bool SetExpandoChain(JSContext* cx, JS::HandleObject target, + JS::HandleObject chain); + + static void SystemIsBeingShutDown(); + + static void TraceWrappedNativesInAllScopes(XPCJSRuntime* xpcrt, + JSTracer* trc); + + void TraceInside(JSTracer* trc) { + if (mXrayExpandos.initialized()) { + mXrayExpandos.trace(trc); + } + JS::TraceEdge(trc, &mIDProto, "XPCWrappedNativeScope::mIDProto"); + JS::TraceEdge(trc, &mIIDProto, "XPCWrappedNativeScope::mIIDProto"); + JS::TraceEdge(trc, &mCIDProto, "XPCWrappedNativeScope::mCIDProto"); + } + + static void SuspectAllWrappers(nsCycleCollectionNoteRootCallback& cb); + + static void SweepAllWrappedNativeTearOffs(); + + void UpdateWeakPointersAfterGC(JSTracer* trc); + + 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(JSContext* cx, + ScopeSizeInfo* scopeSizeInfo); + + void AddSizeOfIncludingThis(JSContext* cx, ScopeSizeInfo* scopeSizeInfo); + + // Check whether our mAllowContentXBLScope state matches the given + // principal. This is used to avoid sharing compartments on + // mismatch. + bool XBLScopeStateMatches(nsIPrincipal* aPrincipal); + + XPCWrappedNativeScope(JS::Compartment* aCompartment, + JS::HandleObject aFirstGlobal); + virtual ~XPCWrappedNativeScope(); + + mozilla::UniquePtr mWaiverWrapperMap; + + JS::Compartment* Compartment() const { return mCompartment; } + + // Returns the global to use for new WrappedNative objects allocated in this + // compartment. This is better than using arbitrary globals we happen to be in + // because it prevents leaks (objects keep their globals alive). + JSObject* GetGlobalForWrappedNatives() { + return js::GetFirstGlobalInCompartment(Compartment()); + } + + bool AllowContentXBLScope(JS::Realm* aRealm); + + // ID Object prototype caches. + JS::Heap mIDProto; + JS::Heap mIIDProto; + JS::Heap mCIDProto; + + protected: + XPCWrappedNativeScope() = delete; + + private: + mozilla::UniquePtr mWrappedNativeMap; + mozilla::UniquePtr mWrappedNativeProtoMap; + RefPtr mComponents; + JS::Compartment* mCompartment; + + JS::WeakMapPtr mXrayExpandos; + + // For remote XUL domains, we run all XBL in the content scope for compat + // reasons (though we sometimes pref this off for automation). We + // track the result of this decision (mAllowContentXBLScope) for now. + bool mAllowContentXBLScope; +}; + +/***************************************************************************/ +// Slots we use for our functions +#define XPC_FUNCTION_NATIVE_MEMBER_SLOT 0 +#define XPC_FUNCTION_PARENT_OBJECT_SLOT 1 + +/***************************************************************************/ +// XPCNativeMember represents a single idl declared method, attribute or +// constant. + +// Tight. No virtual methods. Can be bitwise copied (until any resolution done). + +class XPCNativeMember final { + public: + static bool GetCallInfo(JSObject* funobj, + RefPtr* 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 */ + MOZ_COUNTED_DEFAULT_CTOR(XPCNativeMember) + MOZ_COUNTED_DTOR(XPCNativeMember) + + XPCNativeMember(const XPCNativeMember& other) + : mName(other.mName), + mIndex(other.mIndex), + mFlags(other.mFlags), + mIndexInInterface(other.mIndexInInterface) { + MOZ_COUNT_CTOR(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 enough 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 final { + public: + NS_INLINE_DECL_REFCOUNTING_WITH_DESTROY(XPCNativeInterface, + DestroyInstance(this)) + + static already_AddRefed GetNewOrUsed(JSContext* cx, + const nsIID* iid); + static already_AddRefed GetNewOrUsed( + JSContext* cx, const nsXPTInterfaceInfo* info); + static already_AddRefed GetNewOrUsed(JSContext* cx, + const char* name); + static already_AddRefed GetISupports(JSContext* cx); + + inline const nsXPTInterfaceInfo* GetInterfaceInfo() const { return mInfo; } + inline jsid GetName() const { return mName; } + + inline const nsIID* GetIID() const; + inline const char* GetNameString() const; + inline XPCNativeMember* FindMember(jsid name) 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); + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf); + + void Trace(JSTracer* trc); + + protected: + static already_AddRefed NewInstance( + JSContext* cx, IID2NativeInterfaceMap* aMap, + const nsXPTInterfaceInfo* aInfo); + + XPCNativeInterface() = delete; + XPCNativeInterface(const nsXPTInterfaceInfo* aInfo, jsid aName) + : mInfo(aInfo), mName(aName), mMemberCount(0) {} + ~XPCNativeInterface(); + + void* operator new(size_t, void* p) noexcept(true) { return p; } + + XPCNativeInterface(const XPCNativeInterface& r) = delete; + XPCNativeInterface& operator=(const XPCNativeInterface& r) = delete; + + static void DestroyInstance(XPCNativeInterface* inst); + + private: + const nsXPTInterfaceInfo* mInfo; + jsid mName; + uint16_t mMemberCount; + XPCNativeMember mMembers[1]; // always last - object sized for array +}; + +/***************************************************************************/ +// XPCNativeSetKey is used to key a XPCNativeSet in a NativeSetMap. +// It represents a new XPCNativeSet we are considering constructing, without +// requiring that the set actually be built. + +class MOZ_STACK_CLASS XPCNativeSetKey final { + public: + // This represents an existing set |baseSet|. + explicit XPCNativeSetKey(XPCNativeSet* baseSet) + : mCx(nullptr), mBaseSet(baseSet), mAddition(nullptr) { + MOZ_ASSERT(baseSet); + } + + // This represents a new set containing only nsISupports and + // |addition|. This needs a JSContext because it may need to + // construct some data structures that need one to construct them. + explicit XPCNativeSetKey(JSContext* cx, XPCNativeInterface* addition) + : mCx(cx), mBaseSet(nullptr), mAddition(addition) { + MOZ_ASSERT(cx); + MOZ_ASSERT(addition); + } + + // This represents the existing set |baseSet| with the interface + // |addition| inserted after existing interfaces. |addition| must + // not already be present in |baseSet|. + explicit XPCNativeSetKey(XPCNativeSet* baseSet, XPCNativeInterface* addition); + ~XPCNativeSetKey() = default; + + XPCNativeSet* GetBaseSet() const { return mBaseSet; } + XPCNativeInterface* GetAddition() const { return mAddition; } + + mozilla::HashNumber Hash() const; + + // Allow shallow copy + + private: + JSContext* mCx; + RefPtr mBaseSet; + RefPtr mAddition; +}; + +/***************************************************************************/ +// XPCNativeSet represents an ordered collection of XPCNativeInterface pointers. + +class XPCNativeSet final { + public: + NS_INLINE_DECL_REFCOUNTING_WITH_DESTROY(XPCNativeSet, DestroyInstance(this)) + + static already_AddRefed GetNewOrUsed(JSContext* cx, + const nsIID* iid); + static already_AddRefed GetNewOrUsed(JSContext* cx, + nsIClassInfo* classInfo); + static already_AddRefed GetNewOrUsed(JSContext* cx, + XPCNativeSetKey* key); + + // 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 already_AddRefed GetNewOrUsed( + JSContext* cx, 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, + RefPtr* pInterface) const; + + inline bool FindMember(JS::HandleId name, XPCNativeMember** pMember, + RefPtr* pInterface, + XPCNativeSet* protoSet, bool* pIsLocal) const; + + inline bool HasInterface(XPCNativeInterface* aInterface) const; + + 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; + + void DebugDump(int16_t depth); + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf); + + protected: + static already_AddRefed NewInstance( + JSContext* cx, nsTArray>&& array); + static already_AddRefed NewInstanceMutate(XPCNativeSetKey* key); + + XPCNativeSet() : mInterfaceCount(0) {} + ~XPCNativeSet(); + void* operator new(size_t, void* p) noexcept(true) { return p; } + + static void DestroyInstance(XPCNativeSet* inst); + + private: + uint16_t mInterfaceCount; + // Always last - object sized for array. + // These are strong references. + XPCNativeInterface* mInterfaces[1]; +}; + +/***********************************************/ +// XPCWrappedNativeProtos hold the additional shared wrapper data for +// XPCWrappedNative whose native objects expose nsIClassInfo. +// +// The XPCWrappedNativeProto is owned by its mJSProtoObject, until that object +// is finalized. After that, it is owned by XPCJSRuntime's +// mDyingWrappedNativeProtos. See XPCWrappedNativeProto::JSProtoObjectFinalized +// and XPCJSRuntime::FinalizeCallback. + +class XPCWrappedNativeProto final { + public: + enum Slots { ProtoSlot, SlotCount }; + + static XPCWrappedNativeProto* GetNewOrUsed(JSContext* cx, + XPCWrappedNativeScope* scope, + nsIClassInfo* classInfo, + nsIXPCScriptable* scriptable); + + XPCWrappedNativeScope* GetScope() const { return mScope; } + + XPCJSRuntime* GetRuntime() const { return mScope->GetRuntime(); } + + JSObject* GetJSProtoObject() const { return mJSProtoObject; } + + JSObject* GetJSProtoObjectPreserveColor() const { + return mJSProtoObject.unbarrieredGet(); + } + + nsIClassInfo* GetClassInfo() const { return mClassInfo; } + + XPCNativeSet* GetSet() const { return mSet; } + + nsIXPCScriptable* GetScriptable() const { return mScriptable; } + + void JSProtoObjectFinalized(JS::GCContext* gcx, JSObject* obj); + void JSProtoObjectMoved(JSObject* obj, const JSObject* old); + + static XPCWrappedNativeProto* Get(JSObject* obj); + + void SystemIsBeingShutDown(); + + void DebugDump(int16_t depth); + + void TraceSelf(JSTracer* trc) { + if (mJSProtoObject) { + TraceEdge(trc, &mJSProtoObject, "XPCWrappedNativeProto::mJSProtoObject"); + } + } + + void TraceJS(JSTracer* trc) { TraceSelf(trc); } + + // NOP. This is just here to make the AutoMarkingPtr code compile. + void Mark() const {} + inline void AutoTrace(JSTracer* trc) {} + + ~XPCWrappedNativeProto(); + + protected: + // disable copy ctor and assignment + XPCWrappedNativeProto(const XPCWrappedNativeProto& r) = delete; + XPCWrappedNativeProto& operator=(const XPCWrappedNativeProto& r) = delete; + + // hide ctor + XPCWrappedNativeProto(XPCWrappedNativeScope* Scope, nsIClassInfo* ClassInfo, + RefPtr&& Set); + + bool Init(JSContext* cx, nsIXPCScriptable* scriptable); + + private: +#ifdef DEBUG + static int32_t gDEBUG_LiveProtoCount; +#endif + + private: + XPCWrappedNativeScope* mScope; + JS::Heap mJSProtoObject; + nsCOMPtr mClassInfo; + RefPtr mSet; + nsCOMPtr mScriptable; +}; + +/***********************************************/ +// XPCWrappedNativeTearOff represents the info needed to make calls to one +// interface on the underlying native object of a XPCWrappedNative. + +class XPCWrappedNativeTearOff final { + public: + enum Slots { FlatObjectSlot, TearOffSlot, SlotCount }; + + 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); + + static XPCWrappedNativeTearOff* Get(JSObject* obj); + + XPCWrappedNativeTearOff() : mInterface(nullptr), mJSObject(nullptr) { + MOZ_COUNT_CTOR(XPCWrappedNativeTearOff); + } + ~XPCWrappedNativeTearOff(); + + // NOP. This is just here to make the AutoMarkingPtr code compile. + inline void TraceJS(JSTracer* trc) {} + inline void AutoTrace(JSTracer* trc) {} + + void Mark() { mJSObject.setFlags(1); } + void Unmark() { mJSObject.unsetFlags(1); } + bool IsMarked() const { return mJSObject.hasFlag(1); } + + XPCWrappedNativeTearOff* AddTearOff() { + MOZ_ASSERT(!mNextTearOff); + mNextTearOff = mozilla::MakeUnique(); + return mNextTearOff.get(); + } + + XPCWrappedNativeTearOff* GetNextTearOff() { return mNextTearOff.get(); } + + private: + XPCWrappedNativeTearOff(const XPCWrappedNativeTearOff& r) = delete; + XPCWrappedNativeTearOff& operator=(const XPCWrappedNativeTearOff& r) = delete; + + private: + XPCNativeInterface* mInterface; + // mNative is an nsRefPtr not an nsCOMPtr because it may not be the canonical + // nsISupports pointer. + RefPtr mNative; + JS::TenuredHeap mJSObject; + mozilla::UniquePtr mNextTearOff; +}; + +/***************************************************************************/ +// XPCWrappedNative the wrapper around one instance of a native xpcom object +// to be used from JavaScript. + +class XPCWrappedNative final : public nsIXPConnectWrappedNative { + public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + + NS_DECL_CYCLE_COLLECTION_CLASS(XPCWrappedNative) + + JSObject* GetJSObject() override; + + bool IsValid() const { return mFlatJSObject.hasFlag(FLAT_JS_OBJECT_VALID); } + + nsresult DebugDump(int16_t depth); + +#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; + } + + 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 { 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.unbarrieredGetPtr(); + } + + XPCNativeSet* GetSet() const { return mSet; } + + void SetSet(already_AddRefed set) { mSet = set; } + + static XPCWrappedNative* Get(JSObject* obj) { + MOZ_ASSERT(xpc::IsWrappedNativeReflector(obj)); + return JS::GetObjectISupports(obj); + } + + private: + void SetFlatJSObject(JSObject* object); + void UnsetFlatJSObject(); + + inline void ExpireWrapper() { + mMaybeScope = (XPCWrappedNativeScope*)(XPC_SCOPE_WORD(mMaybeScope) | + XPC_WRAPPER_EXPIRED); + } + + public: + nsIXPCScriptable* GetScriptable() const { return mScriptable; } + + 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(JSContext* cx, xpcObjectHelper& nativeHelper, + nsIPrincipal* principal, + bool initStandardClasses, + JS::RealmOptions& aOptions, + XPCWrappedNative** wrappedGlobal); + + static nsresult GetNewOrUsed(JSContext* cx, xpcObjectHelper& helper, + 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); + } + + XPCWrappedNativeTearOff* FindTearOff(JSContext* cx, + XPCNativeInterface* aInterface, + bool needJSObject = false, + nsresult* pError = nullptr); + XPCWrappedNativeTearOff* FindTearOff(JSContext* cx, const nsIID& iid); + + void Mark() const {} + + inline void TraceInside(JSTracer* trc) { + if (HasProto()) { + GetProto()->TraceSelf(trc); + } + + JSObject* obj = mFlatJSObject.unbarrieredGetPtr(); + if (obj && JS_IsGlobalObject(obj)) { + xpc::TraceXPCGlobal(trc, obj); + } + } + + void TraceJS(JSTracer* trc) { TraceInside(trc); } + + void TraceSelf(JSTracer* trc) { + // If this got called, we're being kept alive by someone who really + // needs us alive and whole. Do not let our mFlatJSObject go away. + // This is the only time we should be tracing our mFlatJSObject, + // normally somebody else is doing that. + JS::TraceEdge(trc, &mFlatJSObject, "XPCWrappedNative::mFlatJSObject"); + } + + static void Trace(JSTracer* trc, JSObject* obj); + + void AutoTrace(JSTracer* trc) { TraceSelf(trc); } + + inline void SweepTearOffs(); + + // Returns a string that should be freed with js_free, or nullptr on + // failure. + char* ToString(XPCWrappedNativeTearOff* to = nullptr) const; + + static nsIXPCScriptable* GatherProtoScriptable(nsIClassInfo* classInfo); + + bool HasExternalReference() const { return mRefCnt > 1; } + + void Suspect(nsCycleCollectionNoteRootCallback& cb); + void NoteTearoffs(nsCycleCollectionTraversalCallback& cb); + + // Make ctor and dtor protected (rather than private) to placate nsCOMPtr. + protected: + XPCWrappedNative() = delete; + + // This ctor is used if this object will have a proto. + XPCWrappedNative(nsCOMPtr&& aIdentity, + XPCWrappedNativeProto* aProto); + + // This ctor is used if this object will NOT have a proto. + XPCWrappedNative(nsCOMPtr&& aIdentity, + XPCWrappedNativeScope* aScope, RefPtr&& aSet); + + virtual ~XPCWrappedNative(); + void Destroy(); + + private: + enum { + // Flags bits for mFlatJSObject: + FLAT_JS_OBJECT_VALID = js::Bit(0) + }; + + bool Init(JSContext* cx, nsIXPCScriptable* scriptable); + bool FinishInit(JSContext* cx); + + bool ExtendSet(JSContext* aCx, XPCNativeInterface* aInterface); + + nsresult InitTearOff(JSContext* cx, XPCWrappedNativeTearOff* aTearOff, + XPCNativeInterface* aInterface, bool needJSObject); + + bool InitTearOffJSObject(JSContext* cx, XPCWrappedNativeTearOff* to); + + public: + static void GatherScriptable(nsISupports* obj, nsIClassInfo* classInfo, + nsIXPCScriptable** scrProto, + nsIXPCScriptable** scrWrapper); + + private: + union { + XPCWrappedNativeScope* mMaybeScope; + XPCWrappedNativeProto* mMaybeProto; + }; + RefPtr mSet; + JS::TenuredHeap mFlatJSObject; + nsCOMPtr mScriptable; + XPCWrappedNativeTearOff mFirstTearOff; +}; + +/*************************************************************************** +**************************************************************************** +* +* Core classes for wrapped JSObject for use from native code... +* +**************************************************************************** +***************************************************************************/ + +/*************************/ +// nsXPCWrappedJS is a wrapper for a single JSObject for use from native code. +// nsXPCWrappedJS objects are chained together to represent the various +// interface on the single underlying (possibly aggregate) JSObject. + +class nsXPCWrappedJS final : protected nsAutoXPTCStub, + public nsIXPConnectWrappedJSUnmarkGray, + public nsSupportsWeakReference, + public mozilla::LinkedListElement { + public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_NSISUPPORTSWEAKREFERENCE + + NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_AMBIGUOUS( + nsXPCWrappedJS, nsIXPConnectWrappedJS) + + JSObject* GetJSObject() override; + + // This method is defined in XPCWrappedJSClass.cpp to preserve VCS blame. + NS_IMETHOD CallMethod(uint16_t methodIndex, const nsXPTMethodInfo* info, + nsXPTCMiniVariant* nativeParams) 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(JSContext* cx, JS::HandleObject aJSObj, + REFNSIID aIID, nsXPCWrappedJS** wrapper); + + nsISomeInterface* GetXPTCStub() { return mXPTCStub; } + + nsresult DebugDump(int16_t depth); + + /** + * 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.unbarrieredGet(); } + + // 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 XPCJSContext. 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; + + const nsXPTInterfaceInfo* GetInfo() const { return mInfo; } + REFNSIID GetIID() const { return mInfo->IID(); } + 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 bool(mJSObj); } + 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(JSTracer* trc) { + MOZ_ASSERT(IsRootWrapper()); + JS_UpdateWeakPointerAfterGC(trc, &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; + } + + // This method is defined in XPCWrappedJSClass.cpp to preserve VCS blame. + static void DebugDumpInterfaceInfo(const nsXPTInterfaceInfo* aInfo, + int16_t depth); + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; + + virtual ~nsXPCWrappedJS(); + + protected: + nsXPCWrappedJS() = delete; + nsXPCWrappedJS(JSContext* cx, JSObject* aJSObj, + const nsXPTInterfaceInfo* aInfo, nsXPCWrappedJS* root, + nsresult* rv); + + bool CanSkip(); + void Destroy(); + void Unlink(); + + private: + friend class nsIXPConnectWrappedJS; + + JS::Compartment* Compartment() const { + return JS::GetCompartment(mJSObj.unbarrieredGet()); + } + + // These methods are defined in XPCWrappedJSClass.cpp to preserve VCS blame. + static const nsXPTInterfaceInfo* GetInterfaceInfo(REFNSIID aIID); + + nsresult DelegatedQueryInterface(REFNSIID aIID, void** aInstancePtr); + + static JSObject* GetRootJSObject(JSContext* cx, JSObject* aJSObj); + + static JSObject* CallQueryInterfaceOnJSObject(JSContext* cx, JSObject* jsobj, + JS::HandleObject scope, + REFNSIID aIID); + + // aObj is the nsXPCWrappedJS's object. We used this as the callee (or |this| + // if getter or setter). + // aSyntheticException, if not null, is the exception we should be using. + // If null, look for an exception on the JSContext hanging off the + // XPCCallContext. + static nsresult CheckForException( + XPCCallContext& ccx, mozilla::dom::AutoEntryScript& aes, + JS::HandleObject aObj, const char* aPropertyName, + const char* anInterfaceName, + mozilla::dom::Exception* aSyntheticException = nullptr); + + static bool GetArraySizeFromParam(const nsXPTMethodInfo* method, + const nsXPTType& type, + nsXPTCMiniVariant* params, + uint32_t* result); + + static bool GetInterfaceTypeFromParam(const nsXPTMethodInfo* method, + const nsXPTType& type, + nsXPTCMiniVariant* params, + nsID* result); + + static void CleanupOutparams(const nsXPTMethodInfo* info, + nsXPTCMiniVariant* nativeParams, bool inOutOnly, + uint8_t count); + + JS::Heap mJSObj; + const nsXPTInterfaceInfo* const mInfo; + nsXPCWrappedJS* mRoot; // If mRoot != this, it is an owning pointer. + nsXPCWrappedJS* mNext; + nsCOMPtr mOuter; // only set in root +}; + +/*************************************************************************** +**************************************************************************** +* +* All manner of utility classes follow... +* +**************************************************************************** +***************************************************************************/ + +namespace xpc { + +// A wrapper around JS iterators which presents an equivalent +// nsISimpleEnumerator interface for their contents. +class XPCWrappedJSIterator final : public nsISimpleEnumerator { + public: + NS_DECL_CYCLE_COLLECTION_CLASS(XPCWrappedJSIterator) + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_NSISIMPLEENUMERATOR + NS_DECL_NSISIMPLEENUMERATORBASE + + explicit XPCWrappedJSIterator(nsIJSEnumerator* aEnum); + + private: + ~XPCWrappedJSIterator() = default; + + nsCOMPtr mEnum; + nsCOMPtr mGlobal; + nsCOMPtr mNext; + mozilla::Maybe mHasNext; +}; + +} // namespace xpc + +/***************************************************************************/ +// class here just for static methods +class XPCConvert { + public: + /** + * Convert a native object into a JS::Value. + * + * @param cx the JSContext representing the global we want the value in + * @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(JSContext* cx, JS::MutableHandleValue d, + const void* s, const nsXPTType& type, + const nsID* iid, uint32_t arrlen, nsresult* pErr); + + static bool JSData2Native(JSContext* cx, void* d, JS::HandleValue s, + const nsXPTType& type, const nsID* iid, + uint32_t arrlen, nsresult* pErr); + + /** + * Convert a native nsISupports into a JSObject. + * + * @param cx the JSContext representing the global we want the object in. + * @param dest [out] the resulting JSObject + * @param src the native object we're working with + * @param iid the interface of src that we want (may be null) + * @param cache the wrapper cache for src (may be null, in which case src + * will be QI'ed to get the cache) + * @param allowNativeWrapper if true, this method may wrap the resulting + * JSObject in an XPCNativeWrapper and return that, as needed. + * @param pErr [out] relevant error code, if any. + * @param src_is_identity optional performance hint. Set to true only + * if src is the identity pointer. + */ + static bool NativeInterface2JSObject(JSContext* cx, + JS::MutableHandleValue dest, + xpcObjectHelper& aHelper, + const nsID* iid, bool allowNativeWrapper, + nsresult* pErr); + + static bool GetNativeInterfaceFromJSObject(void** dest, JSObject* src, + const nsID* iid, nsresult* pErr); + static bool JSObject2NativeInterface(JSContext* cx, 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); + + static nsresult JSValToXPCException(JSContext* cx, JS::MutableHandleValue s, + const char* ifaceName, + const char* methodName, + mozilla::dom::Exception** exception); + + static nsresult ConstructException(nsresult rv, const char* message, + const char* ifaceName, + const char* methodName, nsISupports* data, + mozilla::dom::Exception** exception, + JSContext* cx, JS::Value* jsExceptionPtr); + + private: + /** + * Convert a native array into a JS::Value. + * + * @param cx the JSContext we're working with and in whose global the array + * should be created. + * @param d [out] the resulting JS::Value + * @param buf the native buffer containing input values + * @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(JSContext* cx, JS::MutableHandleValue d, + const void* buf, const nsXPTType& type, + const nsID* iid, uint32_t count, nsresult* pErr); + + using ArrayAllocFixupLen = std::function; + + /** + * Convert a JS::Value into a native array. + * + * @param cx the JSContext we're working with + * @param aJSVal the JS::Value to convert + * @param aEltType the type of objects in the array + * @param aIID the interface of each object in the array + * @param pErr [out] relevant error code, if any + * @param aAllocFixupLen function called with the JS Array's length to + * allocate the backing buffer. This function may + * modify the length of array to be converted. + */ + static bool JSArray2Native(JSContext* cx, JS::HandleValue aJSVal, + const nsXPTType& aEltType, const nsIID* aIID, + nsresult* pErr, + const ArrayAllocFixupLen& aAllocFixupLen); + + XPCConvert() = delete; +}; + +/***************************************************************************/ +// code for throwing exceptions into JS + +class nsXPCException; + +class XPCThrower { + public: + static void Throw(nsresult rv, JSContext* cx); + static void Throw(nsresult rv, XPCCallContext& ccx); + static void ThrowBadResult(nsresult rv, nsresult result, XPCCallContext& ccx); + static void ThrowBadParam(nsresult rv, unsigned paramNum, + XPCCallContext& ccx); + static bool SetVerbosity(bool state) { + bool old = sVerbose; + sVerbose = state; + return old; + } + + static bool CheckForPendingException(nsresult result, JSContext* cx); + + private: + static void Verbosify(XPCCallContext& ccx, char** psz, bool own); + + private: + static bool sVerbose; +}; + +/***************************************************************************/ + +class nsXPCException { + public: + static bool NameAndFormatForNSResult(nsresult rv, const char** name, + const char** format); + + static const void* IterateNSResults(nsresult* rv, const char** name, + const char** format, const void** iterp); + + static uint32_t GetNSResultCount(); +}; + +/***************************************************************************/ +// 'Components' object implementation. + +class nsXPCComponents final : public nsIXPCComponents { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIXPCCOMPONENTS + + public: + void SystemIsBeingShutDown() { ClearMembers(); } + + XPCWrappedNativeScope* GetScope() { return mScope; } + + protected: + ~nsXPCComponents(); + + explicit nsXPCComponents(XPCWrappedNativeScope* aScope); + void ClearMembers(); + + XPCWrappedNativeScope* mScope; + + RefPtr mInterfaces; + RefPtr mResults; + RefPtr mClasses; + RefPtr mID; + RefPtr mException; + RefPtr mConstructor; + RefPtr mUtils; + + friend class XPCWrappedNativeScope; +}; + +/****************************************************************************** + * 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) + : mJSContext(cx), mEvaluated(false) {} + + /** + * 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 mAutoRealm; + + // 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) + : mContext(ccx.GetContext()), + mOld(ccx, mContext->SetResolveName(name)) +#ifdef DEBUG + , + mCheck(ccx, name) +#endif + { + } + + ~AutoResolveName() { + mozilla::DebugOnly old = mContext->SetResolveName(mOld); + MOZ_ASSERT(old == mCheck, "Bad Nesting!"); + } + + private: + XPCJSContext* mContext; + JS::RootedId mOld; +#ifdef DEBUG + JS::RootedId mCheck; +#endif +}; + +/***************************************************************************/ +// 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 = XPCJSContext::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) override { + if (mPtr) { + mPtr->TraceJS(trc); + mPtr->AutoTrace(trc); + } + } + + virtual void MarkAfterJSFinalize() override { + if (mPtr) { + mPtr->Mark(); + } + } + + private: + T* mPtr; +}; + +using AutoMarkingWrappedNativePtr = TypedAutoMarkingPtr; +using AutoMarkingWrappedNativeTearOffPtr = + TypedAutoMarkingPtr; +using AutoMarkingWrappedNativeProtoPtr = + TypedAutoMarkingPtr; + +/***************************************************************************/ +// Definitions 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_SCRIPT_HOLDER_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, + const 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 { return mJSVal; } + + protected: + /** + * 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.unbarrieredGet(); } + + XPCVariant(JSContext* cx, const JS::Value& aJSVal); + + public: + /** + * Convert a variant into a JS::Value. + * + * @param cx 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(JSContext* cx, nsIVariant* variant, + nsresult* pErr, JS::MutableHandleValue pJSVal); + + protected: + virtual ~XPCVariant(); + + bool InitializeData(JSContext* cx); + + void Cleanup(); + + nsDiscriminatedUnion mData; + JS::Heap mJSVal; + bool mReturnRawObject; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(XPCVariant, XPCVARIANT_IID) + +/***************************************************************************/ +// Utilities + +inline JSContext* xpc_GetSafeJSContext() { + return XPCJSContext::Get()->Context(); +} + +namespace xpc { + +// JSNatives to expose atob and btoa in various non-DOM XPConnect scopes. +bool Atob(JSContext* cx, unsigned argc, JS::Value* vp); + +bool Btoa(JSContext* cx, unsigned argc, JS::Value* vp); + +// Helper function that creates a JSFunction that wraps a native function that +// forwards the call to the original 'callable'. +class FunctionForwarderOptions; +bool NewFunctionForwarder(JSContext* cx, JS::HandleId id, + JS::HandleObject callable, + FunctionForwarderOptions& options, + JS::MutableHandleValue vp); + +// Old fashioned xpc error reporter. Try to use JS_ReportError instead. +nsresult ThrowAndFail(nsresult errNum, JSContext* cx, bool* retval); + +struct GlobalProperties { + GlobalProperties() { mozilla::PodZero(this); } + bool Parse(JSContext* cx, JS::HandleObject obj); + bool DefineInXPCComponents(JSContext* cx, JS::HandleObject obj); + bool DefineInSandbox(JSContext* cx, JS::HandleObject obj); + + // Interface objects we can expose. + bool AbortController : 1; + bool Blob : 1; + bool ChromeUtils : 1; + bool CSS : 1; + bool CSSRule : 1; + bool Directory : 1; + bool Document : 1; + bool DOMException : 1; + bool DOMParser : 1; + bool DOMTokenList : 1; + bool Element : 1; + bool Event : 1; + bool File : 1; + bool FileReader : 1; + bool FormData : 1; + bool Headers : 1; + bool IOUtils : 1; + bool InspectorUtils : 1; + bool MessageChannel : 1; + bool MIDIInputMap : 1; + bool MIDIOutputMap : 1; + bool Node : 1; + bool NodeFilter : 1; + bool PathUtils : 1; + bool Performance : 1; + bool PromiseDebugging : 1; + bool Range : 1; + bool Selection : 1; + bool TextDecoder : 1; + bool TextEncoder : 1; + bool URL : 1; + bool URLSearchParams : 1; + bool XMLHttpRequest : 1; + bool WebSocket : 1; + bool Window : 1; + bool XMLSerializer : 1; + bool ReadableStream : 1; + + // Ad-hoc property names we implement. + bool atob : 1; + bool btoa : 1; + bool caches : 1; + bool crypto : 1; + bool fetch : 1; + bool storage : 1; + bool structuredClone : 1; + bool indexedDB : 1; + bool isSecureContext : 1; + bool rtcIdentityProvider : 1; + + private: + bool Define(JSContext* cx, JS::HandleObject obj); +}; + +// 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); + bool ParseUInt32(const char* name, uint32_t* prop); + + JSContext* mCx; + JS::RootedObject mObject; +}; + +class MOZ_STACK_CLASS SandboxOptions : public OptionsBase { + public: + explicit SandboxOptions(JSContext* cx = xpc_GetSafeJSContext(), + JSObject* options = nullptr) + : OptionsBase(cx, options), + wantXrays(true), + allowWaivers(true), + wantComponents(true), + wantExportHelpers(false), + isWebExtensionContentScript(false), + proto(cx), + sameZoneAs(cx), + forceSecureContext(false), + freshCompartment(false), + freshZone(false), + isUAWidgetScope(false), + invisibleToDebugger(false), + discardSource(false), + metadata(cx), + userContextId(0), + originAttributes(cx) {} + + virtual bool Parse() override; + + bool wantXrays; + bool allowWaivers; + bool wantComponents; + bool wantExportHelpers; + bool isWebExtensionContentScript; + JS::RootedObject proto; + nsCString sandboxName; + JS::RootedObject sameZoneAs; + bool forceSecureContext; + bool freshCompartment; + bool freshZone; + bool isUAWidgetScope; + bool invisibleToDebugger; + bool discardSource; + GlobalProperties globalProperties; + JS::RootedValue metadata; + uint32_t userContextId; + JS::RootedObject originAttributes; + + 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, JS::PropertyKey::Void()) {} + + virtual bool Parse() override { 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, JS::PropertyKey::Void()), + allowCrossOriginArguments(false) {} + + virtual bool Parse() override { + 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() override { + 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() override { + 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::RealmOptions& aOptions); + +// Modify the provided compartment options, consistent with |aPrincipal| and +// with globally-cached values of various preferences. +// +// Call this function *before* |aOptions| is used to create the corresponding +// global object, as not all of the options it sets can be modified on an +// existing global object. (The type system should make this obvious, because +// you can't get a *mutable* JS::RealmOptions& from an existing global +// object.) +void InitGlobalObjectOptions(JS::RealmOptions& aOptions, + bool aIsSystemPrincipal, + bool aShouldResistFingerprinting); + +// Finish initializing an already-created, not-yet-exposed-to-script global +// object. This will attach a Components object (if necessary) and call +// |JS_FireOnNewGlobalObject| (if necessary). +// +// If you must modify compartment options, see InitGlobalObjectOptions above. +bool InitGlobalObject(JSContext* aJSContext, JS::Handle 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, bool enforceFilenameRestrictions, + 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::HandleObject sourceScope, JS::MutableHandleValue val); + +} /* namespace xpc */ + +/***************************************************************************/ +// Inlined utilities. + +inline bool xpc_ForcePropertyResolve(JSContext* cx, JS::HandleObject obj, + jsid id); + +inline jsid GetJSIDByIndex(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 CompartmentOriginInfo { + public: + CompartmentOriginInfo(const CompartmentOriginInfo&) = delete; + + CompartmentOriginInfo(mozilla::BasePrincipal* aOrigin, + const mozilla::SiteIdentifier& aSite) + : mOrigin(aOrigin), mSite(aSite) { + MOZ_ASSERT(aOrigin); + MOZ_ASSERT(aSite.IsInitialized()); + } + + bool IsSameOrigin(nsIPrincipal* aOther) const; + + // Does the principal of compartment a subsume the principal of compartment b? + static bool Subsumes(JS::Compartment* aCompA, JS::Compartment* aCompB); + static bool SubsumesIgnoringFPD(JS::Compartment* aCompA, + JS::Compartment* aCompB); + + bool MightBeWebContent() const; + + // Note: this principal must not be used for subsumes/equality checks + // considering document.domain. See mOrigin. + mozilla::BasePrincipal* GetPrincipalIgnoringDocumentDomain() const { + return mOrigin; + } + + const mozilla::SiteIdentifier& SiteRef() const { return mSite; } + + bool HasChangedDocumentDomain() const { return mChangedDocumentDomain; } + void SetChangedDocumentDomain() { mChangedDocumentDomain = true; } + + private: + // All globals in the compartment must have this origin. Note that + // individual globals and principals can have their domain changed via + // document.domain, so this principal must not be used for things like + // subsumesConsideringDomain or equalsConsideringDomain. Use the realm's + // principal for that. + RefPtr mOrigin; + + // In addition to the origin we also store the SiteIdentifier. When realms + // in different compartments can become same-origin (via document.domain), + // these compartments must have equal SiteIdentifiers. (This is derived from + // mOrigin but we cache it here for performance reasons.) + mozilla::SiteIdentifier mSite; + + // True if any global in this compartment mutated document.domain. + bool mChangedDocumentDomain = false; +}; + +// The CompartmentPrivate contains XPConnect-specific stuff related to each JS +// compartment. Since compartments are trust domains, this means mostly +// information needed to select the right security policy for cross-compartment +// wrappers. +class CompartmentPrivate { + CompartmentPrivate() = delete; + CompartmentPrivate(const CompartmentPrivate&) = delete; + + public: + CompartmentPrivate(JS::Compartment* c, + mozilla::UniquePtr scope, + mozilla::BasePrincipal* origin, + const mozilla::SiteIdentifier& site); + + ~CompartmentPrivate(); + + static CompartmentPrivate* Get(JS::Compartment* compartment) { + MOZ_ASSERT(compartment); + void* priv = JS_GetCompartmentPrivate(compartment); + return static_cast(priv); + } + + static CompartmentPrivate* Get(JS::Realm* realm) { + MOZ_ASSERT(realm); + JS::Compartment* compartment = JS::GetCompartmentForRealm(realm); + return Get(compartment); + } + + static CompartmentPrivate* Get(JSObject* object) { + JS::Compartment* compartment = JS::GetCompartment(object); + return Get(compartment); + } + + bool CanShareCompartmentWith(nsIPrincipal* principal) { + // Only share if we're same-origin with the principal. + if (!originInfo.IsSameOrigin(principal)) { + return false; + } + + // Don't share if we have any weird state set. + return !wantXrays && !isWebExtensionContentScript && + !isUAWidgetCompartment && mScope->XBLScopeStateMatches(principal); + } + + CompartmentOriginInfo originInfo; + + // 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 compartment corresponds to a WebExtension content script, and + // receives various bits of special compatibility behavior. + bool isWebExtensionContentScript; + + // True if this compartment is a UA widget compartment. + bool isUAWidgetCompartment; + + // See CompartmentHasExclusiveExpandos. + bool hasExclusiveExpandos; + + // Whether SystemIsBeingShutDown has been called on this compartment. + bool wasShutdown; + + JSObject2WrappedJSMap* GetWrappedJSMap() const { return mWrappedJSMap.get(); } + void UpdateWeakPointersAfterGC(JSTracer* trc); + + void SystemIsBeingShutDown(); + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf); + + struct MapEntryGCPolicy { + static bool traceWeak(JSTracer* trc, const void* /* unused */, + JS::Heap* value) { + return JS::GCPolicy>::traceWeak(trc, value); + } + }; + + typedef JS::GCHashMap, + mozilla::PointerHasher, + js::SystemAllocPolicy, MapEntryGCPolicy> + RemoteProxyMap; + RemoteProxyMap& GetRemoteProxyMap() { return mRemoteProxies; } + + XPCWrappedNativeScope* GetScope() { return mScope.get(); } + + private: + mozilla::UniquePtr mWrappedJSMap; + + // Cache holding proxy objects for Window objects (and their Location object) + // that are loaded in a different process. + RemoteProxyMap mRemoteProxies; + + // Our XPCWrappedNativeScope. + mozilla::UniquePtr mScope; +}; + +inline void CrashIfNotInAutomation() { MOZ_RELEASE_ASSERT(IsInAutomation()); } + +// XPConnect-specific data associated with each JavaScript realm. Per-Window +// settings live here; security-wrapper-related settings live in the +// CompartmentPrivate. +// +// Following the ECMAScript spec, a realm contains a global (e.g. an inner +// Window) and its associated scripts and objects; a compartment may contain +// several same-origin realms. +class RealmPrivate { + RealmPrivate() = delete; + RealmPrivate(const RealmPrivate&) = delete; + + public: + enum LocationHint { LocationHintRegular, LocationHintAddon }; + + explicit RealmPrivate(JS::Realm* realm); + + // Creates the RealmPrivate and CompartmentPrivate (if needed) for a new + // global. + static void Init(JS::HandleObject aGlobal, + const mozilla::SiteIdentifier& aSite); + + static RealmPrivate* Get(JS::Realm* realm) { + MOZ_ASSERT(realm); + void* priv = JS::GetRealmPrivate(realm); + return static_cast(priv); + } + + // Get the RealmPrivate for a given object. `object` must not be a + // cross-compartment wrapper, as CCWs aren't dedicated to a particular + // realm. + static RealmPrivate* Get(JSObject* object) { + JS::Realm* realm = JS::GetObjectRealmOrNull(object); + return Get(realm); + } + + // The scriptability of this realm. + Scriptability scriptability; + + // Whether we've emitted a warning about a property that was filtered out + // by a security wrapper. See XrayWrapper.cpp. + bool wrapperDenialWarnings[WrapperDenialTypeCount]; + + 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; + } else if (NS_FAILED(locationURI->GetSpec(location))) { + location = ""_ns; + } + } + return location; + } + bool GetLocationURI(LocationHint aLocationHint, nsIURI** aURI) { + if (locationURI) { + nsCOMPtr rval = locationURI; + rval.forget(aURI); + return true; + } + return TryParseLocationURI(aLocationHint, aURI); + } + bool GetLocationURI(nsIURI** aURI) { + return GetLocationURI(LocationHintRegular, 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; + } + + // JSStackFrames are tracked on a per-realm basis so they + // can be cleared when the associated window goes away. + void RegisterStackFrame(JSStackFrameBase* aFrame); + void UnregisterStackFrame(JSStackFrameBase* aFrame); + void NukeJSStackFrames(); + + private: + nsCString location; + nsCOMPtr locationURI; + + bool TryParseLocationURI(LocationHint aType, nsIURI** aURI); + + nsTHashtable> mJSStackFrames; +}; + +inline XPCWrappedNativeScope* ObjectScope(JSObject* obj) { + return CompartmentPrivate::Get(obj)->GetScope(); +} + +JSObject* NewOutObject(JSContext* cx); +bool IsOutObject(JSContext* cx, JSObject* obj); + +nsresult HasInstance(JSContext* cx, JS::HandleObject objArg, const nsID* iid, + bool* bp); + +// Returns the principal associated with |obj|'s realm. The object must not be a +// cross-compartment wrapper. +nsIPrincipal* GetObjectPrincipal(JSObject* obj); + +// Attempt to clean up the passed in value pointer. The pointer `value` must be +// a pointer to a value described by the type `nsXPTType`. +// +// This method expects a value of the following types: +// TD_NSIDPTR +// value : nsID* (free) +// TD_ASTRING, TD_CSTRING, TD_UTF8STRING +// value : ns[C]String* (truncate) +// TD_PSTRING, TD_PWSTRING, TD_PSTRING_SIZE_IS, TD_PWSTRING_SIZE_IS +// value : char[16_t]** (free) +// TD_INTERFACE_TYPE, TD_INTERFACE_IS_TYPE +// value : nsISupports** (release) +// TD_LEGACY_ARRAY (NOTE: aArrayLen should be passed) +// value : void** (destroy elements & free) +// TD_ARRAY +// value : nsTArray* (destroy elements & Clear) +// TD_DOMOBJECT +// value : T** (cleanup) +// TD_PROMISE +// value : dom::Promise** (release) +// +// Other types are ignored. +inline void CleanupValue(const nsXPTType& aType, void* aValue, + uint32_t aArrayLen = 0); + +// Out-of-line internals for xpc::CleanupValue. Defined in XPCConvert.cpp. +void InnerCleanupValue(const nsXPTType& aType, void* aValue, + uint32_t aArrayLen); + +// In order to be able to safely call CleanupValue on a generated value, the +// data behind it needs to be initialized to a safe value. This method handles +// initializing the backing data to a safe value to use as an argument to +// XPCConvert methods, or xpc::CleanupValue. +// +// The pointer `aValue` must point to a block of memory at least aType.Stride() +// bytes large, and correctly aligned. +// +// This method accepts the same types as xpc::CleanupValue. +void InitializeValue(const nsXPTType& aType, void* aValue); + +// If a value was initialized with InitializeValue, it should be destroyed with +// DestructValue. This method acts like CleanupValue, except that destructors +// for complex types are also invoked, leaving them in an invalid state. +// +// This method should be called when destroying types initialized with +// InitializeValue. +// +// The pointer 'aValue' must point to a valid value of type 'aType'. +void DestructValue(const nsXPTType& aType, void* aValue, + uint32_t aArrayLen = 0); + +bool SandboxCreateCrypto(JSContext* cx, JS::Handle obj); +bool SandboxCreateFetch(JSContext* cx, JS::Handle obj); +bool SandboxCreateStructuredClone(JSContext* cx, JS::Handle 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" + +/***************************************************************************/ + +#endif /* xpcprivate_h___ */ -- cgit v1.2.3