mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
487 lines
14 KiB
C++
487 lines
14 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) 1999
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* John Bandhauer <jband@netscape.com> (original author)
|
|
* Mark Hammond <MarkH@ActiveState.com>
|
|
*
|
|
* 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 ***** */
|
|
|
|
/* An implementaion of nsIException. */
|
|
|
|
#include "xpcprivate.h"
|
|
#include "nsNetError.h"
|
|
#include "mozStorage.h"
|
|
#include "nsPluginError.h"
|
|
#include "nsIUnicodeDecoder.h"
|
|
|
|
/***************************************************************************/
|
|
/* Quick and dirty mapping of well known result codes to strings. We only
|
|
* call this when building an exception object, so iterating the short array
|
|
* is not too bad.
|
|
*
|
|
* It sure would be nice to have exceptions declared in idl and available
|
|
* in some more global way at runtime.
|
|
*/
|
|
|
|
static struct ResultMap
|
|
{nsresult rv; const char* name; const char* format;} map[] = {
|
|
#define XPC_MSG_DEF(val, format) \
|
|
{(val), #val, format},
|
|
#include "xpc.msg"
|
|
#undef XPC_MSG_DEF
|
|
{0,0,0} // sentinel to mark end of array
|
|
};
|
|
|
|
#define RESULT_COUNT ((sizeof(map) / sizeof(map[0]))-1)
|
|
|
|
// static
|
|
JSBool
|
|
nsXPCException::NameAndFormatForNSResult(nsresult rv,
|
|
const char** name,
|
|
const char** format)
|
|
{
|
|
|
|
for (ResultMap* p = map; p->name; p++) {
|
|
if (rv == p->rv) {
|
|
if (name) *name = p->name;
|
|
if (format) *format = p->format;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// static
|
|
void*
|
|
nsXPCException::IterateNSResults(nsresult* rv,
|
|
const char** name,
|
|
const char** format,
|
|
void** iterp)
|
|
{
|
|
ResultMap* p = (ResultMap*) *iterp;
|
|
if (!p)
|
|
p = map;
|
|
else
|
|
p++;
|
|
if (!p->name)
|
|
p = nsnull;
|
|
else {
|
|
if (rv)
|
|
*rv = p->rv;
|
|
if (name)
|
|
*name = p->name;
|
|
if (format)
|
|
*format = p->format;
|
|
}
|
|
*iterp = p;
|
|
return p;
|
|
}
|
|
|
|
// static
|
|
PRUint32
|
|
nsXPCException::GetNSResultCount()
|
|
{
|
|
return RESULT_COUNT;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
NS_IMPL_CLASSINFO(nsXPCException, NULL, nsIClassInfo::DOM_OBJECT,
|
|
NS_XPCEXCEPTION_CID)
|
|
NS_INTERFACE_MAP_BEGIN(nsXPCException)
|
|
NS_INTERFACE_MAP_ENTRY(nsIException)
|
|
NS_INTERFACE_MAP_ENTRY(nsIXPCException)
|
|
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIException)
|
|
NS_IMPL_QUERY_CLASSINFO(nsXPCException)
|
|
NS_INTERFACE_MAP_END_THREADSAFE
|
|
|
|
NS_IMPL_THREADSAFE_ADDREF(nsXPCException)
|
|
NS_IMPL_THREADSAFE_RELEASE(nsXPCException)
|
|
|
|
NS_IMPL_CI_INTERFACE_GETTER1(nsXPCException, nsIXPCException)
|
|
|
|
nsXPCException::nsXPCException()
|
|
: mMessage(nsnull),
|
|
mResult(0),
|
|
mName(nsnull),
|
|
mLocation(nsnull),
|
|
mData(nsnull),
|
|
mFilename(nsnull),
|
|
mLineNumber(0),
|
|
mInner(nsnull),
|
|
mInitialized(false)
|
|
{
|
|
MOZ_COUNT_CTOR(nsXPCException);
|
|
}
|
|
|
|
nsXPCException::~nsXPCException()
|
|
{
|
|
MOZ_COUNT_DTOR(nsXPCException);
|
|
Reset();
|
|
}
|
|
|
|
/* [noscript] xpcexJSVal stealJSVal (); */
|
|
NS_IMETHODIMP
|
|
nsXPCException::StealJSVal(jsval *vp NS_OUTPARAM)
|
|
{
|
|
if (mThrownJSVal.IsHeld()) {
|
|
*vp = mThrownJSVal.Release();
|
|
return NS_OK;
|
|
}
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
/* [noscript] void stowJSVal (in xpcexJSContextPtr cx, in xpcexJSVal val); */
|
|
NS_IMETHODIMP
|
|
nsXPCException::StowJSVal(JSContext* cx, jsval v)
|
|
{
|
|
if (mThrownJSVal.Hold(cx)) {
|
|
mThrownJSVal = v;
|
|
return NS_OK;
|
|
}
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
void
|
|
nsXPCException::Reset()
|
|
{
|
|
if (mMessage) {
|
|
nsMemory::Free(mMessage);
|
|
mMessage = nsnull;
|
|
}
|
|
if (mName) {
|
|
nsMemory::Free(mName);
|
|
mName = nsnull;
|
|
}
|
|
if (mFilename) {
|
|
nsMemory::Free(mFilename);
|
|
mFilename = nsnull;
|
|
}
|
|
mLineNumber = (PRUint32)-1;
|
|
NS_IF_RELEASE(mLocation);
|
|
NS_IF_RELEASE(mData);
|
|
NS_IF_RELEASE(mInner);
|
|
}
|
|
|
|
/* readonly attribute string message; */
|
|
NS_IMETHODIMP
|
|
nsXPCException::GetMessageMoz(char * *aMessage)
|
|
{
|
|
if (!mInitialized)
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
XPC_STRING_GETTER_BODY(aMessage, mMessage);
|
|
}
|
|
|
|
/* readonly attribute nsresult result; */
|
|
NS_IMETHODIMP
|
|
nsXPCException::GetResult(nsresult *aResult)
|
|
{
|
|
if (!aResult)
|
|
return NS_ERROR_NULL_POINTER;
|
|
if (!mInitialized)
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
*aResult = mResult;
|
|
return NS_OK;
|
|
}
|
|
|
|
/* readonly attribute string name; */
|
|
NS_IMETHODIMP
|
|
nsXPCException::GetName(char * *aName)
|
|
{
|
|
if (!mInitialized)
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
const char* name = mName;
|
|
if (!name)
|
|
NameAndFormatForNSResult(mResult, &name, nsnull);
|
|
|
|
XPC_STRING_GETTER_BODY(aName, name);
|
|
}
|
|
|
|
/* readonly attribute string filename; */
|
|
NS_IMETHODIMP nsXPCException::GetFilename(char * *aFilename)
|
|
{
|
|
if (!mInitialized)
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
XPC_STRING_GETTER_BODY(aFilename, mFilename);
|
|
}
|
|
|
|
/* readonly attribute PRUint32 lineNumber; */
|
|
NS_IMETHODIMP nsXPCException::GetLineNumber(PRUint32 *aLineNumber)
|
|
{
|
|
if (!aLineNumber)
|
|
return NS_ERROR_NULL_POINTER;
|
|
if (!mInitialized)
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
*aLineNumber = mLineNumber;
|
|
return NS_OK;
|
|
}
|
|
|
|
/* readonly attribute PRUint32 columnNumber; */
|
|
NS_IMETHODIMP nsXPCException::GetColumnNumber(PRUint32 *aColumnNumber)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aColumnNumber);
|
|
if (!mInitialized)
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
*aColumnNumber = 0;
|
|
return NS_OK;
|
|
}
|
|
|
|
/* readonly attribute nsIStackFrame location; */
|
|
NS_IMETHODIMP
|
|
nsXPCException::GetLocation(nsIStackFrame * *aLocation)
|
|
{
|
|
if (!aLocation)
|
|
return NS_ERROR_NULL_POINTER;
|
|
if (!mInitialized)
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
*aLocation = mLocation;
|
|
NS_IF_ADDREF(mLocation);
|
|
return NS_OK;
|
|
}
|
|
|
|
/* readonly attribute nsISupports data; */
|
|
NS_IMETHODIMP
|
|
nsXPCException::GetData(nsISupports * *aData)
|
|
{
|
|
if (!aData)
|
|
return NS_ERROR_NULL_POINTER;
|
|
if (!mInitialized)
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
*aData = mData;
|
|
NS_IF_ADDREF(mData);
|
|
return NS_OK;
|
|
}
|
|
|
|
/* readonly attribute nsIException inner; */
|
|
NS_IMETHODIMP
|
|
nsXPCException::GetInner(nsIException* *aException)
|
|
{
|
|
if (!aException)
|
|
return NS_ERROR_NULL_POINTER;
|
|
if (!mInitialized)
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
*aException = mInner;
|
|
NS_IF_ADDREF(mInner);
|
|
return NS_OK;
|
|
}
|
|
|
|
/* void initialize (in string aMessage, in nsresult aResult, in string aName, in nsIStackFrame aLocation, in nsISupports aData, in nsIException aInner); */
|
|
NS_IMETHODIMP
|
|
nsXPCException::Initialize(const char *aMessage, nsresult aResult, const char *aName, nsIStackFrame *aLocation, nsISupports *aData, nsIException *aInner)
|
|
{
|
|
if (mInitialized)
|
|
return NS_ERROR_ALREADY_INITIALIZED;
|
|
|
|
Reset();
|
|
|
|
if (aMessage) {
|
|
if (!(mMessage = (char*) nsMemory::Clone(aMessage,
|
|
sizeof(char)*(strlen(aMessage)+1))))
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
if (aName) {
|
|
if (!(mName = (char*) nsMemory::Clone(aName,
|
|
sizeof(char)*(strlen(aName)+1))))
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
mResult = aResult;
|
|
|
|
if (aLocation) {
|
|
mLocation = aLocation;
|
|
NS_ADDREF(mLocation);
|
|
// For now, fill in our location details from our stack frame.
|
|
// Later we may allow other locations?
|
|
nsresult rc;
|
|
if (NS_FAILED(rc = aLocation->GetFilename(&mFilename)))
|
|
return rc;
|
|
if (NS_FAILED(rc = aLocation->GetLineNumber(&mLineNumber)))
|
|
return rc;
|
|
} else {
|
|
nsresult rv;
|
|
nsXPConnect* xpc = nsXPConnect::GetXPConnect();
|
|
if (!xpc)
|
|
return NS_ERROR_FAILURE;
|
|
rv = xpc->GetCurrentJSStack(&mLocation);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
}
|
|
|
|
if (aData) {
|
|
mData = aData;
|
|
NS_ADDREF(mData);
|
|
}
|
|
if (aInner) {
|
|
mInner = aInner;
|
|
NS_ADDREF(mInner);
|
|
}
|
|
|
|
mInitialized = true;
|
|
return NS_OK;
|
|
}
|
|
|
|
/* string toString (); */
|
|
NS_IMETHODIMP
|
|
nsXPCException::ToString(char **_retval)
|
|
{
|
|
if (!_retval)
|
|
return NS_ERROR_NULL_POINTER;
|
|
if (!mInitialized)
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
static const char defaultMsg[] = "<no message>";
|
|
static const char defaultLocation[] = "<unknown>";
|
|
static const char format[] =
|
|
"[Exception... \"%s\" nsresult: \"0x%x (%s)\" location: \"%s\" data: %s]";
|
|
|
|
char* indicatedLocation = nsnull;
|
|
|
|
if (mLocation) {
|
|
// we need to free this if it does not fail
|
|
nsresult rv = mLocation->ToString(&indicatedLocation);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
}
|
|
|
|
const char* msg = mMessage ? mMessage : nsnull;
|
|
const char* location = indicatedLocation ?
|
|
indicatedLocation : defaultLocation;
|
|
const char* resultName = mName;
|
|
if (!resultName && !NameAndFormatForNSResult(mResult, &resultName,
|
|
(!msg) ? &msg : nsnull)) {
|
|
if (!msg)
|
|
msg = defaultMsg;
|
|
resultName = "<unknown>";
|
|
}
|
|
const char* data = mData ? "yes" : "no";
|
|
|
|
char* temp = JS_smprintf(format, msg, mResult, resultName, location, data);
|
|
if (indicatedLocation)
|
|
nsMemory::Free(indicatedLocation);
|
|
|
|
char* final = nsnull;
|
|
if (temp) {
|
|
final = (char*) nsMemory::Clone(temp, sizeof(char)*(strlen(temp)+1));
|
|
JS_smprintf_free(temp);
|
|
}
|
|
|
|
*_retval = final;
|
|
return final ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
JSBool nsXPCException::sEverMadeOneFromFactory = false;
|
|
|
|
// static
|
|
nsresult
|
|
nsXPCException::NewException(const char *aMessage,
|
|
nsresult aResult,
|
|
nsIStackFrame *aLocation,
|
|
nsISupports *aData,
|
|
nsIException** exceptn)
|
|
{
|
|
// A little hack... The nsIGenericModule nsIClassInfo scheme relies on there
|
|
// having been at least one instance made via the factory. Otherwise, the
|
|
// shared factory/classinsance object never gets created and our QI getter
|
|
// for our instance's pointer to our nsIClassInfo will always return null.
|
|
// This is bad because it means that wrapped exceptions will never have a
|
|
// shared prototype. So... We force one to be created via the factory
|
|
// *once* and then go about our business.
|
|
if (!sEverMadeOneFromFactory) {
|
|
nsCOMPtr<nsIXPCException> e =
|
|
do_CreateInstance(XPC_EXCEPTION_CONTRACTID);
|
|
sEverMadeOneFromFactory = true;
|
|
}
|
|
|
|
nsresult rv;
|
|
nsXPCException* e = new nsXPCException();
|
|
if (e) {
|
|
NS_ADDREF(e);
|
|
|
|
nsIStackFrame* location;
|
|
if (aLocation) {
|
|
location = aLocation;
|
|
NS_ADDREF(location);
|
|
} else {
|
|
nsXPConnect* xpc = nsXPConnect::GetXPConnect();
|
|
if (!xpc) {
|
|
NS_RELEASE(e);
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
rv = xpc->GetCurrentJSStack(&location);
|
|
if (NS_FAILED(rv)) {
|
|
NS_RELEASE(e);
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
// it is legal for there to be no active JS stack, if C++ code
|
|
// is operating on a JS-implemented interface pointer without
|
|
// having been called in turn by JS. This happens in the JS
|
|
// component loader, and will become more common as additional
|
|
// components are implemented in JS.
|
|
}
|
|
// We want to trim off any leading native 'dataless' frames
|
|
if (location)
|
|
while (1) {
|
|
PRUint32 language;
|
|
PRInt32 lineNumber;
|
|
if (NS_FAILED(location->GetLanguage(&language)) ||
|
|
language == nsIProgrammingLanguage::JAVASCRIPT ||
|
|
NS_FAILED(location->GetLineNumber(&lineNumber)) ||
|
|
lineNumber) {
|
|
break;
|
|
}
|
|
nsCOMPtr<nsIStackFrame> caller;
|
|
if (NS_FAILED(location->GetCaller(getter_AddRefs(caller))) || !caller)
|
|
break;
|
|
NS_RELEASE(location);
|
|
caller->QueryInterface(NS_GET_IID(nsIStackFrame), (void **)&location);
|
|
}
|
|
// at this point we have non-null location with one extra addref,
|
|
// or no location at all
|
|
rv = e->Initialize(aMessage, aResult, nsnull, location, aData, nsnull);
|
|
NS_IF_RELEASE(location);
|
|
if (NS_FAILED(rv))
|
|
NS_RELEASE(e);
|
|
}
|
|
|
|
if (!e)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
*exceptn = static_cast<nsIXPCException*>(e);
|
|
return NS_OK;
|
|
}
|