Mozilla/mozilla/extensions/python/dom/src/nsPyDOMModule.cpp
Olli.Pettay%helsinki.fi 7a1f7182a1 Bug 363089, Remove nsIDOMEventReceiver (this time passes mochitest), r+sr=jst
git-svn-id: svn://10.0.0.236/trunk@226373 18797224-902f-48f8-a5cc-f745e15eee43
2007-05-14 09:13:09 +00:00

538 lines
19 KiB
C++

/* ***** 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.org code.
*
* The Initial Developer of the Original Code is mozilla.org.
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Mark Hammond (initial development)
*
* Alternatively, the contents of this file may be used under the terms of
* either 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 ***** */
// "Module" is such an overloaded term! This file serves as both the xpcom
// component module and the implicit Python "_nsdom" module. The _nsdom module
// is implicit in that it is automatically imported as this component
// initializes even though it does not exist as a regular python module.
#include "nsIGenericFactory.h"
#include "nsPyRuntime.h"
#include "nsPyContext.h"
#include "nsIServiceManager.h"
#include "nsIScriptGlobalObject.h"
#include "nsIScriptObjectPrincipal.h"
#include "nsIAtomService.h"
#include "nsIEventListenerManager.h"
#include "nsIScriptTimeoutHandler.h"
#include "nsPIDOMEventTarget.h"
#include "nsIArray.h"
#include "jscntxt.h"
#include "nsPIDOMWindow.h"
#include "nsDOMScriptObjectHolder.h"
// just for constants...
#include "nsIScriptContext.h"
#include "nsIScriptGlobalObject.h"
NS_GENERIC_FACTORY_CONSTRUCTOR(nsPythonRuntime)
static const nsModuleComponentInfo components[] = {
{"Python Script Language", NS_SCRIPT_LANGUAGE_PYTHON_CID,
NS_SCRIPT_LANGUAGE_PYTHON_CONTRACTID_NAME,
nsPythonRuntimeConstructor,},
// And again with the contract ID including the language ID
{"Python Script Language", NS_SCRIPT_LANGUAGE_PYTHON_CID,
NS_SCRIPT_LANGUAGE_PYTHON_CONTRACTID_ID,
nsPythonRuntimeConstructor,},
};
// Implementation of the module object.
NS_IMPL_NSGETMODULE(nsPyDOMModule, components)
// The Python '_nsdom' module
////////////////////////////////////////////////////////////////////////
//
//
PyObject *PyJSExec(PyObject *self, PyObject *args)
{
PyObject *obGlobal;
char *code;
char *url = "<python JSExec script>";
PRUint32 lineNo = 0;
PRUint32 version = 0;
if (!PyArg_ParseTuple(args, "Os|sii", &obGlobal, &code, &url, &lineNo, &version))
return NULL;
nsCOMPtr<nsISupports> isup;
if (!Py_nsISupports::InterfaceFromPyObject(
obGlobal,
NS_GET_IID(nsISupports),
getter_AddRefs(isup),
PR_FALSE, PR_FALSE))
return NULL;
nsCOMPtr<nsIScriptGlobalObject> scriptGlobal = do_QueryInterface(isup);
if (scriptGlobal == nsnull)
return PyErr_Format(PyExc_TypeError, "Object is not an nsIScriptGlobal");
nsIScriptContext *scriptContext =
scriptGlobal->GetScriptContext(nsIProgrammingLanguage::JAVASCRIPT);
if (!scriptContext)
return PyErr_Format(PyExc_RuntimeError, "No javascript context available");
// get the principal
nsIPrincipal *principal = nsnull;
nsCOMPtr<nsIScriptObjectPrincipal> globalData = do_QueryInterface(scriptGlobal);
if (globalData)
principal = globalData->GetPrincipal();
if (!principal)
return PyErr_Format(PyExc_RuntimeError, "No nsIPrincipal available");
nsresult rv;
void *scope = scriptGlobal->GetScriptGlobal(nsIProgrammingLanguage::JAVASCRIPT);
nsScriptObjectHolder scriptObject(scriptContext);
JSContext *ctx = (JSContext *)scriptContext->GetNativeContext();
nsAutoString str;
PRBool bIsUndefined;
Py_BEGIN_ALLOW_THREADS
rv = scriptContext->CompileScript( NS_ConvertASCIItoUTF16(code).get(),
strlen(code),
scope,
principal, // no principal??
url,
lineNo,
version,
scriptObject);
if (NS_SUCCEEDED(rv) && scriptObject)
rv = scriptContext->ExecuteScript(scriptObject, scope, &str,
&bIsUndefined);
Py_END_ALLOW_THREADS
if (NS_FAILED(rv))
return PyXPCOM_BuildPyException(rv);
return Py_BuildValue("NN", PyObject_FromNSString(str), PyBool_FromLong(bIsUndefined));
// XXX - cleanup scriptObject???
}
// Various helpers to avoid exposing non-scriptable interfaces when we only
// need one or 2 functions.
static PyObject *PyIsOuterWindow(PyObject *self, PyObject *args)
{
PyObject *obTarget;
if (!PyArg_ParseTuple(args, "O", &obTarget))
return NULL;
// target
nsCOMPtr<nsPIDOMWindow> target;
if (!Py_nsISupports::InterfaceFromPyObject(
obTarget,
NS_GET_IID(nsPIDOMWindow),
getter_AddRefs(target),
PR_FALSE, PR_FALSE))
return NULL;
return PyBool_FromLong(target->IsOuterWindow());
}
static PyObject *PyGetCurrentInnerWindow(PyObject *self, PyObject *args)
{
PyObject *obTarget;
if (!PyArg_ParseTuple(args, "O", &obTarget))
return NULL;
// target
nsCOMPtr<nsPIDOMWindow> target;
if (!Py_nsISupports::InterfaceFromPyObject(
obTarget,
NS_GET_IID(nsPIDOMWindow),
getter_AddRefs(target),
PR_FALSE, PR_FALSE))
return NULL;
// Use the script global object to fetch the context.
nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryInterface(target));
if (!sgo)
return PyErr_Format(PyExc_ValueError, "Object does not supports nsIScriptGlobalObject");
nsIScriptContext *ctx = sgo->GetScriptContext(nsIProgrammingLanguage::PYTHON);
if (!ctx)
return PyErr_Format(PyExc_ValueError, "Object not initialized for Python");
nsCOMPtr<nsIScriptGlobalObject> innerGlobal(
do_QueryInterface(target->GetCurrentInnerWindow()));
if (!innerGlobal)
return PyErr_Format(PyExc_ValueError, "Result object does not supports nsIScriptGlobalObject");
nsPythonContext *pyctx = (nsPythonContext *)ctx;
return pyctx->PyObject_FromInterface(innerGlobal,
NS_GET_IID(nsIScriptGlobalObject));
}
static PyObject *PySetTimeoutOrInterval(PyObject *self, PyObject *args)
{
PyObject *obTarget;
PyObject *obHandler;
PyObject *obArgs;
int interval;
int isInterval;
if (!PyArg_ParseTuple(args, "OiOO!i:SetTimeoutOrInterval", &obTarget,
&interval, &obHandler,
&PyTuple_Type, &obArgs,
&isInterval))
return NULL;
if (!PySequence_Check(obArgs))
return PyErr_Format(PyExc_TypeError, "args param must be a sequence (got %s)",
obArgs->ob_type->tp_name);
PyObject *funObject = NULL;
nsAutoString strExpr;
if (PyCallable_Check(obHandler)) {
funObject = obHandler;
} else {
if (!PyObject_AsNSString(obHandler, strExpr))
return NULL;
}
// target
nsCOMPtr<nsPIDOMWindow> target;
if (!Py_nsISupports::InterfaceFromPyObject(
obTarget,
NS_GET_IID(nsPIDOMWindow),
getter_AddRefs(target),
PR_FALSE, PR_FALSE))
return NULL;
nsCOMPtr<nsIScriptTimeoutHandler> handler;
nsresult rv;
rv = CreatePyTimeoutHandler(strExpr, funObject, obArgs,
getter_AddRefs(handler));
if (NS_FAILED(rv))
return PyXPCOM_BuildPyException(rv);
PRInt32 ret;
Py_BEGIN_ALLOW_THREADS;
rv = target->SetTimeoutOrInterval(handler, interval, isInterval, &ret);
Py_END_ALLOW_THREADS;
if (NS_FAILED(rv))
return PyXPCOM_BuildPyException(rv);
return PyInt_FromLong(ret);
}
static PyObject *PyClearTimeoutOrInterval(PyObject *self, PyObject *args)
{
int tid;
PyObject *obTarget;
if (!PyArg_ParseTuple(args, "Oi", &obTarget, &tid))
return NULL;
// target
nsCOMPtr<nsPIDOMWindow> target;
if (!Py_nsISupports::InterfaceFromPyObject(
obTarget,
NS_GET_IID(nsPIDOMWindow),
getter_AddRefs(target),
PR_FALSE, PR_FALSE))
return NULL;
nsresult rv;
Py_BEGIN_ALLOW_THREADS;
rv = target->ClearTimeoutOrInterval((PRInt32)tid);
Py_END_ALLOW_THREADS;
if (NS_FAILED(rv))
return PyXPCOM_BuildPyException(rv);
Py_INCREF(Py_None);
return Py_None;
}
// Avoid exposing the entire non-scriptable event listener interface
// We are exposing:
//
//nsEventListenerManager::AddScriptEventListener(nsISupports *aObject,
// nsIAtom *aName,
// const nsAString& aBody,
// PRUint32 aLanguage,
// PRBool aDeferCompilation,
// PRBool aPermitUntrustedEvents)
//
// Implementation of the above seems strange - if !aDeferCompilation then
// aBody is ignored (presumably to later be fetched via GetAttr
static PyObject *PyAddScriptEventListener(PyObject *self, PyObject *args)
{
PyObject *obTarget, *obBody;
char *name;
int lang = nsIProgrammingLanguage::PYTHON;
int defer, untrusted;
if (!PyArg_ParseTuple(args, "OsOii|i", &obTarget, &name, &obBody,
&defer, &untrusted, &lang))
return NULL;
// target
nsCOMPtr<nsISupports> target;
if (!Py_nsISupports::InterfaceFromPyObject(
obTarget,
NS_GET_IID(nsISupports),
getter_AddRefs(target),
PR_FALSE, PR_FALSE))
return NULL;
nsAutoString body;
if (!PyObject_AsNSString(obBody, body))
return NULL;
// The receiver, to get the manager.
nsCOMPtr<nsPIDOMEventTarget> piTarget(do_QueryInterface(target));
if (!piTarget) return PyXPCOM_BuildPyException(NS_ERROR_UNEXPECTED);
nsCOMPtr<nsIEventListenerManager> manager;
piTarget->GetListenerManager(PR_TRUE, getter_AddRefs(manager));
if (!manager) return PyXPCOM_BuildPyException(NS_ERROR_UNEXPECTED);
// avoid do_GetAtom - its not part of the XPCOM glue.
nsresult rv;
nsCOMPtr<nsIAtomService> atomService =
do_GetService(NS_ATOMSERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv))
return PyXPCOM_BuildPyException(rv);
nsCOMPtr<nsIAtom> atom;
rv = atomService->GetAtomUTF8(name, getter_AddRefs(atom));
if (NS_FAILED(rv))
return PyXPCOM_BuildPyException(rv);
Py_BEGIN_ALLOW_THREADS
rv = manager->AddScriptEventListener(target, atom, body, lang, defer,
untrusted);
Py_END_ALLOW_THREADS
if (NS_FAILED(rv))
return PyXPCOM_BuildPyException(rv);
Py_INCREF(Py_None);
return Py_None;
}
// NS_IMETHOD RegisterScriptEventListener(nsIScriptContext *aContext,
// void *aScopeObject,
// nsISupports *aObject,
// nsIAtom* aName) = 0;
static PyObject *PyRegisterScriptEventListener(PyObject *self, PyObject *args)
{
PyObject *obTarget, *obGlobal, *obScope;
char *name;
if (!PyArg_ParseTuple(args, "OOOs", &obGlobal, &obScope, &obTarget, &name))
return NULL;
nsCOMPtr<nsISupports> isup;
if (!Py_nsISupports::InterfaceFromPyObject(
obGlobal,
NS_GET_IID(nsISupports),
getter_AddRefs(isup),
PR_FALSE, PR_FALSE))
return NULL;
nsCOMPtr<nsIScriptGlobalObject> scriptGlobal = do_QueryInterface(isup);
if (scriptGlobal == nsnull)
return PyErr_Format(PyExc_TypeError, "Object is not an nsIScriptGlobal");
nsIScriptContext *scriptContext =
scriptGlobal->GetScriptContext(nsIProgrammingLanguage::PYTHON);
if (!scriptContext)
return PyErr_Format(PyExc_RuntimeError, "Can't find my context??");
// target
nsCOMPtr<nsISupports> target;
if (!Py_nsISupports::InterfaceFromPyObject(
obTarget,
NS_GET_IID(nsISupports),
getter_AddRefs(target),
PR_FALSE, PR_FALSE))
return NULL;
// sob - I don't quite understand this, but things get upset when
// an outer window gets the event.
nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(target));
if (win != nsnull && win->IsOuterWindow()) {
target = win->GetCurrentInnerWindow();
}
// The receiver, to get the manager.
nsCOMPtr<nsPIDOMEventTarget> piTarget(do_QueryInterface(target));
if (!piTarget) return PyXPCOM_BuildPyException(NS_ERROR_UNEXPECTED);
nsCOMPtr<nsIEventListenerManager> manager;
piTarget->GetListenerManager(PR_TRUE, getter_AddRefs(manager));
if (!manager) return PyXPCOM_BuildPyException(NS_ERROR_UNEXPECTED);
nsresult rv;
nsCOMPtr<nsIAtomService> atomService =
do_GetService(NS_ATOMSERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv))
return PyXPCOM_BuildPyException(rv);
nsCOMPtr<nsIAtom> atom;
rv = atomService->GetAtomUTF8(name, getter_AddRefs(atom));
if (NS_FAILED(rv))
return PyXPCOM_BuildPyException(rv);
Py_BEGIN_ALLOW_THREADS
rv = manager->RegisterScriptEventListener(scriptContext, obScope,
target, atom);
Py_END_ALLOW_THREADS
if (NS_FAILED(rv))
return PyXPCOM_BuildPyException(rv);
Py_INCREF(Py_None);
return Py_None;
}
// NS_IMETHOD CompileScriptEventListener(nsIScriptContext *aContext,
// void *aScopeObject,
// nsISupports *aObject,
// nsIAtom* aName,
// PRBool *aDidCompile) = 0;
static PyObject *PyCompileScriptEventListener(PyObject *self, PyObject *args)
{
PyObject *obTarget, *obGlobal, *obScope;
char *name;
if (!PyArg_ParseTuple(args, "OOOs", &obGlobal, &obScope, &obTarget, &name))
return NULL;
nsCOMPtr<nsISupports> isup;
if (!Py_nsISupports::InterfaceFromPyObject(
obGlobal,
NS_GET_IID(nsISupports),
getter_AddRefs(isup),
PR_FALSE, PR_FALSE))
return NULL;
nsCOMPtr<nsIScriptGlobalObject> scriptGlobal = do_QueryInterface(isup);
if (scriptGlobal == nsnull)
return PyErr_Format(PyExc_TypeError, "Object is not an nsIScriptGlobal");
nsIScriptContext *scriptContext =
scriptGlobal->GetScriptContext(nsIProgrammingLanguage::PYTHON);
if (!scriptContext)
return PyErr_Format(PyExc_RuntimeError, "Can't find my context??");
// target
nsCOMPtr<nsISupports> target;
if (!Py_nsISupports::InterfaceFromPyObject(
obTarget,
NS_GET_IID(nsISupports),
getter_AddRefs(target),
PR_FALSE, PR_FALSE))
return NULL;
// sob - I don't quite understand this, but things get upset when
// an outer window gets the event.
nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(target));
if (win != nsnull && win->IsOuterWindow()) {
target = win->GetCurrentInnerWindow();
}
// The receiver, to get the manager.
nsCOMPtr<nsPIDOMEventTarget> piTarget(do_QueryInterface(target));
if (!piTarget) return PyXPCOM_BuildPyException(NS_ERROR_UNEXPECTED);
nsCOMPtr<nsIEventListenerManager> manager;
piTarget->GetListenerManager(PR_TRUE, getter_AddRefs(manager));
if (!manager) return PyXPCOM_BuildPyException(NS_ERROR_UNEXPECTED);
nsresult rv;
nsCOMPtr<nsIAtomService> atomService =
do_GetService(NS_ATOMSERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv))
return PyXPCOM_BuildPyException(rv);
nsCOMPtr<nsIAtom> atom;
rv = atomService->GetAtomUTF8(name, getter_AddRefs(atom));
if (NS_FAILED(rv))
return PyXPCOM_BuildPyException(rv);
PRBool didCompile;
Py_BEGIN_ALLOW_THREADS
rv = manager->CompileScriptEventListener(scriptContext, obScope,
target, atom, &didCompile);
Py_END_ALLOW_THREADS
if (NS_FAILED(rv))
return PyXPCOM_BuildPyException(rv);
return PyBool_FromLong(didCompile);
}
static PyObject *PyMakeArray(PyObject *self, PyObject *args)
{
PyObject *ob;
if (!PyArg_ParseTuple(args, "O!:MakeArray", &PyTuple_Type, &ob))
return NULL;
nsCOMPtr<nsIArray> array;
nsresult rv = NS_CreatePyArgv(ob, getter_AddRefs(array));
if (NS_FAILED(rv) || array == nsnull)
return PyXPCOM_BuildPyException(rv);
return PyObject_FromNSInterface(array, NS_GET_IID(nsIArray));
}
static PyObject *PyMakeDOMObject(PyObject *self, PyObject *args)
{
PyObject *ob, *obContext;
if (!PyArg_ParseTuple(args, "OO:MakeDOMObject", &obContext, &ob))
return NULL;
nsCOMPtr<nsISupports> sup;
if (!Py_nsISupports::InterfaceFromPyObject(ob, NS_GET_IID(nsISupports),
getter_AddRefs(sup),
PR_FALSE, PR_FALSE))
return NULL;
return PyObject_FromNSDOMInterface(obContext, sup, NS_GET_IID(nsISupports));
}
static struct PyMethodDef methods[]=
{
{"JSExec", PyJSExec, 1},
{"AddScriptEventListener", PyAddScriptEventListener, 1},
{"RegisterScriptEventListener", PyRegisterScriptEventListener, 1},
{"CompileScriptEventListener", PyCompileScriptEventListener, 1},
{"GetCurrentInnerWindow", PyGetCurrentInnerWindow, 1},
{"IsOuterWindow", PyIsOuterWindow, 1},
{"SetTimeoutOrInterval", PySetTimeoutOrInterval, 1},
{"ClearTimeoutOrInterval", PyClearTimeoutOrInterval, 1},
{"MakeArray", PyMakeArray, 1},
{"MakeDOMObject", PyMakeDOMObject, 1},
{ NULL }
};
////////////////////////////////////////////////////////////
// The module init code.
//
#define REGISTER_IID(t) { \
PyObject *iid_ob = Py_nsIID::PyObjectFromIID(NS_GET_IID(t)); \
PyDict_SetItemString(dict, "IID_"#t, iid_ob); \
Py_DECREF(iid_ob); \
}
// *NOT* called by Python - this is not a regular Python module.
// Caller must ensure only called once!
void
init_nsdom() {
CEnterLeavePython _celp;
// Create the module and add the functions
PyObject *oModule = Py_InitModule("_nsdom", methods);
PyObject *dict = PyModule_GetDict(oModule);
REGISTER_IID(nsIScriptGlobalObject);
}