<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Chad Austin &#187; mozilla</title>
	<atom:link href="http://chadaustin.me/tag/mozilla/feed/" rel="self" type="application/rss+xml" />
	<link>http://chadaustin.me</link>
	<description></description>
	<lastBuildDate>Tue, 17 Aug 2010 08:51:43 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Handling exceptions from XULRunner callbacks</title>
		<link>http://chadaustin.me/2009/03/handling-exceptions-from-xulrunner-callbacks/</link>
		<comments>http://chadaustin.me/2009/03/handling-exceptions-from-xulrunner-callbacks/#comments</comments>
		<pubDate>Mon, 23 Mar 2009 00:24:23 +0000</pubDate>
		<dc:creator>Chad Austin</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[c++]]></category>
		<category><![CDATA[crashes]]></category>
		<category><![CDATA[imvu]]></category>
		<category><![CDATA[mozilla]]></category>

		<guid isPermaLink="false">http://aegisknight.org/?p=1270</guid>
		<description><![CDATA[(I was composing an e-mail to IMVU&#8217;s engineering team when I realized this information was generally applicable to anyone embedding XULRunner into their application.  Hope it&#8217;s useful.)

XULRunner is written in a subset of C++ that we&#8217;ll call XPCOM.  An embedded XULRunner window communicates back to the host application through XPCOM interfaces that the [...]]]></description>
			<content:encoded><![CDATA[<p>(I was composing an e-mail to IMVU&#8217;s engineering team when I realized this information was generally applicable to anyone embedding XULRunner into their application.  Hope it&#8217;s useful.)</p>

<p><a href="https://developer.mozilla.org/en/XULRunner">XULRunner</a> is written in a subset of C++ that we&#8217;ll call XPCOM.  An embedded XULRunner window communicates back to the host application through XPCOM interfaces that the host implements.  In IMVU, we generally use C++ exceptions to signal failure.  On the other hand, XPCOM uses <code>nsresult</code> error codes.  Specifically, XULRunner is not written to support C++ exceptions, nor is it compiled with them enabled.  (Note that compiling with exceptions enabled is not sufficient to guarantee defined behavior when they&#8217;re thrown.  You must use techniques like <a href="http://en.wikipedia.org/wiki/RAII">RAII</a> to properly unwind and restore state if an exception is thrown.)</p>

<p>If XULRunner is calling into our code, and our code uses exceptions to signal failure, and throwing an exception through a XULRunner call stack results in undefined behavior, what do we do?  This is the strategy I took:</p>

<p>In every method of every IMVU implementation of an XPCOM interface, I bracketed the function body with <code>IMVU_BEGIN_DISALLOW_EXCEPTIONS_XPCOM</code> and <code>IMVU_END_DISALLOW_EXCEPTIONS_XPCOM</code>.  For example:</p>

<pre>
nsresult xpcom_method_runtime_error_with_error_info() {
    IMVU_BEGIN_DISALLOW_EXCEPTIONS_XPCOM {
        boost::throw_exception(std::runtime_error("error"));
    } IMVU_END_DISALLOW_EXCEPTIONS_XPCOM;
}
</pre>

<p>
These two macros generate a <code>try ... catch</code> clause that handles every C++ exception thrown from the body, returning <code>NS_ERROR_UNEXPECTED</code> to the caller.
</p>

<p>
If the exception thrown is a Python error (<code>boost::python::error_already_set</code>), then the Python exception is pulled (<code>PyErr_Fetch</code>) and scheduled to be reraised (<code>PyErr_Restore</code>) in the next iteration through the IMVU client&#8217;s message loop.
</p>

<p>
If the exception thrown is a C++ exception, we&#8217;d like to take the same approach.  However, <a href="http://en.wikipedia.org/wiki/C++0x">C++0x</a> has not shipped, so there&#8217;s no built-in mechanism for transferring exceptions across contexts.  Thus, we take advantage of the <a href="http://www.boost.org/doc/libs/release/libs/exception/index.html">boost::exception</a> framework to copy and rethrow the exception from the main message loop.  Unfortunately, you can&#8217;t just &#8220;throw X()&#8221;.  You have to use <code>boost::throw_exception</code>, which enables the machinery for <code>current_exception()</code> and <code>rethrow_exception()</code>.  To enforce this requirement, I have modified our C++ exception hierarchy so that you must use <code>X::throw_(arguments)</code> instead of <code>throw X(arguments)</code>.
</p>

<p>
If the exception thrown is a C++ exception but not a subclass of <code>std::exception</code>, then we catch it with <code>catch (...)</code> or <code>std::uncaught_exception()</code> in a sentry object&#8217;s destructor, and raise a structured exception to at least indicate that this is occurring in the field.
</p>

<p>For reference, here is the implementation:</p>

<pre>
void handlePythonError();
void handleStandardException(const std::exception&amp; e);

#define IMVU_BEGIN_DISALLOW_EXCEPTIONS         \
    DisallowExceptionsSentry PP_UNIQUE_NAME(); \
    try

#define IMVU_END_DISALLOW_EXCEPTIONS(block)                             \
    catch (const boost::python::error_already_set&amp;) {               \
        handlePythonError();                                            \
        block ;                                                         \
    }                                                                   \
    catch (const std::exception&amp; e) {                               \
        handleStandardException(e);                                     \
        block ;                                                         \
    }

#define IMVU_BEGIN_DISALLOW_EXCEPTIONS_XPCOM IMVU_BEGIN_DISALLOW_EXCEPTIONS
#define IMVU_END_DISALLOW_EXCEPTIONS_XPCOM IMVU_END_DISALLOW_EXCEPTIONS({ return NS_ERROR_UNEXPECTED; })

#define IMVU_DISALLOW_EXCEPTIONS_XPCOM(block)                           \
    IMVU_BEGIN_DISALLOW_EXCEPTIONS_XPCOM {                              \
        block ;                                                         \
    } IMVU_END_DISALLOW_EXCEPTIONS_XPCOM
</pre>

<p>And the source file:</p>

<pre>
DisallowExceptionsSentry::~DisallowExceptionsSentry() {
    if (std::uncaught_exception()) {
        RaiseException(EXC_EXCEPTIONS_NOT_ALLOWED, 0, 0, 0);
    }
}

/*
 * On error handling and the GIL.  Gecko's event handlers run during
 * the message loop, which means the GIL is not held.  Calls back into
 * Python require that the GIL be reacquired.  If the Python call
 * fails, the GIL is released (while error_already_set is unwinding
 * the stack).  The GIL must be reacquired to grab the exception
 * information and marshal it to the main thread.
 *
 * However, PumpWaitingMessages releases the GIL too!  Thus, reraising
 * the error on the main thread requires GIL reacquisition.
 *
 * If another thread acquires the GIL and blocks on the main thread's
 * message pump, a deadlock will occur.  Thus, secondary threads
 * should never block on the main thread's message pump.
 */

void reraisePythonError(PyObject* type, PyObject* value, PyObject* traceback) {
    HoldPythonGIL PP_UNIQUE_NAME();
    PyErr_Restore(type, value, traceback);
    boost::python::throw_error_already_set();
}

void error_but_no_error() {
    throw std::runtime_error("error_already_set but no Python error?");
}

void handlePythonError() {
    PyObject* type;
    PyObject* value;
    PyObject* traceback;
    {
        HoldPythonGIL PP_UNIQUE_NAME();
        PyErr_Fetch(&amp;type, &amp;value, &amp;traceback);
    }
    if (type) {
        boost::function&lt;void()&gt; fn(boost::bind(reraisePythonError, type, value, traceback));
        queueMainThreadJob(fn);
    } else {
        queueMainThreadJob(error_but_no_error);
    }
}

void rethrowStandardException(const std::string&amp; s) {
    std::string prefix("Unknown std::exception: ");
    throw std::runtime_error(prefix + s);
}

void handleStandardException(const std::exception&amp; e) {
    if (boost::exception_detail::get_boost_exception(&amp;e)) {
        queueMainThreadJob(boost::bind(
            boost::rethrow_exception,
            boost::current_exception()));
    } else {
        queueMainThreadJob(boost::bind(rethrowStandardException, std::string(e.what())));
    }
}
</pre>

]]></content:encoded>
			<wfw:commentRss>http://chadaustin.me/2009/03/handling-exceptions-from-xulrunner-callbacks/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Evaluating JavaScript in an Embedded XULRunner/Gecko Window</title>
		<link>http://chadaustin.me/2009/02/evaluating-javascript-in-an-embedded-xulrunnergecko-window/</link>
		<comments>http://chadaustin.me/2009/02/evaluating-javascript-in-an-embedded-xulrunnergecko-window/#comments</comments>
		<pubDate>Wed, 18 Feb 2009 06:41:00 +0000</pubDate>
		<dc:creator>Chad Austin</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[c++]]></category>
		<category><![CDATA[imvu]]></category>
		<category><![CDATA[mozilla]]></category>

		<guid isPermaLink="false">http://aegisknight.org/new/2009/02/17/evaluating-javascript-in-an-embedded-xulrunnergecko-window/</guid>
		<description><![CDATA[
I intended to write something with more substance tonight, but I&#8217;m
exhausted from wrasslin&#8217; with Gecko/XULRunner/SpiderMonkey in a
days-long marathon debugging session.  None of you will understand
this entry, because its intent is to contain enough keywords and
content that others don&#8217;t have to go through the pain that I did.



If you&#8217;re embedding Gecko/XULRunner/SpiderMonkey into your
application, and you [...]]]></description>
			<content:encoded><![CDATA[<p>
I intended to write something with more substance tonight, but I&#8217;m
exhausted from wrasslin&#8217; with Gecko/XULRunner/SpiderMonkey in a
days-long marathon debugging session.  None of you will understand
this entry, because its intent is to contain enough keywords and
content that others don&#8217;t have to go through the pain that I did.
</p>

<p>
If you&#8217;re embedding Gecko/XULRunner/SpiderMonkey into your
application, and you want to evaluate some JavaScript in the context
of an <code>nsIDOMWindow</code> or <code>nsIWebBrowser</code>, you&#8217;d think you&#8217;d have many
approaches.  You could call <code>JS_EvaluateScript</code> or <code>JS_EvaluateUCScript</code>
directly, getting the <code>JSContext</code> from the <code>nsIScriptContext</code> and the
<code>JSObject*</code> global from the <code>nsIScriptGlobalObject</code>&#8230;  However, I simply
could not get this to work: I kept running into crazy errors inside of
<code>JS_InitArrayClass</code>.  I still don&#8217;t understand those errors.
</p>

<p>
People suggested using <code>EvaluateString</code> and <code>EvaluateStringWithValue</code> on
<code>nsIScriptContext</code>, but that failed in an empty window (I define empty
as not having called <code>nsIWebNavigation::LoadURI</code>) because it did not
have a security principal (<code>nsIPrincipal</code>).  Eventually I learned that
you can grab the system principal from the <code>nsIScriptSecurityManager</code>
service and pass that directly to <code>EvaluateStringWithValue</code>.  With a few
more minor details, this approach worked in all cases that we care
about so far!
</p>

<p>
Here is the final magic incantation:
</p>

<pre>
typedef std::map&lt;jsval, boost::python::object&gt; ReferenceMap;

boost::python::object GeckoWindow::evalJavaScript(const std::wstring&amp; js) {
    nsresult rv;

    nsCOMPtr&lt;nsIPrincipal&gt; principal;
    nsCOMPtr&lt;nsIScriptSecurityManager&gt; secMan = do_GetService(
        NS_SCRIPTSECURITYMANAGER_CONTRACTID);
    rv = secMan-&gt;GetSystemPrincipal(getter_AddRefs(principal));
    if (NS_FAILED(rv)) {
        throw GeckoError("Failed to get system principal");
    }

    nsCOMPtr&lt;nsIScriptGlobalObject&gt; sgo = do_GetInterface(webBrowser);
    nsCOMPtr&lt;nsIScriptContext&gt; ctx = sgo-&gt;GetContext();

    JSContext* cx = reinterpret_cast&lt;JSContext*&gt;(ctx-&gt;GetNativeContext());
    Assert(cx);
    uint32 previous = JS_SetOptions(
        cx,
        JS_GetOptions(cx) | JSOPTION_DONT_REPORT_UNCAUGHT);

    jsval out;
    rv = ctx-&gt;EvaluateStringWithValue(
        nsString(js.data(), js.size()),
        sgo-&gt;GetGlobalJSObject(),
        principal,
        "mozembed",
        0,
        nsnull,
        &amp;out,
        nsnull);

    JS_SetOptions(cx, previous);

    JSAutoRequest ar(cx);
    JSAutoLocalRootScope alrs(cx);

    maybeThrowPythonExceptionFromJsContext(cx);

    if (NS_SUCCEEDED(rv)) {
        ReferenceMap references;
        return buildPythonObjectFromJsval(references, cx, out);
    } else {
        throw GeckoEvalUnknownError("eval failed with no exception set");
    }
}

void GeckoWindow::maybeThrowPythonExceptionFromJsContext(JSContext* cx) {
    jsval exception;
    if (JS_GetPendingException(cx, &amp;exception)) {
        JS_ClearPendingException(cx);
        ReferenceMap references;
        boost::python::object py_exc_value(buildPythonObjectFromJsval(
            references,
            cx,
            exception));
        throw GeckoEvalError(py_exc_value.ptr());
    }
}

boost::python::object GeckoWindow::buildPythonObjectFromJsval(
    ReferenceMap&amp; references,
    JSContext* cx,
    const jsval v
) {
    using namespace boost::python;

    if (v == JSVAL_TRUE) {
        return object(handle&lt;&gt;(Py_True));
    } else if (v == JSVAL_FALSE) {
        return object(handle&lt;&gt;(Py_False));
    } else if (v == JSVAL_NULL) {
        return object(handle&lt;&gt;(Py_None));
    } else if (v == JSVAL_VOID) {
        return object(handle&lt;&gt;(Py_None));
    } else if (JSVAL_IS_INT(v)) {
        return object(handle&lt;&gt;(PyInt_FromLong(JSVAL_TO_INT(v))));
    } else if (JSVAL_IS_NUMBER(v)) {
        return object(handle&lt;&gt;(PyFloat_FromDouble(*JSVAL_TO_DOUBLE(v))));
    // } else if (JSVAL_IS_STRING(v)) {
    //
    } else if (JSVAL_IS_OBJECT(v)) {
        JSObject* obj = JSVAL_TO_OBJECT(v);

        if (references.count(v)) {
            return references[v];
        }

        if (JS_IsArrayObject(cx, obj)) {
            list rv;
            references[v] = rv;
            jsuint length;
            if (JS_GetArrayLength(cx, obj, &amp;length)) {
                jsval element;
                for (jsuint i = 0; i &lt; length; ++i) {
                    if (JS_GetElement(cx, obj, i, &amp;element)) {
                        rv.append(buildPythonObjectFromJsval(references, cx, element));
                    }
                }
            }
            return rv;
        } else {
            dict rv;
            references[v] = rv;

            JSObject* iterator = JS_NewPropertyIterator(cx, obj);
            if (!iterator) {
                throw GeckoEvalUnknownError("Error creating object property iterator while marshalling");
            }
            for (;;) {
                jsid propertyName;
                if (!JS_NextProperty(cx, iterator, &amp;propertyName)) {
                    throw GeckoEvalUnknownError("Error enumerating property list of object while marshalling");
                }

                if (propertyName == JSVAL_VOID) {
                    break;
                }

                jsval propertyNameValue;
                jsval propertyValue;
                object k;

                if (!JS_IdToValue(cx, propertyName, &amp;propertyNameValue)) {
                    throw GeckoEvalUnknownError("Error converting property name to jsval while marshalling");
                }
                if (JSVAL_IS_INT(propertyNameValue)) {
                    jsint propertyIndex = JSVAL_TO_INT(propertyNameValue);
                    k = long_(propertyIndex);

                    if (!JS_LookupElement(cx, obj, propertyIndex, &amp;propertyValue)) {
                        throw GeckoEvalUnknownError("Error looking up property value by index");
                    }
                } else if (JSVAL_IS_STRING(propertyNameValue)) {
                    JSString* kjsstr = JSVAL_TO_STRING(propertyNameValue);
                    std::wstring kstr(JS_GetStringChars(kjsstr), JS_GetStringLength(kjsstr));
                    k = object(kstr);

                    if (!JS_LookupUCProperty(cx, obj, kstr.c_str(), kstr.size(), &amp;propertyValue)) {
                        throw GeckoEvalUnknownError("Error looking up property value by name");
                    }
                } else {
                    throw GeckoEvalUnknownError("Unknown property name type while marshalling");
                }

                rv[k] = buildPythonObjectFromJsval(references, cx, propertyValue);
            }
            return rv;
        }
    } else {
        // We don't know what type it is, or we can't marshal it,
        // so convert it to a string and hope for the best...
        JSString* string = JS_ValueToString(cx, v);
        return str(std::wstring(JS_GetStringChars(string), JS_GetStringLength(string)));
    }
}
</pre>

<p>
Hope that helps, and Godspeed.
</p>]]></content:encoded>
			<wfw:commentRss>http://chadaustin.me/2009/02/evaluating-javascript-in-an-embedded-xulrunnergecko-window/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
