// -*- c++ -*-

/*
 *  Copyright (C) 2001-2002, Richard J. Moore <rich@kde.org>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this library; see the file COPYING.LIB.  If not, write to
 *  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 *  Boston, MA 02111-1307, USA.
 */

#ifndef KJSEMBEDJSOBJECTPROXY_H
#define KJSEMBEDJSOBJECTPROXY_H

#include <qguardedptr.h>
#include <qcstring.h>

#include <kjs/object.h>
#include <kjsembed/jsbinding.h>

namespace KJS { class Interpreter; };

namespace KJSEmbed {

class SecurityPolicy;
class JSObjectProxyPrivate;

/**
 * A JS object that provides a binding to a QObject.
 * <h3>Introduction</h3>
 * This class defines a @ref KJS::ObjectImp that allows scripts to access
 * the properties of a QObject. The scripts can also create child objects,
 * load dialogs from .ui files created by Designer and navigate the  object
 * tree in a similar manner to the DCOP/QObject bridge.
 * <h3>Example Usage</h3>
 * The following example creates a @ref KJS::ObjectImp that provides
 * a binding to the properties of a @ref QLineEdit . This binding is
 * then used to create a property 'edit' for the object 'jsparent'.
 * <pre>
 *
 *   QLineEdit *edit = new QLineEdit();
 *   KJSEmbed::JSObjectProxy *proxy = new KJSEmbed::JSObjectProxy( js, edit );
 *   jsparent.put( js->globalExec(), "edit", proxy );
 *
 * </pre>
 * <h3>API Provided To Scripts</h3>
 * By default, the following methods are available to scripts for
 * traversing and manipulating the QObject tree:
 * <ul>
 * <li>parent()
 * <li>childAt(int index)
 * <li>childCount()
 * <li>findChild(string name)
 * <li>create(string name, string classname)
 * </ul>
 * Note that the methods above are restricted to a subset of the widget
 * hierarchy by the security policy.
 *
 * <h3>Security Facilities</h3>
 * In order to ensure scripts don't run amok and to ensure the script
 * interpreter used by KHTML remains secure, JSObjectProxy applies a
 * security policy. Every time a script tries to access an object or
 * property the following tests are performed:
 * <ul>
 * <li>Does this request come from the correct @ref KJS::Interpreter?
 * <li>Is the script allowed to access the specified QObject?
 * <li>Is the script allowed to access the specified property?
 * </ul>
 * The @ref KJSEmbed::SecurityPolicy class decides if the request should
 * be granted. The security policy is automatically inherited by any child
 * proxies created using the object tree accessor methods.
 *
 * @see KJSEmbed::Factory
 * @author Richard Moore, rich@kde.org
 * $Id: jsobjectproxy.h,v 1.1.1.1 2002/10/04 23:32:52 rich Exp $
 */
class JSObjectProxy : public KJS::ObjectImp
{
public:
    /**
     * Create a JS binding to the target object. The binding will allow scripts to
     * access any QObject that is descended the target and no others.
     */
    JSObjectProxy( KJS::Interpreter *js, QObject *target );

    /**
     * Create a JS binding to the target object. The binding will allow scripts to
     * access any QObject that is descended from the specified root. If the specified
     * root is 0 then access is granted to all objects.
     */
    JSObjectProxy( KJS::Interpreter *js, QObject *target, QObject *root );

    /**
     * Create a JS binding to the target object. The binding will allow scripts to
     * access any QObject that is descended from the specified root, according to
     * the specified @ref SecurityPolicy . If the specified root is 0 then access
     * is granted to all objects.
     */
    JSObjectProxy( KJS::Interpreter *js, QObject *target,
		   QObject *root, const SecurityPolicy *sp );

    ~JSObjectProxy() {}

    /**
     * Adds methods for traversing the QObject tree to the specified
     * @ref KJS::Object . Only QObjects descended from the root specified
     * in the constructor can be reached through JS proxies created with
     * these bindings.
     */
    virtual void addBindings( KJS::ExecState *state, KJS::Object &object );

    /**
     * A convenience method that binds to the current interpreter.
     */
    void addBindings( KJS::Object &object );

    void addTreeBindings( KJS::ExecState *state, KJS::Object &object );
    void addSlotBindings( KJS::ExecState *state, KJS::Object &object );
    void addFactoryBindings( KJS::ExecState *state, KJS::Object &object );

    KJS::Object createSubProxy( QObject *target, KJS::ExecState *state=0 ) const;

    /**
     * Returns the interpreter in which this proxy lives.
     */
    KJS::Interpreter *jscript() const { return js; }

    /**
     * Returns the root object that defines the limit of the scope of this proxy.
     */
    QObject *rootObject() const { return root; }

    /**
     * Returns the @ref SecurityPolicy of the proxy.
     */
    const SecurityPolicy *securityPolicy() const;

    /**
     * Specifies the @ref SecurityPolicy that should be applied to this proxy.
     * Setting the policy to 0 restores the default policy.
     */
    void setSecurityPolicy( const SecurityPolicy *sp );

    // FIXME: Remove the virtual from these methods when binary compatibility
    // is next broken. The original design was flawed as sub-proxies always used
    // the default security policy.

    /**
     * Returns true iff the specified interpreter can access this proxy.
     * Do NOT re-implement this method, create your own @ref SecurityPolicy
     * object if you want to change the policy.
     */
    virtual bool isAllowed( KJS::Interpreter *interp ) const;

    /**
     * Returns true iff scripts are allowed to see the specified QObject.
     * Do NOT re-implement this method, create your own @ref SecurityPolicy
     * object if you want to change the policy.
     */
    virtual bool isAllowed( QObject *obj ) const;

    /**
     * Returns true iff scripts are allowed to see the specified property.
     * Do NOT re-implement this method, create your own @ref SecurityPolicy
     * object if you want to change the policy.
     */
    virtual bool isAllowed( QObject *obj, const char *prop ) const;

    /**
     * Reimplemented to return the value of the specified property if present.
     */
    virtual KJS::Value get( KJS::ExecState *state, const KJS::UString &p ) const;

    /**
     * Reimplemented to set the value of the specified property if possible.
     */
    virtual void put( KJS::ExecState *state, const KJS::UString &p,
		      const KJS::Value &v, int attr = KJS::None );

    /**
     * Reimplemented to return the name and class of the target.
     */
    virtual KJS::UString toString( KJS::ExecState *exec ) const;

private:
    /**
     * @internal
     * Provides the implementation of a JS method.
     */
    class MethodImp : public KJS::ObjectImp
    {
    public:
	enum MethodId {
	    MethodParent, MethodChildren, MethodSlots, MethodProps,
	    MethodFindChild, MethodChild,
	    MethodChildCount, MethodChildAt,
	    MethodSlot, MethodSlotInt, MethodSlotBool, MethodSlotString,
	    MethodCreate,
	    MethodCustom = 0x800
	};

	MethodImp( int id, const JSObjectProxy *parent );
	MethodImp( int id, const QCString &name, const JSObjectProxy *parent );
	~MethodImp() {}

	/**
	 * Reimplemented to specify that we implement the call operation.
	 */
	virtual bool implementsCall() const { return true; }

	/**
	 * Reimplemented to perform the actions for the proxy object.
	 */
	virtual KJS::Value call( KJS::ExecState *exec, KJS::Object &self,
				 const KJS::List &args );

	/**
	 * Provides access to the child object at the index specified on the
	 * interpreter stack.
	 */
	KJS::Value callChildAt( KJS::ExecState *exec, KJS::Object &self,
				const KJS::List &args );

	/**
	 * Provides access to the child object whose name is specified on the
	 * interpreter stack.
	 */
	KJS::Value callFindChild( KJS::ExecState *exec, KJS::Object &self,
				  const KJS::List &args );

    private:
	int id;
	QCString slotname;
	const JSObjectProxy *proxy;
	class Private *d;
    };

    KJS::Interpreter *js;
    QGuardedPtr<QObject> obj;
    QGuardedPtr<QObject> root;
    class JSObjectProxyPrivate *d;
    friend class MethodImp;
};

}; // namespace KJSEmbed

#endif // KJSEMBEDJSOBJECTPROXY_H

// Local Variables:
// c-basic-offset: 4
// End:
