summaryrefslogtreecommitdiffstats
path: root/js/xpconnect/src/xpcprivate.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/xpconnect/src/xpcprivate.h')
-rw-r--r--js/xpconnect/src/xpcprivate.h2842
1 files changed, 2842 insertions, 0 deletions
diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h
new file mode 100644
index 0000000000..2c629e5bc2
--- /dev/null
+++ b/js/xpconnect/src/xpcprivate.h
@@ -0,0 +1,2842 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * XPConnect allows JS code to manipulate C++ object and C++ code to manipulate
+ * JS objects. JS manipulation of C++ objects tends to be significantly more
+ * complex. This comment explains how it is orchestrated by XPConnect.
+ *
+ * For each C++ object to be manipulated in JS, there is a corresponding JS
+ * object. This is called the "flattened JS object". By default, there is an
+ * additional C++ object involved of type XPCWrappedNative. The XPCWrappedNative
+ * holds pointers to the C++ object and the flat JS object.
+ *
+ * All XPCWrappedNative objects belong to an XPCWrappedNativeScope. These scopes
+ * are essentially in 1:1 correspondence with JS compartments. The
+ * XPCWrappedNativeScope has a pointer to the JS compartment. The global of a
+ * flattened JS object is one of the globals in this compartment (the exception
+ * to this rule is when a PreCreate hook asks for a different global; see
+ * nsIXPCScriptable below).
+ *
+ * Some C++ objects (notably DOM objects) have information associated with them
+ * that lists the interfaces implemented by these objects. A C++ object exposes
+ * this information by implementing nsIClassInfo. If a C++ object implements
+ * nsIClassInfo, then JS code can call its methods without needing to use
+ * QueryInterface first. Typically, all instances of a C++ class share the same
+ * nsIClassInfo instance. (That is, obj->QueryInterface(nsIClassInfo) returns
+ * the same result for every obj of a given class.)
+ *
+ * XPConnect tracks nsIClassInfo information in an XPCWrappedNativeProto object.
+ * A given XPCWrappedNativeScope will have one XPCWrappedNativeProto for each
+ * nsIClassInfo instance being used. The XPCWrappedNativeProto has an associated
+ * JS object, which is used as the prototype of all flattened JS objects created
+ * for C++ objects with the given nsIClassInfo.
+ *
+ * Each XPCWrappedNativeProto has a pointer to its XPCWrappedNativeScope. If an
+ * XPCWrappedNative wraps a C++ object with class info, then it points to its
+ * XPCWrappedNativeProto. Otherwise it points to its XPCWrappedNativeScope. (The
+ * pointers are smooshed together in a tagged union.) Either way it can reach
+ * its scope.
+ *
+ * An XPCWrappedNativeProto keeps track of the set of interfaces implemented by
+ * the C++ object in an XPCNativeSet. (The list of interfaces is obtained by
+ * calling a method on the nsIClassInfo.) An XPCNativeSet is a collection of
+ * XPCNativeInterfaces. Each interface stores the list of members, which can be
+ * methods, constants, getters, or setters.
+ *
+ * An XPCWrappedNative also points to an XPCNativeSet. Initially this starts out
+ * the same as the XPCWrappedNativeProto's set. If there is no proto, it starts
+ * out as a singleton set containing nsISupports. If JS code QI's new interfaces
+ * outside of the existing set, the set will grow. All QueryInterface results
+ * are cached in XPCWrappedNativeTearOff objects, which are linked off of the
+ * XPCWrappedNative.
+ *
+ * Besides having class info, a C++ object may be "scriptable" (i.e., implement
+ * nsIXPCScriptable). This allows it to implement a more DOM-like interface,
+ * besides just exposing XPCOM methods and constants. An nsIXPCScriptable
+ * instance has hooks that correspond to all the normal JSClass hooks. Each
+ * nsIXPCScriptable instance can have pointers from XPCWrappedNativeProto and
+ * XPCWrappedNative (since C++ objects can have scriptable info without having
+ * class info).
+ */
+
+/* All the XPConnect private declarations - only include locally. */
+
+#ifndef xpcprivate_h___
+#define xpcprivate_h___
+
+#include "mozilla/Alignment.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/CycleCollectedJSContext.h"
+#include "mozilla/CycleCollectedJSRuntime.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/DefineEnum.h"
+#include "mozilla/HashFunctions.h"
+#include "mozilla/LinkedList.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/mozalloc.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Vector.h"
+
+#include "mozilla/dom/ScriptSettings.h"
+
+#include <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
+#include "js/PropertyAndElement.h" // JS_DefineProperty
+#include "js/TracingAPI.h"
+#include "js/WeakMapPtr.h"
+#include "nscore.h"
+#include "nsXPCOM.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsDebug.h"
+#include "nsISupports.h"
+#include "nsIServiceManager.h"
+#include "nsIClassInfoImpl.h"
+#include "nsIComponentManager.h"
+#include "nsIComponentRegistrar.h"
+#include "nsISupportsPrimitives.h"
+#include "nsISimpleEnumerator.h"
+#include "nsIXPConnect.h"
+#include "nsIXPCScriptable.h"
+#include "nsIObserver.h"
+#include "nsWeakReference.h"
+#include "nsCOMPtr.h"
+#include "nsXPTCUtils.h"
+#include "xptinfo.h"
+#include "XPCForwards.h"
+#include "XPCLog.h"
+#include "xpccomponents.h"
+#include "prenv.h"
+#include "prcvar.h"
+#include "nsString.h"
+#include "nsReadableUtils.h"
+
+#include "MainThreadUtils.h"
+
+#include "nsIConsoleService.h"
+
+#include "nsVariant.h"
+#include "nsCOMArray.h"
+#include "nsTArray.h"
+#include "nsBaseHashtable.h"
+#include "nsHashKeys.h"
+#include "nsWrapperCache.h"
+#include "nsStringBuffer.h"
+#include "nsDeque.h"
+
+#include "nsIScriptSecurityManager.h"
+
+#include "nsIPrincipal.h"
+#include "nsJSPrincipals.h"
+#include "nsIScriptObjectPrincipal.h"
+#include "xpcObjectHelper.h"
+
+#include "SandboxPrivate.h"
+#include "BackstagePass.h"
+
+#ifdef XP_WIN
+// Nasty MS defines
+# ifdef GetClassInfo
+# undef GetClassInfo
+# endif
+# ifdef GetClassName
+# undef GetClassName
+# endif
+#endif /* XP_WIN */
+
+namespace mozilla {
+namespace dom {
+class AutoEntryScript;
+class Exception;
+} // namespace dom
+} // namespace mozilla
+
+/***************************************************************************/
+// data declarations...
+extern const char XPC_EXCEPTION_CONTRACTID[];
+extern const char XPC_CONSOLE_CONTRACTID[];
+extern const char XPC_SCRIPT_ERROR_CONTRACTID[];
+extern const char XPC_XPCONNECT_CONTRACTID[];
+
+/***************************************************************************/
+// Helper function.
+
+namespace xpc {
+
+inline bool IsWrappedNativeReflector(JSObject* obj) {
+ return JS::GetClass(obj)->isWrappedNative();
+}
+
+} // namespace xpc
+
+/***************************************************************************
+****************************************************************************
+*
+* Core runtime and context classes...
+*
+****************************************************************************
+***************************************************************************/
+
+// We have a general rule internally that getters that return addref'd interface
+// pointer generally do so using an 'out' parm. When interface pointers are
+// returned as function call result values they are not addref'd. Exceptions
+// to this rule are noted explicitly.
+
+class nsXPConnect final : public nsIXPConnect {
+ public:
+ // all the interface method declarations...
+ NS_DECL_ISUPPORTS
+
+ // non-interface implementation
+ public:
+ static XPCJSRuntime* GetRuntimeInstance();
+ XPCJSContext* GetContext() { return mContext; }
+
+ static nsIScriptSecurityManager* SecurityManager() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(gScriptSecurityManager);
+ return gScriptSecurityManager;
+ }
+
+ static nsIPrincipal* SystemPrincipal() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(gSystemPrincipal);
+ return gSystemPrincipal;
+ }
+
+ // Called by module code in dll startup
+ static void InitStatics();
+ // Called by module code on dll shutdown.
+ static void ReleaseXPConnectSingleton();
+
+ static void InitJSContext();
+
+ void RecordTraversal(void* p, nsISupports* s);
+
+ protected:
+ virtual ~nsXPConnect();
+
+ nsXPConnect();
+
+ private:
+ // Singleton instance
+ static nsXPConnect* gSelf;
+ static bool gOnceAliveNowDead;
+
+ XPCJSContext* mContext = nullptr;
+ XPCJSRuntime* mRuntime = nullptr;
+
+ friend class nsIXPConnect;
+
+ public:
+ static nsIScriptSecurityManager* gScriptSecurityManager;
+ static nsIPrincipal* gSystemPrincipal;
+};
+
+/***************************************************************************/
+
+// In the current xpconnect system there can only be one XPCJSContext.
+// So, xpconnect can only be used on one JSContext within the process.
+
+class WatchdogManager;
+
+// clang-format off
+MOZ_DEFINE_ENUM(WatchdogTimestampCategory, (
+ TimestampWatchdogWakeup,
+ TimestampWatchdogHibernateStart,
+ TimestampWatchdogHibernateStop,
+ TimestampContextStateChange
+));
+// clang-format on
+
+class AsyncFreeSnowWhite;
+class XPCWrappedNativeScope;
+
+using XPCWrappedNativeScopeList = mozilla::LinkedList<XPCWrappedNativeScope>;
+
+class XPCJSContext final : public mozilla::CycleCollectedJSContext,
+ public mozilla::LinkedListElement<XPCJSContext> {
+ public:
+ static XPCJSContext* NewXPCJSContext();
+ static XPCJSContext* Get();
+
+ XPCJSRuntime* Runtime() const;
+
+ virtual mozilla::CycleCollectedJSRuntime* CreateRuntime(
+ JSContext* aCx) override;
+
+ XPCCallContext* GetCallContext() const { return mCallContext; }
+ XPCCallContext* SetCallContext(XPCCallContext* ccx) {
+ XPCCallContext* old = mCallContext;
+ mCallContext = ccx;
+ return old;
+ }
+
+ jsid GetResolveName() const { return mResolveName; }
+ jsid SetResolveName(jsid name) {
+ jsid old = mResolveName;
+ mResolveName = name;
+ return old;
+ }
+
+ XPCWrappedNative* GetResolvingWrapper() const { return mResolvingWrapper; }
+ XPCWrappedNative* SetResolvingWrapper(XPCWrappedNative* w) {
+ XPCWrappedNative* old = mResolvingWrapper;
+ mResolvingWrapper = w;
+ return old;
+ }
+
+ bool JSContextInitialized(JSContext* cx);
+
+ virtual void BeforeProcessTask(bool aMightBlock) override;
+ virtual void AfterProcessTask(uint32_t aNewRecursionDepth) override;
+
+ // Relay to the CCGCScheduler instead of queuing up an idle runnable
+ // (as is done for workers in CycleCollectedJSContext).
+ virtual void MaybePokeGC() override;
+
+ ~XPCJSContext();
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
+
+ bool IsSystemCaller() const override;
+
+ AutoMarkingPtr** GetAutoRootsAdr() { return &mAutoRoots; }
+
+ nsresult GetPendingResult() { return mPendingResult; }
+ void SetPendingResult(nsresult rv) { mPendingResult = rv; }
+
+ PRTime GetWatchdogTimestamp(WatchdogTimestampCategory aCategory);
+
+ static bool RecordScriptActivity(bool aActive);
+
+ bool SetHasScriptActivity(bool aActive) {
+ bool oldValue = mHasScriptActivity;
+ mHasScriptActivity = aActive;
+ return oldValue;
+ }
+
+ static bool InterruptCallback(JSContext* cx);
+
+ // Mapping of often used strings to jsid atoms that live 'forever'.
+ //
+ // To add a new string: add to this list and to XPCJSRuntime::mStrings
+ // at the top of XPCJSRuntime.cpp
+ enum {
+ IDX_CONSTRUCTOR = 0,
+ IDX_TO_STRING,
+ IDX_TO_SOURCE,
+ IDX_VALUE,
+ IDX_QUERY_INTERFACE,
+ IDX_COMPONENTS,
+ IDX_CC,
+ IDX_CI,
+ IDX_CR,
+ IDX_CU,
+ IDX_SERVICES,
+ IDX_WRAPPED_JSOBJECT,
+ IDX_PROTOTYPE,
+ IDX_EVAL,
+ IDX_CONTROLLERS,
+ IDX_CONTROLLERS_CLASS,
+ IDX_LENGTH,
+ IDX_NAME,
+ IDX_UNDEFINED,
+ IDX_EMPTYSTRING,
+ IDX_FILENAME,
+ IDX_LINENUMBER,
+ IDX_COLUMNNUMBER,
+ IDX_STACK,
+ IDX_MESSAGE,
+ IDX_CAUSE,
+ IDX_ERRORS,
+ IDX_LASTINDEX,
+ IDX_THEN,
+ IDX_ISINSTANCE,
+ IDX_INFINITY,
+ IDX_NAN,
+ IDX_CLASS_ID,
+ IDX_INTERFACE_ID,
+ IDX_INITIALIZER,
+ IDX_PRINT,
+ IDX_FETCH,
+ IDX_CRYPTO,
+ IDX_INDEXEDDB,
+ IDX_STRUCTUREDCLONE,
+ IDX_TOTAL_COUNT // just a count of the above
+ };
+
+ inline JS::HandleId GetStringID(unsigned index) const;
+ inline const char* GetStringName(unsigned index) const;
+
+ private:
+ XPCJSContext();
+
+ MOZ_IS_CLASS_INIT
+ nsresult Initialize();
+
+ XPCCallContext* mCallContext;
+ AutoMarkingPtr* mAutoRoots;
+ jsid mResolveName;
+ XPCWrappedNative* mResolvingWrapper;
+ WatchdogManager* mWatchdogManager;
+
+ // Number of XPCJSContexts currently alive.
+ static uint32_t sInstanceCount;
+ static mozilla::StaticAutoPtr<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.
+ enum { CONTEXT_ACTIVE, CONTEXT_INACTIVE } mActive;
+ PRTime mLastStateChange;
+
+ friend class XPCJSRuntime;
+ friend class Watchdog;
+ friend class WatchdogManager;
+ friend class AutoLockWatchdog;
+};
+
+class XPCJSRuntime final : public mozilla::CycleCollectedJSRuntime {
+ public:
+ static XPCJSRuntime* Get();
+
+ void RemoveWrappedJS(nsXPCWrappedJS* wrapper);
+ void AssertInvalidWrappedJSNotInTable(nsXPCWrappedJS* wrapper) const;
+
+ JSObject2WrappedJSMap* GetMultiCompartmentWrappedJSMap() const {
+ return mWrappedJSMap.get();
+ }
+
+ IID2NativeInterfaceMap* GetIID2NativeInterfaceMap() const {
+ return mIID2NativeInterfaceMap.get();
+ }
+
+ ClassInfo2NativeSetMap* GetClassInfo2NativeSetMap() const {
+ return mClassInfo2NativeSetMap.get();
+ }
+
+ NativeSetMap* GetNativeSetMap() const { return mNativeSetMap.get(); }
+
+ using WrappedNativeProtoVector =
+ mozilla::Vector<mozilla::UniquePtr<XPCWrappedNativeProto>, 0,
+ InfallibleAllocPolicy>;
+ WrappedNativeProtoVector& GetDyingWrappedNativeProtos() {
+ return mDyingWrappedNativeProtos;
+ }
+
+ XPCWrappedNativeScopeList& GetWrappedNativeScopes() {
+ return mWrappedNativeScopes;
+ }
+
+ bool InitializeStrings(JSContext* cx);
+
+ virtual bool DescribeCustomObjects(JSObject* aObject, const JSClass* aClasp,
+ char (&aName)[72]) const override;
+ virtual bool NoteCustomGCThingXPCOMChildren(
+ const JSClass* aClasp, JSObject* aObj,
+ nsCycleCollectionTraversalCallback& aCb) const override;
+
+ /**
+ * Infrastructure for classes that need to defer part of the finalization
+ * until after the GC has run, for example for objects that we don't want to
+ * destroy during the GC.
+ */
+
+ public:
+ bool GetDoingFinalization() const { return mDoingFinalization; }
+
+ JS::HandleId GetStringID(unsigned index) const {
+ MOZ_ASSERT(index < XPCJSContext::IDX_TOTAL_COUNT, "index out of range");
+ // fromMarkedLocation() is safe because the string is interned.
+ return JS::HandleId::fromMarkedLocation(&mStrIDs[index]);
+ }
+ const char* GetStringName(unsigned index) const {
+ MOZ_ASSERT(index < XPCJSContext::IDX_TOTAL_COUNT, "index out of range");
+ return mStrings[index];
+ }
+
+ virtual bool UsefulToMergeZones() const override;
+ void TraceNativeBlackRoots(JSTracer* trc) override;
+ void TraceAdditionalNativeGrayRoots(JSTracer* aTracer) override;
+ void TraverseAdditionalNativeRoots(
+ nsCycleCollectionNoteRootCallback& cb) override;
+ void UnmarkSkippableJSHolders();
+ void PrepareForForgetSkippable() override;
+ void BeginCycleCollectionCallback(mozilla::CCReason aReason) override;
+ void EndCycleCollectionCallback(
+ mozilla::CycleCollectorResults& aResults) override;
+ void DispatchDeferredDeletion(bool aContinuation,
+ bool aPurge = false) override;
+
+ void CustomGCCallback(JSGCStatus status) override;
+ void CustomOutOfMemoryCallback() override;
+ void OnLargeAllocationFailure();
+ static void GCSliceCallback(JSContext* cx, JS::GCProgress progress,
+ const JS::GCDescription& desc);
+ static void DoCycleCollectionCallback(JSContext* cx);
+ static void FinalizeCallback(JS::GCContext* gcx, JSFinalizeStatus status,
+ void* data);
+ static void WeakPointerZonesCallback(JSTracer* trc, void* data);
+ static void WeakPointerCompartmentCallback(JSTracer* trc,
+ JS::Compartment* comp, void* data);
+
+ inline void AddSubjectToFinalizationWJS(nsXPCWrappedJS* wrappedJS);
+
+ void DebugDump(int16_t depth);
+
+ bool GCIsRunning() const { return mGCIsRunning; }
+
+ ~XPCJSRuntime();
+
+ void AddGCCallback(xpcGCCallback cb);
+ void RemoveGCCallback(xpcGCCallback cb);
+
+ JSObject* GetUAWidgetScope(JSContext* cx, nsIPrincipal* principal);
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
+
+ /**
+ * The unprivileged junk scope is an unprivileged sandbox global used for
+ * convenience by certain operations which need an unprivileged global but
+ * don't have one immediately handy. It should generally be avoided when
+ * possible.
+ *
+ * The scope is created lazily when it is needed, and held weakly so that it
+ * is destroyed when there are no longer any remaining external references to
+ * it. This means that under low memory conditions, when the scope does not
+ * already exist, we may not be able to create one. In these circumstances,
+ * the infallible version of this API will abort, and the fallible version
+ * will return null. Callers should therefore prefer the fallible version when
+ * on a codepath which can already return failure, but may use the infallible
+ * one otherwise.
+ */
+ JSObject* UnprivilegedJunkScope();
+ JSObject* UnprivilegedJunkScope(const mozilla::fallible_t&);
+
+ bool IsUnprivilegedJunkScope(JSObject*);
+ JSObject* LoaderGlobal();
+
+ void DeleteSingletonScopes();
+
+ private:
+ explicit XPCJSRuntime(JSContext* aCx);
+
+ MOZ_IS_CLASS_INIT
+ void Initialize(JSContext* cx);
+ void Shutdown(JSContext* cx) override;
+
+ static const char* const mStrings[XPCJSContext::IDX_TOTAL_COUNT];
+ jsid mStrIDs[XPCJSContext::IDX_TOTAL_COUNT];
+
+ struct Hasher {
+ using Key = RefPtr<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 MapEntryGCPolicy {
+ static bool traceWeak(JSTracer* trc,
+ RefPtr<mozilla::BasePrincipal>* /* unused */,
+ JS::Heap<JSObject*>* value) {
+ return JS::GCPolicy<JS::Heap<JSObject*>>::traceWeak(trc, value);
+ }
+ };
+
+ typedef JS::GCHashMap<RefPtr<mozilla::BasePrincipal>, JS::Heap<JSObject*>,
+ Hasher, js::SystemAllocPolicy, MapEntryGCPolicy>
+ Principal2JSObjectMap;
+
+ mozilla::UniquePtr<JSObject2WrappedJSMap> mWrappedJSMap;
+ mozilla::UniquePtr<IID2NativeInterfaceMap> mIID2NativeInterfaceMap;
+ mozilla::UniquePtr<ClassInfo2NativeSetMap> mClassInfo2NativeSetMap;
+ mozilla::UniquePtr<NativeSetMap> mNativeSetMap;
+ Principal2JSObjectMap mUAWidgetScopeMap;
+ XPCWrappedNativeScopeList mWrappedNativeScopes;
+ WrappedNativeProtoVector mDyingWrappedNativeProtos;
+ bool mGCIsRunning;
+ nsTArray<nsISupports*> mNativesToReleaseArray;
+ bool mDoingFinalization;
+ mozilla::LinkedList<nsXPCWrappedJS> mSubjectToFinalizationWJS;
+ 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 {
+ public:
+ enum : unsigned { NO_ARGS = (unsigned)-1 };
+
+ explicit XPCCallContext(JSContext* cx, JS::HandleObject obj = nullptr,
+ JS::HandleObject funobj = nullptr,
+ JS::HandleId id = JS::VoidHandlePropertyKey,
+ unsigned argc = NO_ARGS, JS::Value* argv = nullptr,
+ JS::Value* rval = nullptr);
+
+ virtual ~XPCCallContext();
+
+ inline bool IsValid() const;
+
+ inline XPCJSContext* GetContext() const;
+ inline JSContext* GetJSContext() const;
+ inline bool GetContextPopRequired() const;
+ inline XPCCallContext* GetPrevCallContext() const;
+
+ inline JSObject* GetFlattenedJSObject() const;
+ inline XPCWrappedNative* GetWrapper() const;
+
+ inline bool CanGetTearOff() const;
+ inline XPCWrappedNativeTearOff* GetTearOff() const;
+
+ inline nsIXPCScriptable* GetScriptable() const;
+ inline XPCNativeSet* GetSet() const;
+ inline bool CanGetInterface() const;
+ inline XPCNativeInterface* GetInterface() const;
+ inline XPCNativeMember* GetMember() const;
+ inline bool HasInterfaceAndMember() const;
+ inline bool GetStaticMemberIsLocal() const;
+ inline unsigned GetArgc() const;
+ inline JS::Value* GetArgv() const;
+
+ inline uint16_t GetMethodIndex() const;
+
+ inline jsid GetResolveName() const;
+ inline jsid SetResolveName(JS::HandleId name);
+
+ inline XPCWrappedNative* GetResolvingWrapper() const;
+ inline XPCWrappedNative* SetResolvingWrapper(XPCWrappedNative* w);
+
+ inline void SetRetVal(const JS::Value& val);
+
+ void SetName(jsid name);
+ void SetArgsAndResultPtr(unsigned argc, JS::Value* argv, JS::Value* rval);
+ void SetCallInfo(XPCNativeInterface* iface, XPCNativeMember* member,
+ bool isSetter);
+
+ nsresult CanCallNow();
+
+ void SystemIsBeingShutDown();
+
+ operator JSContext*() const { return GetJSContext(); }
+
+ private:
+ // no copy ctor or assignment allowed
+ XPCCallContext(const XPCCallContext& r) = delete;
+ XPCCallContext& operator=(const XPCCallContext& r) = delete;
+
+ private:
+ // posible values for mState
+ enum State {
+ INIT_FAILED,
+ SYSTEM_SHUTDOWN,
+ HAVE_CONTEXT,
+ HAVE_OBJECT,
+ HAVE_NAME,
+ HAVE_ARGS,
+ READY_TO_CALL,
+ CALL_DONE
+ };
+
+#ifdef DEBUG
+ inline void CHECK_STATE(int s) const { MOZ_ASSERT(mState >= s, "bad state"); }
+#else
+# define CHECK_STATE(s) ((void)0)
+#endif
+
+ private:
+ State mState;
+
+ nsCOMPtr<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> {
+ public:
+ XPCJSRuntime* GetRuntime() const { return XPCJSRuntime::Get(); }
+
+ Native2WrappedNativeMap* GetWrappedNativeMap() const {
+ return mWrappedNativeMap.get();
+ }
+
+ ClassInfo2WrappedNativeProtoMap* GetWrappedNativeProtoMap() const {
+ return mWrappedNativeProtoMap.get();
+ }
+
+ nsXPCComponents* GetComponents() const { return mComponents; }
+
+ bool AttachComponentsObject(JSContext* aCx);
+
+ bool AttachJSServices(JSContext* aCx);
+
+ // Returns the JS object reflection of the Components object.
+ bool GetComponentsJSObject(JSContext* cx, JS::MutableHandleObject obj);
+
+ JSObject* GetExpandoChain(JS::HandleObject target);
+
+ JSObject* DetachExpandoChain(JS::HandleObject target);
+
+ bool SetExpandoChain(JSContext* cx, JS::HandleObject target,
+ JS::HandleObject chain);
+
+ static void SystemIsBeingShutDown();
+
+ static void TraceWrappedNativesInAllScopes(XPCJSRuntime* xpcrt,
+ JSTracer* trc);
+
+ void TraceInside(JSTracer* trc) {
+ if (mXrayExpandos.initialized()) {
+ mXrayExpandos.trace(trc);
+ }
+ JS::TraceEdge(trc, &mIDProto, "XPCWrappedNativeScope::mIDProto");
+ JS::TraceEdge(trc, &mIIDProto, "XPCWrappedNativeScope::mIIDProto");
+ JS::TraceEdge(trc, &mCIDProto, "XPCWrappedNativeScope::mCIDProto");
+ }
+
+ static void SuspectAllWrappers(nsCycleCollectionNoteRootCallback& cb);
+
+ static void SweepAllWrappedNativeTearOffs();
+
+ void UpdateWeakPointersAfterGC(JSTracer* trc);
+
+ static void DebugDumpAllScopes(int16_t depth);
+
+ void DebugDump(int16_t depth);
+
+ struct ScopeSizeInfo {
+ explicit ScopeSizeInfo(mozilla::MallocSizeOf mallocSizeOf)
+ : mMallocSizeOf(mallocSizeOf),
+ mScopeAndMapSize(0),
+ mProtoAndIfaceCacheSize(0) {}
+
+ mozilla::MallocSizeOf mMallocSizeOf;
+ size_t mScopeAndMapSize;
+ size_t mProtoAndIfaceCacheSize;
+ };
+
+ static void AddSizeOfAllScopesIncludingThis(JSContext* cx,
+ ScopeSizeInfo* scopeSizeInfo);
+
+ void AddSizeOfIncludingThis(JSContext* cx, ScopeSizeInfo* scopeSizeInfo);
+
+ // Check whether our mAllowContentXBLScope state matches the given
+ // principal. This is used to avoid sharing compartments on
+ // mismatch.
+ bool XBLScopeStateMatches(nsIPrincipal* aPrincipal);
+
+ XPCWrappedNativeScope(JS::Compartment* aCompartment,
+ JS::HandleObject aFirstGlobal);
+ virtual ~XPCWrappedNativeScope();
+
+ mozilla::UniquePtr<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;
+
+ protected:
+ XPCWrappedNativeScope() = delete;
+
+ private:
+ 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
+#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 */
+ MOZ_COUNTED_DEFAULT_CTOR(XPCNativeMember)
+ MOZ_COUNTED_DTOR(XPCNativeMember)
+
+ XPCNativeMember(const XPCNativeMember& other)
+ : mName(other.mName),
+ mIndex(other.mIndex),
+ mFlags(other.mFlags),
+ mIndexInInterface(other.mIndexInInterface) {
+ MOZ_COUNT_CTOR(XPCNativeMember);
+ }
+
+ private:
+ bool Resolve(XPCCallContext& ccx, XPCNativeInterface* iface,
+ JS::HandleObject parent, JS::Value* vp);
+
+ enum {
+ METHOD = 0x01,
+ CONSTANT = 0x02,
+ GETTER = 0x04,
+ SETTER_TOO = 0x08
+ // If you add a flag here, you may need to make mFlags wider and either
+ // make mIndexInInterface narrower (and adjust
+ // XPCNativeInterface::NewInstance accordingly) or make this object
+ // bigger.
+ };
+
+ private:
+ // our only data...
+ jsid mName;
+ uint16_t mIndex;
+ // mFlags needs to be wide enough to hold the flags in the above enum.
+ uint16_t mFlags : 4;
+ // mIndexInInterface is the index of this in our XPCNativeInterface's
+ // mMembers. In theory our XPCNativeInterface could have as many as 2^15-1
+ // members (since mMemberCount is 15-bit) but in practice we prevent
+ // creation of XPCNativeInterfaces which have more than 2^12 members.
+ // If the width of this field changes, update GetMaxIndexInInterface.
+ uint16_t mIndexInInterface : 12;
+};
+
+/***************************************************************************/
+// XPCNativeInterface represents a single idl declared interface. This is
+// primarily the set of XPCNativeMembers.
+
+// Tight. No virtual methods.
+
+class XPCNativeInterface final {
+ public:
+ NS_INLINE_DECL_REFCOUNTING_WITH_DESTROY(XPCNativeInterface,
+ DestroyInstance(this))
+
+ static already_AddRefed<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);
+
+ void Trace(JSTracer* trc);
+
+ protected:
+ static already_AddRefed<XPCNativeInterface> NewInstance(
+ JSContext* cx, IID2NativeInterfaceMap* aMap,
+ const nsXPTInterfaceInfo* aInfo);
+
+ XPCNativeInterface() = delete;
+ XPCNativeInterface(const nsXPTInterfaceInfo* aInfo, jsid aName)
+ : mInfo(aInfo), mName(aName), mMemberCount(0) {}
+ ~XPCNativeInterface();
+
+ void* operator new(size_t, void* p) noexcept(true) { return p; }
+
+ XPCNativeInterface(const XPCNativeInterface& r) = delete;
+ XPCNativeInterface& operator=(const XPCNativeInterface& r) = delete;
+
+ static void DestroyInstance(XPCNativeInterface* inst);
+
+ private:
+ const nsXPTInterfaceInfo* mInfo;
+ jsid mName;
+ uint16_t mMemberCount;
+ XPCNativeMember mMembers[1]; // always last - object sized for array
+};
+
+/***************************************************************************/
+// XPCNativeSetKey is used to key a XPCNativeSet in a NativeSetMap.
+// It represents a new XPCNativeSet we are considering constructing, without
+// requiring that the set actually be built.
+
+class MOZ_STACK_CLASS XPCNativeSetKey final {
+ public:
+ // This represents an existing set |baseSet|.
+ explicit XPCNativeSetKey(XPCNativeSet* baseSet)
+ : mCx(nullptr), mBaseSet(baseSet), mAddition(nullptr) {
+ MOZ_ASSERT(baseSet);
+ }
+
+ // This represents a new set containing only nsISupports and
+ // |addition|. This needs a JSContext because it may need to
+ // construct some data structures that need one to construct them.
+ explicit XPCNativeSetKey(JSContext* cx, XPCNativeInterface* addition)
+ : mCx(cx), mBaseSet(nullptr), mAddition(addition) {
+ MOZ_ASSERT(cx);
+ MOZ_ASSERT(addition);
+ }
+
+ // This represents the existing set |baseSet| with the interface
+ // |addition| inserted after existing interfaces. |addition| must
+ // not already be present in |baseSet|.
+ explicit XPCNativeSetKey(XPCNativeSet* baseSet, XPCNativeInterface* addition);
+ ~XPCNativeSetKey() = default;
+
+ XPCNativeSet* GetBaseSet() const { return mBaseSet; }
+ XPCNativeInterface* GetAddition() const { return mAddition; }
+
+ mozilla::HashNumber Hash() const;
+
+ // Allow shallow copy
+
+ private:
+ JSContext* mCx;
+ RefPtr<XPCNativeSet> mBaseSet;
+ RefPtr<XPCNativeInterface> mAddition;
+};
+
+/***************************************************************************/
+// XPCNativeSet represents an ordered collection of XPCNativeInterface pointers.
+
+class XPCNativeSet final {
+ public:
+ NS_INLINE_DECL_REFCOUNTING_WITH_DESTROY(XPCNativeSet, DestroyInstance(this))
+
+ static already_AddRefed<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);
+
+ protected:
+ static already_AddRefed<XPCNativeSet> NewInstance(
+ JSContext* cx, nsTArray<RefPtr<XPCNativeInterface>>&& array);
+ static already_AddRefed<XPCNativeSet> NewInstanceMutate(XPCNativeSetKey* key);
+
+ XPCNativeSet() : mInterfaceCount(0) {}
+ ~XPCNativeSet();
+ void* operator new(size_t, void* p) noexcept(true) { return p; }
+
+ static void DestroyInstance(XPCNativeSet* inst);
+
+ private:
+ uint16_t mInterfaceCount;
+ // Always last - object sized for array.
+ // These are strong references.
+ XPCNativeInterface* mInterfaces[1];
+};
+
+/***********************************************/
+// XPCWrappedNativeProtos hold the additional shared wrapper data for
+// XPCWrappedNative whose native objects expose nsIClassInfo.
+//
+// The XPCWrappedNativeProto is owned by its mJSProtoObject, until that object
+// is finalized. After that, it is owned by XPCJSRuntime's
+// mDyingWrappedNativeProtos. See XPCWrappedNativeProto::JSProtoObjectFinalized
+// and XPCJSRuntime::FinalizeCallback.
+
+class XPCWrappedNativeProto final {
+ public:
+ enum Slots { ProtoSlot, SlotCount };
+
+ static XPCWrappedNativeProto* GetNewOrUsed(JSContext* cx,
+ XPCWrappedNativeScope* scope,
+ nsIClassInfo* classInfo,
+ nsIXPCScriptable* scriptable);
+
+ XPCWrappedNativeScope* GetScope() const { return mScope; }
+
+ XPCJSRuntime* GetRuntime() const { return mScope->GetRuntime(); }
+
+ JSObject* GetJSProtoObject() const { return mJSProtoObject; }
+
+ JSObject* GetJSProtoObjectPreserveColor() const {
+ return mJSProtoObject.unbarrieredGet();
+ }
+
+ nsIClassInfo* GetClassInfo() const { return mClassInfo; }
+
+ XPCNativeSet* GetSet() const { return mSet; }
+
+ nsIXPCScriptable* GetScriptable() const { return mScriptable; }
+
+ void JSProtoObjectFinalized(JS::GCContext* gcx, JSObject* obj);
+ void JSProtoObjectMoved(JSObject* obj, const JSObject* old);
+
+ static XPCWrappedNativeProto* Get(JSObject* obj);
+
+ void SystemIsBeingShutDown();
+
+ void DebugDump(int16_t depth);
+
+ void TraceSelf(JSTracer* trc) {
+ if (mJSProtoObject) {
+ TraceEdge(trc, &mJSProtoObject, "XPCWrappedNativeProto::mJSProtoObject");
+ }
+ }
+
+ void TraceJS(JSTracer* trc) { TraceSelf(trc); }
+
+ // NOP. This is just here to make the AutoMarkingPtr code compile.
+ void Mark() const {}
+ inline void AutoTrace(JSTracer* trc) {}
+
+ ~XPCWrappedNativeProto();
+
+ protected:
+ // disable copy ctor and assignment
+ XPCWrappedNativeProto(const XPCWrappedNativeProto& r) = delete;
+ XPCWrappedNativeProto& operator=(const XPCWrappedNativeProto& r) = delete;
+
+ // hide ctor
+ XPCWrappedNativeProto(XPCWrappedNativeScope* Scope, nsIClassInfo* ClassInfo,
+ RefPtr<XPCNativeSet>&& Set);
+
+ bool Init(JSContext* cx, nsIXPCScriptable* scriptable);
+
+ private:
+#ifdef DEBUG
+ static int32_t gDEBUG_LiveProtoCount;
+#endif
+
+ private:
+ 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 {
+ public:
+ enum Slots { FlatObjectSlot, TearOffSlot, SlotCount };
+
+ bool IsAvailable() const { return mInterface == nullptr; }
+ bool IsReserved() const { return mInterface == (XPCNativeInterface*)1; }
+ bool IsValid() const { return !IsAvailable() && !IsReserved(); }
+ void SetReserved() { mInterface = (XPCNativeInterface*)1; }
+
+ XPCNativeInterface* GetInterface() const { return mInterface; }
+ nsISupports* GetNative() const { return mNative; }
+ JSObject* GetJSObject();
+ JSObject* GetJSObjectPreserveColor() const;
+ void SetInterface(XPCNativeInterface* Interface) { mInterface = Interface; }
+ void SetNative(nsISupports* Native) { mNative = Native; }
+ already_AddRefed<nsISupports> TakeNative() { return mNative.forget(); }
+ void SetJSObject(JSObject* JSObj);
+
+ void JSObjectFinalized() { SetJSObject(nullptr); }
+ void JSObjectMoved(JSObject* obj, const JSObject* old);
+
+ static XPCWrappedNativeTearOff* Get(JSObject* obj);
+
+ XPCWrappedNativeTearOff() : mInterface(nullptr), mJSObject(nullptr) {
+ MOZ_COUNT_CTOR(XPCWrappedNativeTearOff);
+ }
+ ~XPCWrappedNativeTearOff();
+
+ // NOP. This is just here to make the AutoMarkingPtr code compile.
+ inline void TraceJS(JSTracer* trc) {}
+ inline void AutoTrace(JSTracer* trc) {}
+
+ void Mark() { mJSObject.setFlags(1); }
+ void Unmark() { mJSObject.unsetFlags(1); }
+ bool IsMarked() const { return mJSObject.hasFlag(1); }
+
+ XPCWrappedNativeTearOff* AddTearOff() {
+ MOZ_ASSERT(!mNextTearOff);
+ mNextTearOff = mozilla::MakeUnique<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_CYCLE_COLLECTION_CLASS(XPCWrappedNative)
+
+ JSObject* GetJSObject() override;
+
+ bool IsValid() const { return mFlatJSObject.hasFlag(FLAT_JS_OBJECT_VALID); }
+
+ nsresult DebugDump(int16_t depth);
+
+#define XPC_SCOPE_WORD(s) (intptr_t(s))
+#define XPC_SCOPE_MASK (intptr_t(0x3))
+#define XPC_SCOPE_TAG (intptr_t(0x1))
+#define XPC_WRAPPER_EXPIRED (intptr_t(0x2))
+
+ static inline bool IsTaggedScope(XPCWrappedNativeScope* s) {
+ return XPC_SCOPE_WORD(s) & XPC_SCOPE_TAG;
+ }
+
+ static inline XPCWrappedNativeScope* TagScope(XPCWrappedNativeScope* s) {
+ MOZ_ASSERT(!IsTaggedScope(s), "bad pointer!");
+ return (XPCWrappedNativeScope*)(XPC_SCOPE_WORD(s) | XPC_SCOPE_TAG);
+ }
+
+ static inline XPCWrappedNativeScope* UnTagScope(XPCWrappedNativeScope* s) {
+ return (XPCWrappedNativeScope*)(XPC_SCOPE_WORD(s) & ~XPC_SCOPE_TAG);
+ }
+
+ inline bool IsWrapperExpired() const {
+ return XPC_SCOPE_WORD(mMaybeScope) & XPC_WRAPPER_EXPIRED;
+ }
+
+ bool HasProto() const { return !IsTaggedScope(mMaybeScope); }
+
+ XPCWrappedNativeProto* GetProto() const {
+ return HasProto() ? (XPCWrappedNativeProto*)(XPC_SCOPE_WORD(mMaybeProto) &
+ ~XPC_SCOPE_MASK)
+ : nullptr;
+ }
+
+ XPCWrappedNativeScope* GetScope() const {
+ return GetProto() ? GetProto()->GetScope()
+ : (XPCWrappedNativeScope*)(XPC_SCOPE_WORD(mMaybeScope) &
+ ~XPC_SCOPE_MASK);
+ }
+
+ nsISupports* GetIdentityObject() const { return mIdentity; }
+
+ /**
+ * This getter clears the gray bit before handing out the JSObject which
+ * means that the object is guaranteed to be kept alive past the next CC.
+ */
+ JSObject* GetFlatJSObject() const { return mFlatJSObject; }
+
+ /**
+ * This getter does not change the color of the JSObject meaning that the
+ * object returned is not guaranteed to be kept alive past the next CC.
+ *
+ * This should only be called if you are certain that the return value won't
+ * be passed into a JS API function and that it won't be stored without
+ * being rooted (or otherwise signaling the stored value to the CC).
+ */
+ JSObject* GetFlatJSObjectPreserveColor() const {
+ return mFlatJSObject.unbarrieredGetPtr();
+ }
+
+ XPCNativeSet* GetSet() const { return mSet; }
+
+ void SetSet(already_AddRefed<XPCNativeSet> set) { mSet = set; }
+
+ static XPCWrappedNative* Get(JSObject* obj) {
+ MOZ_ASSERT(xpc::IsWrappedNativeReflector(obj));
+ return JS::GetObjectISupports<XPCWrappedNative>(obj);
+ }
+
+ private:
+ void SetFlatJSObject(JSObject* object);
+ void UnsetFlatJSObject();
+
+ inline void ExpireWrapper() {
+ mMaybeScope = (XPCWrappedNativeScope*)(XPC_SCOPE_WORD(mMaybeScope) |
+ XPC_WRAPPER_EXPIRED);
+ }
+
+ public:
+ nsIXPCScriptable* GetScriptable() const { return mScriptable; }
+
+ nsIClassInfo* GetClassInfo() const {
+ return IsValid() && HasProto() ? GetProto()->GetClassInfo() : nullptr;
+ }
+
+ bool HasMutatedSet() const {
+ return IsValid() && (!HasProto() || GetSet() != GetProto()->GetSet());
+ }
+
+ XPCJSRuntime* GetRuntime() const {
+ XPCWrappedNativeScope* scope = GetScope();
+ return scope ? scope->GetRuntime() : nullptr;
+ }
+
+ static nsresult WrapNewGlobal(JSContext* cx, xpcObjectHelper& nativeHelper,
+ nsIPrincipal* principal,
+ bool initStandardClasses,
+ JS::RealmOptions& aOptions,
+ XPCWrappedNative** wrappedGlobal);
+
+ static nsresult GetNewOrUsed(JSContext* cx, xpcObjectHelper& helper,
+ XPCWrappedNativeScope* Scope,
+ XPCNativeInterface* Interface,
+ XPCWrappedNative** wrapper);
+
+ void FlatJSObjectFinalized();
+ void FlatJSObjectMoved(JSObject* obj, const JSObject* old);
+
+ void SystemIsBeingShutDown();
+
+ enum CallMode { CALL_METHOD, CALL_GETTER, CALL_SETTER };
+
+ static bool CallMethod(XPCCallContext& ccx, CallMode mode = CALL_METHOD);
+
+ static bool GetAttribute(XPCCallContext& ccx) {
+ return CallMethod(ccx, CALL_GETTER);
+ }
+
+ static bool SetAttribute(XPCCallContext& ccx) {
+ return CallMethod(ccx, CALL_SETTER);
+ }
+
+ XPCWrappedNativeTearOff* FindTearOff(JSContext* cx,
+ XPCNativeInterface* aInterface,
+ bool needJSObject = false,
+ nsresult* pError = nullptr);
+ XPCWrappedNativeTearOff* FindTearOff(JSContext* cx, const nsIID& iid);
+
+ void Mark() const {}
+
+ inline void TraceInside(JSTracer* trc) {
+ if (HasProto()) {
+ GetProto()->TraceSelf(trc);
+ }
+
+ JSObject* obj = mFlatJSObject.unbarrieredGetPtr();
+ if (obj && JS_IsGlobalObject(obj)) {
+ xpc::TraceXPCGlobal(trc, obj);
+ }
+ }
+
+ void TraceJS(JSTracer* trc) { TraceInside(trc); }
+
+ void TraceSelf(JSTracer* trc) {
+ // If this got called, we're being kept alive by someone who really
+ // needs us alive and whole. Do not let our mFlatJSObject go away.
+ // This is the only time we should be tracing our mFlatJSObject,
+ // normally somebody else is doing that.
+ JS::TraceEdge(trc, &mFlatJSObject, "XPCWrappedNative::mFlatJSObject");
+ }
+
+ static void Trace(JSTracer* trc, JSObject* obj);
+
+ void AutoTrace(JSTracer* trc) { TraceSelf(trc); }
+
+ inline void SweepTearOffs();
+
+ // Returns a string that should be freed with js_free, or nullptr on
+ // failure.
+ char* ToString(XPCWrappedNativeTearOff* to = nullptr) const;
+
+ static nsIXPCScriptable* GatherProtoScriptable(nsIClassInfo* classInfo);
+
+ bool HasExternalReference() const { return mRefCnt > 1; }
+
+ void Suspect(nsCycleCollectionNoteRootCallback& cb);
+ void NoteTearoffs(nsCycleCollectionTraversalCallback& cb);
+
+ // Make ctor and dtor protected (rather than private) to placate nsCOMPtr.
+ protected:
+ XPCWrappedNative() = delete;
+
+ // This ctor is used if this object will have a proto.
+ XPCWrappedNative(nsCOMPtr<nsISupports>&& aIdentity,
+ XPCWrappedNativeProto* aProto);
+
+ // This ctor is used if this object will NOT have a proto.
+ XPCWrappedNative(nsCOMPtr<nsISupports>&& aIdentity,
+ XPCWrappedNativeScope* aScope, RefPtr<XPCNativeSet>&& aSet);
+
+ virtual ~XPCWrappedNative();
+ void Destroy();
+
+ private:
+ enum {
+ // Flags bits for mFlatJSObject:
+ FLAT_JS_OBJECT_VALID = js::Bit(0)
+ };
+
+ bool Init(JSContext* cx, nsIXPCScriptable* scriptable);
+ bool FinishInit(JSContext* cx);
+
+ bool ExtendSet(JSContext* aCx, XPCNativeInterface* aInterface);
+
+ nsresult InitTearOff(JSContext* cx, XPCWrappedNativeTearOff* aTearOff,
+ XPCNativeInterface* aInterface, bool needJSObject);
+
+ bool InitTearOffJSObject(JSContext* cx, XPCWrappedNativeTearOff* to);
+
+ public:
+ static void GatherScriptable(nsISupports* obj, nsIClassInfo* classInfo,
+ nsIXPCScriptable** scrProto,
+ nsIXPCScriptable** scrWrapper);
+
+ private:
+ union {
+ XPCWrappedNativeScope* mMaybeScope;
+ XPCWrappedNativeProto* mMaybeProto;
+ };
+ RefPtr<XPCNativeSet> mSet;
+ JS::TenuredHeap<JSObject*> mFlatJSObject;
+ nsCOMPtr<nsIXPCScriptable> mScriptable;
+ XPCWrappedNativeTearOff mFirstTearOff;
+};
+
+/***************************************************************************
+****************************************************************************
+*
+* Core classes for wrapped JSObject for use from native code...
+*
+****************************************************************************
+***************************************************************************/
+
+/*************************/
+// nsXPCWrappedJS is a wrapper for a single JSObject for use from native code.
+// nsXPCWrappedJS objects are chained together to represent the various
+// interface on the single underlying (possibly aggregate) JSObject.
+
+class nsXPCWrappedJS final : protected nsAutoXPTCStub,
+ public nsIXPConnectWrappedJSUnmarkGray,
+ public nsSupportsWeakReference,
+ public mozilla::LinkedListElement<nsXPCWrappedJS> {
+ public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_NSISUPPORTSWEAKREFERENCE
+
+ NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_AMBIGUOUS(
+ nsXPCWrappedJS, nsIXPConnectWrappedJS)
+
+ JSObject* GetJSObject() override;
+
+ // This method is defined in XPCWrappedJSClass.cpp to preserve VCS blame.
+ NS_IMETHOD CallMethod(uint16_t methodIndex, const nsXPTMethodInfo* info,
+ nsXPTCMiniVariant* nativeParams) override;
+
+ /*
+ * This is rarely called directly. Instead one usually calls
+ * XPCConvert::JSObject2NativeInterface which will handles cases where the
+ * JS object is already a wrapped native or a DOM object.
+ */
+
+ static nsresult GetNewOrUsed(JSContext* cx, JS::HandleObject aJSObj,
+ REFNSIID aIID, nsXPCWrappedJS** wrapper);
+
+ nsISomeInterface* GetXPTCStub() { return mXPTCStub; }
+
+ nsresult DebugDump(int16_t depth);
+
+ /**
+ * This getter does not change the color of the JSObject meaning that the
+ * object returned is not guaranteed to be kept alive past the next CC.
+ *
+ * This should only be called if you are certain that the return value won't
+ * be passed into a JS API function and that it won't be stored without
+ * being rooted (or otherwise signaling the stored value to the CC).
+ */
+ JSObject* GetJSObjectPreserveColor() const { return mJSObj.unbarrieredGet(); }
+
+ // Returns true if the wrapper chain contains references to multiple
+ // compartments. If the wrapper chain contains references to multiple
+ // compartments, then it must be registered on the XPCJSContext. Otherwise,
+ // it should be registered in the CompartmentPrivate for the compartment of
+ // the root's JS object. This will only return correct results when called
+ // on the root wrapper and will assert if not called on a root wrapper.
+ bool IsMultiCompartment() const;
+
+ const nsXPTInterfaceInfo* GetInfo() const { return mInfo; }
+ REFNSIID GetIID() const { return mInfo->IID(); }
+ nsXPCWrappedJS* GetRootWrapper() const { return mRoot; }
+ nsXPCWrappedJS* GetNextWrapper() const { return mNext; }
+
+ nsXPCWrappedJS* Find(REFNSIID aIID);
+ nsXPCWrappedJS* FindInherited(REFNSIID aIID);
+ nsXPCWrappedJS* FindOrFindInherited(REFNSIID aIID) {
+ nsXPCWrappedJS* wrapper = Find(aIID);
+ if (wrapper) {
+ return wrapper;
+ }
+ return FindInherited(aIID);
+ }
+
+ bool IsRootWrapper() const { return mRoot == this; }
+ bool IsValid() const { return bool(mJSObj); }
+ void SystemIsBeingShutDown();
+
+ // These two methods are used by JSObject2WrappedJSMap::FindDyingJSObjects
+ // to find non-rooting wrappers for dying JS objects. See the top of
+ // XPCWrappedJS.cpp for more details.
+ bool IsSubjectToFinalization() const { return IsValid() && mRefCnt == 1; }
+
+ void UpdateObjectPointerAfterGC(JSTracer* trc) {
+ MOZ_ASSERT(IsRootWrapper());
+ JS_UpdateWeakPointerAfterGC(trc, &mJSObj);
+ }
+
+ bool IsAggregatedToNative() const { return mRoot->mOuter != nullptr; }
+ nsISupports* GetAggregatedNativeObject() const { return mRoot->mOuter; }
+ void SetAggregatedNativeObject(nsISupports* aNative) {
+ MOZ_ASSERT(aNative);
+ if (mRoot->mOuter) {
+ MOZ_ASSERT(mRoot->mOuter == aNative,
+ "Only one aggregated native can be set");
+ return;
+ }
+ mRoot->mOuter = aNative;
+ }
+
+ // This method is defined in XPCWrappedJSClass.cpp to preserve VCS blame.
+ static void DebugDumpInterfaceInfo(const nsXPTInterfaceInfo* aInfo,
+ int16_t depth);
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+
+ virtual ~nsXPCWrappedJS();
+
+ protected:
+ nsXPCWrappedJS() = delete;
+ nsXPCWrappedJS(JSContext* cx, JSObject* aJSObj,
+ const nsXPTInterfaceInfo* aInfo, nsXPCWrappedJS* root,
+ nsresult* rv);
+
+ bool CanSkip();
+ void Destroy();
+ void Unlink();
+
+ private:
+ friend class nsIXPConnectWrappedJS;
+
+ JS::Compartment* Compartment() const {
+ return JS::GetCompartment(mJSObj.unbarrieredGet());
+ }
+
+ // These methods are defined in XPCWrappedJSClass.cpp to preserve VCS blame.
+ static const nsXPTInterfaceInfo* GetInterfaceInfo(REFNSIID aIID);
+
+ nsresult DelegatedQueryInterface(REFNSIID aIID, void** aInstancePtr);
+
+ static JSObject* GetRootJSObject(JSContext* cx, JSObject* aJSObj);
+
+ static JSObject* CallQueryInterfaceOnJSObject(JSContext* cx, JSObject* jsobj,
+ JS::HandleObject scope,
+ REFNSIID aIID);
+
+ // aObj is the nsXPCWrappedJS's object. We used this as the callee (or |this|
+ // if getter or setter).
+ // aSyntheticException, if not null, is the exception we should be using.
+ // If null, look for an exception on the JSContext hanging off the
+ // XPCCallContext.
+ static nsresult CheckForException(
+ XPCCallContext& ccx, mozilla::dom::AutoEntryScript& aes,
+ JS::HandleObject aObj, const char* aPropertyName,
+ const char* anInterfaceName,
+ mozilla::dom::Exception* aSyntheticException = nullptr);
+
+ static bool GetArraySizeFromParam(const nsXPTMethodInfo* method,
+ const nsXPTType& type,
+ nsXPTCMiniVariant* params,
+ uint32_t* result);
+
+ static bool GetInterfaceTypeFromParam(const nsXPTMethodInfo* method,
+ const nsXPTType& type,
+ nsXPTCMiniVariant* params,
+ nsID* result);
+
+ static void CleanupOutparams(const nsXPTMethodInfo* info,
+ nsXPTCMiniVariant* nativeParams, bool inOutOnly,
+ uint8_t count);
+
+ JS::Heap<JSObject*> mJSObj;
+ const nsXPTInterfaceInfo* const mInfo;
+ nsXPCWrappedJS* mRoot; // If mRoot != this, it is an owning pointer.
+ nsXPCWrappedJS* mNext;
+ nsCOMPtr<nsISupports> mOuter; // only set in root
+};
+
+/***************************************************************************
+****************************************************************************
+*
+* All manner of utility classes follow...
+*
+****************************************************************************
+***************************************************************************/
+
+namespace xpc {
+
+// A wrapper around JS iterators which presents an equivalent
+// nsISimpleEnumerator interface for their contents.
+class XPCWrappedJSIterator final : public nsISimpleEnumerator {
+ public:
+ NS_DECL_CYCLE_COLLECTION_CLASS(XPCWrappedJSIterator)
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_NSISIMPLEENUMERATOR
+ NS_DECL_NSISIMPLEENUMERATORBASE
+
+ explicit XPCWrappedJSIterator(nsIJSEnumerator* aEnum);
+
+ private:
+ ~XPCWrappedJSIterator() = default;
+
+ nsCOMPtr<nsIJSEnumerator> mEnum;
+ nsCOMPtr<nsIGlobalObject> mGlobal;
+ nsCOMPtr<nsISupports> mNext;
+ mozilla::Maybe<bool> mHasNext;
+};
+
+} // namespace xpc
+
+/***************************************************************************/
+// class here just for static methods
+class XPCConvert {
+ public:
+ /**
+ * Convert a native object into a JS::Value.
+ *
+ * @param cx the JSContext representing the global we want the value in
+ * @param d [out] the resulting JS::Value
+ * @param s the native object we're working with
+ * @param type the type of object that s is
+ * @param iid the interface of s that we want
+ * @param scope the default scope to put on the new JSObject's parent
+ * chain
+ * @param pErr [out] relevant error code, if any.
+ */
+
+ static bool NativeData2JS(JSContext* cx, JS::MutableHandleValue d,
+ const void* s, const nsXPTType& type,
+ const nsID* iid, uint32_t arrlen, nsresult* pErr);
+
+ static bool JSData2Native(JSContext* cx, void* d, JS::HandleValue s,
+ const nsXPTType& type, const nsID* iid,
+ uint32_t arrlen, nsresult* pErr);
+
+ /**
+ * Convert a native nsISupports into a JSObject.
+ *
+ * @param cx the JSContext representing the global we want the object in.
+ * @param dest [out] the resulting JSObject
+ * @param src the native object we're working with
+ * @param iid the interface of src that we want (may be null)
+ * @param cache the wrapper cache for src (may be null, in which case src
+ * will be QI'ed to get the cache)
+ * @param allowNativeWrapper if true, this method may wrap the resulting
+ * JSObject in an XPCNativeWrapper and return that, as needed.
+ * @param pErr [out] relevant error code, if any.
+ * @param src_is_identity optional performance hint. Set to true only
+ * if src is the identity pointer.
+ */
+ static bool NativeInterface2JSObject(JSContext* cx,
+ JS::MutableHandleValue dest,
+ xpcObjectHelper& aHelper,
+ const nsID* iid, bool allowNativeWrapper,
+ nsresult* pErr);
+
+ static bool GetNativeInterfaceFromJSObject(void** dest, JSObject* src,
+ const nsID* iid, nsresult* pErr);
+ static bool JSObject2NativeInterface(JSContext* cx, void** dest,
+ JS::HandleObject src, const nsID* iid,
+ nsISupports* aOuter, nsresult* pErr);
+
+ // Note - This return the XPCWrappedNative, rather than the native itself,
+ // for the WN case. You probably want UnwrapReflectorToISupports.
+ static bool GetISupportsFromJSObject(JSObject* obj, nsISupports** iface);
+
+ static nsresult JSValToXPCException(JSContext* cx, JS::MutableHandleValue s,
+ const char* ifaceName,
+ const char* methodName,
+ mozilla::dom::Exception** exception);
+
+ static nsresult ConstructException(nsresult rv, const char* message,
+ const char* ifaceName,
+ const char* methodName, nsISupports* data,
+ mozilla::dom::Exception** exception,
+ JSContext* cx, JS::Value* jsExceptionPtr);
+
+ private:
+ /**
+ * Convert a native array into a JS::Value.
+ *
+ * @param cx the JSContext we're working with and in whose global the array
+ * should be created.
+ * @param d [out] the resulting JS::Value
+ * @param buf the native buffer containing input values
+ * @param type the type of objects in the array
+ * @param iid the interface of each object in the array that we want
+ * @param count the number of items in the array
+ * @param scope the default scope to put on the new JSObjects' parent chain
+ * @param pErr [out] relevant error code, if any.
+ */
+ static bool NativeArray2JS(JSContext* cx, JS::MutableHandleValue d,
+ const void* buf, const nsXPTType& type,
+ const nsID* iid, uint32_t count, nsresult* pErr);
+
+ using ArrayAllocFixupLen = std::function<void*(uint32_t*)>;
+
+ /**
+ * Convert a JS::Value into a native array.
+ *
+ * @param cx the JSContext we're working with
+ * @param aJSVal the JS::Value to convert
+ * @param aEltType the type of objects in the array
+ * @param aIID the interface of each object in the array
+ * @param pErr [out] relevant error code, if any
+ * @param aAllocFixupLen function called with the JS Array's length to
+ * allocate the backing buffer. This function may
+ * modify the length of array to be converted.
+ */
+ static bool JSArray2Native(JSContext* cx, JS::HandleValue aJSVal,
+ const nsXPTType& aEltType, const nsIID* aIID,
+ nsresult* pErr,
+ const ArrayAllocFixupLen& aAllocFixupLen);
+
+ XPCConvert() = delete;
+};
+
+/***************************************************************************/
+// code for throwing exceptions into JS
+
+class nsXPCException;
+
+class XPCThrower {
+ public:
+ static void Throw(nsresult rv, JSContext* cx);
+ static void Throw(nsresult rv, XPCCallContext& ccx);
+ static void ThrowBadResult(nsresult rv, nsresult result, XPCCallContext& ccx);
+ static void ThrowBadParam(nsresult rv, unsigned paramNum,
+ XPCCallContext& ccx);
+ static bool SetVerbosity(bool state) {
+ bool old = sVerbose;
+ sVerbose = state;
+ return old;
+ }
+
+ static bool CheckForPendingException(nsresult result, JSContext* cx);
+
+ private:
+ static void Verbosify(XPCCallContext& ccx, char** psz, bool own);
+
+ private:
+ static bool sVerbose;
+};
+
+/***************************************************************************/
+
+class nsXPCException {
+ public:
+ static bool NameAndFormatForNSResult(nsresult rv, const char** name,
+ const char** format);
+
+ static const void* IterateNSResults(nsresult* rv, const char** name,
+ const char** format, const void** iterp);
+
+ static uint32_t GetNSResultCount();
+};
+
+/***************************************************************************/
+// 'Components' object implementation.
+
+class nsXPCComponents final : public nsIXPCComponents {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIXPCCOMPONENTS
+
+ public:
+ void SystemIsBeingShutDown() { ClearMembers(); }
+
+ XPCWrappedNativeScope* GetScope() { return mScope; }
+
+ protected:
+ ~nsXPCComponents();
+
+ explicit nsXPCComponents(XPCWrappedNativeScope* aScope);
+ void ClearMembers();
+
+ XPCWrappedNativeScope* mScope;
+
+ RefPtr<nsXPCComponents_Interfaces> mInterfaces;
+ RefPtr<nsXPCComponents_Results> mResults;
+ RefPtr<nsXPCComponents_Classes> mClasses;
+ RefPtr<nsXPCComponents_ID> mID;
+ RefPtr<nsXPCComponents_Exception> mException;
+ RefPtr<nsXPCComponents_Constructor> mConstructor;
+ RefPtr<nsXPCComponents_Utils> mUtils;
+
+ friend class XPCWrappedNativeScope;
+};
+
+/******************************************************************************
+ * Handles pre/post script processing.
+ */
+class MOZ_RAII AutoScriptEvaluate {
+ public:
+ /**
+ * Saves the JSContext as well as initializing our state
+ * @param cx The JSContext, this can be null, we don't do anything then
+ */
+ explicit AutoScriptEvaluate(JSContext* cx)
+ : mJSContext(cx), mEvaluated(false) {}
+
+ /**
+ * Does the pre script evaluation.
+ * This function should only be called once, and will assert if called
+ * more than once
+ */
+
+ bool StartEvaluating(JS::HandleObject scope);
+
+ /**
+ * Does the post script evaluation.
+ */
+ ~AutoScriptEvaluate();
+
+ private:
+ JSContext* mJSContext;
+ mozilla::Maybe<JS::AutoSaveExceptionState> mState;
+ bool mEvaluated;
+ mozilla::Maybe<JSAutoRealm> mAutoRealm;
+
+ // No copying or assignment allowed
+ AutoScriptEvaluate(const AutoScriptEvaluate&) = delete;
+ AutoScriptEvaluate& operator=(const AutoScriptEvaluate&) = delete;
+};
+
+/***************************************************************************/
+class MOZ_RAII AutoResolveName {
+ public:
+ AutoResolveName(XPCCallContext& ccx, JS::HandleId name)
+ : mContext(ccx.GetContext()),
+ mOld(ccx, mContext->SetResolveName(name))
+#ifdef DEBUG
+ ,
+ mCheck(ccx, name)
+#endif
+ {
+ }
+
+ ~AutoResolveName() {
+ mozilla::DebugOnly<jsid> old = mContext->SetResolveName(mOld);
+ MOZ_ASSERT(old == mCheck, "Bad Nesting!");
+ }
+
+ private:
+ XPCJSContext* mContext;
+ JS::RootedId mOld;
+#ifdef DEBUG
+ JS::RootedId mCheck;
+#endif
+};
+
+/***************************************************************************/
+// AutoMarkingPtr is the base class for the various AutoMarking pointer types
+// below. This system allows us to temporarily protect instances of our garbage
+// collected types after they are constructed but before they are safely
+// attached to other rooted objects.
+// This base class has pure virtual support for marking.
+
+class AutoMarkingPtr {
+ public:
+ explicit AutoMarkingPtr(JSContext* cx) {
+ mRoot = XPCJSContext::Get()->GetAutoRootsAdr();
+ mNext = *mRoot;
+ *mRoot = this;
+ }
+
+ virtual ~AutoMarkingPtr() {
+ if (mRoot) {
+ MOZ_ASSERT(*mRoot == this);
+ *mRoot = mNext;
+ }
+ }
+
+ void TraceJSAll(JSTracer* trc) {
+ for (AutoMarkingPtr* cur = this; cur; cur = cur->mNext) {
+ cur->TraceJS(trc);
+ }
+ }
+
+ void MarkAfterJSFinalizeAll() {
+ for (AutoMarkingPtr* cur = this; cur; cur = cur->mNext) {
+ cur->MarkAfterJSFinalize();
+ }
+ }
+
+ protected:
+ virtual void TraceJS(JSTracer* trc) = 0;
+ virtual void MarkAfterJSFinalize() = 0;
+
+ private:
+ AutoMarkingPtr** mRoot;
+ AutoMarkingPtr* mNext;
+};
+
+template <class 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) override {
+ if (mPtr) {
+ mPtr->TraceJS(trc);
+ mPtr->AutoTrace(trc);
+ }
+ }
+
+ virtual void MarkAfterJSFinalize() override {
+ if (mPtr) {
+ mPtr->Mark();
+ }
+ }
+
+ private:
+ T* mPtr;
+};
+
+using AutoMarkingWrappedNativePtr = TypedAutoMarkingPtr<XPCWrappedNative>;
+using AutoMarkingWrappedNativeTearOffPtr =
+ TypedAutoMarkingPtr<XPCWrappedNativeTearOff>;
+using AutoMarkingWrappedNativeProtoPtr =
+ TypedAutoMarkingPtr<XPCWrappedNativeProto>;
+
+/***************************************************************************/
+// Definitions in XPCVariant.cpp.
+
+// {1809FD50-91E8-11d5-90F9-0010A4E73D9A}
+#define XPCVARIANT_IID \
+ { \
+ 0x1809fd50, 0x91e8, 0x11d5, { \
+ 0x90, 0xf9, 0x0, 0x10, 0xa4, 0xe7, 0x3d, 0x9a \
+ } \
+ }
+
+// {DC524540-487E-4501-9AC7-AAA784B17C1C}
+#define XPCVARIANT_CID \
+ { \
+ 0xdc524540, 0x487e, 0x4501, { \
+ 0x9a, 0xc7, 0xaa, 0xa7, 0x84, 0xb1, 0x7c, 0x1c \
+ } \
+ }
+
+class XPCVariant : public nsIVariant {
+ public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_NSIVARIANT
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(XPCVariant)
+
+ // If this class ever implements nsIWritableVariant, take special care with
+ // the case when mJSVal is JSVAL_STRING, since we don't own the data in
+ // that case.
+
+ // We #define and iid so that out module local code can use QI to detect
+ // if a given nsIVariant is in fact an XPCVariant.
+ NS_DECLARE_STATIC_IID_ACCESSOR(XPCVARIANT_IID)
+
+ static already_AddRefed<XPCVariant> newVariant(JSContext* cx,
+ const JS::Value& aJSVal);
+
+ /**
+ * This getter clears the gray bit before handing out the Value if the Value
+ * represents a JSObject. That means that the object is guaranteed to be
+ * kept alive past the next CC.
+ */
+ JS::Value GetJSVal() const { return mJSVal; }
+
+ protected:
+ /**
+ * This getter does not change the color of the Value (if it represents a
+ * JSObject) meaning that the value returned is not guaranteed to be kept
+ * alive past the next CC.
+ *
+ * This should only be called if you are certain that the return value won't
+ * be passed into a JS API function and that it won't be stored without
+ * being rooted (or otherwise signaling the stored value to the CC).
+ */
+ JS::Value GetJSValPreserveColor() const { return mJSVal.unbarrieredGet(); }
+
+ XPCVariant(JSContext* cx, const JS::Value& aJSVal);
+
+ public:
+ /**
+ * Convert a variant into a JS::Value.
+ *
+ * @param cx the context for the whole procedure
+ * @param variant the variant to convert
+ * @param scope the default scope to put on the new JSObject's parent chain
+ * @param pErr [out] relevant error code, if any.
+ * @param pJSVal [out] the resulting jsval.
+ */
+ static bool VariantDataToJS(JSContext* cx, nsIVariant* variant,
+ nsresult* pErr, JS::MutableHandleValue pJSVal);
+
+ protected:
+ virtual ~XPCVariant();
+
+ bool InitializeData(JSContext* cx);
+
+ void Cleanup();
+
+ nsDiscriminatedUnion mData;
+ JS::Heap<JS::Value> mJSVal;
+ bool mReturnRawObject;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(XPCVariant, XPCVARIANT_IID)
+
+/***************************************************************************/
+// Utilities
+
+inline JSContext* xpc_GetSafeJSContext() {
+ return XPCJSContext::Get()->Context();
+}
+
+namespace xpc {
+
+// JSNatives to expose atob and btoa in various non-DOM XPConnect scopes.
+bool Atob(JSContext* cx, unsigned argc, JS::Value* vp);
+
+bool Btoa(JSContext* cx, unsigned argc, JS::Value* vp);
+
+// Helper function that creates a JSFunction that wraps a native function that
+// forwards the call to the original 'callable'.
+class FunctionForwarderOptions;
+bool NewFunctionForwarder(JSContext* cx, JS::HandleId id,
+ JS::HandleObject callable,
+ FunctionForwarderOptions& options,
+ JS::MutableHandleValue vp);
+
+// Old fashioned xpc error reporter. Try to use JS_ReportError instead.
+nsresult ThrowAndFail(nsresult errNum, JSContext* cx, bool* retval);
+
+struct GlobalProperties {
+ GlobalProperties() { mozilla::PodZero(this); }
+ bool Parse(JSContext* cx, JS::HandleObject obj);
+ bool DefineInXPCComponents(JSContext* cx, JS::HandleObject obj);
+ bool DefineInSandbox(JSContext* cx, JS::HandleObject obj);
+
+ // Interface objects we can expose.
+ bool AbortController : 1;
+ bool Blob : 1;
+ bool ChromeUtils : 1;
+ bool CSS : 1;
+ bool CSSRule : 1;
+ bool Directory : 1;
+ bool Document : 1;
+ bool DOMException : 1;
+ bool DOMParser : 1;
+ bool DOMTokenList : 1;
+ bool Element : 1;
+ bool Event : 1;
+ bool File : 1;
+ bool FileReader : 1;
+ bool FormData : 1;
+ bool Headers : 1;
+ bool IOUtils : 1;
+ bool InspectorUtils : 1;
+ bool MessageChannel : 1;
+ bool MIDIInputMap : 1;
+ bool MIDIOutputMap : 1;
+ bool Node : 1;
+ bool NodeFilter : 1;
+ bool PathUtils : 1;
+ bool Performance : 1;
+ bool PromiseDebugging : 1;
+ bool Range : 1;
+ bool Selection : 1;
+ bool TextDecoder : 1;
+ bool TextEncoder : 1;
+ bool URL : 1;
+ bool URLSearchParams : 1;
+ bool XMLHttpRequest : 1;
+ bool WebSocket : 1;
+ bool Window : 1;
+ bool XMLSerializer : 1;
+ bool ReadableStream : 1;
+
+ // Ad-hoc property names we implement.
+ bool atob : 1;
+ bool btoa : 1;
+ bool caches : 1;
+ bool crypto : 1;
+ bool fetch : 1;
+ bool storage : 1;
+ bool structuredClone : 1;
+ bool indexedDB : 1;
+ bool isSecureContext : 1;
+ bool rtcIdentityProvider : 1;
+
+ private:
+ bool Define(JSContext* cx, JS::HandleObject obj);
+};
+
+// Infallible.
+already_AddRefed<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),
+ proto(cx),
+ sameZoneAs(cx),
+ forceSecureContext(false),
+ freshCompartment(false),
+ freshZone(false),
+ isUAWidgetScope(false),
+ invisibleToDebugger(false),
+ discardSource(false),
+ metadata(cx),
+ userContextId(0),
+ originAttributes(cx) {}
+
+ virtual bool Parse() override;
+
+ bool wantXrays;
+ bool allowWaivers;
+ bool wantComponents;
+ bool wantExportHelpers;
+ bool isWebExtensionContentScript;
+ JS::RootedObject proto;
+ nsCString sandboxName;
+ JS::RootedObject sameZoneAs;
+ bool forceSecureContext;
+ bool freshCompartment;
+ bool freshZone;
+ bool isUAWidgetScope;
+ bool invisibleToDebugger;
+ bool discardSource;
+ GlobalProperties globalProperties;
+ JS::RootedValue metadata;
+ uint32_t userContextId;
+ JS::RootedObject originAttributes;
+
+ protected:
+ bool ParseGlobalProperties();
+};
+
+class MOZ_STACK_CLASS CreateObjectInOptions : public OptionsBase {
+ public:
+ explicit CreateObjectInOptions(JSContext* cx = xpc_GetSafeJSContext(),
+ JSObject* options = nullptr)
+ : OptionsBase(cx, options), defineAs(cx, JS::PropertyKey::Void()) {}
+
+ virtual bool Parse() override { return ParseId("defineAs", &defineAs); }
+
+ JS::RootedId defineAs;
+};
+
+class MOZ_STACK_CLASS ExportFunctionOptions : public OptionsBase {
+ public:
+ explicit ExportFunctionOptions(JSContext* cx = xpc_GetSafeJSContext(),
+ JSObject* options = nullptr)
+ : OptionsBase(cx, options),
+ defineAs(cx, JS::PropertyKey::Void()),
+ allowCrossOriginArguments(false) {}
+
+ virtual bool Parse() override {
+ return ParseId("defineAs", &defineAs) &&
+ ParseBoolean("allowCrossOriginArguments",
+ &allowCrossOriginArguments);
+ }
+
+ JS::RootedId defineAs;
+ bool allowCrossOriginArguments;
+};
+
+class MOZ_STACK_CLASS FunctionForwarderOptions : public OptionsBase {
+ public:
+ explicit FunctionForwarderOptions(JSContext* cx = xpc_GetSafeJSContext(),
+ JSObject* options = nullptr)
+ : OptionsBase(cx, options), allowCrossOriginArguments(false) {}
+
+ JSObject* ToJSObject(JSContext* cx) {
+ JS::RootedObject obj(cx, JS_NewObjectWithGivenProto(cx, nullptr, nullptr));
+ if (!obj) {
+ return nullptr;
+ }
+
+ JS::RootedValue val(cx);
+ unsigned attrs = JSPROP_READONLY | JSPROP_PERMANENT;
+ val = JS::BooleanValue(allowCrossOriginArguments);
+ if (!JS_DefineProperty(cx, obj, "allowCrossOriginArguments", val, attrs)) {
+ return nullptr;
+ }
+
+ return obj;
+ }
+
+ virtual bool Parse() override {
+ return ParseBoolean("allowCrossOriginArguments",
+ &allowCrossOriginArguments);
+ }
+
+ bool allowCrossOriginArguments;
+};
+
+class MOZ_STACK_CLASS StackScopedCloneOptions : public OptionsBase {
+ public:
+ explicit StackScopedCloneOptions(JSContext* cx = xpc_GetSafeJSContext(),
+ JSObject* options = nullptr)
+ : OptionsBase(cx, options),
+ wrapReflectors(false),
+ cloneFunctions(false),
+ deepFreeze(false) {}
+
+ virtual bool Parse() override {
+ return ParseBoolean("wrapReflectors", &wrapReflectors) &&
+ ParseBoolean("cloneFunctions", &cloneFunctions) &&
+ ParseBoolean("deepFreeze", &deepFreeze);
+ }
+
+ // When a reflector is encountered, wrap it rather than aborting the clone.
+ bool wrapReflectors;
+
+ // When a function is encountered, clone it (exportFunction-style) rather than
+ // aborting the clone.
+ bool cloneFunctions;
+
+ // If true, the resulting object is deep-frozen after being cloned.
+ bool deepFreeze;
+};
+
+JSObject* CreateGlobalObject(JSContext* cx, const JSClass* clasp,
+ nsIPrincipal* principal,
+ JS::RealmOptions& aOptions);
+
+// Modify the provided compartment options, consistent with |aPrincipal| and
+// with globally-cached values of various preferences.
+//
+// Call this function *before* |aOptions| is used to create the corresponding
+// global object, as not all of the options it sets can be modified on an
+// existing global object. (The type system should make this obvious, because
+// you can't get a *mutable* JS::RealmOptions& from an existing global
+// object.)
+void InitGlobalObjectOptions(JS::RealmOptions& aOptions,
+ bool aIsSystemPrincipal,
+ bool aShouldResistFingerprinting);
+
+// Finish initializing an already-created, not-yet-exposed-to-script global
+// object. This will attach a Components object (if necessary) and call
+// |JS_FireOnNewGlobalObject| (if necessary).
+//
+// If you must modify compartment options, see InitGlobalObjectOptions above.
+bool InitGlobalObject(JSContext* aJSContext, JS::Handle<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, bool enforceFilenameRestrictions,
+ JS::MutableHandleValue rval);
+
+// Helper for retrieving metadata stored in a reserved slot. The metadata
+// is set during the sandbox creation using the "metadata" option.
+nsresult GetSandboxMetadata(JSContext* cx, JS::HandleObject sandboxArg,
+ JS::MutableHandleValue rval);
+
+nsresult SetSandboxMetadata(JSContext* cx, JS::HandleObject sandboxArg,
+ JS::HandleValue metadata);
+
+bool CreateObjectIn(JSContext* cx, JS::HandleValue vobj,
+ CreateObjectInOptions& options,
+ JS::MutableHandleValue rval);
+
+bool EvalInWindow(JSContext* cx, const nsAString& source,
+ JS::HandleObject scope, JS::MutableHandleValue rval);
+
+bool ExportFunction(JSContext* cx, JS::HandleValue vscope,
+ JS::HandleValue vfunction, JS::HandleValue voptions,
+ JS::MutableHandleValue rval);
+
+bool CloneInto(JSContext* cx, JS::HandleValue vobj, JS::HandleValue vscope,
+ JS::HandleValue voptions, JS::MutableHandleValue rval);
+
+bool StackScopedClone(JSContext* cx, StackScopedCloneOptions& options,
+ JS::HandleObject sourceScope, JS::MutableHandleValue val);
+
+} /* namespace xpc */
+
+/***************************************************************************/
+// Inlined utilities.
+
+inline bool xpc_ForcePropertyResolve(JSContext* cx, JS::HandleObject obj,
+ jsid id);
+
+inline jsid GetJSIDByIndex(JSContext* cx, unsigned index);
+
+namespace xpc {
+
+enum WrapperDenialType {
+ WrapperDenialForXray = 0,
+ WrapperDenialForCOW,
+ WrapperDenialTypeCount
+};
+bool ReportWrapperDenial(JSContext* cx, JS::HandleId id, WrapperDenialType type,
+ const char* reason);
+
+class CompartmentOriginInfo {
+ public:
+ CompartmentOriginInfo(const CompartmentOriginInfo&) = delete;
+
+ CompartmentOriginInfo(mozilla::BasePrincipal* aOrigin,
+ const mozilla::SiteIdentifier& aSite)
+ : mOrigin(aOrigin), mSite(aSite) {
+ MOZ_ASSERT(aOrigin);
+ MOZ_ASSERT(aSite.IsInitialized());
+ }
+
+ bool IsSameOrigin(nsIPrincipal* aOther) const;
+
+ // Does the principal of compartment a subsume the principal of compartment b?
+ static bool Subsumes(JS::Compartment* aCompA, JS::Compartment* aCompB);
+ static bool SubsumesIgnoringFPD(JS::Compartment* aCompA,
+ JS::Compartment* aCompB);
+
+ bool MightBeWebContent() const;
+
+ // Note: this principal must not be used for subsumes/equality checks
+ // considering document.domain. See mOrigin.
+ mozilla::BasePrincipal* GetPrincipalIgnoringDocumentDomain() const {
+ return mOrigin;
+ }
+
+ const mozilla::SiteIdentifier& SiteRef() const { return mSite; }
+
+ bool HasChangedDocumentDomain() const { return mChangedDocumentDomain; }
+ void SetChangedDocumentDomain() { mChangedDocumentDomain = true; }
+
+ private:
+ // All globals in the compartment must have this origin. Note that
+ // individual globals and principals can have their domain changed via
+ // document.domain, so this principal must not be used for things like
+ // subsumesConsideringDomain or equalsConsideringDomain. Use the realm's
+ // principal for that.
+ RefPtr<mozilla::BasePrincipal> mOrigin;
+
+ // In addition to the origin we also store the SiteIdentifier. When realms
+ // in different compartments can become same-origin (via document.domain),
+ // these compartments must have equal SiteIdentifiers. (This is derived from
+ // mOrigin but we cache it here for performance reasons.)
+ mozilla::SiteIdentifier mSite;
+
+ // True if any global in this compartment mutated document.domain.
+ bool mChangedDocumentDomain = false;
+};
+
+// The CompartmentPrivate contains XPConnect-specific stuff related to each JS
+// compartment. Since compartments are trust domains, this means mostly
+// information needed to select the right security policy for cross-compartment
+// wrappers.
+class CompartmentPrivate {
+ CompartmentPrivate() = delete;
+ CompartmentPrivate(const CompartmentPrivate&) = delete;
+
+ public:
+ CompartmentPrivate(JS::Compartment* c,
+ mozilla::UniquePtr<XPCWrappedNativeScope> scope,
+ mozilla::BasePrincipal* origin,
+ const mozilla::SiteIdentifier& site);
+
+ ~CompartmentPrivate();
+
+ static CompartmentPrivate* Get(JS::Compartment* compartment) {
+ MOZ_ASSERT(compartment);
+ void* priv = JS_GetCompartmentPrivate(compartment);
+ return static_cast<CompartmentPrivate*>(priv);
+ }
+
+ static CompartmentPrivate* Get(JS::Realm* realm) {
+ MOZ_ASSERT(realm);
+ JS::Compartment* compartment = JS::GetCompartmentForRealm(realm);
+ return Get(compartment);
+ }
+
+ static CompartmentPrivate* Get(JSObject* object) {
+ JS::Compartment* compartment = JS::GetCompartment(object);
+ return Get(compartment);
+ }
+
+ bool CanShareCompartmentWith(nsIPrincipal* principal) {
+ // Only share if we're same-origin with the principal.
+ if (!originInfo.IsSameOrigin(principal)) {
+ return false;
+ }
+
+ // Don't share if we have any weird state set.
+ return !wantXrays && !isWebExtensionContentScript &&
+ !isUAWidgetCompartment && mScope->XBLScopeStateMatches(principal);
+ }
+
+ CompartmentOriginInfo originInfo;
+
+ // Controls whether this compartment gets Xrays to same-origin. This behavior
+ // is deprecated, but is still the default for sandboxes for compatibity
+ // reasons.
+ bool wantXrays;
+
+ // Controls whether this compartment is allowed to waive Xrays to content
+ // that it subsumes. This should generally be true, except in cases where we
+ // want to prevent code from depending on Xray Waivers (which might make it
+ // more portable to other browser architectures).
+ bool allowWaivers;
+
+ // This compartment corresponds to a WebExtension content script, and
+ // receives various bits of special compatibility behavior.
+ bool isWebExtensionContentScript;
+
+ // True if this compartment is a UA widget compartment.
+ bool isUAWidgetCompartment;
+
+ // See CompartmentHasExclusiveExpandos.
+ bool hasExclusiveExpandos;
+
+ // Whether SystemIsBeingShutDown has been called on this compartment.
+ bool wasShutdown;
+
+ JSObject2WrappedJSMap* GetWrappedJSMap() const { return mWrappedJSMap.get(); }
+ void UpdateWeakPointersAfterGC(JSTracer* trc);
+
+ void SystemIsBeingShutDown();
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
+
+ struct MapEntryGCPolicy {
+ static bool traceWeak(JSTracer* trc, const void* /* unused */,
+ JS::Heap<JSObject*>* value) {
+ return JS::GCPolicy<JS::Heap<JSObject*>>::traceWeak(trc, value);
+ }
+ };
+
+ typedef JS::GCHashMap<const void*, JS::Heap<JSObject*>,
+ mozilla::PointerHasher<const void*>,
+ js::SystemAllocPolicy, MapEntryGCPolicy>
+ RemoteProxyMap;
+ RemoteProxyMap& GetRemoteProxyMap() { return mRemoteProxies; }
+
+ XPCWrappedNativeScope* GetScope() { return mScope.get(); }
+
+ private:
+ mozilla::UniquePtr<JSObject2WrappedJSMap> mWrappedJSMap;
+
+ // Cache holding proxy objects for Window objects (and their Location object)
+ // that are loaded in a different process.
+ RemoteProxyMap mRemoteProxies;
+
+ // Our XPCWrappedNativeScope.
+ mozilla::UniquePtr<XPCWrappedNativeScope> mScope;
+};
+
+inline void CrashIfNotInAutomation() { MOZ_RELEASE_ASSERT(IsInAutomation()); }
+
+// XPConnect-specific data associated with each JavaScript realm. Per-Window
+// settings live here; security-wrapper-related settings live in the
+// CompartmentPrivate.
+//
+// Following the ECMAScript spec, a realm contains a global (e.g. an inner
+// Window) and its associated scripts and objects; a compartment may contain
+// several same-origin realms.
+class RealmPrivate {
+ RealmPrivate() = delete;
+ RealmPrivate(const RealmPrivate&) = delete;
+
+ public:
+ enum LocationHint { LocationHintRegular, LocationHintAddon };
+
+ explicit RealmPrivate(JS::Realm* realm);
+
+ // Creates the RealmPrivate and CompartmentPrivate (if needed) for a new
+ // global.
+ static void Init(JS::HandleObject aGlobal,
+ const mozilla::SiteIdentifier& aSite);
+
+ static RealmPrivate* Get(JS::Realm* realm) {
+ MOZ_ASSERT(realm);
+ void* priv = JS::GetRealmPrivate(realm);
+ return static_cast<RealmPrivate*>(priv);
+ }
+
+ // Get the RealmPrivate for a given object. `object` must not be a
+ // cross-compartment wrapper, as CCWs aren't dedicated to a particular
+ // realm.
+ static RealmPrivate* Get(JSObject* object) {
+ JS::Realm* realm = JS::GetObjectRealmOrNull(object);
+ return Get(realm);
+ }
+
+ // The scriptability of this realm.
+ Scriptability scriptability;
+
+ // Whether we've emitted a warning about a property that was filtered out
+ // by a security wrapper. See XrayWrapper.cpp.
+ bool wrapperDenialWarnings[WrapperDenialTypeCount];
+
+ const nsACString& GetLocation() {
+ if (location.IsEmpty() && locationURI) {
+ nsCOMPtr<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 = "<JS-implemented nsIURI location>"_ns;
+ } else if (NS_FAILED(locationURI->GetSpec(location))) {
+ location = "<unknown location>"_ns;
+ }
+ }
+ return location;
+ }
+ bool GetLocationURI(LocationHint aLocationHint, nsIURI** aURI) {
+ if (locationURI) {
+ nsCOMPtr<nsIURI> rval = locationURI;
+ rval.forget(aURI);
+ return true;
+ }
+ return TryParseLocationURI(aLocationHint, aURI);
+ }
+ bool GetLocationURI(nsIURI** aURI) {
+ return GetLocationURI(LocationHintRegular, aURI);
+ }
+
+ void SetLocation(const nsACString& aLocation) {
+ if (aLocation.IsEmpty()) {
+ return;
+ }
+ if (!location.IsEmpty() || locationURI) {
+ return;
+ }
+ location = aLocation;
+ }
+ void SetLocationURI(nsIURI* aLocationURI) {
+ if (!aLocationURI) {
+ return;
+ }
+ if (locationURI) {
+ return;
+ }
+ locationURI = aLocationURI;
+ }
+
+ // JSStackFrames are tracked on a per-realm basis so they
+ // can be cleared when the associated window goes away.
+ void RegisterStackFrame(JSStackFrameBase* aFrame);
+ void UnregisterStackFrame(JSStackFrameBase* aFrame);
+ void NukeJSStackFrames();
+
+ private:
+ nsCString location;
+ nsCOMPtr<nsIURI> locationURI;
+
+ bool TryParseLocationURI(LocationHint aType, nsIURI** aURI);
+
+ nsTHashtable<nsPtrHashKey<JSStackFrameBase>> mJSStackFrames;
+};
+
+inline XPCWrappedNativeScope* ObjectScope(JSObject* obj) {
+ return CompartmentPrivate::Get(obj)->GetScope();
+}
+
+JSObject* NewOutObject(JSContext* cx);
+bool IsOutObject(JSContext* cx, JSObject* obj);
+
+nsresult HasInstance(JSContext* cx, JS::HandleObject objArg, const nsID* iid,
+ bool* bp);
+
+// Returns the principal associated with |obj|'s realm. The object must not be a
+// cross-compartment wrapper.
+nsIPrincipal* GetObjectPrincipal(JSObject* obj);
+
+// Attempt to clean up the passed in value pointer. The pointer `value` must be
+// a pointer to a value described by the type `nsXPTType`.
+//
+// This method expects a value of the following types:
+// TD_NSIDPTR
+// value : nsID* (free)
+// TD_ASTRING, TD_CSTRING, TD_UTF8STRING
+// value : ns[C]String* (truncate)
+// TD_PSTRING, TD_PWSTRING, TD_PSTRING_SIZE_IS, TD_PWSTRING_SIZE_IS
+// value : char[16_t]** (free)
+// TD_INTERFACE_TYPE, TD_INTERFACE_IS_TYPE
+// value : nsISupports** (release)
+// TD_LEGACY_ARRAY (NOTE: aArrayLen should be passed)
+// value : void** (destroy elements & free)
+// TD_ARRAY
+// value : nsTArray<T>* (destroy elements & Clear)
+// TD_DOMOBJECT
+// value : T** (cleanup)
+// TD_PROMISE
+// value : dom::Promise** (release)
+//
+// Other types are ignored.
+inline void CleanupValue(const nsXPTType& aType, void* aValue,
+ uint32_t aArrayLen = 0);
+
+// Out-of-line internals for xpc::CleanupValue. Defined in XPCConvert.cpp.
+void InnerCleanupValue(const nsXPTType& aType, void* aValue,
+ uint32_t aArrayLen);
+
+// In order to be able to safely call CleanupValue on a generated value, the
+// data behind it needs to be initialized to a safe value. This method handles
+// initializing the backing data to a safe value to use as an argument to
+// XPCConvert methods, or xpc::CleanupValue.
+//
+// The pointer `aValue` must point to a block of memory at least aType.Stride()
+// bytes large, and correctly aligned.
+//
+// This method accepts the same types as xpc::CleanupValue.
+void InitializeValue(const nsXPTType& aType, void* aValue);
+
+// If a value was initialized with InitializeValue, it should be destroyed with
+// DestructValue. This method acts like CleanupValue, except that destructors
+// for complex types are also invoked, leaving them in an invalid state.
+//
+// This method should be called when destroying types initialized with
+// InitializeValue.
+//
+// The pointer 'aValue' must point to a valid value of type 'aType'.
+void DestructValue(const nsXPTType& aType, void* aValue,
+ uint32_t aArrayLen = 0);
+
+bool SandboxCreateCrypto(JSContext* cx, JS::Handle<JSObject*> obj);
+bool SandboxCreateFetch(JSContext* cx, JS::Handle<JSObject*> obj);
+bool SandboxCreateStructuredClone(JSContext* cx, JS::Handle<JSObject*> obj);
+
+} // namespace xpc
+
+namespace mozilla {
+namespace dom {
+extern bool DefineStaticJSVals(JSContext* cx);
+} // namespace dom
+} // namespace mozilla
+
+bool xpc_LocalizeRuntime(JSRuntime* rt);
+void xpc_DelocalizeRuntime(JSRuntime* rt);
+
+/***************************************************************************/
+// Inlines use the above - include last.
+
+#include "XPCInlines.h"
+
+/***************************************************************************/
+
+#endif /* xpcprivate_h___ */