/* -*- 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).
 */

/* 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/CycleCollectedJSContext.h"
#include "mozilla/GuardObjects.h"
#include "mozilla/Maybe.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Preferences.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/UniquePtr.h"

#include "mozilla/dom/ScriptSettings.h"

#include <math.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include "xpcpublic.h"
#include "js/TracingAPI.h"
#include "js/WeakMapPtr.h"
#include "PLDHashTable.h"
#include "nscore.h"
#include "nsXPCOM.h"
#include "nsAutoPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsDebug.h"
#include "nsISupports.h"
#include "nsIServiceManager.h"
#include "nsIClassInfoImpl.h"
#include "nsIComponentManager.h"
#include "nsIComponentRegistrar.h"
#include "nsISupportsPrimitives.h"
#include "nsMemory.h"
#include "nsIXPConnect.h"
#include "nsIInterfaceInfo.h"
#include "nsIXPCScriptable.h"
#include "nsIObserver.h"
#include "nsWeakReference.h"
#include "nsCOMPtr.h"
#include "nsXPTCUtils.h"
#include "xptinfo.h"
#include "XPCForwards.h"
#include "XPCLog.h"
#include "xpccomponents.h"
#include "xpcexception.h"
#include "xpcjsid.h"
#include "prenv.h"
#include "prclist.h"
#include "prcvar.h"
#include "nsString.h"
#include "nsReadableUtils.h"
#include "nsXPIDLString.h"

#include "MainThreadUtils.h"

#include "nsIConsoleService.h"
#include "nsIScriptError.h"
#include "nsIException.h"

#include "nsVariant.h"
#include "nsIPropertyBag.h"
#include "nsIProperty.h"
#include "nsCOMArray.h"
#include "nsTArray.h"
#include "nsBaseHashtable.h"
#include "nsHashKeys.h"
#include "nsWrapperCache.h"
#include "nsStringBuffer.h"
#include "nsDataHashtable.h"
#include "nsDeque.h"

#include "nsIScriptSecurityManager.h"

#include "nsIPrincipal.h"
#include "nsJSPrincipals.h"
#include "nsIScriptObjectPrincipal.h"
#include "xpcObjectHelper.h"

#include "SandboxPrivate.h"
#include "BackstagePass.h"
#include "nsAXPCNativeCallContext.h"

#ifdef XP_WIN
// Nasty MS defines
#ifdef GetClassInfo
#undef GetClassInfo
#endif
#ifdef GetClassName
#undef GetClassName
#endif
#endif /* XP_WIN */

/***************************************************************************/
// default initial sizes for maps (hashtables)

#define XPC_JS_MAP_LENGTH                       32
#define XPC_JS_CLASS_MAP_LENGTH                 32

#define XPC_NATIVE_MAP_LENGTH                    8
#define XPC_NATIVE_PROTO_MAP_LENGTH              8
#define XPC_DYING_NATIVE_PROTO_MAP_LENGTH        8
#define XPC_NATIVE_INTERFACE_MAP_LENGTH         32
#define XPC_NATIVE_SET_MAP_LENGTH               32
#define XPC_THIS_TRANSLATOR_MAP_LENGTH           4
#define XPC_WRAPPER_MAP_LENGTH                   8

/***************************************************************************/
// data declarations...
extern const char XPC_CONTEXT_STACK_CONTRACTID[];
extern const char XPC_EXCEPTION_CONTRACTID[];
extern const char XPC_CONSOLE_CONTRACTID[];
extern const char XPC_SCRIPT_ERROR_CONTRACTID[];
extern const char XPC_ID_CONTRACTID[];
extern const char XPC_XPCONNECT_CONTRACTID[];

/***************************************************************************/
// Useful macros...

#define XPC_STRING_GETTER_BODY(dest, src)                                     \
    NS_ENSURE_ARG_POINTER(dest);                                              \
    char* result;                                                             \
    if (src)                                                                  \
        result = (char*) nsMemory::Clone(src,                                 \
                                         sizeof(char)*(strlen(src)+1));       \
    else                                                                      \
        result = nullptr;                                                      \
    *dest = result;                                                           \
    return (result || !src) ? NS_OK : NS_ERROR_OUT_OF_MEMORY

// If IS_WN_CLASS for the JSClass of an object is true, the object is a
// wrappednative wrapper, holding the XPCWrappedNative in its private slot.
static inline bool IS_WN_CLASS(const js::Class* clazz)
{
    return clazz->isWrappedNative();
}

static inline bool IS_WN_REFLECTOR(JSObject* obj)
{
    return IS_WN_CLASS(js::GetObjectClass(obj));
}

/***************************************************************************
****************************************************************************
*
* Core runtime and context classes...
*
****************************************************************************
***************************************************************************/

// We have a general rule internally that getters that return addref'd interface
// pointer generally do so using an 'out' parm. When interface pointers are
// returned as function call result values they are not addref'd. Exceptions
// to this rule are noted explicitly.

class nsXPConnect final : public nsIXPConnect
{
public:
    // all the interface method declarations...
    NS_DECL_ISUPPORTS
    NS_DECL_NSIXPCONNECT

    // non-interface implementation
public:
    // These get non-addref'd pointers
    static nsXPConnect*  XPConnect()
    {
        // Do a release-mode assert that we're not doing anything significant in
        // XPConnect off the main thread. If you're an extension developer hitting
        // this, you need to change your code. See bug 716167.
        if (!MOZ_LIKELY(NS_IsMainThread()))
            MOZ_CRASH();

        return gSelf;
    }

    static XPCJSContext* GetContextInstance();
    XPCJSContext* GetContext() {return mContext;}

    static bool IsISupportsDescendant(nsIInterfaceInfo* info);

    static nsIScriptSecurityManager* SecurityManager()
    {
        MOZ_ASSERT(NS_IsMainThread());
        MOZ_ASSERT(gScriptSecurityManager);
        return gScriptSecurityManager;
    }

    static nsIPrincipal* SystemPrincipal()
    {
        MOZ_ASSERT(NS_IsMainThread());
        MOZ_ASSERT(gSystemPrincipal);
        return gSystemPrincipal;
    }

    // This returns an AddRef'd pointer. It does not do this with an 'out' param
    // only because this form is required by the generic module macro:
    // NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR
    static nsXPConnect* GetSingleton();

    // Called by module code in dll startup
    static void InitStatics();
    // Called by module code on dll shutdown.
    static void ReleaseXPConnectSingleton();

    bool IsShuttingDown() const {return mShuttingDown;}

    nsresult GetInfoForIID(const nsIID * aIID, nsIInterfaceInfo** info);
    nsresult GetInfoForName(const char * name, nsIInterfaceInfo** info);

    virtual nsIPrincipal* GetPrincipal(JSObject* obj,
                                       bool allowShortCircuit) const override;

    void RecordTraversal(void* p, nsISupports* s);
    virtual char* DebugPrintJSStack(bool showArgs,
                                    bool showLocals,
                                    bool showThisProps) override;

protected:
    virtual ~nsXPConnect();

    nsXPConnect();

private:
    // Singleton instance
    static nsXPConnect*             gSelf;
    static bool                     gOnceAliveNowDead;

    XPCJSContext*                   mContext;
    bool                            mShuttingDown;

public:
    static nsIScriptSecurityManager* gScriptSecurityManager;
    static nsIPrincipal* gSystemPrincipal;
};

/***************************************************************************/

class XPCRootSetElem
{
public:
    XPCRootSetElem()
    {
#ifdef DEBUG
        mNext = nullptr;
        mSelfp = nullptr;
#endif
    }

    ~XPCRootSetElem()
    {
        MOZ_ASSERT(!mNext, "Must be unlinked");
        MOZ_ASSERT(!mSelfp, "Must be unlinked");
    }

    inline XPCRootSetElem* GetNextRoot() { return mNext; }
    void AddToRootSet(XPCRootSetElem** listHead);
    void RemoveFromRootSet();

private:
    XPCRootSetElem* mNext;
    XPCRootSetElem** mSelfp;
};

/***************************************************************************/

// In the current xpconnect system there can only be one XPCJSContext.
// So, xpconnect can only be used on one JSContext within the process.

class WatchdogManager;

enum WatchdogTimestampCategory
{
    TimestampContextStateChange = 0,
    TimestampWatchdogWakeup,
    TimestampWatchdogHibernateStart,
    TimestampWatchdogHibernateStop,
    TimestampCount
};

class AsyncFreeSnowWhite;

template <class StringType>
class ShortLivedStringBuffer
{
public:
    StringType* Create()
    {
        for (uint32_t i = 0; i < ArrayLength(mStrings); ++i) {
            if (!mStrings[i]) {
                mStrings[i].emplace();
                return mStrings[i].ptr();
            }
        }

        // All our internal string wrappers are used, allocate a new string.
        return new StringType();
    }

    void Destroy(StringType* string)
    {
        for (uint32_t i = 0; i < ArrayLength(mStrings); ++i) {
            if (mStrings[i] && mStrings[i].ptr() == string) {
                // One of our internal strings is no longer in use, mark
                // it as such and free its data.
                mStrings[i].reset();
                return;
            }
        }

        // We're done with a string that's not one of our internal
        // strings, delete it.
        delete string;
    }

    ~ShortLivedStringBuffer()
    {
#ifdef DEBUG
        for (uint32_t i = 0; i < ArrayLength(mStrings); ++i) {
            MOZ_ASSERT(!mStrings[i], "Short lived string still in use");
        }
#endif
    }

private:
    mozilla::Maybe<StringType> mStrings[2];
};

class XPCJSContext final : public mozilla::CycleCollectedJSContext
{
public:
    static XPCJSContext* newXPCJSContext();
    static XPCJSContext* Get() { return nsXPConnect::XPConnect()->GetContext(); }

    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;}

    XPCWrappedNativeProtoMap* GetDyingWrappedNativeProtoMap() const
        {return mDyingWrappedNativeProtoMap;}

    bool JSContextInitialized(JSContext* cx);

    virtual bool
    DescribeCustomObjects(JSObject* aObject, const js::Class* aClasp,
                          char (&aName)[72]) const override;
    virtual bool
    NoteCustomGCThingXPCOMChildren(const js::Class* aClasp, JSObject* aObj,
                                   nsCycleCollectionTraversalCallback& aCb) const override;

    virtual void BeforeProcessTask(bool aMightBlock) override;
    virtual void AfterProcessTask(uint32_t aNewRecursionDepth) override;

    /**
     * Infrastructure for classes that need to defer part of the finalization
     * until after the GC has run, for example for objects that we don't want to
     * destroy during the GC.
     */

public:
    bool GetDoingFinalization() const {return mDoingFinalization;}

    // Mapping of often used strings to jsid atoms that live 'forever'.
    //
    // To add a new string: add to this list and to XPCJSContext::mStrings
    // at the top of XPCJSContext.cpp
    enum {
        IDX_CONSTRUCTOR             = 0 ,
        IDX_TO_STRING               ,
        IDX_TO_SOURCE               ,
        IDX_LAST_RESULT             ,
        IDX_RETURN_CODE             ,
        IDX_VALUE                   ,
        IDX_QUERY_INTERFACE         ,
        IDX_COMPONENTS              ,
        IDX_WRAPPED_JSOBJECT        ,
        IDX_OBJECT                  ,
        IDX_FUNCTION                ,
        IDX_PROTOTYPE               ,
        IDX_CREATE_INSTANCE         ,
        IDX_ITEM                    ,
        IDX_PROTO                   ,
        IDX_ITERATOR                ,
        IDX_EXPOSEDPROPS            ,
        IDX_EVAL                    ,
        IDX_CONTROLLERS             ,
        IDX_REALFRAMEELEMENT        ,
        IDX_LENGTH                  ,
        IDX_NAME                    ,
        IDX_UNDEFINED               ,
        IDX_EMPTYSTRING             ,
        IDX_FILENAME                ,
        IDX_LINENUMBER              ,
        IDX_COLUMNNUMBER            ,
        IDX_STACK                   ,
        IDX_MESSAGE                 ,
        IDX_LASTINDEX               ,
        IDX_TOTAL_COUNT // just a count of the above
    };

    JS::HandleId GetStringID(unsigned index) const
    {
        MOZ_ASSERT(index < IDX_TOTAL_COUNT, "index out of range");
        // fromMarkedLocation() is safe because the string is interned.
        return JS::HandleId::fromMarkedLocation(&mStrIDs[index]);
    }
    JS::HandleValue GetStringJSVal(unsigned index) const
    {
        MOZ_ASSERT(index < IDX_TOTAL_COUNT, "index out of range");
        // fromMarkedLocation() is safe because the string is interned.
        return JS::HandleValue::fromMarkedLocation(&mStrJSVals[index]);
    }
    const char* GetStringName(unsigned index) const
    {
        MOZ_ASSERT(index < IDX_TOTAL_COUNT, "index out of range");
        return mStrings[index];
    }

    virtual bool UsefulToMergeZones() const override;
    void TraceNativeBlackRoots(JSTracer* trc) override;
    void TraceAdditionalNativeGrayRoots(JSTracer* aTracer) override;
    void TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback& cb) override;
    void UnmarkSkippableJSHolders();
    void PrepareForForgetSkippable() override;
    void BeginCycleCollectionCallback() override;
    void EndCycleCollectionCallback(mozilla::CycleCollectorResults& aResults) override;
    void DispatchDeferredDeletion(bool aContinuation, bool aPurge = false) override;

    void CustomGCCallback(JSGCStatus status) override;
    void CustomOutOfMemoryCallback() override;
    void CustomLargeAllocationFailureCallback() override;
    static void GCSliceCallback(JSContext* cx,
                                JS::GCProgress progress,
                                const JS::GCDescription& desc);
    static void DoCycleCollectionCallback(JSContext* cx);
    static void FinalizeCallback(JSFreeOp* fop,
                                 JSFinalizeStatus status,
                                 bool isZoneGC,
                                 void* data);
    static void WeakPointerZoneGroupCallback(JSContext* cx, void* data);
    static void WeakPointerCompartmentCallback(JSContext* cx, JSCompartment* comp, void* data);

    inline void AddVariantRoot(XPCTraceableVariant* variant);
    inline void AddWrappedJSRoot(nsXPCWrappedJS* wrappedJS);
    inline void AddObjectHolderRoot(XPCJSObjectHolder* holder);

    void DebugDump(int16_t depth);

    bool GCIsRunning() const {return mGCIsRunning;}

    ~XPCJSContext();

    ShortLivedStringBuffer<nsString> mScratchStrings;
    ShortLivedStringBuffer<nsCString> mScratchCStrings;

    void AddGCCallback(xpcGCCallback cb);
    void RemoveGCCallback(xpcGCCallback cb);

    static void ActivityCallback(void* arg, bool active);
    static bool InterruptCallback(JSContext* cx);

    size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);

    AutoMarkingPtr**  GetAutoRootsAdr() {return &mAutoRoots;}

    JSObject* UnprivilegedJunkScope() { return mUnprivilegedJunkScope; }
    JSObject* PrivilegedJunkScope() { return mPrivilegedJunkScope; }
    JSObject* CompilationScope() { return mCompilationScope; }

    void InitSingletonScopes();
    void DeleteSingletonScopes();

    PRTime GetWatchdogTimestamp(WatchdogTimestampCategory aCategory);

    nsresult GetPendingResult() { return mPendingResult; }
    void SetPendingResult(nsresult rv) { mPendingResult = rv; }

private:
    XPCJSContext();

    nsresult Initialize();

    void ReleaseIncrementally(nsTArray<nsISupports*>& array);

    static const char* const mStrings[IDX_TOTAL_COUNT];
    jsid mStrIDs[IDX_TOTAL_COUNT];
    JS::Value mStrJSVals[IDX_TOTAL_COUNT];

    XPCCallContext*          mCallContext;
    AutoMarkingPtr*          mAutoRoots;
    jsid                     mResolveName;
    XPCWrappedNative*        mResolvingWrapper;
    JSObject2WrappedJSMap*   mWrappedJSMap;
    IID2WrappedJSClassMap*   mWrappedJSClassMap;
    IID2NativeInterfaceMap*  mIID2NativeInterfaceMap;
    ClassInfo2NativeSetMap*  mClassInfo2NativeSetMap;
    NativeSetMap*            mNativeSetMap;
    IID2ThisTranslatorMap*   mThisTranslatorMap;
    XPCWrappedNativeProtoMap* mDyingWrappedNativeProtoMap;
    bool mGCIsRunning;
    nsTArray<nsISupports*> mNativesToReleaseArray;
    bool mDoingFinalization;
    XPCRootSetElem* mVariantRoots;
    XPCRootSetElem* mWrappedJSRoots;
    XPCRootSetElem* mObjectHolderRoots;
    nsTArray<xpcGCCallback> extraGCCallbacks;
    RefPtr<WatchdogManager> mWatchdogManager;
    JS::GCSliceCallback mPrevGCSliceCallback;
    JS::DoCycleCollectionCallback mPrevDoCycleCollectionCallback;
    JS::PersistentRootedObject mUnprivilegedJunkScope;
    JS::PersistentRootedObject mPrivilegedJunkScope;
    JS::PersistentRootedObject mCompilationScope;
    RefPtr<AsyncFreeSnowWhite> mAsyncSnowWhiteFreer;

    // If we spend too much time running JS code in an event handler, then we
    // want to show the slow script UI. The timeout T is controlled by prefs. We
    // invoke the interrupt callback once after T/2 seconds and set
    // mSlowScriptSecondHalf to true. After another T/2 seconds, we invoke the
    // interrupt callback again. Since mSlowScriptSecondHalf is now true, it
    // shows the slow script UI. The reason we invoke the callback twice is to
    // ensure that putting the computer to sleep while running a script doesn't
    // cause the UI to be shown. If the laptop goes to sleep during one of the
    // timeout periods, the script still has the other T/2 seconds to complete
    // before the slow script UI is shown.
    bool mSlowScriptSecondHalf;

    // mSlowScriptCheckpoint is set to the time when:
    // 1. We started processing the current event, or
    // 2. mSlowScriptSecondHalf was set to true
    // (whichever comes later). We use it to determine whether the interrupt
    // callback needs to do anything.
    mozilla::TimeStamp mSlowScriptCheckpoint;
    // Accumulates total time we actually waited for telemetry
    mozilla::TimeDuration mSlowScriptActualWait;
    bool mTimeoutAccumulated;

    // mPendingResult is used to implement Components.returnCode.  Only really
    // meaningful while calling through XPCWrappedJS.
    nsresult mPendingResult;

    friend class Watchdog;
    friend class AutoLockWatchdog;
    friend class XPCIncrementalReleaseRunnable;
};

/***************************************************************************/

// No virtuals
// XPCCallContext is ALWAYS declared as a local variable in some function;
// i.e. instance lifetime is always controled by some C++ function returning.
//
// These things are created frequently in many places. We *intentionally* do
// not inialialize all members in order to save on construction overhead.
// Some constructor pass more valid params than others. We init what must be
// init'd and leave other members undefined. In debug builds the accessors
// use a CHECK_STATE macro to track whether or not the object is in a valid
// state to answer the question a caller might be asking. As long as this
// class is maintained correctly it can do its job without a bunch of added
// overhead from useless initializations and non-DEBUG error checking.
//
// Note that most accessors are inlined.

class MOZ_STACK_CLASS XPCCallContext final : public nsAXPCNativeCallContext
{
public:
    NS_IMETHOD GetCallee(nsISupports** aResult);
    NS_IMETHOD GetCalleeMethodIndex(uint16_t* aResult);
    NS_IMETHOD GetJSContext(JSContext** aResult);
    NS_IMETHOD GetArgc(uint32_t* aResult);
    NS_IMETHOD GetArgvPtr(JS::Value** aResult);
    NS_IMETHOD GetCalleeInterface(nsIInterfaceInfo** aResult);
    NS_IMETHOD GetCalleeClassInfo(nsIClassInfo** aResult);
    NS_IMETHOD GetPreviousCallContext(nsAXPCNativeCallContext** aResult);

    enum {NO_ARGS = (unsigned) -1};

    explicit XPCCallContext(JSContext* cx,
                            JS::HandleObject obj    = nullptr,
                            JS::HandleObject funobj = nullptr,
                            JS::HandleId id         = JSID_VOIDHANDLE,
                            unsigned argc           = NO_ARGS,
                            JS::Value* argv         = nullptr,
                            JS::Value* rval         = nullptr);

    virtual ~XPCCallContext();

    inline bool                         IsValid() const ;

    inline XPCJSContext*                GetContext() const ;
    inline JSContext*                   GetJSContext() const ;
    inline bool                         GetContextPopRequired() const ;
    inline XPCCallContext*              GetPrevCallContext() const ;

    inline JSObject*                    GetFlattenedJSObject() const ;
    inline nsISupports*                 GetIdentityObject() const ;
    inline XPCWrappedNative*            GetWrapper() const ;
    inline XPCWrappedNativeProto*       GetProto() const ;

    inline bool                         CanGetTearOff() const ;
    inline XPCWrappedNativeTearOff*     GetTearOff() const ;

    inline XPCNativeScriptableInfo*     GetScriptableInfo() const ;
    inline bool                         CanGetSet() const ;
    inline XPCNativeSet*                GetSet() const ;
    inline bool                         CanGetInterface() const ;
    inline XPCNativeInterface*          GetInterface() const ;
    inline XPCNativeMember*             GetMember() const ;
    inline bool                         HasInterfaceAndMember() const ;
    inline jsid                         GetName() const ;
    inline bool                         GetStaticMemberIsLocal() const ;
    inline unsigned                     GetArgc() const ;
    inline JS::Value*                   GetArgv() const ;
    inline JS::Value*                   GetRetVal() const ;

    inline uint16_t                     GetMethodIndex() const ;
    inline void                         SetMethodIndex(uint16_t index) ;

    inline jsid GetResolveName() const;
    inline jsid SetResolveName(JS::HandleId name);

    inline XPCWrappedNative* GetResolvingWrapper() const;
    inline XPCWrappedNative* SetResolvingWrapper(XPCWrappedNative* w);

    inline void SetRetVal(JS::Value val);

    void SetName(jsid name);
    void SetArgsAndResultPtr(unsigned argc, JS::Value* argv, JS::Value* rval);
    void SetCallInfo(XPCNativeInterface* iface, XPCNativeMember* member,
                     bool isSetter);

    nsresult  CanCallNow();

    void SystemIsBeingShutDown();

    operator JSContext*() const {return GetJSContext();}

private:

    // no copy ctor or assignment allowed
    XPCCallContext(const XPCCallContext& r) = delete;
    XPCCallContext& operator= (const XPCCallContext& r) = delete;

private:
    // posible values for mState
    enum State {
        INIT_FAILED,
        SYSTEM_SHUTDOWN,
        HAVE_CONTEXT,
        HAVE_OBJECT,
        HAVE_NAME,
        HAVE_ARGS,
        READY_TO_CALL,
        CALL_DONE
    };

#ifdef DEBUG
inline void CHECK_STATE(int s) const {MOZ_ASSERT(mState >= s, "bad state");}
#else
#define CHECK_STATE(s) ((void)0)
#endif

private:
    JSAutoRequest                   mAr;
    State                           mState;

    RefPtr<nsXPConnect>           mXPC;

    XPCJSContext*                   mXPCJSContext;
    JSContext*                      mJSContext;

    // ctor does not necessarily init the following. BEWARE!

    XPCCallContext*                 mPrevCallContext;

    XPCWrappedNative*               mWrapper;
    XPCWrappedNativeTearOff*        mTearOff;

    XPCNativeScriptableInfo*        mScriptableInfo;

    XPCNativeSet*                   mSet;
    RefPtr<XPCNativeInterface>      mInterface;
    XPCNativeMember*                mMember;

    JS::RootedId                    mName;
    bool                            mStaticMemberIsLocal;

    unsigned                        mArgc;
    JS::Value*                      mArgv;
    JS::Value*                      mRetVal;

    uint16_t                        mMethodIndex;
};

/***************************************************************************
****************************************************************************
*
* Core classes for wrapped native objects for use from JavaScript...
*
****************************************************************************
***************************************************************************/

// These are the various JSClasses and callbacks whose use that required
// visibility from more than one .cpp file.

extern const js::Class XPC_WN_NoHelper_JSClass;
extern const js::Class XPC_WN_NoMods_Proto_JSClass;
extern const js::Class XPC_WN_ModsAllowed_Proto_JSClass;
extern const js::Class XPC_WN_Tearoff_JSClass;
#define XPC_WN_TEAROFF_RESERVED_SLOTS 1
#define XPC_WN_TEAROFF_FLAT_OBJECT_SLOT 0
extern const js::Class XPC_WN_NoHelper_Proto_JSClass;

extern bool
XPC_WN_CallMethod(JSContext* cx, unsigned argc, JS::Value* vp);

extern bool
XPC_WN_GetterSetter(JSContext* cx, unsigned argc, JS::Value* vp);

// Maybe this macro should check for class->enumerate ==
// XPC_WN_Shared_Proto_Enumerate or something rather than checking for
// 4 classes?
static inline bool IS_PROTO_CLASS(const js::Class* clazz)
{
    return clazz == &XPC_WN_NoMods_Proto_JSClass ||
           clazz == &XPC_WN_ModsAllowed_Proto_JSClass;
}

typedef js::HashSet<size_t,
                    js::DefaultHasher<size_t>,
                    js::SystemAllocPolicy> InterpositionWhitelist;

struct InterpositionWhitelistPair {
    nsIAddonInterposition* interposition;
    InterpositionWhitelist whitelist;
};

typedef nsTArray<InterpositionWhitelistPair> InterpositionWhitelistArray;

/***************************************************************************/
// XPCWrappedNativeScope is one-to-one with a JS global object.

class nsIAddonInterposition;
class nsXPCComponentsBase;
class XPCWrappedNativeScope final : public PRCList
{
public:

    XPCJSContext*
    GetContext() const {return XPCJSContext::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, XPCJSContext* cx);

    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(XPCJSContext* cx, nsCycleCollectionNoteRootCallback& cb);

    static void
    MarkAllWrappedNativesAndProtos();

#ifdef DEBUG
    static void
    ASSERT_NoInterfaceSetsAreMarked();
#endif

    static void
    SweepAllWrappedNativeTearOffs();

    static void
    UpdateWeakPointersAfterGC(XPCJSContext* cx);

    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 mContext != nullptr;}

    static bool
    IsDyingScope(XPCWrappedNativeScope* scope);

    typedef js::HashSet<JS::Heap<JSObject*>,
                        js::MovableCellHasher<JS::Heap<JSObject*>>,
                        js::SystemAllocPolicy> DOMExpandoSet;

    bool RegisterDOMExpandoObject(JSObject* expando) {
        // Expandos are proxy objects, and proxies are always tenured.
        JS::AssertGCThingMustBeTenured(expando);
        if (!mDOMExpandoSet) {
            mDOMExpandoSet = new DOMExpandoSet();
            if (!mDOMExpandoSet->init(8))
                return false;
        }
        return mDOMExpandoSet->put(JS::Heap<JSObject*>(expando));
    }
    void RemoveDOMExpandoObject(JSObject* expando) {
        if (mDOMExpandoSet) {
            DOMExpandoSet::Ptr p = mDOMExpandoSet->lookup(JS::Heap<JSObject*>(expando));
            MOZ_ASSERT(p.found());
            mDOMExpandoSet->remove(p);
        }
    }

    typedef js::HashMap<JSAddonId*,
                        nsCOMPtr<nsIAddonInterposition>,
                        js::PointerHasher<JSAddonId*, 3>,
                        js::SystemAllocPolicy> InterpositionMap;

    typedef js::HashSet<JSAddonId*,
                        js::PointerHasher<JSAddonId*, 3>,
                        js::SystemAllocPolicy> AddonSet;

    // Gets the appropriate scope object for XBL in this scope. The context
    // must be same-compartment with the global upon entering, and the scope
    // object is wrapped into the compartment of the global.
    JSObject* EnsureContentXBLScope(JSContext* cx);

    JSObject* EnsureAddonScope(JSContext* cx, JSAddonId* addonId);

    XPCWrappedNativeScope(JSContext* cx, JS::HandleObject aGlobal);

    nsAutoPtr<JSObject2JSObjectMap> mWaiverWrapperMap;

    bool IsContentXBLScope() { return mIsContentXBLScope; }
    bool AllowContentXBLScope();
    bool UseContentXBLScope() { return mUseContentXBLScope; }
    void ClearContentXBLScope() { mContentXBLScope = nullptr; }

    bool IsAddonScope() { return mIsAddonScope; }

    bool HasInterposition() { return mInterposition; }
    nsCOMPtr<nsIAddonInterposition> GetInterposition();

    static bool SetAddonInterposition(JSContext* cx,
                                      JSAddonId* addonId,
                                      nsIAddonInterposition* interp);

    static InterpositionWhitelist* GetInterpositionWhitelist(nsIAddonInterposition* interposition);
    static bool UpdateInterpositionWhitelist(JSContext* cx,
                                             nsIAddonInterposition* interposition);

    void SetAddonCallInterposition() { mHasCallInterpositions = true; }
    bool HasCallInterposition() { return mHasCallInterpositions; };

    static bool AllowCPOWsInAddon(JSContext* cx, JSAddonId* addonId, bool allow);

protected:
    virtual ~XPCWrappedNativeScope();

    XPCWrappedNativeScope() = delete;

private:
    class ClearInterpositionsObserver final : public nsIObserver {
        ~ClearInterpositionsObserver() {}

      public:
        NS_DECL_ISUPPORTS
        NS_DECL_NSIOBSERVER
    };

    static XPCWrappedNativeScope* gScopes;
    static XPCWrappedNativeScope* gDyingScopes;

    static bool                      gShutdownObserverInitialized;
    static InterpositionMap*         gInterpositionMap;
    static AddonSet*                 gAllowCPOWAddonSet;

    static InterpositionWhitelistArray* gInterpositionWhitelists;

    XPCJSContext*                    mContext;
    Native2WrappedNativeMap*         mWrappedNativeMap;
    ClassInfo2WrappedNativeProtoMap* mWrappedNativeProtoMap;
    RefPtr<nsXPCComponentsBase>    mComponents;
    XPCWrappedNativeScope*           mNext;
    // The JS global object for this scope.  If non-null, this will be the
    // default parent for the XPCWrappedNatives that have us as the scope,
    // unless a PreCreate hook overrides it.  Note that this _may_ be null (see
    // constructor).
    JS::ObjectPtr                    mGlobalJSObject;

    // XBL Scope. This is is a lazily-created sandbox for non-system scopes.
    // EnsureContentXBLScope() decides whether it needs to be created or not.
    // This reference is wrapped into the compartment of mGlobalJSObject.
    JS::ObjectPtr                    mContentXBLScope;

    // Lazily created sandboxes for addon code.
    nsTArray<JS::ObjectPtr>          mAddonScopes;

    // This is a service that will be use to interpose on some property accesses on
    // objects from other scope, for add-on compatibility reasons.
    nsCOMPtr<nsIAddonInterposition>  mInterposition;

    // If this flag is set, we intercept function calls on vanilla JS function objects
    // from this scope if the caller scope has mInterposition set.
    bool mHasCallInterpositions;

    nsAutoPtr<DOMExpandoSet> mDOMExpandoSet;

    JS::WeakMapPtr<JSObject*, JSObject*> mXrayExpandos;

    bool mIsContentXBLScope;
    bool mIsAddonScope;

    // For remote XUL domains, we run all XBL in the content scope for compat
    // reasons (though we sometimes pref this off for automation). We separately
    // track the result of this decision (mAllowContentXBLScope), from the decision
    // of whether to actually _use_ an XBL scope (mUseContentXBLScope), which depends
    // on the type of global and whether the compartment is system principal
    // or not.
    //
    // This distinction is useful primarily because, if true, we know that we
    // have no way of distinguishing XBL script from content script for the
    // given scope. In these (unsupported) situations, we just always claim to
    // be XBL.
    bool mAllowContentXBLScope;
    bool mUseContentXBLScope;
};

/***************************************************************************/
// Slots we use for our functions
#define XPC_FUNCTION_NATIVE_MEMBER_SLOT 0
#define XPC_FUNCTION_PARENT_OBJECT_SLOT 1

/***************************************************************************/
// XPCNativeMember represents a single idl declared method, attribute or
// constant.

// Tight. No virtual methods. Can be bitwise copied (until any resolution done).

class XPCNativeMember final
{
public:
    static bool GetCallInfo(JSObject* funobj,
                            RefPtr<XPCNativeInterface>* pInterface,
                            XPCNativeMember** pMember);

    jsid   GetName() const {return mName;}

    uint16_t GetIndex() const {return mIndex;}

    bool GetConstantValue(XPCCallContext& ccx, XPCNativeInterface* iface,
                          JS::Value* pval)
        {MOZ_ASSERT(IsConstant(),
                    "Only call this if you're sure this is a constant!");
         return Resolve(ccx, iface, nullptr, pval);}

    bool NewFunctionObject(XPCCallContext& ccx, XPCNativeInterface* iface,
                           JS::HandleObject parent, JS::Value* pval);

    bool IsMethod() const
        {return 0 != (mFlags & METHOD);}

    bool IsConstant() const
        {return 0 != (mFlags & CONSTANT);}

    bool IsAttribute() const
        {return 0 != (mFlags & GETTER);}

    bool IsWritableAttribute() const
        {return 0 != (mFlags & SETTER_TOO);}

    bool IsReadOnlyAttribute() const
        {return IsAttribute() && !IsWritableAttribute();}


    void SetName(jsid a) {mName = a;}

    void SetMethod(uint16_t index)
        {mFlags = METHOD; mIndex = index;}

    void SetConstant(uint16_t index)
        {mFlags = CONSTANT; mIndex = index;}

    void SetReadOnlyAttribute(uint16_t index)
        {mFlags = GETTER; mIndex = index;}

    void SetWritableAttribute()
        {MOZ_ASSERT(mFlags == GETTER,"bad"); mFlags = GETTER | SETTER_TOO;}

    static uint16_t GetMaxIndexInInterface()
        {return (1<<12) - 1;}

    inline XPCNativeInterface* GetInterface() const;

    void SetIndexInInterface(uint16_t index)
        {mIndexInInterface = index;}

    /* default ctor - leave random contents */
    XPCNativeMember()  {MOZ_COUNT_CTOR(XPCNativeMember);}
    ~XPCNativeMember() {MOZ_COUNT_DTOR(XPCNativeMember);}

private:
    bool Resolve(XPCCallContext& ccx, XPCNativeInterface* iface,
                 JS::HandleObject parent, JS::Value* vp);

    enum {
        METHOD      = 0x01,
        CONSTANT    = 0x02,
        GETTER      = 0x04,
        SETTER_TOO  = 0x08
        // If you add a flag here, you may need to make mFlags wider and either
        // make mIndexInInterface narrower (and adjust
        // XPCNativeInterface::NewInstance accordingly) or make this object
        // bigger.
    };

private:
    // our only data...
    jsid     mName;
    uint16_t mIndex;
    // mFlags needs to be wide enogh to hold the flags in the above enum.
    uint16_t mFlags : 4;
    // mIndexInInterface is the index of this in our XPCNativeInterface's
    // mMembers.  In theory our XPCNativeInterface could have as many as 2^15-1
    // members (since mMemberCount is 15-bit) but in practice we prevent
    // creation of XPCNativeInterfaces which have more than 2^12 members.
    // If the width of this field changes, update GetMaxIndexInInterface.
    uint16_t mIndexInInterface : 12;
} JS_HAZ_NON_GC_POINTER; // Only stores a pinned string

/***************************************************************************/
// XPCNativeInterface represents a single idl declared interface. This is
// primarily the set of XPCNativeMembers.

// Tight. No virtual methods.

class XPCNativeInterface final
{
  public:
    NS_INLINE_DECL_REFCOUNTING_WITH_DESTROY(XPCNativeInterface,
                                            DestroyInstance(this))

    static already_AddRefed<XPCNativeInterface> GetNewOrUsed(const nsIID* iid);
    static already_AddRefed<XPCNativeInterface> GetNewOrUsed(nsIInterfaceInfo* info);
    static already_AddRefed<XPCNativeInterface> GetNewOrUsed(const char* name);
    static already_AddRefed<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);

    size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);

  protected:
    static already_AddRefed<XPCNativeInterface> NewInstance(nsIInterfaceInfo* aInfo);

    XPCNativeInterface() = delete;
    XPCNativeInterface(nsIInterfaceInfo* aInfo, jsid aName)
      : mInfo(aInfo), mName(aName), mMemberCount(0)
    {}
    ~XPCNativeInterface();

    void* operator new(size_t, void* p) CPP_THROW_NEW {return p;}

    XPCNativeInterface(const XPCNativeInterface& r) = delete;
    XPCNativeInterface& operator= (const XPCNativeInterface& r) = delete;

    static void DestroyInstance(XPCNativeInterface* inst);

private:
    nsCOMPtr<nsIInterfaceInfo> 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 XPCNativeSetKey final
{
public:
    // This represents an existing set |baseSet|.
    explicit XPCNativeSetKey(XPCNativeSet* baseSet)
        : mBaseSet(baseSet), mAddition(nullptr)
    {
        MOZ_ASSERT(baseSet);
    }

    // This represents a new set containing only nsISupports and
    // |addition|.
    explicit XPCNativeSetKey(XPCNativeInterface* addition)
        : mBaseSet(nullptr), mAddition(addition)
    {
        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() {}

    XPCNativeSet* GetBaseSet() const {return mBaseSet;}
    XPCNativeInterface* GetAddition() const {return mAddition;}

    PLDHashNumber Hash() const;

    // Allow shallow copy

private:
    XPCNativeSet* mBaseSet;
    XPCNativeInterface* mAddition;
};

/***************************************************************************/
// XPCNativeSet represents an ordered collection of XPCNativeInterface pointers.

class XPCNativeSet final
{
  public:
    static XPCNativeSet* GetNewOrUsed(const nsIID* iid);
    static XPCNativeSet* GetNewOrUsed(nsIClassInfo* classInfo);
    static XPCNativeSet* GetNewOrUsed(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 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,
                           RefPtr<XPCNativeInterface>* pInterface) const;

    inline bool FindMember(JS::HandleId name,
                           XPCNativeMember** pMember,
                           RefPtr<XPCNativeInterface>* pInterface,
                           XPCNativeSet* protoSet,
                           bool* pIsLocal) const;

    inline bool HasInterface(XPCNativeInterface* aInterface) const;
    inline bool HasInterfaceWithAncestor(XPCNativeInterface* aInterface) const;
    inline bool HasInterfaceWithAncestor(const nsIID* iid) const;

    inline XPCNativeInterface* FindInterfaceWithIID(const nsIID& iid) const;

    inline XPCNativeInterface* FindNamedInterface(jsid name) const;

    uint16_t GetMemberCount() const {
        return mMemberCount;
    }
    uint16_t GetInterfaceCount() const {
        return mInterfaceCount;
    }
    XPCNativeInterface** GetInterfaceArray() {
        return mInterfaces;
    }

    XPCNativeInterface* GetInterfaceAt(uint16_t i)
        {MOZ_ASSERT(i < mInterfaceCount, "bad index"); return mInterfaces[i];}

    inline bool MatchesSetUpToInterface(const XPCNativeSet* other,
                                          XPCNativeInterface* iface) const;

    inline void Mark();

    // NOP. This is just here to make the AutoMarkingPtr code compile.
    inline void TraceJS(JSTracer* trc) {}
    inline void AutoTrace(JSTracer* trc) {}

  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(nsTArray<RefPtr<XPCNativeInterface>>&& array);
    static XPCNativeSet* NewInstanceMutate(XPCNativeSetKey* key);
    XPCNativeSet()
      : mMemberCount(0), mInterfaceCount(0), mMarked(0)
    {
        MOZ_COUNT_CTOR(XPCNativeSet);
    }
    ~XPCNativeSet() {
        for (int i = 0; i < mInterfaceCount; i++) {
            NS_RELEASE(mInterfaces[i]);
        }
        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;
    // Always last - object sized for array.
    // These are strong references.
    XPCNativeInterface*     mInterfaces[1];
};

/***************************************************************************/
// XPCNativeScriptableFlags is a wrapper class that holds the flags returned
// from calls to nsIXPCScriptable::GetScriptableFlags(). It has convenience
// methods to check for particular bitflags.

class XPCNativeScriptableFlags final
{
public:
    explicit XPCNativeScriptableFlags(uint32_t flags = 0) : mFlags(flags) {}

    uint32_t GetFlags() const { return mFlags; }
    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;
    }

#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

private:
    uint32_t mFlags;
};

/***************************************************************************/
// XPCNativeScriptableInfo is a trivial wrapper for nsIXPCScriptable which
// should be removed eventually.

class XPCNativeScriptableInfo final
{
public:
    static XPCNativeScriptableInfo*
    Construct(const XPCNativeScriptableCreateInfo* sci);

    nsIXPCScriptable*
    GetCallback() const { return mCallback; }

    XPCNativeScriptableFlags
    GetFlags() const { return XPCNativeScriptableFlags(mCallback->GetScriptableFlags()); }

    const JSClass*
    GetJSClass() { return Jsvalify(mCallback->GetClass()); }

protected:
    explicit XPCNativeScriptableInfo(nsIXPCScriptable* aCallback)
        : mCallback(aCallback)
    {
        MOZ_COUNT_CTOR(XPCNativeScriptableInfo);
    }
public:
    ~XPCNativeScriptableInfo()
    {
        MOZ_COUNT_DTOR(XPCNativeScriptableInfo);
    }
private:

    // disable copy ctor and assignment
    XPCNativeScriptableInfo(const XPCNativeScriptableInfo& r) = delete;
    XPCNativeScriptableInfo& operator= (const XPCNativeScriptableInfo& r) = delete;

private:
    nsCOMPtr<nsIXPCScriptable> mCallback;
};

/***************************************************************************/
// XPCNativeScriptableCreateInfo is used in creating new wrapper and protos.
// it abstracts out the scriptable interface pointer and the flags. After
// creation these are factored differently using XPCNativeScriptableInfo.

class MOZ_STACK_CLASS XPCNativeScriptableCreateInfo final
{
public:

    explicit XPCNativeScriptableCreateInfo(const XPCNativeScriptableInfo& si)
        : mCallback(si.GetCallback()), mFlags(si.GetFlags()) {}

    XPCNativeScriptableCreateInfo(already_AddRefed<nsIXPCScriptable>&& callback,
                                  XPCNativeScriptableFlags flags)
        : mCallback(callback), mFlags(flags) {}

    XPCNativeScriptableCreateInfo()
        : mFlags(0) {}


    nsIXPCScriptable*
    GetCallback() const {return mCallback;}

    const XPCNativeScriptableFlags&
    GetFlags() const      {return mFlags;}

    void
    SetCallback(already_AddRefed<nsIXPCScriptable>&& callback)
        {mCallback = callback;}

    void
    SetFlags(const XPCNativeScriptableFlags& flags)  {mFlags = flags;}

private:
    // XXX: the flags are the same as the ones gettable from the callback. This
    // redundancy should be removed eventually.
    nsCOMPtr<nsIXPCScriptable>  mCallback;
    XPCNativeScriptableFlags    mFlags;
};

/***********************************************/
// XPCWrappedNativeProto hold the additional shared wrapper data
// for XPCWrappedNative whose native objects expose nsIClassInfo.

class XPCWrappedNativeProto final
{
public:
    static XPCWrappedNativeProto*
    GetNewOrUsed(XPCWrappedNativeScope* scope,
                 nsIClassInfo* classInfo,
                 const XPCNativeScriptableCreateInfo* scriptableCreateInfo,
                 bool callPostCreatePrototype = true);

    XPCWrappedNativeScope*
    GetScope()   const {return mScope;}

    XPCJSContext*
    GetContext() const {return mScope->GetContext();}

    JSObject*
    GetJSProtoObject() const {
        JS::ExposeObjectToActiveJS(mJSProtoObject);
        return mJSProtoObject;
    }

    nsIClassInfo*
    GetClassInfo()     const {return mClassInfo;}

    XPCNativeSet*
    GetSet()           const {return mSet;}

    XPCNativeScriptableInfo*
    GetScriptableInfo()   {return mScriptableInfo;}

    bool CallPostCreatePrototype();
    void JSProtoObjectFinalized(js::FreeOp* fop, JSObject* obj);
    void JSProtoObjectMoved(JSObject* obj, const JSObject* old);

    void SystemIsBeingShutDown();

    void DebugDump(int16_t depth);

    void TraceSelf(JSTracer* trc) {
        if (mJSProtoObject)
            mJSProtoObject.trace(trc, "XPCWrappedNativeProto::mJSProtoObject");
    }

    void TraceInside(JSTracer* trc) {
        if (trc->isMarkingTracer()) {
            mSet->Mark();
        }

        GetScope()->TraceSelf(trc);
    }

    void TraceJS(JSTracer* trc) {
        TraceSelf(trc);
        TraceInside(trc);
    }

    void WriteBarrierPre(JSContext* cx)
    {
        if (JS::IsIncrementalBarrierNeeded(cx) && mJSProtoObject)
            mJSProtoObject.writeBarrierPre(cx);
    }

    // NOP. This is just here to make the AutoMarkingPtr code compile.
    inline void AutoTrace(JSTracer* trc) {}

    void Mark() const {mSet->Mark();}

#ifdef DEBUG
    void ASSERT_SetNotMarked() const {mSet->ASSERT_NotMarked();}
#endif

    ~XPCWrappedNativeProto();

protected:
    // disable copy ctor and assignment
    XPCWrappedNativeProto(const XPCWrappedNativeProto& r) = delete;
    XPCWrappedNativeProto& operator= (const XPCWrappedNativeProto& r) = delete;

    // hide ctor
    XPCWrappedNativeProto(XPCWrappedNativeScope* Scope,
                          nsIClassInfo* ClassInfo,
                          XPCNativeSet* Set);

    bool Init(const XPCNativeScriptableCreateInfo* scriptableCreateInfo,
              bool callPostCreatePrototype);

private:
#ifdef DEBUG
    static int32_t gDEBUG_LiveProtoCount;
#endif

private:
    XPCWrappedNativeScope*   mScope;
    JS::ObjectPtr            mJSProtoObject;
    nsCOMPtr<nsIClassInfo>   mClassInfo;
    XPCNativeSet*            mSet;
    XPCNativeScriptableInfo* mScriptableInfo;
};

/***********************************************/
// XPCWrappedNativeTearOff represents the info needed to make calls to one
// interface on the underlying native object of a XPCWrappedNative.

class XPCWrappedNativeTearOff final
{
public:
    bool IsAvailable() const {return mInterface == nullptr;}
    bool IsReserved()  const {return mInterface == (XPCNativeInterface*)1;}
    bool IsValid()     const {return !IsAvailable() && !IsReserved();}
    void   SetReserved()       {mInterface = (XPCNativeInterface*)1;}

    XPCNativeInterface* GetInterface() const {return mInterface;}
    nsISupports*        GetNative()    const {return mNative;}
    JSObject*           GetJSObject();
    JSObject*           GetJSObjectPreserveColor() const;
    void SetInterface(XPCNativeInterface*  Interface) {mInterface = Interface;}
    void SetNative(nsISupports*  Native)              {mNative = Native;}
    already_AddRefed<nsISupports> TakeNative() { return mNative.forget(); }
    void SetJSObject(JSObject*  JSObj);

    void JSObjectFinalized() {SetJSObject(nullptr);}
    void JSObjectMoved(JSObject* obj, const JSObject* old);

    XPCWrappedNativeTearOff()
        : mInterface(nullptr), mJSObject(nullptr)
    {
        MOZ_COUNT_CTOR(XPCWrappedNativeTearOff);
    }
    ~XPCWrappedNativeTearOff();

    // NOP. This is just here to make the AutoMarkingPtr code compile.
    inline void TraceJS(JSTracer* trc) {}
    inline void AutoTrace(JSTracer* trc) {}

    void Mark()       {mJSObject.setFlags(1);}
    void Unmark()     {mJSObject.unsetFlags(1);}
    bool IsMarked() const {return mJSObject.hasFlag(1);}

    XPCWrappedNativeTearOff* AddTearOff()
    {
        MOZ_ASSERT(!mNextTearOff);
        mNextTearOff = mozilla::MakeUnique<XPCWrappedNativeTearOff>();
        return mNextTearOff.get();
    }

    XPCWrappedNativeTearOff* GetNextTearOff() {return mNextTearOff.get();}

private:
    XPCWrappedNativeTearOff(const XPCWrappedNativeTearOff& r) = delete;
    XPCWrappedNativeTearOff& operator= (const XPCWrappedNativeTearOff& r) = delete;

private:
    XPCNativeInterface* mInterface;
    // mNative is an nsRefPtr not an nsCOMPtr because it may not be the canonical
    // nsISupports pointer.
    RefPtr<nsISupports> mNative;
    JS::TenuredHeap<JSObject*> mJSObject;
    mozilla::UniquePtr<XPCWrappedNativeTearOff> mNextTearOff;
};


/***************************************************************************/
// XPCWrappedNative the wrapper around one instance of a native xpcom object
// to be used from JavaScript.

class XPCWrappedNative final : public nsIXPConnectWrappedNative
{
public:
    NS_DECL_CYCLE_COLLECTING_ISUPPORTS
    NS_DECL_NSIXPCONNECTJSOBJECTHOLDER
    NS_DECL_NSIXPCONNECTWRAPPEDNATIVE

    NS_DECL_CYCLE_COLLECTION_CLASS(XPCWrappedNative)

    nsIPrincipal* GetObjectPrincipal() const;

    bool
    IsValid() const { return mFlatJSObject.hasFlag(FLAT_JS_OBJECT_VALID); }

#define XPC_SCOPE_WORD(s)   (intptr_t(s))
#define XPC_SCOPE_MASK      (intptr_t(0x3))
#define XPC_SCOPE_TAG       (intptr_t(0x1))
#define XPC_WRAPPER_EXPIRED (intptr_t(0x2))

    static inline bool
    IsTaggedScope(XPCWrappedNativeScope* s)
        {return XPC_SCOPE_WORD(s) & XPC_SCOPE_TAG;}

    static inline XPCWrappedNativeScope*
    TagScope(XPCWrappedNativeScope* s)
        {MOZ_ASSERT(!IsTaggedScope(s), "bad pointer!");
         return (XPCWrappedNativeScope*)(XPC_SCOPE_WORD(s) | XPC_SCOPE_TAG);}

    static inline XPCWrappedNativeScope*
    UnTagScope(XPCWrappedNativeScope* s)
        {return (XPCWrappedNativeScope*)(XPC_SCOPE_WORD(s) & ~XPC_SCOPE_TAG);}

    inline bool
    IsWrapperExpired() const
        {return XPC_SCOPE_WORD(mMaybeScope) & XPC_WRAPPER_EXPIRED;}

    bool
    HasProto() const {return !IsTaggedScope(mMaybeScope);}

    XPCWrappedNativeProto*
    GetProto() const
        {return HasProto() ?
         (XPCWrappedNativeProto*)
         (XPC_SCOPE_WORD(mMaybeProto) & ~XPC_SCOPE_MASK) : nullptr;}

    void SetProto(XPCWrappedNativeProto* p);

    XPCWrappedNativeScope*
    GetScope() const
        {return GetProto() ? GetProto()->GetScope() :
         (XPCWrappedNativeScope*)
         (XPC_SCOPE_WORD(mMaybeScope) & ~XPC_SCOPE_MASK);}

    nsISupports*
    GetIdentityObject() const {return mIdentity;}

    /**
     * This getter clears the gray bit before handing out the JSObject which
     * means that the object is guaranteed to be kept alive past the next CC.
     */
    JSObject*
    GetFlatJSObject() const
    {
        JS::ExposeObjectToActiveJS(mFlatJSObject);
        return mFlatJSObject;
    }

    /**
     * This getter does not change the color of the JSObject meaning that the
     * object returned is not guaranteed to be kept alive past the next CC.
     *
     * This should only be called if you are certain that the return value won't
     * be passed into a JS API function and that it won't be stored without
     * being rooted (or otherwise signaling the stored value to the CC).
     */
    JSObject*
    GetFlatJSObjectPreserveColor() const {return mFlatJSObject;}

    XPCNativeSet*
    GetSet() const {return mSet;}

    void
    SetSet(XPCNativeSet* set) {mSet = set;}

    static XPCWrappedNative* Get(JSObject* obj) {
        MOZ_ASSERT(IS_WN_REFLECTOR(obj));
        return (XPCWrappedNative*)js::GetObjectPrivate(obj);
    }

private:
    inline void
    ExpireWrapper()
        {mMaybeScope = (XPCWrappedNativeScope*)
                       (XPC_SCOPE_WORD(mMaybeScope) | XPC_WRAPPER_EXPIRED);}

public:

    XPCNativeScriptableInfo*
    GetScriptableInfo() const {return mScriptableInfo;}

    nsIXPCScriptable*      // call this wrong and you deserve to crash
    GetScriptableCallback() const  {return mScriptableInfo->GetCallback();}

    nsIClassInfo*
    GetClassInfo() const {return IsValid() && HasProto() ?
                            GetProto()->GetClassInfo() : nullptr;}

    bool
    HasMutatedSet() const {return IsValid() &&
                                  (!HasProto() ||
                                   GetSet() != GetProto()->GetSet());}

    XPCJSContext*
    GetContext() const {XPCWrappedNativeScope* scope = GetScope();
                        return scope ? scope->GetContext() : 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 (HasProto()) GetProto()->Mark();
    }

    inline void TraceInside(JSTracer* trc) {
        if (trc->isMarkingTracer()) {
            mSet->Mark();
        }
        if (HasProto())
            GetProto()->TraceSelf(trc);
        else
            GetScope()->TraceSelf(trc);
        if (mFlatJSObject && JS_IsGlobalObject(mFlatJSObject))
        {
            xpc::TraceXPCGlobal(trc, mFlatJSObject);
        }
    }

    void TraceJS(JSTracer* trc) {
        TraceInside(trc);
    }

    void TraceSelf(JSTracer* trc) {
        // If this got called, we're being kept alive by someone who really
        // needs us alive and whole.  Do not let our mFlatJSObject go away.
        // This is the only time we should be tracing our mFlatJSObject,
        // normally somebody else is doing that.
        JS::TraceEdge(trc, &mFlatJSObject, "XPCWrappedNative::mFlatJSObject");
    }

    static void Trace(JSTracer* trc, JSObject* obj);

    void AutoTrace(JSTracer* trc) {
        TraceSelf(trc);
    }

#ifdef DEBUG
    void ASSERT_SetsNotMarked() const
        {mSet->ASSERT_NotMarked();
         if (HasProto()){GetProto()->ASSERT_SetNotMarked();}}
#endif

    inline void SweepTearOffs();

    // Returns a string that shuld be free'd using JS_smprintf_free (or null).
    char* ToString(XPCWrappedNativeTearOff* to = nullptr) const;

    static void GatherProtoScriptableCreateInfo(nsIClassInfo* classInfo,
                                                XPCNativeScriptableCreateInfo& sciProto);

    bool HasExternalReference() const {return mRefCnt > 1;}

    void Suspect(nsCycleCollectionNoteRootCallback& cb);
    void NoteTearoffs(nsCycleCollectionTraversalCallback& cb);

    // Make ctor and dtor protected (rather than private) to placate nsCOMPtr.
protected:
    XPCWrappedNative() = delete;

    // This ctor is used if this object will have a proto.
    XPCWrappedNative(already_AddRefed<nsISupports>&& aIdentity,
                     XPCWrappedNativeProto* aProto);

    // This ctor is used if this object will NOT have a proto.
    XPCWrappedNative(already_AddRefed<nsISupports>&& aIdentity,
                     XPCWrappedNativeScope* aScope,
                     XPCNativeSet* aSet);

    virtual ~XPCWrappedNative();
    void Destroy();

    void UpdateScriptableInfo(XPCNativeScriptableInfo* si);

private:
    enum {
        // Flags bits for mFlatJSObject:
        FLAT_JS_OBJECT_VALID = JS_BIT(0)
    };

    bool Init(const XPCNativeScriptableCreateInfo* sci);
    bool FinishInit();

    bool ExtendSet(XPCNativeInterface* aInterface);

    nsresult InitTearOff(XPCWrappedNativeTearOff* aTearOff,
                         XPCNativeInterface* aInterface,
                         bool needJSObject);

    bool InitTearOffJSObject(XPCWrappedNativeTearOff* to);

public:
    static const XPCNativeScriptableCreateInfo& GatherScriptableCreateInfo(nsISupports* obj,
                                                                           nsIClassInfo* classInfo,
                                                                           XPCNativeScriptableCreateInfo& sciProto,
                                                                           XPCNativeScriptableCreateInfo& sciWrapper);

private:
    union
    {
        XPCWrappedNativeScope* mMaybeScope;
        XPCWrappedNativeProto* mMaybeProto;
    };
    XPCNativeSet* mSet;
    JS::TenuredHeap<JSObject*> mFlatJSObject;
    XPCNativeScriptableInfo* mScriptableInfo;
    XPCWrappedNativeTearOff mFirstTearOff;
};

/***************************************************************************
****************************************************************************
*
* Core classes for wrapped JSObject for use from native code...
*
****************************************************************************
***************************************************************************/

// this interfaces exists so we can refcount nsXPCWrappedJSClass
// {2453EBA0-A9B8-11d2-BA64-00805F8A5DD7}
#define NS_IXPCONNECT_WRAPPED_JS_CLASS_IID                                    \
{ 0x2453eba0, 0xa9b8, 0x11d2,                                                 \
  { 0xba, 0x64, 0x0, 0x80, 0x5f, 0x8a, 0x5d, 0xd7 } }

class nsIXPCWrappedJSClass : public nsISupports
{
public:
    NS_DECLARE_STATIC_IID_ACCESSOR(NS_IXPCONNECT_WRAPPED_JS_CLASS_IID)
    NS_IMETHOD DebugDump(int16_t depth) = 0;
};

NS_DEFINE_STATIC_IID_ACCESSOR(nsIXPCWrappedJSClass,
                              NS_IXPCONNECT_WRAPPED_JS_CLASS_IID)

/*************************/
// nsXPCWrappedJSClass represents the sharable factored out common code and
// data for nsXPCWrappedJS instances for the same interface type.

class nsXPCWrappedJSClass final : public nsIXPCWrappedJSClass
{
    // all the interface method declarations...
    NS_DECL_ISUPPORTS
    NS_IMETHOD DebugDump(int16_t depth) override;
public:

    static already_AddRefed<nsXPCWrappedJSClass>
    GetNewOrUsed(JSContext* cx,
                 REFNSIID aIID,
                 bool allowNonScriptable = false);

    REFNSIID GetIID() const {return mIID;}
    XPCJSContext* GetContext() const {return mContext;}
    nsIInterfaceInfo* GetInterfaceInfo() const {return mInfo;}
    const char* GetInterfaceName();

    static bool IsWrappedJS(nsISupports* aPtr);

    NS_IMETHOD DelegatedQueryInterface(nsXPCWrappedJS* self, REFNSIID aIID,
                                       void** aInstancePtr);

    JSObject* GetRootJSObject(JSContext* cx, JSObject* aJSObj);

    NS_IMETHOD CallMethod(nsXPCWrappedJS* wrapper, uint16_t methodIndex,
                          const XPTMethodDescriptor* info,
                          nsXPTCMiniVariant* params);

    JSObject*  CallQueryInterfaceOnJSObject(JSContext* cx,
                                            JSObject* jsobj, REFNSIID aIID);

    static nsresult BuildPropertyEnumerator(XPCCallContext& ccx,
                                            JSObject* aJSObj,
                                            nsISimpleEnumerator** aEnumerate);

    static nsresult GetNamedPropertyAsVariant(XPCCallContext& ccx,
                                              JSObject* aJSObj,
                                              const nsAString& aName,
                                              nsIVariant** aResult);

private:
    // aSyntheticException, if not null, is the exception we should be using.
    // If null, look for an exception on the JSContext hanging off the
    // XPCCallContext.
    static nsresult CheckForException(XPCCallContext & ccx,
                                      mozilla::dom::AutoEntryScript& aes,
                                      const char * aPropertyName,
                                      const char * anInterfaceName,
                                      nsIException* aSyntheticException = nullptr);
    virtual ~nsXPCWrappedJSClass();

    nsXPCWrappedJSClass() = delete;
    nsXPCWrappedJSClass(JSContext* cx, REFNSIID aIID,
                        nsIInterfaceInfo* aInfo);

    bool IsReflectable(uint16_t i) const
        {return (bool)(mDescriptors[i/32] & (1 << (i%32)));}
    void SetReflectable(uint16_t i, bool b)
        {if (b) mDescriptors[i/32] |= (1 << (i%32));
         else mDescriptors[i/32] &= ~(1 << (i%32));}

    bool GetArraySizeFromParam(JSContext* cx,
                               const XPTMethodDescriptor* method,
                               const nsXPTParamInfo& param,
                               uint16_t methodIndex,
                               uint8_t paramIndex,
                               nsXPTCMiniVariant* params,
                               uint32_t* result) const;

    bool GetInterfaceTypeFromParam(JSContext* cx,
                                   const XPTMethodDescriptor* method,
                                   const nsXPTParamInfo& param,
                                   uint16_t methodIndex,
                                   const nsXPTType& type,
                                   nsXPTCMiniVariant* params,
                                   nsID* result) const;

    static void CleanupPointerArray(const nsXPTType& datum_type,
                                    uint32_t array_count,
                                    void** arrayp);

    static void CleanupPointerTypeObject(const nsXPTType& type,
                                         void** pp);

    void CleanupOutparams(JSContext* cx, uint16_t methodIndex, const nsXPTMethodInfo* info,
                          nsXPTCMiniVariant* nativeParams, bool inOutOnly, uint8_t n) const;

private:
    XPCJSContext* mContext;
    nsCOMPtr<nsIInterfaceInfo> mInfo;
    char* mName;
    nsIID mIID;
    uint32_t* mDescriptors;
};

/*************************/
// nsXPCWrappedJS is a wrapper for a single JSObject for use from native code.
// nsXPCWrappedJS objects are chained together to represent the various
// interface on the single underlying (possibly aggregate) JSObject.

class nsXPCWrappedJS final : protected nsAutoXPTCStub,
                             public nsIXPConnectWrappedJSUnmarkGray,
                             public nsSupportsWeakReference,
                             public nsIPropertyBag,
                             public XPCRootSetElem
{
public:
    NS_DECL_CYCLE_COLLECTING_ISUPPORTS
    NS_DECL_NSIXPCONNECTJSOBJECTHOLDER
    NS_DECL_NSIXPCONNECTWRAPPEDJS
    NS_DECL_NSIXPCONNECTWRAPPEDJSUNMARKGRAY
    NS_DECL_NSISUPPORTSWEAKREFERENCE
    NS_DECL_NSIPROPERTYBAG

    NS_DECL_CYCLE_COLLECTION_SKIPPABLE_CLASS_AMBIGUOUS(nsXPCWrappedJS, nsIXPConnectWrappedJS)

    NS_IMETHOD CallMethod(uint16_t methodIndex,
                          const XPTMethodDescriptor* info,
                          nsXPTCMiniVariant* params) override;

    /*
    * This is rarely called directly. Instead one usually calls
    * XPCConvert::JSObject2NativeInterface which will handles cases where the
    * JS object is already a wrapped native or a DOM object.
    */

    static nsresult
    GetNewOrUsed(JS::HandleObject aJSObj,
                 REFNSIID aIID,
                 nsXPCWrappedJS** wrapper);

    nsISomeInterface* GetXPTCStub() { return mXPTCStub; }

    /**
     * This getter does not change the color of the JSObject meaning that the
     * object returned is not guaranteed to be kept alive past the next CC.
     *
     * This should only be called if you are certain that the return value won't
     * be passed into a JS API function and that it won't be stored without
     * being rooted (or otherwise signaling the stored value to the CC).
     */
    JSObject* GetJSObjectPreserveColor() const {return mJSObj;}

    // Returns true if the wrapper chain contains references to multiple
    // compartments. If the wrapper chain contains references to multiple
    // compartments, then it must be registered on the 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;

    nsXPCWrappedJSClass*  GetClass() const {return mClass;}
    REFNSIID GetIID() const {return GetClass()->GetIID();}
    nsXPCWrappedJS* GetRootWrapper() const {return mRoot;}
    nsXPCWrappedJS* GetNextWrapper() const {return mNext;}

    nsXPCWrappedJS* Find(REFNSIID aIID);
    nsXPCWrappedJS* FindInherited(REFNSIID aIID);
    nsXPCWrappedJS* FindOrFindInherited(REFNSIID aIID) {
        nsXPCWrappedJS* wrapper = Find(aIID);
        if (wrapper)
            return wrapper;
        return FindInherited(aIID);
    }

    bool IsRootWrapper() const {return mRoot == this;}
    bool IsValid() const {return mJSObj != nullptr;}
    void SystemIsBeingShutDown();

    // These two methods are used by JSObject2WrappedJSMap::FindDyingJSObjects
    // to find non-rooting wrappers for dying JS objects. See the top of
    // XPCWrappedJS.cpp for more details.
    bool IsSubjectToFinalization() const {return IsValid() && mRefCnt == 1;}
    void UpdateObjectPointerAfterGC() {JS_UpdateWeakPointerAfterGC(&mJSObj);}

    bool IsAggregatedToNative() const {return mRoot->mOuter != nullptr;}
    nsISupports* GetAggregatedNativeObject() const {return mRoot->mOuter;}
    void SetAggregatedNativeObject(nsISupports* aNative) {
        MOZ_ASSERT(aNative);
        if (mRoot->mOuter) {
            MOZ_ASSERT(mRoot->mOuter == aNative,
                       "Only one aggregated native can be set");
            return;
        }
        mRoot->mOuter = aNative;
    }

    void TraceJS(JSTracer* trc);

    size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;

    virtual ~nsXPCWrappedJS();
protected:
    nsXPCWrappedJS() = delete;
    nsXPCWrappedJS(JSContext* cx,
                   JSObject* aJSObj,
                   nsXPCWrappedJSClass* aClass,
                   nsXPCWrappedJS* root,
                   nsresult* rv);

    bool CanSkip();
    void Destroy();
    void Unlink();

private:
    JS::Heap<JSObject*> mJSObj;
    RefPtr<nsXPCWrappedJSClass> mClass;
    nsXPCWrappedJS* mRoot;    // If mRoot != this, it is an owning pointer.
    nsXPCWrappedJS* mNext;
    nsCOMPtr<nsISupports> mOuter;    // only set in root
};

/***************************************************************************/

class XPCJSObjectHolder final : public nsIXPConnectJSObjectHolder,
                                public XPCRootSetElem
{
public:
    // all the interface method declarations...
    NS_DECL_ISUPPORTS
    NS_DECL_NSIXPCONNECTJSOBJECTHOLDER

    // non-interface implementation

public:
    void TraceJS(JSTracer* trc);

    explicit XPCJSObjectHolder(JSObject* obj);

private:
    virtual ~XPCJSObjectHolder();

    XPCJSObjectHolder() = delete;

    JS::Heap<JSObject*> mJSObj;
};

/***************************************************************************
****************************************************************************
*
* All manner of utility classes follow...
*
****************************************************************************
***************************************************************************/

class xpcProperty : public nsIProperty
{
public:
  NS_DECL_ISUPPORTS
  NS_DECL_NSIPROPERTY

  xpcProperty(const char16_t* aName, uint32_t aNameLen, nsIVariant* aValue);

private:
    virtual ~xpcProperty() {}

    nsString             mName;
    nsCOMPtr<nsIVariant> mValue;
};

/***************************************************************************/
// class here just for static methods
class XPCConvert
{
public:
    static bool IsMethodReflectable(const XPTMethodDescriptor& info);

    /**
     * Convert a native object into a JS::Value.
     *
     * @param d [out] the resulting JS::Value
     * @param s the native object we're working with
     * @param type the type of object that s is
     * @param iid the interface of s that we want
     * @param scope the default scope to put on the new JSObject's parent
     *        chain
     * @param pErr [out] relevant error code, if any.
     */

    static bool NativeData2JS(JS::MutableHandleValue d,
                              const void* s, const nsXPTType& type,
                              const nsID* iid, nsresult* pErr);

    static bool JSData2Native(void* d, JS::HandleValue s,
                              const nsXPTType& type,
                              const nsID* iid,
                              nsresult* pErr);

    /**
     * Convert a native nsISupports into a JSObject.
     *
     * @param dest [out] the resulting JSObject
     * @param src the native object we're working with
     * @param iid the interface of src that we want (may be null)
     * @param cache the wrapper cache for src (may be null, in which case src
     *              will be QI'ed to get the cache)
     * @param allowNativeWrapper if true, this method may wrap the resulting
     *        JSObject in an XPCNativeWrapper and return that, as needed.
     * @param pErr [out] relevant error code, if any.
     * @param src_is_identity optional performance hint. Set to true only
     *                        if src is the identity pointer.
     */
    static bool NativeInterface2JSObject(JS::MutableHandleValue d,
                                         nsIXPConnectJSObjectHolder** dest,
                                         xpcObjectHelper& aHelper,
                                         const nsID* iid,
                                         bool allowNativeWrapper,
                                         nsresult* pErr);

    static bool GetNativeInterfaceFromJSObject(void** dest, JSObject* src,
                                               const nsID* iid,
                                               nsresult* pErr);
    static bool JSObject2NativeInterface(void** dest, JS::HandleObject src,
                                         const nsID* iid,
                                         nsISupports* aOuter,
                                         nsresult* pErr);

    // Note - This return the XPCWrappedNative, rather than the native itself,
    // for the WN case. You probably want UnwrapReflectorToISupports.
    static bool GetISupportsFromJSObject(JSObject* obj, nsISupports** iface);

    /**
     * Convert a native array into a JS::Value.
     *
     * @param d [out] the resulting JS::Value
     * @param s the native array we're working with
     * @param type the type of objects in the array
     * @param iid the interface of each object in the array that we want
     * @param count the number of items in the array
     * @param scope the default scope to put on the new JSObjects' parent chain
     * @param pErr [out] relevant error code, if any.
     */
    static bool NativeArray2JS(JS::MutableHandleValue d, const void** s,
                               const nsXPTType& type, const nsID* iid,
                               uint32_t count, nsresult* pErr);

    static bool JSArray2Native(void** d, JS::HandleValue s,
                               uint32_t count, const nsXPTType& type,
                               const nsID* iid, nsresult* pErr);

    static bool JSTypedArray2Native(void** d,
                                    JSObject* jsarray,
                                    uint32_t count,
                                    const nsXPTType& type,
                                    nsresult* pErr);

    static bool NativeStringWithSize2JS(JS::MutableHandleValue d, const void* s,
                                        const nsXPTType& type,
                                        uint32_t count,
                                        nsresult* pErr);

    static bool JSStringWithSize2Native(void* d, JS::HandleValue s,
                                        uint32_t count, const nsXPTType& type,
                                        nsresult* pErr);

    static nsresult JSValToXPCException(JS::MutableHandleValue s,
                                        const char* ifaceName,
                                        const char* methodName,
                                        nsIException** exception);

    static nsresult JSErrorToXPCException(const char* message,
                                          const char* ifaceName,
                                          const char* methodName,
                                          const JSErrorReport* report,
                                          nsIException** exception);

    static nsresult ConstructException(nsresult rv, const char* message,
                                       const char* ifaceName,
                                       const char* methodName,
                                       nsISupports* data,
                                       nsIException** exception,
                                       JSContext* cx,
                                       JS::Value* jsExceptionPtr);

private:
    XPCConvert() = delete;

};

/***************************************************************************/
// code for throwing exceptions into JS

class nsXPCException;

class XPCThrower
{
public:
    static void Throw(nsresult rv, JSContext* cx);
    static void Throw(nsresult rv, XPCCallContext& ccx);
    static void ThrowBadResult(nsresult rv, nsresult result, XPCCallContext& ccx);
    static void ThrowBadParam(nsresult rv, unsigned paramNum, XPCCallContext& ccx);
    static bool SetVerbosity(bool state)
        {bool old = sVerbose; sVerbose = state; return old;}

    static bool CheckForPendingException(nsresult result, JSContext* cx);

private:
    static void Verbosify(XPCCallContext& ccx,
                          char** psz, bool own);

private:
    static bool sVerbose;
};

/***************************************************************************/

class nsXPCException
{
public:
    static bool NameAndFormatForNSResult(nsresult rv,
                                         const char** name,
                                         const char** format);

    static const void* IterateNSResults(nsresult* rv,
                                        const char** name,
                                        const char** format,
                                        const void** iterp);

    static uint32_t GetNSResultCount();
};

/***************************************************************************/
/*
* nsJSID implements nsIJSID. It is also used by nsJSIID and nsJSCID as a
* member (as a hidden implementaion detail) to which they delegate many calls.
*/

// Initialization is done on demand, and calling the destructor below is always
// safe.
extern void xpc_DestroyJSxIDClassObjects();

class nsJSID final : public nsIJSID
{
public:
    NS_DEFINE_STATIC_CID_ACCESSOR(NS_JS_ID_CID)

    NS_DECL_ISUPPORTS
    NS_DECL_NSIJSID

    bool InitWithName(const nsID& id, const char* nameString);
    bool SetName(const char* name);
    void   SetNameToNoString()
        {MOZ_ASSERT(!mName, "name already set"); mName = const_cast<char*>(gNoString);}
    bool NameIsSet() const {return nullptr != mName;}
    const nsID& ID() const {return mID;}
    bool IsValid() const {return !mID.Equals(GetInvalidIID());}

    static already_AddRefed<nsJSID> NewID(const char* str);
    static already_AddRefed<nsJSID> NewID(const nsID& id);

    nsJSID();

    void Reset();
    const nsID& GetInvalidIID() const;

protected:
    virtual ~nsJSID();
    static const char gNoString[];
    nsID    mID;
    char*   mNumber;
    char*   mName;
};


// nsJSIID

class nsJSIID : public nsIJSIID,
                public nsIXPCScriptable
{
public:
    NS_DECL_ISUPPORTS

    // we manually delagate these to nsJSID
    NS_DECL_NSIJSID

    // we implement the rest...
    NS_DECL_NSIJSIID
    NS_DECL_NSIXPCSCRIPTABLE

    static already_AddRefed<nsJSIID> NewID(nsIInterfaceInfo* aInfo);

    explicit nsJSIID(nsIInterfaceInfo* aInfo);
    nsJSIID() = delete;

private:
    virtual ~nsJSIID();

    nsCOMPtr<nsIInterfaceInfo> mInfo;
};

// nsJSCID

class nsJSCID : public nsIJSCID, public nsIXPCScriptable
{
public:
    NS_DECL_ISUPPORTS

    // we manually delagate these to nsJSID
    NS_DECL_NSIJSID

    // we implement the rest...
    NS_DECL_NSIJSCID
    NS_DECL_NSIXPCSCRIPTABLE

    static already_AddRefed<nsJSCID> NewID(const char* str);

    nsJSCID();

private:
    virtual ~nsJSCID();

    void ResolveName();

private:
    RefPtr<nsJSID> mDetails;
};


/***************************************************************************/
// 'Components' object implementations. nsXPCComponentsBase has the
// less-privileged stuff that we're willing to expose to XBL.

class nsXPCComponentsBase : public nsIXPCComponentsBase
{
public:
    NS_DECL_ISUPPORTS
    NS_DECL_NSIXPCCOMPONENTSBASE

public:
    void SystemIsBeingShutDown() { ClearMembers(); }

    XPCWrappedNativeScope* GetScope() { return mScope; }

protected:
    virtual ~nsXPCComponentsBase();

    explicit nsXPCComponentsBase(XPCWrappedNativeScope* aScope);
    virtual void ClearMembers();

    XPCWrappedNativeScope*                   mScope;

    // Unprivileged members from nsIXPCComponentsBase.
    RefPtr<nsXPCComponents_Interfaces>     mInterfaces;
    RefPtr<nsXPCComponents_InterfacesByID> mInterfacesByID;
    RefPtr<nsXPCComponents_Results>        mResults;

    friend class XPCWrappedNativeScope;
};

class nsXPCComponents : public nsXPCComponentsBase,
                        public nsIXPCComponents
{
public:
    NS_DECL_ISUPPORTS_INHERITED
    NS_FORWARD_NSIXPCCOMPONENTSBASE(nsXPCComponentsBase::)
    NS_DECL_NSIXPCCOMPONENTS

protected:
    explicit nsXPCComponents(XPCWrappedNativeScope* aScope);
    virtual ~nsXPCComponents();
    virtual void ClearMembers() override;

    // Privileged members added by nsIXPCComponents.
    RefPtr<nsXPCComponents_Classes>     mClasses;
    RefPtr<nsXPCComponents_ClassesByID> mClassesByID;
    RefPtr<nsXPCComponents_ID>          mID;
    RefPtr<nsXPCComponents_Exception>   mException;
    RefPtr<nsXPCComponents_Constructor> mConstructor;
    RefPtr<nsXPCComponents_Utils>       mUtils;

    friend class XPCWrappedNativeScope;
};


/***************************************************************************/

extern JSObject*
xpc_NewIDObject(JSContext* cx, JS::HandleObject jsobj, const nsID& aID);

extern const nsID*
xpc_JSObjectToID(JSContext* cx, JSObject* obj);

extern bool
xpc_JSObjectIsID(JSContext* cx, JSObject* obj);

/***************************************************************************/
// in XPCDebug.cpp

extern bool
xpc_DumpJSStack(bool showArgs, bool showLocals, bool showThisProps);

// Return a newly-allocated string containing a representation of the
// current JS stack.  It is the *caller's* responsibility to free this
// string with JS_smprintf_free().
extern char*
xpc_PrintJSStack(JSContext* cx, bool showArgs, bool showLocals,
                 bool showThisProps);

/***************************************************************************/

// Definition of nsScriptError, defined here because we lack a place to put
// XPCOM objects associated with the JavaScript engine.
class nsScriptErrorBase : public nsIScriptError {
public:
    nsScriptErrorBase();

  // TODO - do something reasonable on getting null from these babies.

    NS_DECL_NSICONSOLEMESSAGE
    NS_DECL_NSISCRIPTERROR

protected:
    virtual ~nsScriptErrorBase();

    void
    InitializeOnMainThread();

    nsString mMessage;
    nsString mMessageName;
    nsString mSourceName;
    uint32_t mLineNumber;
    nsString mSourceLine;
    uint32_t mColumnNumber;
    uint32_t mFlags;
    nsCString mCategory;
    // mOuterWindowID is set on the main thread from InitializeOnMainThread().
    uint64_t mOuterWindowID;
    uint64_t mInnerWindowID;
    int64_t mTimeStamp;
    // mInitializedOnMainThread and mIsFromPrivateWindow are set on the main
    // thread from InitializeOnMainThread().
    mozilla::Atomic<bool> mInitializedOnMainThread;
    bool mIsFromPrivateWindow;
};

class nsScriptError final : public nsScriptErrorBase {
public:
    nsScriptError() {}
    NS_DECL_THREADSAFE_ISUPPORTS

private:
    virtual ~nsScriptError() {}
};

class nsScriptErrorWithStack : public nsScriptErrorBase {
public:
    explicit nsScriptErrorWithStack(JS::HandleObject);

    NS_DECL_CYCLE_COLLECTING_ISUPPORTS
    NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsScriptErrorWithStack)

    NS_IMETHOD Init(const nsAString& message,
                    const nsAString& sourceName,
                    const nsAString& sourceLine,
                    uint32_t lineNumber,
                    uint32_t columnNumber,
                    uint32_t flags,
                    const char* category) override;

    NS_IMETHOD GetStack(JS::MutableHandleValue) override;
    NS_IMETHOD ToString(nsACString& aResult) override;

private:
    virtual ~nsScriptErrorWithStack();
    // Complete stackframe where the error happened.
    // Must be SavedFrame object.
    JS::Heap<JSObject*>  mStack;
};

/******************************************************************************
 * Handles pre/post script processing.
 */
class MOZ_RAII AutoScriptEvaluate
{
public:
    /**
     * Saves the JSContext as well as initializing our state
     * @param cx The JSContext, this can be null, we don't do anything then
     */
    explicit AutoScriptEvaluate(JSContext * cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
         : mJSContext(cx), mEvaluated(false) {
        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
    }

    /**
     * Does the pre script evaluation.
     * This function should only be called once, and will assert if called
     * more than once
     */

    bool StartEvaluating(JS::HandleObject scope);

    /**
     * Does the post script evaluation.
     */
    ~AutoScriptEvaluate();
private:
    JSContext* mJSContext;
    mozilla::Maybe<JS::AutoSaveExceptionState> mState;
    bool mEvaluated;
    mozilla::Maybe<JSAutoCompartment> mAutoCompartment;
    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER

    // No copying or assignment allowed
    AutoScriptEvaluate(const AutoScriptEvaluate&) = delete;
    AutoScriptEvaluate & operator =(const AutoScriptEvaluate&) = delete;
};

/***************************************************************************/
class MOZ_RAII AutoResolveName
{
public:
    AutoResolveName(XPCCallContext& ccx, JS::HandleId name
                    MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
          mOld(ccx, XPCJSContext::Get()->SetResolveName(name))
#ifdef DEBUG
          ,mCheck(ccx, name)
#endif
    {
        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
    }
    ~AutoResolveName()
        {
#ifdef DEBUG
            jsid old =
#endif
            XPCJSContext::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 = 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 T>
class TypedAutoMarkingPtr : public AutoMarkingPtr
{
  public:
    explicit TypedAutoMarkingPtr(JSContext* cx) : AutoMarkingPtr(cx), mPtr(nullptr) {}
    TypedAutoMarkingPtr(JSContext* cx, T* ptr) : AutoMarkingPtr(cx), mPtr(ptr) {}

    T* get() const { return mPtr; }
    operator T*() const { return mPtr; }
    T* operator->() const { return mPtr; }

    TypedAutoMarkingPtr<T>& operator =(T* ptr) { mPtr = ptr; return *this; }

  protected:
    virtual void TraceJS(JSTracer* trc)
    {
        if (mPtr) {
            mPtr->TraceJS(trc);
            mPtr->AutoTrace(trc);
        }
    }

    virtual void MarkAfterJSFinalize()
    {
        if (mPtr)
            mPtr->Mark();
    }

  private:
    T* mPtr;
};

typedef TypedAutoMarkingPtr<XPCNativeSet> AutoMarkingNativeSetPtr;
typedef TypedAutoMarkingPtr<XPCWrappedNative> AutoMarkingWrappedNativePtr;
typedef TypedAutoMarkingPtr<XPCWrappedNativeTearOff> AutoMarkingWrappedNativeTearOffPtr;
typedef TypedAutoMarkingPtr<XPCWrappedNativeProto> AutoMarkingWrappedNativeProtoPtr;

/***************************************************************************/
namespace xpc {
// Allocates a string that grants all access ("AllAccess")
char*
CloneAllAccess();

// Returns access if wideName is in list
char*
CheckAccessList(const char16_t* wideName, const char* const list[]);
} /* namespace xpc */

/***************************************************************************/
// in xpcvariant.cpp...

// {1809FD50-91E8-11d5-90F9-0010A4E73D9A}
#define XPCVARIANT_IID                                                        \
    {0x1809fd50, 0x91e8, 0x11d5,                                              \
      { 0x90, 0xf9, 0x0, 0x10, 0xa4, 0xe7, 0x3d, 0x9a } }

// {DC524540-487E-4501-9AC7-AAA784B17C1C}
#define XPCVARIANT_CID                                                        \
    {0xdc524540, 0x487e, 0x4501,                                              \
      { 0x9a, 0xc7, 0xaa, 0xa7, 0x84, 0xb1, 0x7c, 0x1c } }

class XPCVariant : public nsIVariant
{
public:
    NS_DECL_CYCLE_COLLECTING_ISUPPORTS
    NS_DECL_NSIVARIANT
    NS_DECL_CYCLE_COLLECTION_CLASS(XPCVariant)

    // If this class ever implements nsIWritableVariant, take special care with
    // the case when mJSVal is JSVAL_STRING, since we don't own the data in
    // that case.

    // We #define and iid so that out module local code can use QI to detect
    // if a given nsIVariant is in fact an XPCVariant.
    NS_DECLARE_STATIC_IID_ACCESSOR(XPCVARIANT_IID)

    static already_AddRefed<XPCVariant> newVariant(JSContext* cx, JS::Value aJSVal);

    /**
     * This getter clears the gray bit before handing out the Value if the Value
     * represents a JSObject. That means that the object is guaranteed to be
     * kept alive past the next CC.
     */
    JS::Value GetJSVal() const {
        if (!mJSVal.isPrimitive())
            JS::ExposeObjectToActiveJS(&mJSVal.toObject());
        return mJSVal;
    }

    /**
     * This getter does not change the color of the Value (if it represents a
     * JSObject) meaning that the value returned is not guaranteed to be kept
     * alive past the next CC.
     *
     * This should only be called if you are certain that the return value won't
     * be passed into a JS API function and that it won't be stored without
     * being rooted (or otherwise signaling the stored value to the CC).
     */
    JS::Value GetJSValPreserveColor() const {return mJSVal;}

    XPCVariant(JSContext* cx, JS::Value aJSVal);

    /**
     * Convert a variant into a JS::Value.
     *
     * @param ccx the context for the whole procedure
     * @param variant the variant to convert
     * @param scope the default scope to put on the new JSObject's parent chain
     * @param pErr [out] relevant error code, if any.
     * @param pJSVal [out] the resulting jsval.
     */
    static bool VariantDataToJS(nsIVariant* variant,
                                nsresult* pErr, JS::MutableHandleValue pJSVal);

    bool IsPurple()
    {
        return mRefCnt.IsPurple();
    }

    void RemovePurple()
    {
        mRefCnt.RemovePurple();
    }

    void SetCCGeneration(uint32_t aGen)
    {
        mCCGeneration = aGen;
    }

    uint32_t CCGeneration() { return mCCGeneration; }
protected:
    virtual ~XPCVariant() { }

    bool InitializeData(JSContext* cx);

protected:
    nsDiscriminatedUnion mData;
    JS::Heap<JS::Value>  mJSVal;
    bool                 mReturnRawObject : 1;
    uint32_t             mCCGeneration : 31;
};

NS_DEFINE_STATIC_IID_ACCESSOR(XPCVariant, XPCVARIANT_IID)

class XPCTraceableVariant: public XPCVariant,
                           public XPCRootSetElem
{
public:
    XPCTraceableVariant(JSContext* cx, JS::Value aJSVal)
        : XPCVariant(cx, aJSVal)
    {
         nsXPConnect::GetContextInstance()->AddVariantRoot(this);
    }

    virtual ~XPCTraceableVariant();

    void TraceJS(JSTracer* trc);
};

/***************************************************************************/
// Utilities

inline void*
xpc_GetJSPrivate(JSObject* obj)
{
    return js::GetObjectPrivate(obj);
}

inline JSContext*
xpc_GetSafeJSContext()
{
    return XPCJSContext::Get()->Context();
}

namespace xpc {

JSAddonId*
NewAddonId(JSContext* cx, const nsACString& id);

// JSNatives to expose atob and btoa in various non-DOM XPConnect scopes.
bool
Atob(JSContext* cx, unsigned argc, JS::Value* vp);

bool
Btoa(JSContext* cx, unsigned argc, JS::Value* vp);

// Helper function that creates a JSFunction that wraps a native function that
// forwards the call to the original 'callable'.
class FunctionForwarderOptions;
bool
NewFunctionForwarder(JSContext* cx, JS::HandleId id, JS::HandleObject callable,
                     FunctionForwarderOptions& options, JS::MutableHandleValue vp);

// Old fashioned xpc error reporter. Try to use JS_ReportError instead.
nsresult
ThrowAndFail(nsresult errNum, JSContext* cx, bool* retval);

struct GlobalProperties {
    GlobalProperties() {
      mozilla::PodZero(this);

    }
    bool Parse(JSContext* cx, JS::HandleObject obj);
    bool DefineInXPCComponents(JSContext* cx, JS::HandleObject obj);
    bool DefineInSandbox(JSContext* cx, JS::HandleObject obj);
    bool CSS : 1;
    bool indexedDB : 1;
    bool XMLHttpRequest : 1;
    bool TextDecoder : 1;
    bool TextEncoder : 1;
    bool URL : 1;
    bool URLSearchParams : 1;
    bool atob : 1;
    bool btoa : 1;
    bool Blob : 1;
    bool Directory : 1;
    bool File : 1;
    bool crypto : 1;
    bool rtcIdentityProvider : 1;
    bool fetch : 1;
    bool caches : 1;
    bool fileReader: 1;
private:
    bool Define(JSContext* cx, JS::HandleObject obj);
};

// Infallible.
already_AddRefed<nsIXPCComponents_utils_Sandbox>
NewSandboxConstructor();

// Returns true if class of 'obj' is SandboxClass.
bool
IsSandbox(JSObject* obj);

class MOZ_STACK_CLASS OptionsBase {
public:
    explicit OptionsBase(JSContext* cx = xpc_GetSafeJSContext(),
                         JSObject* options = nullptr)
        : mCx(cx)
        , mObject(cx, options)
    { }

    virtual bool Parse() = 0;

protected:
    bool ParseValue(const char* name, JS::MutableHandleValue prop, bool* found = nullptr);
    bool ParseBoolean(const char* name, bool* prop);
    bool ParseObject(const char* name, JS::MutableHandleObject prop);
    bool ParseJSString(const char* name, JS::MutableHandleString prop);
    bool ParseString(const char* name, nsCString& prop);
    bool ParseString(const char* name, nsString& prop);
    bool ParseId(const char* name, JS::MutableHandleId id);
    bool ParseUInt32(const char* name, uint32_t* prop);

    JSContext* mCx;
    JS::RootedObject mObject;
};

class MOZ_STACK_CLASS SandboxOptions : public OptionsBase {
public:
    explicit SandboxOptions(JSContext* cx = xpc_GetSafeJSContext(),
                            JSObject* options = nullptr)
        : OptionsBase(cx, options)
        , wantXrays(true)
        , allowWaivers(true)
        , wantComponents(true)
        , wantExportHelpers(false)
        , isWebExtensionContentScript(false)
        , waiveInterposition(false)
        , proto(cx)
        , addonId(cx)
        , writeToGlobalPrototype(false)
        , sameZoneAs(cx)
        , freshZone(false)
        , invisibleToDebugger(false)
        , discardSource(false)
        , metadata(cx)
        , userContextId(0)
        , originAttributes(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;
    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, JSID_VOID)
    { }

    virtual bool Parse() { return ParseId("defineAs", &defineAs); }

    JS::RootedId defineAs;
};

class MOZ_STACK_CLASS ExportFunctionOptions : public OptionsBase {
public:
    explicit ExportFunctionOptions(JSContext* cx = xpc_GetSafeJSContext(),
                                   JSObject* options = nullptr)
        : OptionsBase(cx, options)
        , defineAs(cx, JSID_VOID)
        , allowCrossOriginArguments(false)
    { }

    virtual bool Parse() {
        return ParseId("defineAs", &defineAs) &&
               ParseBoolean("allowCrossOriginArguments", &allowCrossOriginArguments);
    }

    JS::RootedId defineAs;
    bool allowCrossOriginArguments;
};

class MOZ_STACK_CLASS FunctionForwarderOptions : public OptionsBase {
public:
    explicit FunctionForwarderOptions(JSContext* cx = xpc_GetSafeJSContext(),
                                      JSObject* options = nullptr)
        : OptionsBase(cx, options)
        , allowCrossOriginArguments(false)
    { }

    JSObject* ToJSObject(JSContext* cx) {
        JS::RootedObject obj(cx, JS_NewObjectWithGivenProto(cx, nullptr, nullptr));
        if (!obj)
            return nullptr;

        JS::RootedValue val(cx);
        unsigned attrs = JSPROP_READONLY | JSPROP_PERMANENT;
        val = JS::BooleanValue(allowCrossOriginArguments);
        if (!JS_DefineProperty(cx, obj, "allowCrossOriginArguments", val, attrs))
            return nullptr;

        return obj;
    }

    virtual bool Parse() {
        return ParseBoolean("allowCrossOriginArguments", &allowCrossOriginArguments);
    }

    bool allowCrossOriginArguments;
};

class MOZ_STACK_CLASS StackScopedCloneOptions : public OptionsBase {
public:
    explicit StackScopedCloneOptions(JSContext* cx = xpc_GetSafeJSContext(),
                                     JSObject* options = nullptr)
        : OptionsBase(cx, options)
        , wrapReflectors(false)
        , cloneFunctions(false)
        , deepFreeze(false)
    { }

    virtual bool Parse() {
        return ParseBoolean("wrapReflectors", &wrapReflectors) &&
               ParseBoolean("cloneFunctions", &cloneFunctions) &&
               ParseBoolean("deepFreeze", &deepFreeze);
    }

    // When a reflector is encountered, wrap it rather than aborting the clone.
    bool wrapReflectors;

    // When a function is encountered, clone it (exportFunction-style) rather than
    // aborting the clone.
    bool cloneFunctions;

    // If true, the resulting object is deep-frozen after being cloned.
    bool deepFreeze;
};

JSObject*
CreateGlobalObject(JSContext* cx, const JSClass* clasp, nsIPrincipal* principal,
                   JS::CompartmentOptions& aOptions);

// Modify the provided compartment options, consistent with |aPrincipal| and
// with globally-cached values of various preferences.
//
// Call this function *before* |aOptions| is used to create the corresponding
// global object, as not all of the options it sets can be modified on an
// existing global object.  (The type system should make this obvious, because
// you can't get a *mutable* JS::CompartmentOptions& from an existing global
// object.)
void
InitGlobalObjectOptions(JS::CompartmentOptions& aOptions,
                        nsIPrincipal* aPrincipal);

// Finish initializing an already-created, not-yet-exposed-to-script global
// object.  This will attach a Components object (if necessary) and call
// |JS_FireOnNewGlobalObject| (if necessary).
//
// If you must modify compartment options, see InitGlobalObjectOptions above.
bool
InitGlobalObject(JSContext* aJSContext, JS::Handle<JSObject*> aGlobal,
                 uint32_t aFlags);

// Helper for creating a sandbox object to use for evaluating
// untrusted code completely separated from all other code in the
// system using EvalInSandbox(). Takes the JSContext on which to
// do setup etc on, puts the sandbox object in *vp (which must be
// rooted by the caller), and uses the principal that's either
// directly passed in prinOrSop or indirectly as an
// nsIScriptObjectPrincipal holding the principal. If no principal is
// reachable through prinOrSop, a new null principal will be created
// and used.
nsresult
CreateSandboxObject(JSContext* cx, JS::MutableHandleValue vp, nsISupports* prinOrSop,
                    xpc::SandboxOptions& options);
// Helper for evaluating scripts in a sandbox object created with
// CreateSandboxObject(). The caller is responsible of ensuring
// that *rval doesn't get collected during the call or usage after the
// call. This helper will use filename and lineNo for error reporting,
// and if no filename is provided it will use the codebase from the
// principal and line number 1 as a fallback.
nsresult
EvalInSandbox(JSContext* cx, JS::HandleObject sandbox, const nsAString& source,
              const nsACString& filename, int32_t lineNo,
              JSVersion jsVersion, JS::MutableHandleValue rval);

nsresult
GetSandboxAddonId(JSContext* cx, JS::HandleObject sandboxArg,
                  JS::MutableHandleValue rval);

// Helper for retrieving metadata stored in a reserved slot. The metadata
// is set during the sandbox creation using the "metadata" option.
nsresult
GetSandboxMetadata(JSContext* cx, JS::HandleObject sandboxArg,
                   JS::MutableHandleValue rval);

nsresult
SetSandboxMetadata(JSContext* cx, JS::HandleObject sandboxArg,
                   JS::HandleValue metadata);

bool
CreateObjectIn(JSContext* cx, JS::HandleValue vobj, CreateObjectInOptions& options,
               JS::MutableHandleValue rval);

bool
EvalInWindow(JSContext* cx, const nsAString& source, JS::HandleObject scope,
             JS::MutableHandleValue rval);

bool
ExportFunction(JSContext* cx, JS::HandleValue vscope, JS::HandleValue vfunction,
               JS::HandleValue voptions, JS::MutableHandleValue rval);

bool
CloneInto(JSContext* cx, JS::HandleValue vobj, JS::HandleValue vscope,
          JS::HandleValue voptions, JS::MutableHandleValue rval);

bool
StackScopedClone(JSContext* cx, StackScopedCloneOptions& options, JS::MutableHandleValue val);

} /* namespace xpc */


/***************************************************************************/
// Inlined utilities.

inline bool
xpc_ForcePropertyResolve(JSContext* cx, JS::HandleObject obj, jsid id);

inline jsid
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 CompartmentPrivate
{
public:
    enum LocationHint {
        LocationHintRegular,
        LocationHintAddon
    };

    explicit CompartmentPrivate(JSCompartment* c);

    ~CompartmentPrivate();

    static CompartmentPrivate* Get(JSCompartment* compartment)
    {
        MOZ_ASSERT(compartment);
        void* priv = JS_GetCompartmentPrivate(compartment);
        return static_cast<CompartmentPrivate*>(priv);
    }

    static CompartmentPrivate* Get(JSObject* object)
    {
        JSCompartment* compartment = js::GetObjectCompartment(object);
        return Get(compartment);
    }

    // Controls whether this compartment gets Xrays to same-origin. This behavior
    // is deprecated, but is still the default for sandboxes for compatibity
    // reasons.
    bool wantXrays;

    // Controls whether this compartment is allowed to waive Xrays to content
    // that it subsumes. This should generally be true, except in cases where we
    // want to prevent code from depending on Xray Waivers (which might make it
    // more portable to other browser architectures).
    bool allowWaivers;

    // This flag is intended for a very specific use, internal to Gecko. It may
    // go away or change behavior at any time. It should not be added to any
    // documentation and it should not be used without consulting the XPConnect
    // module owner.
    bool writeToGlobalPrototype;

    // When writeToGlobalPrototype is true, we use this flag to temporarily
    // disable the writeToGlobalPrototype behavior (when resolving standard
    // classes, for example).
    bool skipWriteToGlobalPrototype;

    // This scope corresponds to a WebExtension content script, and receives
    // various bits of special compatibility behavior.
    bool isWebExtensionContentScript;

    // Even if an add-on needs interposition, it does not necessary need it
    // for every scope. If this flag is set we waive interposition for this
    // scope.
    bool waiveInterposition;

    // If CPOWs are disabled for browser code via the
    // dom.ipc.cpows.forbid-unsafe-from-browser preferences, then only
    // add-ons can use CPOWs. This flag allows a non-addon scope
    // to opt into CPOWs. It's necessary for the implementation of
    // RemoteAddonsParent.jsm.
    bool allowCPOWs;

    // This is only ever set during mochitest runs when enablePrivilege is called.
    // It's intended as a temporary stopgap measure until we can finish ripping out
    // enablePrivilege. Once set, this value is never unset (i.e., it doesn't follow
    // the old scoping rules of enablePrivilege).
    //
    // Using it in production is inherently unsafe.
    bool universalXPConnectEnabled;

    // This is only ever set during mochitest runs when enablePrivilege is called.
    // It allows the SpecialPowers scope to waive the normal chrome security
    // wrappers and expose properties directly to content. This lets us avoid a
    // bunch of overhead and complexity in our SpecialPowers automation glue.
    //
    // Using it in production is inherently unsafe.
    bool forcePermissiveCOWs;

    // Whether we've emitted a warning about a property that was filtered out
    // by a security wrapper. See XrayWrapper.cpp.
    bool wrapperDenialWarnings[WrapperDenialTypeCount];

    // The scriptability of this compartment.
    Scriptability scriptability;

    // Our XPCWrappedNativeScope. This is non-null if and only if this is an
    // XPConnect compartment.
    XPCWrappedNativeScope* scope;

    const nsACString& GetLocation() {
        if (location.IsEmpty() && locationURI) {

            nsCOMPtr<nsIXPConnectWrappedJS> jsLocationURI =
                 do_QueryInterface(locationURI);
            if (jsLocationURI) {
                // We cannot call into JS-implemented nsIURI objects, because
                // we are iterating over the JS heap at this point.
                location =
                    NS_LITERAL_CSTRING("<JS-implemented nsIURI location>");
            } else if (NS_FAILED(locationURI->GetSpec(location))) {
                location = NS_LITERAL_CSTRING("<unknown location>");
            }
        }
        return location;
    }
    bool GetLocationURI(nsIURI** aURI) {
        return GetLocationURI(LocationHintRegular, aURI);
    }
    bool GetLocationURI(LocationHint aLocationHint, nsIURI** aURI) {
        if (locationURI) {
            nsCOMPtr<nsIURI> rval = locationURI;
            rval.forget(aURI);
            return true;
        }
        return TryParseLocationURI(aLocationHint, aURI);
    }
    void SetLocation(const nsACString& aLocation) {
        if (aLocation.IsEmpty())
            return;
        if (!location.IsEmpty() || locationURI)
            return;
        location = aLocation;
    }
    void SetLocationURI(nsIURI* aLocationURI) {
        if (!aLocationURI)
            return;
        if (locationURI)
            return;
        locationURI = aLocationURI;
    }

    JSObject2WrappedJSMap* GetWrappedJSMap() const { return mWrappedJSMap; }
    void UpdateWeakPointersAfterGC(XPCJSContext* context);

    size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);

private:
    nsCString location;
    nsCOMPtr<nsIURI> locationURI;
    JSObject2WrappedJSMap* mWrappedJSMap;

    bool TryParseLocationURI(LocationHint aType, nsIURI** aURI);
};

bool IsUniversalXPConnectEnabled(JSCompartment* compartment);
bool IsUniversalXPConnectEnabled(JSContext* cx);
bool EnableUniversalXPConnect(JSContext* cx);

inline void
CrashIfNotInAutomation()
{
    MOZ_RELEASE_ASSERT(IsInAutomation());
}

inline XPCWrappedNativeScope*
ObjectScope(JSObject* obj)
{
    return CompartmentPrivate::Get(obj)->scope;
}

JSObject* NewOutObject(JSContext* cx);
bool IsOutObject(JSContext* cx, JSObject* obj);

nsresult HasInstance(JSContext* cx, JS::HandleObject objArg, const nsID* iid, bool* bp);

nsIPrincipal* GetObjectPrincipal(JSObject* obj);

} // namespace xpc

namespace mozilla {
namespace dom {
extern bool
DefineStaticJSVals(JSContext* cx);
} // namespace dom
} // namespace mozilla

bool
xpc_LocalizeContext(JSContext* cx);
void
xpc_DelocalizeContext(JSContext* cx);

/***************************************************************************/
// Inlines use the above - include last.

#include "XPCInlines.h"

/***************************************************************************/
// Maps have inlines that use the above - include last.

#include "XPCMaps.h"

/***************************************************************************/

#endif /* xpcprivate_h___ */
