~nhoad/roland

a7680f4e9760e2efec73a3bb2fe705f443718c01 — Nathan Hoad 7 years ago 0c8d03d
Rebase webextension on upstream's.
1 files changed, 109 insertions(+), 44 deletions(-)

M webextensions/pythonloader.c
M webextensions/pythonloader.c => webextensions/pythonloader.c +109 -44
@@ 1,13 1,33 @@
/*
 * pythonloader.c
 * Copyright (C) 2015 Adrian Perez <aperez@igalia.com>
 * Copyright (C) 2015-2016 Adrian Perez <aperez@igalia.com>
 * Copyright (C) 2016 Nathan Hoad <nathan@getoffmalawn.com>
 *
 * Distributed under terms of the MIT license.
 */

#include <webkit2/webkit-web-extension.h>
#include <pygobject.h>
#include <stdarg.h>

/*
 * XXX: Hacky workaround ahead!
 *
 * PyGObject internally uses _pygi_struct_new_from_g_type() to wrap
 * GVariant instances into a gi.Struct, but the function is not in the
 * public API. Instead, we temporarily wrap the GVariant into a GValue
 * (ugh!), and use pyg_value_as_pyobject(), which in turn calls function
 * pygi_value_to_py_structured_type() —also private—, and finally that
 * in turn calls _pygi_struct_new_from_g_type() as initially desired.
 */
static PyObject*
g_variant_to_pyobject (GVariant *variant)
{
    GValue value = { 0, };
    g_value_init (&value, G_TYPE_VARIANT);
    g_value_set_variant (&value, variant);
    return pyg_value_as_pyobject (&value, FALSE);
}

#define py_auto __attribute__((cleanup(py_object_cleanup)))



@@ 22,12 42,66 @@ py_object_cleanup(void *ptr)
}


#define PY_CHECK_ACT(expr, act, err_fmt, ...)     \
    do {                                          \
        if (!(expr)) {                            \
            g_printerr (err_fmt, ##__VA_ARGS__);  \
            if (PyErr_Occurred ()) {              \
                g_printerr (": ");                \
                PyErr_Print ();                   \
            } else {                              \
                g_printerr (" (no error given)"); \
            }                                     \
            act;                                  \
        }                                         \
    } while (0)

#define PY_CHECK(expr, err_fmt, ...) \
        PY_CHECK_ACT (expr, return, err_fmt, ##__VA_ARGS__)


/* This would be "extension.py" from the source directory. */
static const char *extension_name = "roland.webextension";


static gboolean
pygi_require (const gchar *module, ...)
{
    PyObject py_auto *gi_module = PyImport_ImportModule ("gi");
    PY_CHECK_ACT (gi_module, return FALSE, "Could not import 'gi'");

    PyObject py_auto *func = PyObject_GetAttrString (gi_module, "require_version");
    PY_CHECK_ACT (func, return FALSE,
                  "Could not obtain 'gi.require_version'");
    PY_CHECK_ACT (PyCallable_Check (func), return FALSE,
                  "Object 'gi.require_version' is not callable");

    gboolean result = TRUE;
    va_list arglist;
    va_start (arglist, module);
    while (module) {
        /*
         * For each module and version, call: gi.require(module, version)
         */
        const gchar *version = va_arg (arglist, const gchar*);
        {
            PyObject py_auto *args = Py_BuildValue ("(ss)", module, version);
            PyObject py_auto *rval = PyObject_CallObject (func, args);
            PY_CHECK_ACT (rval, result = FALSE; break,
                          "Error calling 'gi.require_version(\"%s\", \"%s\")'",
                          module, version);
        }
        module = va_arg (arglist, const gchar*);
    }
    va_end (arglist);
    return result;
}


G_MODULE_EXPORT void
webkit_web_extension_initialize_with_user_data (WebKitWebExtension *extension,
                                                const GVariant     *user_data)
                                                GVariant           *user_data)
{
    g_printerr ("Python loader, extension=%p\n", extension);

    Py_Initialize ();

#if PY_VERSION_HEX < 0x03000000


@@ 40,57 114,48 @@ webkit_web_extension_initialize_with_user_data (WebKitWebExtension *extension,

    pygobject_init (-1, -1, -1);
    if (PyErr_Occurred ()) {
        g_warning ("Could not initialize PyGObject");
        g_printerr ("Could not initialize PyGObject");
        return;
    }

    pyg_enable_threads ();
    PyEval_InitThreads ();

    PyObject py_auto *web_ext_module =
            PyImport_ImportModule ("gi.repository.WebKit2WebExtension");
    if (!web_ext_module) {
        if (PyErr_Occurred ()) {
            g_printerr ("Could not import gi.repository.WebKit2WebExtension: ");
            PyErr_Print ();
        } else {
            g_printerr ("Could not import gi.repository.WebKit2WebExtension"
                        " (no error given)\n");
        }
    if (!pygi_require ("GLib", "2.0",
                       "WebKit2WebExtension", "4.0",
                       NULL))
        return;
    }

    PyObject py_auto *py_filename = PyUnicode_FromString ("roland.webextension");
    PyObject py_auto *web_ext_module =
            PyImport_ImportModule ("gi.repository.WebKit2WebExtension");
    PY_CHECK (web_ext_module,
              "Could not import 'gi.repository.WebKit2WebExtension'");

    /*
     * TODO: Instead of assuming that the Python import path contains the
     *       directory where the extension is, manually load and compile the
     *       extension code, then use PyImport_AddModule() to programmatically
     *       create a new module and PyImport_ExecCodeModule() to import it
     *       from a bytecode object.
     */
    PyObject py_auto *py_filename = PyUnicode_FromString (extension_name);
    PyObject py_auto *py_module = PyImport_Import (py_filename);
    if (!py_module) {
        g_warning ("Could not import '%s'", "extension");
        // FIXME: submit this upstream
        PyErr_Print ();
        return;
    }
    PY_CHECK (py_module, "Could not import '%s'", extension_name);

    PyObject py_auto *py_func = PyObject_GetAttrString (py_module, "initialize");
    if (!py_func) {
        g_warning ("Could not obtain '%s.initialize'", "extension");
        return;
    }
    if (!PyCallable_Check (py_func)) {
        g_warning ("Object '%s.initialize' is not callable", "extension");
        return;
    }
    PY_CHECK (py_func, "Could not obtain '%s.initialize'", extension_name);
    PY_CHECK (PyCallable_Check (py_func),
              "Object '%s.initialize' is not callable", extension_name);

    PyObject py_auto *py_extension = pygobject_new (G_OBJECT (extension));
    PyObject py_auto *py_extra_args = pyg_boxed_new (G_TYPE_VARIANT,
                                                     g_variant_ref(user_data),
                                                     FALSE,
                                                     FALSE);

    PyObject py_auto *py_func_args = PyTuple_New (2);
    PyTuple_SetItem (py_func_args, 0, py_extension);
    PyTuple_SetItem (py_func_args, 1, py_extra_args);
    PyObject py_auto *py_retval = PyObject_CallObject (py_func, py_func_args);
    if (!py_retval) {
        g_printerr ("Error calling '%s.initialize':\n", "extension");
        PyErr_Print ();
    }
    PyObject py_auto *py_extra_args = g_variant_to_pyobject (user_data);

    PY_CHECK (py_extra_args, "Cannot create GLib.Variant");

    PyObject py_auto py_auto *func_args = PyTuple_New (2);
    PyTuple_SetItem (func_args, 0, py_extension);
    PyTuple_SetItem (func_args, 1, py_extra_args);

    PyObject py_auto *py_retval = PyObject_CallObject (py_func, func_args);
    PY_CHECK (py_retval, "Error calling '%s.initialize'", extension_name);
}