/* -*- 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/. */ /* Call context. */ #include "xpcprivate.h" #include "jsfriendapi.h" #include "js/Object.h" // JS::GetClass, JS::GetReservedSlot #include "js/Wrapper.h" #include "nsContentUtils.h" using namespace mozilla; using namespace xpc; using namespace JS; static inline bool IsTearoffClass(const JSClass* clazz) { return clazz == &XPC_WN_Tearoff_JSClass; } XPCCallContext::XPCCallContext( JSContext* cx, HandleObject obj /* = nullptr */, HandleObject funobj /* = nullptr */, HandleId name /* = JSID_VOID */, unsigned argc /* = NO_ARGS */, Value* argv /* = nullptr */, Value* rval /* = nullptr */) : mState(INIT_FAILED), mXPC(nsXPConnect::XPConnect()), mXPCJSContext(nullptr), mJSContext(cx), mWrapper(nullptr), mTearOff(nullptr), mMember(nullptr), mName(cx), mStaticMemberIsLocal(false), mArgc(0), mArgv(nullptr), mRetVal(nullptr) { MOZ_ASSERT(cx); MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext()); if (!mXPC) { return; } mXPCJSContext = XPCJSContext::Get(); // hook into call context chain. mPrevCallContext = mXPCJSContext->SetCallContext(this); mState = HAVE_CONTEXT; if (!obj) { return; } mMethodIndex = 0xDEAD; mState = HAVE_OBJECT; mTearOff = nullptr; JSObject* unwrapped = js::CheckedUnwrapDynamic(obj, cx, /* stopAtWindowProxy = */ false); if (!unwrapped) { JS_ReportErrorASCII(mJSContext, "Permission denied to call method on |this|"); mState = INIT_FAILED; return; } const JSClass* clasp = JS::GetClass(unwrapped); if (clasp->isWrappedNative()) { mWrapper = XPCWrappedNative::Get(unwrapped); } else if (IsTearoffClass(clasp)) { mTearOff = XPCWrappedNativeTearOff::Get(unwrapped); mWrapper = XPCWrappedNative::Get( &JS::GetReservedSlot(unwrapped, XPCWrappedNativeTearOff::FlatObjectSlot) .toObject()); } if (mWrapper && !mTearOff) { mScriptable = mWrapper->GetScriptable(); } if (!name.isVoid()) { SetName(name); } if (argc != NO_ARGS) { SetArgsAndResultPtr(argc, argv, rval); } CHECK_STATE(HAVE_OBJECT); } void XPCCallContext::SetName(jsid name) { CHECK_STATE(HAVE_OBJECT); mName = name; if (mTearOff) { mSet = nullptr; mInterface = mTearOff->GetInterface(); mMember = mInterface->FindMember(mName); mStaticMemberIsLocal = true; if (mMember && !mMember->IsConstant()) { mMethodIndex = mMember->GetIndex(); } } else { mSet = mWrapper ? mWrapper->GetSet() : nullptr; if (mSet && mSet->FindMember( mName, &mMember, &mInterface, mWrapper->HasProto() ? mWrapper->GetProto()->GetSet() : nullptr, &mStaticMemberIsLocal)) { if (mMember && !mMember->IsConstant()) { mMethodIndex = mMember->GetIndex(); } } else { mMember = nullptr; mInterface = nullptr; mStaticMemberIsLocal = false; } } mState = HAVE_NAME; } void XPCCallContext::SetCallInfo(XPCNativeInterface* iface, XPCNativeMember* member, bool isSetter) { CHECK_STATE(HAVE_CONTEXT); // We are going straight to the method info and need not do a lookup // by id. // don't be tricked if method is called with wrong 'this' if (mTearOff && mTearOff->GetInterface() != iface) { mTearOff = nullptr; } mSet = nullptr; mInterface = iface; mMember = member; mMethodIndex = mMember->GetIndex() + (isSetter ? 1 : 0); mName = mMember->GetName(); if (mState < HAVE_NAME) { mState = HAVE_NAME; } } void XPCCallContext::SetArgsAndResultPtr(unsigned argc, Value* argv, Value* rval) { CHECK_STATE(HAVE_OBJECT); if (mState < HAVE_NAME) { mSet = nullptr; mInterface = nullptr; mMember = nullptr; mStaticMemberIsLocal = false; } mArgc = argc; mArgv = argv; mRetVal = rval; mState = HAVE_ARGS; } nsresult XPCCallContext::CanCallNow() { nsresult rv; if (!HasInterfaceAndMember()) { return NS_ERROR_UNEXPECTED; } if (mState < HAVE_ARGS) { return NS_ERROR_UNEXPECTED; } if (!mTearOff) { mTearOff = mWrapper->FindTearOff(mJSContext, mInterface, false, &rv); if (!mTearOff || mTearOff->GetInterface() != mInterface) { mTearOff = nullptr; return NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED; } } // Refresh in case FindTearOff extended the set mSet = mWrapper->GetSet(); mState = READY_TO_CALL; return NS_OK; } void XPCCallContext::SystemIsBeingShutDown() { // XXX This is pretty questionable since the per thread cleanup stuff // can be making this call on one thread for call contexts on another // thread. NS_WARNING( "Shutting Down XPConnect even through there is a live XPCCallContext"); mXPCJSContext = nullptr; mState = SYSTEM_SHUTDOWN; mSet = nullptr; mInterface = nullptr; if (mPrevCallContext) { mPrevCallContext->SystemIsBeingShutDown(); } } XPCCallContext::~XPCCallContext() { if (mXPCJSContext) { DebugOnly old = mXPCJSContext->SetCallContext(mPrevCallContext); MOZ_ASSERT(old == this, "bad pop from per thread data"); } }