mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
552 lines
16 KiB
C++
552 lines
16 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
*
|
|
* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is Mozilla Communicator client code, released
|
|
* March 31, 1998.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Netscape Communications Corporation.
|
|
* Portions created by the Initial Developer are Copyright (C) 1998
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* John Bandhauer <jband@netscape.com> (original author)
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
|
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
/* Call context. */
|
|
|
|
#include "xpcprivate.h"
|
|
|
|
XPCCallContext::XPCCallContext(XPCContext::LangType callerLanguage,
|
|
JSContext* cx /* = nsnull */,
|
|
JSObject* obj /* = nsnull */,
|
|
JSObject* funobj /* = nsnull */,
|
|
jsid name /* = JSID_VOID */,
|
|
uintN argc /* = NO_ARGS */,
|
|
jsval *argv /* = nsnull */,
|
|
jsval *rval /* = nsnull */)
|
|
: mState(INIT_FAILED),
|
|
mXPC(nsXPConnect::GetXPConnect()),
|
|
mThreadData(nsnull),
|
|
mXPCContext(nsnull),
|
|
mJSContext(cx),
|
|
mContextPopRequired(false),
|
|
mDestroyJSContextInDestructor(false),
|
|
mCallerLanguage(callerLanguage)
|
|
{
|
|
Init(callerLanguage, callerLanguage == NATIVE_CALLER, obj, funobj,
|
|
INIT_SHOULD_LOOKUP_WRAPPER, name, argc, argv, rval);
|
|
}
|
|
|
|
XPCCallContext::XPCCallContext(XPCContext::LangType callerLanguage,
|
|
JSContext* cx,
|
|
JSBool callBeginRequest,
|
|
JSObject* obj,
|
|
JSObject* flattenedJSObject,
|
|
XPCWrappedNative* wrapper,
|
|
XPCWrappedNativeTearOff* tearOff)
|
|
: mState(INIT_FAILED),
|
|
mXPC(nsXPConnect::GetXPConnect()),
|
|
mThreadData(nsnull),
|
|
mXPCContext(nsnull),
|
|
mJSContext(cx),
|
|
mContextPopRequired(false),
|
|
mDestroyJSContextInDestructor(false),
|
|
mCallerLanguage(callerLanguage),
|
|
mFlattenedJSObject(flattenedJSObject),
|
|
mWrapper(wrapper),
|
|
mTearOff(tearOff)
|
|
{
|
|
Init(callerLanguage, callBeginRequest, obj, nsnull,
|
|
WRAPPER_PASSED_TO_CONSTRUCTOR, JSID_VOID, NO_ARGS,
|
|
nsnull, nsnull);
|
|
}
|
|
|
|
void
|
|
XPCCallContext::Init(XPCContext::LangType callerLanguage,
|
|
JSBool callBeginRequest,
|
|
JSObject* obj,
|
|
JSObject* funobj,
|
|
WrapperInitOptions wrapperInitOptions,
|
|
jsid name,
|
|
uintN argc,
|
|
jsval *argv,
|
|
jsval *rval)
|
|
{
|
|
if (!mXPC)
|
|
return;
|
|
|
|
mThreadData = XPCPerThreadData::GetData(mJSContext);
|
|
|
|
if (!mThreadData)
|
|
return;
|
|
|
|
XPCJSContextStack* stack = mThreadData->GetJSContextStack();
|
|
JSContext* topJSContext;
|
|
|
|
if (!stack || NS_FAILED(stack->Peek(&topJSContext))) {
|
|
// If we don't have a stack we're probably in shutdown.
|
|
NS_ASSERTION(!stack, "Bad, Peek failed!");
|
|
mJSContext = nsnull;
|
|
return;
|
|
}
|
|
|
|
if (!mJSContext) {
|
|
// This is slightly questionable. If called without an explicit
|
|
// JSContext (generally a call to a wrappedJS) we will use the JSContext
|
|
// on the top of the JSContext stack - if there is one - *before*
|
|
// falling back on the safe JSContext.
|
|
// This is good AND bad because it makes calls from JS -> native -> JS
|
|
// have JS stack 'continuity' for purposes of stack traces etc.
|
|
// Note: this *is* what the pre-XPCCallContext xpconnect did too.
|
|
|
|
if (topJSContext)
|
|
mJSContext = topJSContext;
|
|
else if (NS_FAILED(stack->GetSafeJSContext(&mJSContext)) || !mJSContext)
|
|
return;
|
|
}
|
|
|
|
if (topJSContext != mJSContext) {
|
|
if (NS_FAILED(stack->Push(mJSContext))) {
|
|
NS_ERROR("bad!");
|
|
return;
|
|
}
|
|
mContextPopRequired = true;
|
|
}
|
|
|
|
// Get into the request as early as we can to avoid problems with scanning
|
|
// callcontexts on other threads from within the gc callbacks.
|
|
|
|
NS_ASSERTION(!callBeginRequest || mCallerLanguage == NATIVE_CALLER,
|
|
"Don't call JS_BeginRequest unless the caller is native.");
|
|
if (callBeginRequest)
|
|
JS_BeginRequest(mJSContext);
|
|
|
|
mXPCContext = XPCContext::GetXPCContext(mJSContext);
|
|
mPrevCallerLanguage = mXPCContext->SetCallingLangType(mCallerLanguage);
|
|
|
|
// hook into call context chain for our thread
|
|
mPrevCallContext = mThreadData->SetCallContext(this);
|
|
|
|
// We only need to addref xpconnect once so only do it if this is the first
|
|
// context in the chain.
|
|
if (!mPrevCallContext)
|
|
NS_ADDREF(mXPC);
|
|
|
|
mState = HAVE_CONTEXT;
|
|
|
|
if (!obj)
|
|
return;
|
|
|
|
mScopeForNewJSObjects = obj;
|
|
|
|
mState = HAVE_SCOPE;
|
|
|
|
mMethodIndex = 0xDEAD;
|
|
|
|
mState = HAVE_OBJECT;
|
|
|
|
mTearOff = nsnull;
|
|
if (wrapperInitOptions == INIT_SHOULD_LOOKUP_WRAPPER) {
|
|
mWrapper = XPCWrappedNative::GetWrappedNativeOfJSObject(mJSContext, obj,
|
|
funobj,
|
|
&mFlattenedJSObject,
|
|
&mTearOff);
|
|
if (mWrapper) {
|
|
DEBUG_CheckWrapperThreadSafety(mWrapper);
|
|
|
|
mFlattenedJSObject = mWrapper->GetFlatJSObject();
|
|
|
|
if (mTearOff)
|
|
mScriptableInfo = nsnull;
|
|
else
|
|
mScriptableInfo = mWrapper->GetScriptableInfo();
|
|
} else {
|
|
NS_ABORT_IF_FALSE(!mFlattenedJSObject || IS_SLIM_WRAPPER(mFlattenedJSObject),
|
|
"should have a slim wrapper");
|
|
}
|
|
}
|
|
|
|
if (!JSID_IS_VOID(name))
|
|
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 = nsnull;
|
|
mInterface = mTearOff->GetInterface();
|
|
mMember = mInterface->FindMember(name);
|
|
mStaticMemberIsLocal = true;
|
|
if (mMember && !mMember->IsConstant())
|
|
mMethodIndex = mMember->GetIndex();
|
|
} else {
|
|
mSet = mWrapper ? mWrapper->GetSet() : nsnull;
|
|
|
|
if (mSet &&
|
|
mSet->FindMember(name, &mMember, &mInterface,
|
|
mWrapper->HasProto() ?
|
|
mWrapper->GetProto()->GetSet() :
|
|
nsnull,
|
|
&mStaticMemberIsLocal)) {
|
|
if (mMember && !mMember->IsConstant())
|
|
mMethodIndex = mMember->GetIndex();
|
|
} else {
|
|
mMember = nsnull;
|
|
mInterface = nsnull;
|
|
mStaticMemberIsLocal = false;
|
|
}
|
|
}
|
|
|
|
mState = HAVE_NAME;
|
|
}
|
|
|
|
void
|
|
XPCCallContext::SetCallInfo(XPCNativeInterface* iface, XPCNativeMember* member,
|
|
JSBool 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 = nsnull;
|
|
|
|
mSet = nsnull;
|
|
mInterface = iface;
|
|
mMember = member;
|
|
mMethodIndex = mMember->GetIndex() + (isSetter ? 1 : 0);
|
|
mName = mMember->GetName();
|
|
|
|
if (mState < HAVE_NAME)
|
|
mState = HAVE_NAME;
|
|
}
|
|
|
|
void
|
|
XPCCallContext::SetArgsAndResultPtr(uintN argc,
|
|
jsval *argv,
|
|
jsval *rval)
|
|
{
|
|
CHECK_STATE(HAVE_OBJECT);
|
|
|
|
if (mState < HAVE_NAME) {
|
|
mSet = nsnull;
|
|
mInterface = nsnull;
|
|
mMember = nsnull;
|
|
mStaticMemberIsLocal = false;
|
|
}
|
|
|
|
mArgc = argc;
|
|
mArgv = argv;
|
|
mRetVal = rval;
|
|
|
|
mReturnValueWasSet = false;
|
|
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(*this, mInterface, false, &rv);
|
|
if (!mTearOff || mTearOff->GetInterface() != mInterface) {
|
|
mTearOff = nsnull;
|
|
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");
|
|
mThreadData = nsnull;
|
|
mXPCContext = nsnull;
|
|
mState = SYSTEM_SHUTDOWN;
|
|
if (mPrevCallContext)
|
|
mPrevCallContext->SystemIsBeingShutDown();
|
|
}
|
|
|
|
XPCCallContext::~XPCCallContext()
|
|
{
|
|
// do cleanup...
|
|
|
|
bool shouldReleaseXPC = false;
|
|
|
|
if (mXPCContext) {
|
|
mXPCContext->SetCallingLangType(mPrevCallerLanguage);
|
|
|
|
#ifdef DEBUG
|
|
XPCCallContext* old = mThreadData->SetCallContext(mPrevCallContext);
|
|
NS_ASSERTION(old == this, "bad pop from per thread data");
|
|
#else
|
|
(void) mThreadData->SetCallContext(mPrevCallContext);
|
|
#endif
|
|
|
|
shouldReleaseXPC = mPrevCallContext == nsnull;
|
|
}
|
|
|
|
// NB: Needs to happen before the context stack pop.
|
|
if (mJSContext && mCallerLanguage == NATIVE_CALLER)
|
|
JS_EndRequest(mJSContext);
|
|
|
|
if (mContextPopRequired) {
|
|
XPCJSContextStack* stack = mThreadData->GetJSContextStack();
|
|
NS_ASSERTION(stack, "bad!");
|
|
if (stack) {
|
|
#ifdef DEBUG
|
|
JSContext* poppedCX;
|
|
nsresult rv = stack->Pop(&poppedCX);
|
|
NS_ASSERTION(NS_SUCCEEDED(rv) && poppedCX == mJSContext, "bad pop");
|
|
#else
|
|
(void) stack->Pop(nsnull);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (mJSContext) {
|
|
if (mDestroyJSContextInDestructor) {
|
|
#ifdef DEBUG_xpc_hacker
|
|
printf("!xpc - doing deferred destruction of JSContext @ %p\n",
|
|
mJSContext);
|
|
#endif
|
|
NS_ASSERTION(!mThreadData->GetJSContextStack() ||
|
|
!mThreadData->GetJSContextStack()->
|
|
DEBUG_StackHasJSContext(mJSContext),
|
|
"JSContext still in threadjscontextstack!");
|
|
|
|
JS_DestroyContext(mJSContext);
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
for (PRUint32 i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i) {
|
|
NS_ASSERTION(!mScratchStrings[i].mInUse, "Uh, string wrapper still in use!");
|
|
}
|
|
#endif
|
|
|
|
if (shouldReleaseXPC && mXPC)
|
|
NS_RELEASE(mXPC);
|
|
}
|
|
|
|
XPCReadableJSStringWrapper *
|
|
XPCCallContext::NewStringWrapper(const PRUnichar *str, PRUint32 len)
|
|
{
|
|
for (PRUint32 i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i) {
|
|
StringWrapperEntry& ent = mScratchStrings[i];
|
|
|
|
if (!ent.mInUse) {
|
|
ent.mInUse = true;
|
|
|
|
// Construct the string using placement new.
|
|
|
|
return new (ent.mString.addr()) XPCReadableJSStringWrapper(str, len);
|
|
}
|
|
}
|
|
|
|
// All our internal string wrappers are used, allocate a new string.
|
|
|
|
return new XPCReadableJSStringWrapper(str, len);
|
|
}
|
|
|
|
void
|
|
XPCCallContext::DeleteString(nsAString *string)
|
|
{
|
|
for (PRUint32 i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i) {
|
|
StringWrapperEntry& ent = mScratchStrings[i];
|
|
if (string == ent.mString.addr()) {
|
|
// One of our internal strings is no longer in use, mark
|
|
// it as such and destroy the string.
|
|
|
|
ent.mInUse = false;
|
|
ent.mString.addr()->~XPCReadableJSStringWrapper();
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
// We're done with a string that's not one of our internal
|
|
// strings, delete it.
|
|
delete string;
|
|
}
|
|
|
|
/* readonly attribute nsISupports Callee; */
|
|
NS_IMETHODIMP
|
|
XPCCallContext::GetCallee(nsISupports * *aCallee)
|
|
{
|
|
nsISupports* temp = mWrapper ? mWrapper->GetIdentityObject() : nsnull;
|
|
NS_IF_ADDREF(temp);
|
|
*aCallee = temp;
|
|
return NS_OK;
|
|
}
|
|
|
|
/* readonly attribute PRUint16 CalleeMethodIndex; */
|
|
NS_IMETHODIMP
|
|
XPCCallContext::GetCalleeMethodIndex(PRUint16 *aCalleeMethodIndex)
|
|
{
|
|
*aCalleeMethodIndex = mMethodIndex;
|
|
return NS_OK;
|
|
}
|
|
|
|
/* readonly attribute nsIXPConnectWrappedNative CalleeWrapper; */
|
|
NS_IMETHODIMP
|
|
XPCCallContext::GetCalleeWrapper(nsIXPConnectWrappedNative * *aCalleeWrapper)
|
|
{
|
|
nsIXPConnectWrappedNative* temp = mWrapper;
|
|
NS_IF_ADDREF(temp);
|
|
*aCalleeWrapper = temp;
|
|
return NS_OK;
|
|
}
|
|
|
|
/* readonly attribute XPCNativeInterface CalleeInterface; */
|
|
NS_IMETHODIMP
|
|
XPCCallContext::GetCalleeInterface(nsIInterfaceInfo * *aCalleeInterface)
|
|
{
|
|
nsIInterfaceInfo* temp = mInterface->GetInterfaceInfo();
|
|
NS_IF_ADDREF(temp);
|
|
*aCalleeInterface = temp;
|
|
return NS_OK;
|
|
}
|
|
|
|
/* readonly attribute nsIClassInfo CalleeClassInfo; */
|
|
NS_IMETHODIMP
|
|
XPCCallContext::GetCalleeClassInfo(nsIClassInfo * *aCalleeClassInfo)
|
|
{
|
|
nsIClassInfo* temp = mWrapper ? mWrapper->GetClassInfo() : nsnull;
|
|
NS_IF_ADDREF(temp);
|
|
*aCalleeClassInfo = temp;
|
|
return NS_OK;
|
|
}
|
|
|
|
/* readonly attribute JSContextPtr JSContext; */
|
|
NS_IMETHODIMP
|
|
XPCCallContext::GetJSContext(JSContext * *aJSContext)
|
|
{
|
|
JS_AbortIfWrongThread(JS_GetRuntime(mJSContext));
|
|
*aJSContext = mJSContext;
|
|
return NS_OK;
|
|
}
|
|
|
|
/* readonly attribute PRUint32 Argc; */
|
|
NS_IMETHODIMP
|
|
XPCCallContext::GetArgc(PRUint32 *aArgc)
|
|
{
|
|
*aArgc = (PRUint32) mArgc;
|
|
return NS_OK;
|
|
}
|
|
|
|
/* readonly attribute JSValPtr ArgvPtr; */
|
|
NS_IMETHODIMP
|
|
XPCCallContext::GetArgvPtr(jsval * *aArgvPtr)
|
|
{
|
|
*aArgvPtr = mArgv;
|
|
return NS_OK;
|
|
}
|
|
|
|
/* readonly attribute JSValPtr RetValPtr; */
|
|
NS_IMETHODIMP
|
|
XPCCallContext::GetRetValPtr(jsval * *aRetValPtr)
|
|
{
|
|
*aRetValPtr = mRetVal;
|
|
return NS_OK;
|
|
}
|
|
|
|
/* attribute bool ReturnValueWasSet; */
|
|
NS_IMETHODIMP
|
|
XPCCallContext::GetReturnValueWasSet(bool *aReturnValueWasSet)
|
|
{
|
|
*aReturnValueWasSet = mReturnValueWasSet;
|
|
return NS_OK;
|
|
}
|
|
NS_IMETHODIMP
|
|
XPCCallContext::SetReturnValueWasSet(bool aReturnValueWasSet)
|
|
{
|
|
mReturnValueWasSet = aReturnValueWasSet;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
XPCCallContext::GetPreviousCallContext(nsAXPCNativeCallContext **aResult)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aResult);
|
|
*aResult = GetPrevCallContext();
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
XPCCallContext::GetLanguage(PRUint16 *aResult)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aResult);
|
|
*aResult = GetCallerLanguage();
|
|
return NS_OK;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
// static
|
|
void
|
|
XPCLazyCallContext::AssertContextIsTopOfStack(JSContext* cx)
|
|
{
|
|
XPCPerThreadData* tls = XPCPerThreadData::GetData(cx);
|
|
XPCJSContextStack* stack = tls->GetJSContextStack();
|
|
|
|
JSContext* topJSContext;
|
|
nsresult rv = stack->Peek(&topJSContext);
|
|
NS_ASSERTION(NS_SUCCEEDED(rv), "XPCJSContextStack::Peek failed");
|
|
|
|
NS_ASSERTION(cx == topJSContext, "wrong context on XPCJSContextStack!");
|
|
}
|
|
#endif
|