1/*
2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 * Copyright (C) 2009 Pawel Hajdan (phajdan.jr@chromium.org)
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 *     * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *     * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 *     * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33  CppBoundClass class:
34  This base class serves as a parent for C++ classes designed to be bound to
35  JavaScript objects.
36
37  Subclasses should define the constructor to build the property and method
38  lists needed to bind this class to a JS object.  They should also declare
39  and define member variables and methods to be exposed to JS through
40  that object.
41*/
42
43#ifndef CppBoundClass_h
44#define CppBoundClass_h
45
46#include "CppVariant.h"
47#include <wtf/HashMap.h>
48#include <wtf/Noncopyable.h>
49#include <wtf/OwnPtr.h>
50#include <wtf/Vector.h>
51
52namespace WebKit {
53class WebFrame;
54class WebString;
55}
56
57typedef Vector<CppVariant> CppArgumentList;
58
59// CppBoundClass lets you map Javascript method calls and property accesses
60// directly to C++ method calls and CppVariant* variable access.
61class CppBoundClass {
62    WTF_MAKE_NONCOPYABLE(CppBoundClass);
63public:
64    class PropertyCallback {
65    public:
66        virtual ~PropertyCallback() { }
67
68        // Sets |value| to the value of the property. Returns false in case of
69        // failure. |value| is always non-0.
70        virtual bool getValue(CppVariant* result) = 0;
71
72        // sets the property value to |value|. Returns false in case of failure.
73        virtual bool setValue(const CppVariant&) = 0;
74    };
75
76    // Callback class for "void function(CppVariant*)"
77    class GetterCallback {
78    public:
79        virtual ~GetterCallback() {}
80        virtual void run(CppVariant*) = 0;
81    };
82
83    // The constructor should call BindMethod, BindProperty, and
84    // SetFallbackMethod as needed to set up the methods, properties, and
85    // fallback method.
86    CppBoundClass() : m_boundToFrame(false) {}
87    virtual ~CppBoundClass();
88
89    // Return a CppVariant representing this class, for use with BindProperty().
90    // The variant type is guaranteed to be NPVariantType_Object.
91    CppVariant* getAsCppVariant();
92
93    // Given a WebFrame, BindToJavascript builds the NPObject that will represent
94    // the class and binds it to the frame's window under the given name.  This
95    // should generally be called from the WebView delegate's
96    // WindowObjectCleared(). A class so bound will be accessible to JavaScript
97    // as window.<classname>. The owner of the CppBoundObject is responsible for
98    // keeping the object around while the frame is alive, and for destroying it
99    // afterwards.
100    void bindToJavascript(WebKit::WebFrame*, const WebKit::WebString& classname);
101
102    // Used by a test.  Returns true if a method with name |name| exists,
103    // regardless of whether a fallback is registered.
104    bool isMethodRegistered(const std::string&) const;
105
106protected:
107    // Callback for "void function(const CppArguemntList&, CppVariant*)"
108    class Callback {
109    public:
110        virtual ~Callback() {}
111        virtual void run(const CppArgumentList&, CppVariant*) = 0;
112    };
113
114    // Callback for "void T::method(const CppArguemntList&, CppVariant*)"
115    template <class T> class MemberCallback : public Callback {
116    public:
117        typedef void (T::*MethodType)(const CppArgumentList&, CppVariant*);
118        MemberCallback(T* object, MethodType method)
119            : m_object(object)
120            , m_method(method) {}
121        virtual ~MemberCallback() {}
122
123        virtual void run(const CppArgumentList& arguments, CppVariant* result)
124        {
125            (m_object->*m_method)(arguments, result);
126        }
127    private:
128        T* m_object;
129        MethodType m_method;
130    };
131
132    // Callback class for "void T::method(CppVariant*)"
133    template <class T> class MemberGetterCallback : public GetterCallback {
134    public:
135        typedef void (T::*MethodType)(CppVariant*);
136        MemberGetterCallback(T* object, MethodType method)
137            : m_object(object)
138            , m_method(method) {}
139        virtual ~MemberGetterCallback() {}
140
141        virtual void run(CppVariant* result) { (m_object->*m_method)(result); }
142    private:
143        T* m_object;
144        MethodType m_method;
145    };
146
147    // Bind the Javascript method called the string parameter to the C++ method.
148    void bindCallback(const std::string&, Callback*);
149
150    // A wrapper for bindCallback, to simplify the common case of binding a
151    // method on the current object.  Though not verified here, |method|
152    // must be a method of this CppBoundClass subclass.
153    template<class T>
154    void bindMethod(const std::string& name, void (T::*method)(const CppArgumentList&, CppVariant*))
155    {
156        Callback* callback = new MemberCallback<T>(static_cast<T*>(this), method);
157        bindCallback(name, callback);
158    }
159
160    // Bind Javascript property |name| to the C++ getter callback |callback|.
161    // This can be used to create read-only properties.
162    void bindGetterCallback(const std::string&, GetterCallback*);
163
164    // A wrapper for BindGetterCallback, to simplify the common case of binding a
165    // property on the current object.  Though not verified here, |method|
166    // must be a method of this CppBoundClass subclass.
167    template<class T>
168    void bindProperty(const std::string& name, void (T::*method)(CppVariant*))
169    {
170        GetterCallback* callback = new MemberGetterCallback<T>(static_cast<T*>(this), method);
171        bindGetterCallback(name, callback);
172    }
173
174    // Bind the Javascript property called |name| to a CppVariant.
175    void bindProperty(const std::string&, CppVariant*);
176
177    // Bind Javascript property called |name| to a PropertyCallback.
178    // CppBoundClass assumes control over the life time of the callback.
179    void bindProperty(const std::string&, PropertyCallback*);
180
181    // Set the fallback callback, which is called when when a callback is
182    // invoked that isn't bound.
183    // If it is 0 (its default value), a JavaScript exception is thrown in
184    // that case (as normally expected). If non 0, the fallback method is
185    // invoked and the script continues its execution.
186    // Passing 0 clears out any existing binding.
187    // It is used for tests and should probably only be used in such cases
188    // as it may cause unexpected behaviors (a JavaScript object with a
189    // fallback always returns true when checked for a method's
190    // existence).
191    void bindFallbackCallback(Callback* fallbackCallback)
192    {
193        m_fallbackCallback.set(fallbackCallback);
194    }
195
196    // A wrapper for BindFallbackCallback, to simplify the common case of
197    // binding a method on the current object.  Though not verified here,
198    // |method| must be a method of this CppBoundClass subclass.
199    // Passing 0 for |method| clears out any existing binding.
200    template<class T>
201    void bindFallbackMethod(void (T::*method)(const CppArgumentList&, CppVariant*))
202    {
203        if (method) {
204            Callback* callback = new MemberCallback<T>(static_cast<T*>(this), method);
205            bindFallbackCallback(callback);
206        } else
207            bindFallbackCallback(0);
208    }
209
210    // Some fields are protected because some tests depend on accessing them,
211    // but otherwise they should be considered private.
212
213    typedef HashMap<NPIdentifier, PropertyCallback*> PropertyList;
214    typedef HashMap<NPIdentifier, Callback*> MethodList;
215    // These maps associate names with property and method pointers to be
216    // exposed to JavaScript.
217    PropertyList m_properties;
218    MethodList m_methods;
219
220    // The callback gets invoked when a call is made to an nonexistent method.
221    OwnPtr<Callback> m_fallbackCallback;
222
223private:
224    // NPObject callbacks.
225    friend struct CppNPObject;
226    bool hasMethod(NPIdentifier) const;
227    bool invoke(NPIdentifier, const NPVariant* args, size_t argCount,
228                NPVariant* result);
229    bool hasProperty(NPIdentifier) const;
230    bool getProperty(NPIdentifier, NPVariant* result) const;
231    bool setProperty(NPIdentifier, const NPVariant*);
232
233    // A lazily-initialized CppVariant representing this class.  We retain 1
234    // reference to this object, and it is released on deletion.
235    CppVariant m_selfVariant;
236
237    // True if our np_object has been bound to a WebFrame, in which case it must
238    // be unregistered with V8 when we delete it.
239    bool m_boundToFrame;
240};
241
242#endif // CppBoundClass_h
243