XPCCallContext.cpp (5616B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* Call context. */ 8 9 #include "xpcprivate.h" 10 #include "jsfriendapi.h" 11 #include "js/Object.h" // JS::GetClass, JS::GetReservedSlot 12 #include "js/Wrapper.h" 13 #include "nsContentUtils.h" 14 15 using namespace mozilla; 16 using namespace xpc; 17 using namespace JS; 18 19 static inline bool IsTearoffClass(const JSClass* clazz) { 20 return clazz == &XPC_WN_Tearoff_JSClass; 21 } 22 23 XPCCallContext::XPCCallContext( 24 JSContext* cx, HandleObject obj /* = nullptr */, 25 HandleObject funobj /* = nullptr */, 26 HandleId name /* = JSID_VOID */, unsigned argc /* = NO_ARGS */, 27 Value* argv /* = nullptr */, Value* rval /* = nullptr */) 28 : mState(INIT_FAILED), 29 mXPC(nsXPConnect::XPConnect()), 30 mXPCJSContext(nullptr), 31 mJSContext(cx), 32 mWrapper(nullptr), 33 mTearOff(nullptr), 34 mMember(nullptr), 35 mName(cx), 36 mStaticMemberIsLocal(false), 37 mArgc(0), 38 mArgv(nullptr), 39 mRetVal(nullptr) { 40 MOZ_ASSERT(cx); 41 MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext()); 42 43 if (!mXPC) { 44 return; 45 } 46 47 mXPCJSContext = XPCJSContext::Get(); 48 49 // hook into call context chain. 50 mPrevCallContext = mXPCJSContext->SetCallContext(this); 51 52 mState = HAVE_CONTEXT; 53 54 if (!obj) { 55 return; 56 } 57 58 mMethodIndex = 0xDEAD; 59 60 mState = HAVE_OBJECT; 61 62 mTearOff = nullptr; 63 64 JSObject* unwrapped = 65 js::CheckedUnwrapDynamic(obj, cx, /* stopAtWindowProxy = */ false); 66 if (!unwrapped) { 67 JS_ReportErrorASCII(mJSContext, 68 "Permission denied to call method on |this|"); 69 mState = INIT_FAILED; 70 return; 71 } 72 const JSClass* clasp = JS::GetClass(unwrapped); 73 if (clasp->isWrappedNative()) { 74 mWrapper = XPCWrappedNative::Get(unwrapped); 75 } else if (IsTearoffClass(clasp)) { 76 mTearOff = XPCWrappedNativeTearOff::Get(unwrapped); 77 mWrapper = XPCWrappedNative::Get( 78 &JS::GetReservedSlot(unwrapped, XPCWrappedNativeTearOff::FlatObjectSlot) 79 .toObject()); 80 } 81 if (mWrapper && !mTearOff) { 82 mScriptable = mWrapper->GetScriptable(); 83 } 84 85 if (!name.isVoid()) { 86 SetName(name); 87 } 88 89 if (argc != NO_ARGS) { 90 SetArgsAndResultPtr(argc, argv, rval); 91 } 92 93 CHECK_STATE(HAVE_OBJECT); 94 } 95 96 void XPCCallContext::SetName(jsid name) { 97 CHECK_STATE(HAVE_OBJECT); 98 99 mName = name; 100 101 if (mTearOff) { 102 mSet = nullptr; 103 mInterface = mTearOff->GetInterface(); 104 mMember = mInterface->FindMember(mName); 105 mStaticMemberIsLocal = true; 106 if (mMember && !mMember->IsConstant()) { 107 mMethodIndex = mMember->GetIndex(); 108 } 109 } else { 110 mSet = mWrapper ? mWrapper->GetSet() : nullptr; 111 112 if (mSet && 113 mSet->FindMember( 114 mName, &mMember, &mInterface, 115 mWrapper->HasProto() ? mWrapper->GetProto()->GetSet() : nullptr, 116 &mStaticMemberIsLocal)) { 117 if (mMember && !mMember->IsConstant()) { 118 mMethodIndex = mMember->GetIndex(); 119 } 120 } else { 121 mMember = nullptr; 122 mInterface = nullptr; 123 mStaticMemberIsLocal = false; 124 } 125 } 126 127 mState = HAVE_NAME; 128 } 129 130 void XPCCallContext::SetCallInfo(XPCNativeInterface* iface, 131 XPCNativeMember* member, bool isSetter) { 132 CHECK_STATE(HAVE_CONTEXT); 133 134 // We are going straight to the method info and need not do a lookup 135 // by id. 136 137 // don't be tricked if method is called with wrong 'this' 138 if (mTearOff && mTearOff->GetInterface() != iface) { 139 mTearOff = nullptr; 140 } 141 142 mSet = nullptr; 143 mInterface = iface; 144 mMember = member; 145 mMethodIndex = mMember->GetIndex() + (isSetter ? 1 : 0); 146 mName = mMember->GetName(); 147 148 if (mState < HAVE_NAME) { 149 mState = HAVE_NAME; 150 } 151 } 152 153 void XPCCallContext::SetArgsAndResultPtr(unsigned argc, Value* argv, 154 Value* rval) { 155 CHECK_STATE(HAVE_OBJECT); 156 157 if (mState < HAVE_NAME) { 158 mSet = nullptr; 159 mInterface = nullptr; 160 mMember = nullptr; 161 mStaticMemberIsLocal = false; 162 } 163 164 mArgc = argc; 165 mArgv = argv; 166 mRetVal = rval; 167 168 mState = HAVE_ARGS; 169 } 170 171 nsresult XPCCallContext::CanCallNow() { 172 nsresult rv; 173 174 if (!HasInterfaceAndMember()) { 175 return NS_ERROR_UNEXPECTED; 176 } 177 if (mState < HAVE_ARGS) { 178 return NS_ERROR_UNEXPECTED; 179 } 180 181 if (!mTearOff) { 182 mTearOff = mWrapper->FindTearOff(mJSContext, mInterface, false, &rv); 183 if (!mTearOff || mTearOff->GetInterface() != mInterface) { 184 mTearOff = nullptr; 185 return NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED; 186 } 187 } 188 189 // Refresh in case FindTearOff extended the set 190 mSet = mWrapper->GetSet(); 191 192 mState = READY_TO_CALL; 193 return NS_OK; 194 } 195 196 void XPCCallContext::SystemIsBeingShutDown() { 197 // XXX This is pretty questionable since the per thread cleanup stuff 198 // can be making this call on one thread for call contexts on another 199 // thread. 200 NS_WARNING( 201 "Shutting Down XPConnect even through there is a live XPCCallContext"); 202 mXPCJSContext = nullptr; 203 mState = SYSTEM_SHUTDOWN; 204 mSet = nullptr; 205 mInterface = nullptr; 206 207 if (mPrevCallContext) { 208 mPrevCallContext->SystemIsBeingShutDown(); 209 } 210 } 211 212 XPCCallContext::~XPCCallContext() { 213 if (mXPCJSContext) { 214 DebugOnly<XPCCallContext*> old = 215 mXPCJSContext->SetCallContext(mPrevCallContext); 216 MOZ_ASSERT(old == this, "bad pop from per thread data"); 217 } 218 }