Source code

Revision control

Other Tools

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at */
* XPConnect allows JS code to manipulate C++ object and C++ code to manipulate
* JS objects. JS manipulation of C++ objects tends to be significantly more
* complex. This comment explains how it is orchestrated by XPConnect.
* For each C++ object to be manipulated in JS, there is a corresponding JS
* object. This is called the "flattened JS object". By default, there is an
* additional C++ object involved of type XPCWrappedNative. The XPCWrappedNative
* holds pointers to the C++ object and the flat JS object.
* All XPCWrappedNative objects belong to an XPCWrappedNativeScope. These scopes
* are essentially in 1:1 correspondence with JS compartments. The
* XPCWrappedNativeScope has a pointer to the JS compartment. The global of a
* flattened JS object is one of the globals in this compartment (the exception
* to this rule is when a PreCreate hook asks for a different global; see
* nsIXPCScriptable below).
* Some C++ objects (notably DOM objects) have information associated with them
* that lists the interfaces implemented by these objects. A C++ object exposes
* this information by implementing nsIClassInfo. If a C++ object implements
* nsIClassInfo, then JS code can call its methods without needing to use
* QueryInterface first. Typically, all instances of a C++ class share the same
* nsIClassInfo instance. (That is, obj->QueryInterface(nsIClassInfo) returns
* the same result for every obj of a given class.)
* XPConnect tracks nsIClassInfo information in an XPCWrappedNativeProto object.
* A given XPCWrappedNativeScope will have one XPCWrappedNativeProto for each
* nsIClassInfo instance being used. The XPCWrappedNativeProto has an associated
* JS object, which is used as the prototype of all flattened JS objects created
* for C++ objects with the given nsIClassInfo.
* Each XPCWrappedNativeProto has a pointer to its XPCWrappedNativeScope. If an
* XPCWrappedNative wraps a C++ object with class info, then it points to its
* XPCWrappedNativeProto. Otherwise it points to its XPCWrappedNativeScope. (The
* pointers are smooshed together in a tagged union.) Either way it can reach
* its scope.
* An XPCWrappedNativeProto keeps track of the set of interfaces implemented by
* the C++ object in an XPCNativeSet. (The list of interfaces is obtained by
* calling a method on the nsIClassInfo.) An XPCNativeSet is a collection of
* XPCNativeInterfaces. Each interface stores the list of members, which can be
* methods, constants, getters, or setters.
* An XPCWrappedNative also points to an XPCNativeSet. Initially this starts out
* the same as the XPCWrappedNativeProto's set. If there is no proto, it starts
* out as a singleton set containing nsISupports. If JS code QI's new interfaces
* outside of the existing set, the set will grow. All QueryInterface results
* are cached in XPCWrappedNativeTearOff objects, which are linked off of the
* XPCWrappedNative.
* Besides having class info, a C++ object may be "scriptable" (i.e., implement
* nsIXPCScriptable). This allows it to implement a more DOM-like interface,
* besides just exposing XPCOM methods and constants. An nsIXPCScriptable
* instance has hooks that correspond to all the normal JSClass hooks. Each
* nsIXPCScriptable instance can have pointers from XPCWrappedNativeProto and
* XPCWrappedNative (since C++ objects can have scriptable info without having
* class info).
/* All the XPConnect private declarations - only include locally. */
#ifndef xpcprivate_h___
#define xpcprivate_h___
#include "mozilla/Alignment.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/Atomics.h"
#include "mozilla/Attributes.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/CycleCollectedJSContext.h"
#include "mozilla/CycleCollectedJSRuntime.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/DefineEnum.h"
#include "mozilla/LinkedList.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/HashTable.h"
#include "js/GCHashTable.h"
#include "js/Object.h" // JS::GetClass, JS::GetCompartment, JS::GetPrivate
#include "js/TracingAPI.h"
#include "js/WeakMapPtr.h"
#include "PLDHashTable.h"
#include "nscore.h"
#include "nsXPCOM.h"
#include "nsCycleCollectionParticipant.h"
#include "nsDebug.h"
#include "nsISupports.h"
#include "nsIServiceManager.h"
#include "nsIClassInfoImpl.h"
#include "nsIComponentManager.h"
#include "nsIComponentRegistrar.h"
#include "nsISupportsPrimitives.h"
#include "nsISimpleEnumerator.h"
#include "nsMemory.h"
#include "nsIXPConnect.h"
#include "nsIXPCScriptable.h"
#include "nsIObserver.h"
#include "nsWeakReference.h"
#include "nsCOMPtr.h"
#include "nsXPTCUtils.h"
#include "xptinfo.h"
#include "XPCForwards.h"
#include "XPCLog.h"
#include "xpccomponents.h"
#include "prenv.h"
#include "prcvar.h"
#include "nsString.h"
#include "nsReadableUtils.h"
#include "MainThreadUtils.h"
#include "nsIConsoleService.h"
#include "nsVariant.h"
#include "nsCOMArray.h"
#include "nsTArray.h"
#include "nsBaseHashtable.h"
#include "nsHashKeys.h"
#include "nsWrapperCache.h"
#include "nsStringBuffer.h"
#include "nsDeque.h"
#include "nsIScriptSecurityManager.h"
#include "nsIPrincipal.h"
#include "nsJSPrincipals.h"
#include "nsIScriptObjectPrincipal.h"
#include "xpcObjectHelper.h"
#include "SandboxPrivate.h"
#include "BackstagePass.h"
#ifdef XP_WIN
// Nasty MS defines
# ifdef GetClassInfo
# undef GetClassInfo
# endif
# ifdef GetClassName
# undef GetClassName
# endif
#endif /* XP_WIN */
namespace mozilla {
namespace dom {
class AutoEntryScript;
class Exception;
} // namespace dom
} // namespace mozilla
// data declarations...
extern const char XPC_EXCEPTION_CONTRACTID[];
extern const char XPC_CONSOLE_CONTRACTID[];
extern const char XPC_SCRIPT_ERROR_CONTRACTID[];
extern const char XPC_XPCONNECT_CONTRACTID[];
// Useful macros...
#define XPC_STRING_GETTER_BODY(dest, src) \
*dest = src ? moz_xstrdup(src) : nullptr; \
return NS_OK
// 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 JSClass* clazz) {
return clazz->isWrappedNative();
static inline bool IS_WN_REFLECTOR(JSObject* obj) {
return IS_WN_CLASS(JS::GetClass(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 {
// all the interface method declarations...
// non-interface implementation
static XPCJSRuntime* GetRuntimeInstance();
XPCJSContext* GetContext() { return mContext; }
static nsIScriptSecurityManager* SecurityManager() {
return gScriptSecurityManager;
static nsIPrincipal* SystemPrincipal() {
return gSystemPrincipal;
// Called by module code in dll startup
static void InitStatics();
// Called by module code on dll shutdown.
static void ReleaseXPConnectSingleton();
static void InitJSContext();
void RecordTraversal(void* p, nsISupports* s);
virtual ~nsXPConnect();
// Singleton instance
static nsXPConnect* gSelf;
static bool gOnceAliveNowDead;
XPCJSContext* mContext = nullptr;
XPCJSRuntime* mRuntime = nullptr;
bool mShuttingDown;
friend class nsIXPConnect;
static nsIScriptSecurityManager* gScriptSecurityManager;
static nsIPrincipal* gSystemPrincipal;
class XPCRootSetElem {
XPCRootSetElem() : mNext(nullptr), mSelfp(nullptr) {}
~XPCRootSetElem() {
MOZ_ASSERT(!mNext, "Must be unlinked");
MOZ_ASSERT(!mSelfp, "Must be unlinked");
inline XPCRootSetElem* GetNextRoot() { return mNext; }
void AddToRootSet(XPCRootSetElem** listHead);
void RemoveFromRootSet();
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;
// clang-format off
MOZ_DEFINE_ENUM(WatchdogTimestampCategory, (
// clang-format on
class AsyncFreeSnowWhite;
class XPCWrappedNativeScope;
using XPCWrappedNativeScopeList = mozilla::LinkedList<XPCWrappedNativeScope>;
class XPCJSContext final : public mozilla::CycleCollectedJSContext,
public mozilla::LinkedListElement<XPCJSContext> {
static XPCJSContext* NewXPCJSContext();
static XPCJSContext* Get();
XPCJSRuntime* Runtime() const;
virtual mozilla::CycleCollectedJSRuntime* CreateRuntime(
JSContext* aCx) override;
XPCCallContext* GetCallContext() const { return mCallContext; }
XPCCallContext* SetCallContext(XPCCallContext* ccx) {
XPCCallContext* old = mCallContext;
mCallContext = ccx;
return old;
jsid GetResolveName() const { return mResolveName; }
jsid SetResolveName(jsid name) {
jsid old = mResolveName;
mResolveName = name;
return old;
XPCWrappedNative* GetResolvingWrapper() const { return mResolvingWrapper; }
XPCWrappedNative* SetResolvingWrapper(XPCWrappedNative* w) {
XPCWrappedNative* old = mResolvingWrapper;
mResolvingWrapper = w;
return old;
bool JSContextInitialized(JSContext* cx);
virtual void BeforeProcessTask(bool aMightBlock) override;
virtual void AfterProcessTask(uint32_t aNewRecursionDepth) override;
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
bool IsSystemCaller() const override;
AutoMarkingPtr** GetAutoRootsAdr() { return &mAutoRoots; }
nsresult GetPendingResult() { return mPendingResult; }
void SetPendingResult(nsresult rv) { mPendingResult = rv; }
PRTime GetWatchdogTimestamp(WatchdogTimestampCategory aCategory);
static bool RecordScriptActivity(bool aActive);
bool SetHasScriptActivity(bool aActive) {
bool oldValue = mHasScriptActivity;
mHasScriptActivity = aActive;
return oldValue;
static bool InterruptCallback(JSContext* cx);
// Mapping of often used strings to jsid atoms that live 'forever'.
// To add a new string: add to this list and to XPCJSRuntime::mStrings
// at the top of XPCJSRuntime.cpp
enum {
IDX_TOTAL_COUNT // just a count of the above
inline JS::HandleId GetStringID(unsigned index) const;
inline const char* GetStringName(unsigned index) const;
nsresult Initialize();
XPCCallContext* mCallContext;
AutoMarkingPtr* mAutoRoots;
jsid mResolveName;
XPCWrappedNative* mResolvingWrapper;
WatchdogManager* mWatchdogManager;
// Number of XPCJSContexts currently alive.
static uint32_t sInstanceCount;
static mozilla::StaticAutoPtr<WatchdogManager> sWatchdogInstance;
static WatchdogManager* GetWatchdogManager();
// If we spend too much time running JS code in an event handler, then we
// want to show the slow script UI. The timeout T is controlled by prefs. We
// invoke the interrupt callback once after T/2 seconds and set
// mSlowScriptSecondHalf to true. After another T/2 seconds, we invoke the
// interrupt callback again. Since mSlowScriptSecondHalf is now true, it
// shows the slow script UI. The reason we invoke the callback twice is to
// ensure that putting the computer to sleep while running a script doesn't
// cause the UI to be shown. If the laptop goes to sleep during one of the
// timeout periods, the script still has the other T/2 seconds to complete
// before the slow script UI is shown.
bool mSlowScriptSecondHalf;
// mSlowScriptCheckpoint is set to the time when:
// 1. We started processing the current event, or
// 2. mSlowScriptSecondHalf was set to true
// (whichever comes later). We use it to determine whether the interrupt
// callback needs to do anything.
mozilla::TimeStamp mSlowScriptCheckpoint;
// Accumulates total time we actually waited for telemetry
mozilla::TimeDuration mSlowScriptActualWait;
bool mTimeoutAccumulated;
bool mExecutedChromeScript;
bool mHasScriptActivity;
// mPendingResult is used to implement Components.returnCode. Only really
// meaningful while calling through XPCWrappedJS.
nsresult mPendingResult;
// These members must be accessed via WatchdogManager.
PRTime mLastStateChange;
friend class XPCJSRuntime;
friend class Watchdog;
friend class WatchdogManager;
friend class AutoLockWatchdog;
class XPCJSRuntime final : public mozilla::CycleCollectedJSRuntime {
static XPCJSRuntime* Get();
void RemoveWrappedJS(nsXPCWrappedJS* wrapper);
void AssertInvalidWrappedJSNotInTable(nsXPCWrappedJS* wrapper) const;
JSObject2WrappedJSMap* GetMultiCompartmentWrappedJSMap() const {
return mWrappedJSMap.get();
IID2NativeInterfaceMap* GetIID2NativeInterfaceMap() const {
return mIID2NativeInterfaceMap.get();
ClassInfo2NativeSetMap* GetClassInfo2NativeSetMap() const {
return mClassInfo2NativeSetMap.get();
NativeSetMap* GetNativeSetMap() const { return mNativeSetMap.get(); }
XPCWrappedNativeProtoMap* GetDyingWrappedNativeProtoMap() const {
return mDyingWrappedNativeProtoMap.get();
XPCWrappedNativeScopeList& GetWrappedNativeScopes() {
return mWrappedNativeScopes;
bool InitializeStrings(JSContext* cx);
virtual bool DescribeCustomObjects(JSObject* aObject, const JSClass* aClasp,
char (&aName)[72]) const override;
virtual bool NoteCustomGCThingXPCOMChildren(
const JSClass* aClasp, JSObject* aObj,
nsCycleCollectionTraversalCallback& aCb) const override;
* Infrastructure for classes that need to defer part of the finalization
* until after the GC has run, for example for objects that we don't want to
* destroy during the GC.
bool GetDoingFinalization() const { return mDoingFinalization; }
JS::HandleId GetStringID(unsigned index) const {
MOZ_ASSERT(index < XPCJSContext::IDX_TOTAL_COUNT, "index out of range");
// fromMarkedLocation() is safe because the string is interned.
return JS::HandleId::fromMarkedLocation(&mStrIDs[index]);
JS::HandleValue GetStringJSVal(unsigned index) const {
MOZ_ASSERT(index < XPCJSContext::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 < XPCJSContext::IDX_TOTAL_COUNT, "index out of range");
return mStrings[index];
virtual bool UsefulToMergeZones() const override;
void TraceNativeBlackRoots(JSTracer* trc) override;
void TraceAdditionalNativeGrayRoots(JSTracer* aTracer) override;
void TraverseAdditionalNativeRoots(
nsCycleCollectionNoteRootCallback& cb) override;
void UnmarkSkippableJSHolders();
void PrepareForForgetSkippable() override;
void BeginCycleCollectionCallback() override;
void EndCycleCollectionCallback(
mozilla::CycleCollectorResults& aResults) override;
void DispatchDeferredDeletion(bool aContinuation,
bool aPurge = false) override;
void CustomGCCallback(JSGCStatus status) override;
void CustomOutOfMemoryCallback() override;
void OnLargeAllocationFailure();
static void GCSliceCallback(JSContext* cx, JS::GCProgress progress,
const JS::GCDescription& desc);
static void DoCycleCollectionCallback(JSContext* cx);
static void FinalizeCallback(JSFreeOp* fop, JSFinalizeStatus status,
void* data);
static void WeakPointerZonesCallback(JSContext* cx, void* data);
static void WeakPointerCompartmentCallback(JSContext* cx,
JS::Compartment* comp, void* data);
inline void AddVariantRoot(XPCTraceableVariant* variant);
inline void AddWrappedJSRoot(nsXPCWrappedJS* wrappedJS);
void DebugDump(int16_t depth);
bool GCIsRunning() const { return mGCIsRunning; }
void AddGCCallback(xpcGCCallback cb);
void RemoveGCCallback(xpcGCCallback cb);
JSObject* GetUAWidgetScope(JSContext* cx, nsIPrincipal* principal);
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
* The unprivileged junk scope is an unprivileged sandbox global used for
* convenience by certain operations which need an unprivileged global but
* don't have one immediately handy. It should generally be avoided when
* possible.
* The scope is created lazily when it is needed, and held weakly so that it
* is destroyed when there are no longer any remaining external references to
* it. This means that under low memory conditions, when the scope does not
* already exist, we may not be able to create one. In these circumstances,
* the infallible version of this API will abort, and the fallible version
* will return null. Callers should therefore prefer the fallible version when
* on a codepath which can already return failure, but may use the infallible
* one otherwise.
JSObject* UnprivilegedJunkScope();
JSObject* UnprivilegedJunkScope(const mozilla::fallible_t&);
bool IsUnprivilegedJunkScope(JSObject*);
JSObject* LoaderGlobal();
void DeleteSingletonScopes();
void SystemIsBeingShutDown();
explicit XPCJSRuntime(JSContext* aCx);
void Initialize(JSContext* cx);
void Shutdown(JSContext* cx) override;
static const char* const mStrings[XPCJSContext::IDX_TOTAL_COUNT];
jsid mStrIDs[XPCJSContext::IDX_TOTAL_COUNT];
JS::Value mStrJSVals[XPCJSContext::IDX_TOTAL_COUNT];
struct Hasher {
using Key = RefPtr<mozilla::BasePrincipal>;
using Lookup = Key;
static uint32_t hash(const Lookup& l) { return l->GetOriginNoSuffixHash(); }
static bool match(const Key& k, const Lookup& l) {
return k->FastEquals(l);
struct SweepPolicy {
static bool needsSweep(RefPtr<mozilla::BasePrincipal>* /* unused */,
JS::Heap<JSObject*>* value) {
return JS::GCPolicy<JS::Heap<JSObject*>>::needsSweep(value);
typedef JS::GCHashMap<RefPtr<mozilla::BasePrincipal>, JS::Heap<JSObject*>,
Hasher, js::SystemAllocPolicy, SweepPolicy>
mozilla::UniquePtr<JSObject2WrappedJSMap> mWrappedJSMap;
mozilla::UniquePtr<IID2NativeInterfaceMap> mIID2NativeInterfaceMap;
mozilla::UniquePtr<ClassInfo2NativeSetMap> mClassInfo2NativeSetMap;
mozilla::UniquePtr<NativeSetMap> mNativeSetMap;
Principal2JSObjectMap mUAWidgetScopeMap;
XPCWrappedNativeScopeList mWrappedNativeScopes;
mozilla::UniquePtr<XPCWrappedNativeProtoMap> mDyingWrappedNativeProtoMap;
bool mGCIsRunning;
nsTArray<nsISupports*> mNativesToReleaseArray;
bool mDoingFinalization;
XPCRootSetElem* mVariantRoots;
XPCRootSetElem* mWrappedJSRoots;
nsTArray<xpcGCCallback> extraGCCallbacks;
JS::GCSliceCallback mPrevGCSliceCallback;
JS::DoCycleCollectionCallback mPrevDoCycleCollectionCallback;
mozilla::WeakPtr<SandboxPrivate> mUnprivilegedJunkScope;
JS::PersistentRootedObject mLoaderGlobal;
RefPtr<AsyncFreeSnowWhite> mAsyncSnowWhiteFreer;
friend class XPCJSContext;
friend class XPCIncrementalReleaseRunnable;
inline JS::HandleId XPCJSContext::GetStringID(unsigned index) const {
return Runtime()->GetStringID(index);
inline const char* XPCJSContext::GetStringName(unsigned index) const {
return Runtime()->GetStringName(index);
// No virtuals
// XPCCallContext is ALWAYS declared as a local variable in some function;
// i.e. instance lifetime is always controled by some C++ function returning.
// These things are created frequently in many places. We *intentionally* do
// not inialialize all members in order to save on construction overhead.
// Some constructor pass more valid params than others. We init what must be
// init'd and leave other members undefined. In debug builds the accessors
// use a CHECK_STATE macro to track whether or not the object is in a valid
// state to answer the question a caller might be asking. As long as this
// class is maintained correctly it can do its job without a bunch of added
// overhead from useless initializations and non-DEBUG error checking.
// Note that most accessors are inlined.
class MOZ_STACK_CLASS XPCCallContext final {
enum { NO_ARGS = (unsigned)-1 };
explicit XPCCallContext(JSContext* cx, JS::HandleObject obj = nullptr,
JS::HandleObject funobj = nullptr,
unsigned argc = NO_ARGS, JS::Value* argv = nullptr,
JS::Value* rval = nullptr);
virtual ~XPCCallContext();
inline bool IsValid() const;
inline XPCJSContext* GetContext() const;
inline JSContext* GetJSContext() const;
inline bool GetContextPopRequired() const;
inline XPCCallContext* GetPrevCallContext() const;
inline JSObject* GetFlattenedJSObject() const;
inline XPCWrappedNative* GetWrapper() const;
inline bool CanGetTearOff() const;
inline XPCWrappedNativeTearOff* GetTearOff() const;
inline nsIXPCScriptable* GetScriptable() const;
inline XPCNativeSet* GetSet() const;
inline bool CanGetInterface() const;
inline XPCNativeInterface* GetInterface() const;
inline XPCNativeMember* GetMember() const;
inline bool HasInterfaceAndMember() const;
inline bool GetStaticMemberIsLocal() const;
inline unsigned GetArgc() const;
inline JS::Value* GetArgv() const;
inline uint16_t GetMethodIndex() const;
inline jsid GetResolveName() const;
inline jsid SetResolveName(JS::HandleId name);
inline XPCWrappedNative* GetResolvingWrapper() const;
inline XPCWrappedNative* SetResolvingWrapper(XPCWrappedNative* w);
inline void SetRetVal(const JS::Value& val);
void SetName(jsid name);
void SetArgsAndResultPtr(unsigned argc, JS::Value* argv, JS::Value* rval);
void SetCallInfo(XPCNativeInterface* iface, XPCNativeMember* member,
bool isSetter);
nsresult CanCallNow();
void SystemIsBeingShutDown();
operator JSContext*() const { return GetJSContext(); }
// no copy ctor or assignment allowed
XPCCallContext(const XPCCallContext& r) = delete;
XPCCallContext& operator=(const XPCCallContext& r) = delete;
// posible values for mState
enum State {
#ifdef DEBUG
inline void CHECK_STATE(int s) const { MOZ_ASSERT(mState >= s, "bad state"); }
# define CHECK_STATE(s) ((void)0)
State mState;
nsCOMPtr<nsIXPConnect> mXPC;
XPCJSContext* mXPCJSContext;
JSContext* mJSContext;
// ctor does not necessarily init the following. BEWARE!
XPCCallContext* mPrevCallContext;
XPCWrappedNative* mWrapper;
XPCWrappedNativeTearOff* mTearOff;
nsCOMPtr<nsIXPCScriptable> mScriptable;
RefPtr<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 JSClass XPC_WN_NoHelper_JSClass;
extern const JSClass XPC_WN_Proto_JSClass;
extern const JSClass XPC_WN_Tearoff_JSClass;
extern const JSClass XPC_WN_NoHelper_Proto_JSClass;
extern bool XPC_WN_CallMethod(JSContext* cx, unsigned argc, JS::Value* vp);
extern bool XPC_WN_GetterSetter(JSContext* cx, unsigned argc, JS::Value* vp);
// XPCWrappedNativeScope is one-to-one with a JS compartment.
class XPCWrappedNativeScope final
: public mozilla::LinkedListElement<XPCWrappedNativeScope> {
XPCJSRuntime* GetRuntime() const { return XPCJSRuntime::Get(); }
Native2WrappedNativeMap* GetWrappedNativeMap() const {
return mWrappedNativeMap.get();
ClassInfo2WrappedNativeProtoMap* GetWrappedNativeProtoMap() const {
return mWrappedNativeProtoMap.get();
nsXPCComponents* GetComponents() const { return mComponents; }
bool AttachComponentsObject(JSContext* aCx);
// Returns the JS object reflection of the Components object.
bool GetComponentsJSObject(JSContext* cx, JS::MutableHandleObject obj);
JSObject* GetExpandoChain(JS::HandleObject target);
JSObject* DetachExpandoChain(JS::HandleObject target);
bool SetExpandoChain(JSContext* cx, JS::HandleObject target,
JS::HandleObject chain);
static void SystemIsBeingShutDown();
static void TraceWrappedNativesInAllScopes(XPCJSRuntime* xpcrt,
JSTracer* trc);
void TraceInside(JSTracer* trc) {
if (mXrayExpandos.initialized()) {
JS::TraceEdge(trc, &mIDProto, "XPCWrappedNativeScope::mIDProto");
JS::TraceEdge(trc, &mIIDProto, "XPCWrappedNativeScope::mIIDProto");
JS::TraceEdge(trc, &mCIDProto, "XPCWrappedNativeScope::mCIDProto");
static void SuspectAllWrappers(nsCycleCollectionNoteRootCallback& cb);
static void SweepAllWrappedNativeTearOffs();
void UpdateWeakPointersAfterGC();
static void DebugDumpAllScopes(int16_t depth);
void DebugDump(int16_t depth);
struct ScopeSizeInfo {
explicit ScopeSizeInfo(mozilla::MallocSizeOf mallocSizeOf)
: mMallocSizeOf(mallocSizeOf),
mProtoAndIfaceCacheSize(0) {}
mozilla::MallocSizeOf mMallocSizeOf;
size_t mScopeAndMapSize;
size_t mProtoAndIfaceCacheSize;
static void AddSizeOfAllScopesIncludingThis(JSContext* cx,
ScopeSizeInfo* scopeSizeInfo);
void AddSizeOfIncludingThis(JSContext* cx, ScopeSizeInfo* scopeSizeInfo);
// Check whether our mAllowContentXBLScope state matches the given
// principal. This is used to avoid sharing compartments on
// mismatch.
bool XBLScopeStateMatches(nsIPrincipal* aPrincipal);
XPCWrappedNativeScope(JS::Compartment* aCompartment,
JS::HandleObject aFirstGlobal);
virtual ~XPCWrappedNativeScope();
mozilla::UniquePtr<JSObject2JSObjectMap> mWaiverWrapperMap;
JS::Compartment* Compartment() const { return mCompartment; }
// Returns the global to use for new WrappedNative objects allocated in this
// compartment. This is better than using arbitrary globals we happen to be in
// because it prevents leaks (objects keep their globals alive).
JSObject* GetGlobalForWrappedNatives() {
return js::GetFirstGlobalInCompartment(Compartment());
bool AllowContentXBLScope(JS::Realm* aRealm);
// ID Object prototype caches.
JS::Heap<JSObject*> mIDProto;
JS::Heap<JSObject*> mIIDProto;
JS::Heap<JSObject*> mCIDProto;
XPCWrappedNativeScope() = delete;
mozilla::UniquePtr<Native2WrappedNativeMap> mWrappedNativeMap;
mozilla::UniquePtr<ClassInfo2WrappedNativeProtoMap> mWrappedNativeProtoMap;
RefPtr<nsXPCComponents> mComponents;
JS::Compartment* mCompartment;
JS::WeakMapPtr<JSObject*, JSObject*> mXrayExpandos;
// For remote XUL domains, we run all XBL in the content scope for compat
// reasons (though we sometimes pref this off for automation). We
// track the result of this decision (mAllowContentXBLScope) for now.
bool mAllowContentXBLScope;
// Slots we use for our functions
// 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 {
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) {
"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");
static uint16_t GetMaxIndexInInterface() { return (1 << 12) - 1; }
inline XPCNativeInterface* GetInterface() const;
void SetIndexInInterface(uint16_t index) { mIndexInInterface = index; }
/* default ctor - leave random contents */
bool Resolve(XPCCallContext& ccx, XPCNativeInterface* iface,
JS::HandleObject parent, JS::Value* vp);
enum {
METHOD = 0x01,
CONSTANT = 0x02,
GETTER = 0x04,
// 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.
// our only data...
jsid mName;
uint16_t mIndex;
// mFlags needs to be wide enough to hold the flags in the above enum.
uint16_t mFlags : 4;
// mIndexInInterface is the index of this in our XPCNativeInterface's
// mMembers. In theory our XPCNativeInterface could have as many as 2^15-1
// members (since mMemberCount is 15-bit) but in practice we prevent
// creation of XPCNativeInterfaces which have more than 2^12 members.
// If the width of this field changes, update GetMaxIndexInInterface.
uint16_t mIndexInInterface : 12;
} 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 {
static already_AddRefed<XPCNativeInterface> GetNewOrUsed(JSContext* cx,
const nsIID* iid);
static already_AddRefed<XPCNativeInterface> GetNewOrUsed(
JSContext* cx, const nsXPTInterfaceInfo* info);
static already_AddRefed<XPCNativeInterface> GetNewOrUsed(JSContext* cx,
const char* name);
static already_AddRefed<XPCNativeInterface> GetISupports(JSContext* cx);
inline const nsXPTInterfaceInfo* GetInterfaceInfo() const { return mInfo; }
inline jsid GetName() const { return mName; }
inline const nsIID* GetIID() const;
inline const char* GetNameString() const;
inline XPCNativeMember* FindMember(jsid name) const;
static inline size_t OffsetOfMembers();
uint16_t GetMemberCount() const { return mMemberCount; }
XPCNativeMember* GetMemberAt(uint16_t i) {
MOZ_ASSERT(i < mMemberCount, "bad index");
return &mMembers[i];
void DebugDump(int16_t depth);
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
static already_AddRefed<XPCNativeInterface> NewInstance(
JSContext* cx, const nsXPTInterfaceInfo* aInfo);
XPCNativeInterface() = delete;
XPCNativeInterface(const nsXPTInterfaceInfo* aInfo, jsid aName)
: mInfo(aInfo), mName(aName), mMemberCount(0) {}
void* operator new(size_t, void* p) noexcept(true) { return p; }
XPCNativeInterface(const XPCNativeInterface& r) = delete;
XPCNativeInterface& operator=(const XPCNativeInterface& r) = delete;
static void DestroyInstance(XPCNativeInterface* inst);
const nsXPTInterfaceInfo* mInfo;
jsid mName;
uint16_t mMemberCount;
XPCNativeMember mMembers[1]; // always last - object sized for array
// XPCNativeSetKey is used to key a XPCNativeSet in a NativeSetMap.
// It represents a new XPCNativeSet we are considering constructing, without
// requiring that the set actually be built.
class MOZ_STACK_CLASS XPCNativeSetKey final {
// This represents an existing set |baseSet|.
explicit XPCNativeSetKey(XPCNativeSet* baseSet)
: mCx(nullptr), mBaseSet(baseSet), mAddition(nullptr) {
// This represents a new set containing only nsISupports and
// |addition|. This needs a JSContext because it may need to
// construct some data structures that need one to construct them.
explicit XPCNativeSetKey(JSContext* cx, XPCNativeInterface* addition)
: mCx(cx), mBaseSet(nullptr), mAddition(addition) {
// This represents the existing set |baseSet| with the interface
// |addition| inserted after existing interfaces. |addition| must
// not already be present in |baseSet|.
explicit XPCNativeSetKey(XPCNativeSet* baseSet, XPCNativeInterface* addition);
~XPCNativeSetKey() = default;
XPCNativeSet* GetBaseSet() const { return mBaseSet; }
XPCNativeInterface* GetAddition() const { return mAddition; }
PLDHashNumber Hash() const;
// Allow shallow copy
JSContext* mCx;
RefPtr<XPCNativeSet> mBaseSet;
RefPtr<XPCNativeInterface> mAddition;
// XPCNativeSet represents an ordered collection of XPCNativeInterface pointers.
class XPCNativeSet final {
static already_AddRefed<XPCNativeSet> GetNewOrUsed(JSContext* cx,
const nsIID* iid);
static already_AddRefed<XPCNativeSet> GetNewOrUsed(JSContext* cx,
nsIClassInfo* classInfo);
static already_AddRefed<XPCNativeSet> GetNewOrUsed(JSContext* cx,
XPCNativeSetKey* key);
// This generates a union set.
// If preserveFirstSetOrder is true, the elements from |firstSet| come first,
// followed by any non-duplicate items from |secondSet|. If false, the same
// algorithm is applied; but if we detect that |secondSet| is a superset of
// |firstSet|, we return |secondSet| without worrying about whether the
// ordering might differ from |firstSet|.
static already_AddRefed<XPCNativeSet> GetNewOrUsed(
JSContext* cx, XPCNativeSet* firstSet, XPCNativeSet* secondSet,
bool preserveFirstSetOrder);
static void ClearCacheEntryForClassInfo(nsIClassInfo* classInfo);
inline bool FindMember(jsid name, XPCNativeMember** pMember,
uint16_t* pInterfaceIndex) const;
inline bool FindMember(jsid name, XPCNativeMember** pMember,
RefPtr<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;
uint16_t GetInterfaceCount() const { return mInterfaceCount; }
XPCNativeInterface** GetInterfaceArray() { return mInterfaces; }
XPCNativeInterface* GetInterfaceAt(uint16_t i) {
MOZ_ASSERT(i < mInterfaceCount, "bad index");
return mInterfaces[i];
inline bool MatchesSetUpToInterface(const XPCNativeSet* other,
XPCNativeInterface* iface) const;
void DebugDump(int16_t depth);
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
static already_AddRefed<XPCNativeSet> NewInstance(
JSContext* cx, nsTArray<RefPtr<XPCNativeInterface>>&& array);
static already_AddRefed<XPCNativeSet> NewInstanceMutate(XPCNativeSetKey* key);
XPCNativeSet() : mInterfaceCount(0) {}
void* operator new(size_t, void* p) noexcept(true) { return p; }
static void DestroyInstance(XPCNativeSet* inst);
uint16_t mInterfaceCount;
// Always last - object sized for array.
// These are strong references.
XPCNativeInterface* mInterfaces[1];
// XPCWrappedNativeProtos hold the additional shared wrapper data for
// XPCWrappedNative whose native objects expose nsIClassInfo.
// The XPCWrappedNativeProto is owned by its mJSProtoObject, until that object
// is finalized. After that, it is owned by XPCJSRuntime's
// mDyingWrappedNativeProtoMap. See
// XPCWrappedNativeProto::JSProtoObjectFinalized and
// XPCJSRuntime::FinalizeCallback.
class XPCWrappedNativeProto final {
static XPCWrappedNativeProto* GetNewOrUsed(JSContext* cx,
XPCWrappedNativeScope* scope,
nsIClassInfo* classInfo,
nsIXPCScriptable* scriptable);
XPCWrappedNativeScope* GetScope() const { return mScope; }
XPCJSRuntime* GetRuntime() const { return mScope->GetRuntime(); }
JSObject* GetJSProtoObject() const { return mJSProtoObject; }
JSObject* GetJSProtoObjectPreserveColor() const {
return mJSProtoObject.unbarrieredGet();
nsIClassInfo* GetClassInfo() const { return mClassInfo; }
XPCNativeSet* GetSet() const { return mSet; }
nsIXPCScriptable* GetScriptable() const { return mScriptable; }
void JSProtoObjectFinalized(JSFreeOp* fop, JSObject* obj);
void JSProtoObjectMoved(JSObject* obj, const JSObject* old);
void SystemIsBeingShutDown();
void DebugDump(int16_t depth);
void TraceSelf(JSTracer* trc) {
if (mJSProtoObject) {
TraceEdge(trc, &mJSProtoObject, "XPCWrappedNativeProto::mJSProtoObject");
void TraceJS(JSTracer* trc) { TraceSelf(trc); }
void WriteBarrierPre(JSContext* cx) {
if (JS::IsIncrementalBarrierNeeded(cx) && mJSProtoObject) {
// NOP. This is just here to make the AutoMarkingPtr code compile.
void Mark() const {}
inline void AutoTrace(JSTracer* trc) {}
// disable copy ctor and assignment
XPCWrappedNativeProto(const XPCWrappedNativeProto& r) = delete;
XPCWrappedNativeProto& operator=(const XPCWrappedNativeProto& r) = delete;
// hide ctor
XPCWrappedNativeProto(XPCWrappedNativeScope* Scope, nsIClassInfo* ClassInfo,
RefPtr<XPCNativeSet>&& Set);
bool Init(JSContext* cx, nsIXPCScriptable* scriptable);
#ifdef DEBUG
static int32_t gDEBUG_LiveProtoCount;
XPCWrappedNativeScope* mScope;
JS::Heap<JSObject*> mJSProtoObject;
nsCOMPtr<nsIClassInfo> mClassInfo;
RefPtr<XPCNativeSet> mSet;
nsCOMPtr<nsIXPCScriptable> mScriptable;
// XPCWrappedNativeTearOff represents the info needed to make calls to one
// interface on the underlying native object of a XPCWrappedNative.
class XPCWrappedNativeTearOff final {
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) {
// 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() {
mNextTearOff = mozilla::MakeUnique<XPCWrappedNativeTearOff>();
return mNextTearOff.get();
XPCWrappedNativeTearOff* GetNextTearOff() { return mNextTearOff.get(); }
XPCWrappedNativeTearOff(const XPCWrappedNativeTearOff& r) = delete;
XPCWrappedNativeTearOff& operator=(const XPCWrappedNativeTearOff& r) = delete;
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 {
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) {
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 {
bool HasProto() const { return !IsTaggedScope(mMaybeScope); }
XPCWrappedNativeProto* GetProto() const {
return HasProto() ? (XPCWrappedNativeProto*)(XPC_SCOPE_WORD(mMaybeProto) &
: nullptr;
XPCWrappedNativeScope* GetScope() const {
return GetProto() ? GetProto()->GetScope()
: (XPCWrappedNativeScope*)(XPC_SCOPE_WORD(mMaybeScope) &
nsISupports* GetIdentityObject() const { return mIdentity; }
* This getter clears the gray bit before handing out the JSObject which
* means that the object is guaranteed to be kept alive past the next CC.
JSObject* GetFlatJSObject() const { return mFlatJSObject; }
* This getter does not change the color of the JSObject meaning that the
* object returned is not guaranteed to be kept alive past the next CC.
* This should only be called if you are certain that the return value won't
* be passed into a JS API function and that it won't be stored without
* being rooted (or otherwise signaling the stored value to the CC).
JSObject* GetFlatJSObjectPreserveColor() const {
return mFlatJSObject.unbarrieredGetPtr();
XPCNativeSet* GetSet() const { return mSet; }
void SetSet(already_AddRefed<XPCNativeSet> set) { mSet = set; }
static XPCWrappedNative* Get(JSObject* obj) {
return (XPCWrappedNative*)JS::GetPrivate(obj);
void SetFlatJSObject(JSObject* object);
void UnsetFlatJSObject();
inline void ExpireWrapper() {
mMaybeScope = (XPCWrappedNativeScope*)(XPC_SCOPE_WORD(mMaybeScope) |
nsIXPCScriptable* GetScriptable() const { return mScriptable; }
nsIClassInfo* GetClassInfo() const {
return IsValid() && HasProto() ? GetProto()->GetClassInfo() : nullptr;
bool HasMutatedSet() const {
return IsValid() && (!HasProto() || GetSet() != GetProto()->GetSet());
XPCJSRuntime* GetRuntime() const {
XPCWrappedNativeScope* scope = GetScope();
return scope ? scope->GetRuntime() : nullptr;
static nsresult WrapNewGlobal(JSContext* cx, xpcObjectHelper& nativeHelper,
nsIPrincipal* principal,
bool initStandardClasses,
JS::RealmOptions& aOptions,
XPCWrappedNative** wrappedGlobal);
static nsresult GetNewOrUsed(JSContext* cx, xpcObjectHelper& helper,
XPCWrappedNativeScope* Scope,
XPCNativeInterface* Interface,
XPCWrappedNative** wrapper);
void FlatJSObjectFinalized();
void FlatJSObjectMoved(JSObject* obj, const JSObject* old);
void SystemIsBeingShutDown();
static bool CallMethod(XPCCallContext& ccx, CallMode mode = CALL_METHOD);
static bool GetAttribute(XPCCallContext& ccx) {
return CallMethod(ccx, CALL_GETTER);
static bool SetAttribute(XPCCallContext& ccx) {
return CallMethod(ccx, CALL_SETTER);
XPCWrappedNativeTearOff* FindTearOff(JSContext* cx,
XPCNativeInterface* aInterface,
bool needJSObject = false,
nsresult* pError = nullptr);
XPCWrappedNativeTearOff* FindTearOff(JSContext* cx, const nsIID& iid);
void Mark() const {}
inline void TraceInside(JSTracer* trc) {
if (HasProto()) {
JSObject* obj = mFlatJSObject.unbarrieredGetPtr();
if (obj && JS_IsGlobalObject(obj)) {
xpc::TraceXPCGlobal(trc, obj);
void TraceJS(JSTracer* trc) { TraceInside(trc); }
void TraceSelf(JSTracer* trc) {
// If this got called, we're being kept alive by someone who really
// needs us alive and whole. Do not let our mFlatJSObject go away.
// This is the only time we should be tracing our mFlatJSObject,
// normally somebody else is doing that.
JS::TraceEdge(trc, &mFlatJSObject, "XPCWrappedNative::mFlatJSObject"