- add sources.
[platform/framework/web/crosswalk.git] / src / webkit / renderer / cpp_bound_class.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // This file contains definitions for CppBoundClass
6
7 // Here's the control flow of a JS method getting forwarded to a class.
8 // - Something calls our NPObject with a function like "Invoke".
9 // - CppNPObject's static invoke() function forwards it to its attached
10 //   CppBoundClass's Invoke() method.
11 // - CppBoundClass has then overridden Invoke() to look up the function
12 //   name in its internal map of methods, and then calls the appropriate
13 //   method.
14
15 #include "webkit/renderer/cpp_bound_class.h"
16
17 #include "base/compiler_specific.h"
18 #include "base/logging.h"
19 #include "base/stl_util.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "third_party/WebKit/public/web/WebBindings.h"
22 #include "third_party/WebKit/public/web/WebFrame.h"
23 #include "third_party/WebKit/public/platform/WebString.h"
24
25 using WebKit::WebBindings;
26 using WebKit::WebFrame;
27
28 namespace webkit_glue {
29
30 namespace {
31
32 class CppVariantPropertyCallback : public CppBoundClass::PropertyCallback {
33  public:
34   CppVariantPropertyCallback(CppVariant* value) : value_(value) { }
35
36   virtual bool GetValue(CppVariant* value) OVERRIDE {
37     value->Set(*value_);
38     return true;
39   }
40   virtual bool SetValue(const CppVariant& value) OVERRIDE {
41     value_->Set(value);
42     return true;
43   }
44
45  private:
46   CppVariant* value_;
47 };
48
49 class GetterPropertyCallback : public CppBoundClass::PropertyCallback {
50 public:
51   GetterPropertyCallback(const CppBoundClass::GetterCallback& callback)
52       : callback_(callback) { }
53
54   virtual bool GetValue(CppVariant* value) OVERRIDE {
55     callback_.Run(value);
56     return true;
57   }
58
59   virtual bool SetValue(const CppVariant& value) OVERRIDE {
60     return false;
61   }
62
63 private:
64   CppBoundClass::GetterCallback callback_;
65 };
66
67 }
68
69 // Our special NPObject type.  We extend an NPObject with a pointer to a
70 // CppBoundClass, which is just a C++ interface that we forward all NPObject
71 // callbacks to.
72 struct CppNPObject {
73   NPObject parent;  // This must be the first field in the struct.
74   CppBoundClass* bound_class;
75
76   //
77   // All following objects and functions are static, and just used to interface
78   // with NPObject/NPClass.
79   //
80
81   // An NPClass associates static functions of CppNPObject with the
82   // function pointers used by the JS runtime.
83   static NPClass np_class_;
84
85   // Allocate a new NPObject with the specified class.
86   static NPObject* allocate(NPP npp, NPClass* aClass);
87
88   // Free an object.
89   static void deallocate(NPObject* obj);
90
91   // Returns true if the C++ class associated with this NPObject exposes the
92   // given property.  Called by the JS runtime.
93   static bool hasProperty(NPObject *obj, NPIdentifier ident);
94
95   // Returns true if the C++ class associated with this NPObject exposes the
96   // given method.  Called by the JS runtime.
97   static bool hasMethod(NPObject *obj, NPIdentifier ident);
98
99   // If the given method is exposed by the C++ class associated with this
100   // NPObject, invokes it with the given args and returns a result.  Otherwise,
101   // returns "undefined" (in the JavaScript sense).  Called by the JS runtime.
102   static bool invoke(NPObject *obj, NPIdentifier ident,
103                      const NPVariant *args, uint32_t arg_count,
104                      NPVariant *result);
105
106   // If the given property is exposed by the C++ class associated with this
107   // NPObject, returns its value.  Otherwise, returns "undefined" (in the
108   // JavaScript sense).  Called by the JS runtime.
109   static bool getProperty(NPObject *obj, NPIdentifier ident,
110                           NPVariant *result);
111
112   // If the given property is exposed by the C++ class associated with this
113   // NPObject, sets its value.  Otherwise, does nothing. Called by the JS
114   // runtime.
115   static bool setProperty(NPObject *obj, NPIdentifier ident,
116                           const NPVariant *value);
117 };
118
119 // Build CppNPObject's static function pointers into an NPClass, for use
120 // in constructing NPObjects for the C++ classes.
121 NPClass CppNPObject::np_class_ = {
122   NP_CLASS_STRUCT_VERSION,
123   CppNPObject::allocate,
124   CppNPObject::deallocate,
125   /* NPInvalidateFunctionPtr */ NULL,
126   CppNPObject::hasMethod,
127   CppNPObject::invoke,
128   /* NPInvokeDefaultFunctionPtr */ NULL,
129   CppNPObject::hasProperty,
130   CppNPObject::getProperty,
131   CppNPObject::setProperty,
132   /* NPRemovePropertyFunctionPtr */ NULL
133 };
134
135 /* static */ NPObject* CppNPObject::allocate(NPP npp, NPClass* aClass) {
136   CppNPObject* obj = new CppNPObject;
137   // obj->parent will be initialized by the NPObject code calling this.
138   obj->bound_class = NULL;
139   return &obj->parent;
140 }
141
142 /* static */ void CppNPObject::deallocate(NPObject* np_obj) {
143   CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj);
144   delete obj;
145 }
146
147 /* static */ bool CppNPObject::hasMethod(NPObject* np_obj,
148                                          NPIdentifier ident) {
149   CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj);
150   return obj->bound_class->HasMethod(ident);
151 }
152
153 /* static */ bool CppNPObject::hasProperty(NPObject* np_obj,
154                                            NPIdentifier ident) {
155   CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj);
156   return obj->bound_class->HasProperty(ident);
157 }
158
159 /* static */ bool CppNPObject::invoke(NPObject* np_obj, NPIdentifier ident,
160                                       const NPVariant* args, uint32_t arg_count,
161                                       NPVariant* result) {
162   CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj);
163   return obj->bound_class->Invoke(ident, args, arg_count, result);
164 }
165
166 /* static */ bool CppNPObject::getProperty(NPObject* np_obj,
167                                            NPIdentifier ident,
168                                            NPVariant* result) {
169   CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj);
170   return obj->bound_class->GetProperty(ident, result);
171 }
172
173 /* static */ bool CppNPObject::setProperty(NPObject* np_obj,
174                                            NPIdentifier ident,
175                                            const NPVariant* value) {
176   CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj);
177   return obj->bound_class->SetProperty(ident, value);
178 }
179
180 CppBoundClass::CppBoundClass() : bound_to_frame_(false), npp_(new NPP_t) {
181   WebBindings::registerObjectOwner(npp_.get());
182 }
183
184 CppBoundClass::~CppBoundClass() {
185   STLDeleteValues(&properties_);
186
187   // TODO(wez): Remove once crrev.com/14019005 lands.
188   if (bound_to_frame_)
189     WebBindings::unregisterObject(NPVARIANT_TO_OBJECT(self_variant_));
190
191   WebBindings::unregisterObjectOwner(npp_.get());
192 }
193
194 bool CppBoundClass::HasMethod(NPIdentifier ident) const {
195   return (methods_.find(ident) != methods_.end());
196 }
197
198 bool CppBoundClass::HasProperty(NPIdentifier ident) const {
199   return (properties_.find(ident) != properties_.end());
200 }
201
202 bool CppBoundClass::Invoke(NPIdentifier ident,
203                            const NPVariant* args,
204                            size_t arg_count,
205                            NPVariant* result) {
206   MethodList::const_iterator method = methods_.find(ident);
207   Callback callback;
208   if (method == methods_.end()) {
209     if (!fallback_callback_.is_null()) {
210       callback = fallback_callback_;
211     } else {
212       VOID_TO_NPVARIANT(*result);
213       return false;
214     }
215   } else {
216     callback = method->second;
217   }
218
219   // Build a CppArgumentList argument vector from the NPVariants coming in.
220   CppArgumentList cpp_args(arg_count);
221   for (size_t i = 0; i < arg_count; i++)
222     cpp_args[i].Set(args[i]);
223
224   CppVariant cpp_result;
225   callback.Run(cpp_args, &cpp_result);
226
227   cpp_result.CopyToNPVariant(result);
228   return true;
229 }
230
231 bool CppBoundClass::GetProperty(NPIdentifier ident, NPVariant* result) const {
232   PropertyList::const_iterator callback = properties_.find(ident);
233   if (callback == properties_.end()) {
234     VOID_TO_NPVARIANT(*result);
235     return false;
236   }
237
238   CppVariant cpp_value;
239   if (!callback->second->GetValue(&cpp_value))
240     return false;
241   cpp_value.CopyToNPVariant(result);
242   return true;
243 }
244
245 bool CppBoundClass::SetProperty(NPIdentifier ident,
246                                 const NPVariant* value) {
247   PropertyList::iterator callback = properties_.find(ident);
248   if (callback == properties_.end())
249     return false;
250
251   CppVariant cpp_value;
252   cpp_value.Set(*value);
253   return (*callback).second->SetValue(cpp_value);
254 }
255
256 void CppBoundClass::BindCallback(const std::string& name,
257                                  const Callback& callback) {
258   NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str());
259   if (callback.is_null()) {
260     methods_.erase(ident);
261     return;
262   }
263
264   methods_[ident] = callback;
265 }
266
267 void CppBoundClass::BindGetterCallback(const std::string& name,
268                                        const GetterCallback& callback) {
269   PropertyCallback* property_callback = callback.is_null() ?
270       NULL : new GetterPropertyCallback(callback);
271
272   BindProperty(name, property_callback);
273 }
274
275 void CppBoundClass::BindProperty(const std::string& name, CppVariant* prop) {
276   PropertyCallback* property_callback = prop == NULL ?
277       NULL : new CppVariantPropertyCallback(prop);
278
279   BindProperty(name, property_callback);
280 }
281
282 void CppBoundClass::BindProperty(const std::string& name,
283                                  PropertyCallback* callback) {
284   NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str());
285   PropertyList::iterator old_callback = properties_.find(ident);
286   if (old_callback != properties_.end()) {
287     delete old_callback->second;
288     if (callback == NULL) {
289       properties_.erase(old_callback);
290       return;
291     }
292   }
293
294   properties_[ident] = callback;
295 }
296
297 bool CppBoundClass::IsMethodRegistered(const std::string& name) const {
298   NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str());
299   MethodList::const_iterator callback = methods_.find(ident);
300   return (callback != methods_.end());
301 }
302
303 CppVariant* CppBoundClass::GetAsCppVariant() {
304   if (!self_variant_.isObject()) {
305     // Create an NPObject using our static NPClass.  The first argument has type
306     // NPP, but is only used to track object ownership, so passing this is fine.
307     NPObject* np_obj = WebBindings::createObject(
308         npp_.get(), &CppNPObject::np_class_);
309     CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj);
310     obj->bound_class = this;
311     self_variant_.Set(np_obj);
312     WebBindings::releaseObject(np_obj);  // CppVariant takes the reference.
313   }
314   DCHECK(self_variant_.isObject());
315   return &self_variant_;
316 }
317
318 void CppBoundClass::BindToJavascript(WebFrame* frame,
319                                      const std::string& classname) {
320   // BindToWindowObject will take its own reference to the NPObject, and clean
321   // up after itself. It will also (indirectly) register the object with V8,
322   // against an owner pointer we supply, so we must register that as an owner,
323   // and unregister when we teardown.
324   frame->bindToWindowObject(ASCIIToUTF16(classname),
325                             NPVARIANT_TO_OBJECT(*GetAsCppVariant()));
326   bound_to_frame_ = true;
327 }
328
329 }  // namespace webkit_glue