• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2006-2008 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 "base/compiler_specific.h"
16 #include "base/logging.h"
17 #include "base/utf_string_conversions.h"
18 #include "third_party/WebKit/Source/WebKit/chromium/public/WebBindings.h"
19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
20 #include "third_party/WebKit/Source/WebKit/chromium/public/WebString.h"
21 #include "webkit/glue/cpp_bound_class.h"
22 
23 using WebKit::WebBindings;
24 using WebKit::WebFrame;
25 
26 namespace {
27 
28 class CppVariantPropertyCallback : public CppBoundClass::PropertyCallback {
29  public:
CppVariantPropertyCallback(CppVariant * value)30   CppVariantPropertyCallback(CppVariant* value) : value_(value) { }
31 
GetValue(CppVariant * value)32   virtual bool GetValue(CppVariant* value) {
33     value->Set(*value_);
34     return true;
35   }
SetValue(const CppVariant & value)36   virtual bool SetValue(const CppVariant& value) {
37     value_->Set(value);
38     return true;
39   }
40 
41  private:
42   CppVariant* value_;
43 };
44 
45 class GetterPropertyCallback : public CppBoundClass::PropertyCallback {
46 public:
GetterPropertyCallback(CppBoundClass::GetterCallback * callback)47   GetterPropertyCallback(CppBoundClass::GetterCallback* callback)
48       : callback_(callback) { }
49 
GetValue(CppVariant * value)50   virtual bool GetValue(CppVariant* value) {
51     callback_->Run(value);
52     return true;
53   }
54 
SetValue(const CppVariant & value)55   virtual bool SetValue(const CppVariant& value) {
56     return false;
57   }
58 
59 private:
60   scoped_ptr<CppBoundClass::GetterCallback> callback_;
61 };
62 
63 }
64 
65 // Our special NPObject type.  We extend an NPObject with a pointer to a
66 // CppBoundClass, which is just a C++ interface that we forward all NPObject
67 // callbacks to.
68 struct CppNPObject {
69   NPObject parent;  // This must be the first field in the struct.
70   CppBoundClass* bound_class;
71 
72   //
73   // All following objects and functions are static, and just used to interface
74   // with NPObject/NPClass.
75   //
76 
77   // An NPClass associates static functions of CppNPObject with the
78   // function pointers used by the JS runtime.
79   static NPClass np_class_;
80 
81   // Allocate a new NPObject with the specified class.
82   static NPObject* allocate(NPP npp, NPClass* aClass);
83 
84   // Free an object.
85   static void deallocate(NPObject* obj);
86 
87   // Returns true if the C++ class associated with this NPObject exposes the
88   // given property.  Called by the JS runtime.
89   static bool hasProperty(NPObject *obj, NPIdentifier ident);
90 
91   // Returns true if the C++ class associated with this NPObject exposes the
92   // given method.  Called by the JS runtime.
93   static bool hasMethod(NPObject *obj, NPIdentifier ident);
94 
95   // If the given method is exposed by the C++ class associated with this
96   // NPObject, invokes it with the given args and returns a result.  Otherwise,
97   // returns "undefined" (in the JavaScript sense).  Called by the JS runtime.
98   static bool invoke(NPObject *obj, NPIdentifier ident,
99                      const NPVariant *args, uint32_t arg_count,
100                      NPVariant *result);
101 
102   // If the given property is exposed by the C++ class associated with this
103   // NPObject, returns its value.  Otherwise, returns "undefined" (in the
104   // JavaScript sense).  Called by the JS runtime.
105   static bool getProperty(NPObject *obj, NPIdentifier ident,
106                           NPVariant *result);
107 
108   // If the given property is exposed by the C++ class associated with this
109   // NPObject, sets its value.  Otherwise, does nothing. Called by the JS
110   // runtime.
111   static bool setProperty(NPObject *obj, NPIdentifier ident,
112                           const NPVariant *value);
113 };
114 
115 // Build CppNPObject's static function pointers into an NPClass, for use
116 // in constructing NPObjects for the C++ classes.
117 NPClass CppNPObject::np_class_ = {
118   NP_CLASS_STRUCT_VERSION,
119   CppNPObject::allocate,
120   CppNPObject::deallocate,
121   /* NPInvalidateFunctionPtr */ NULL,
122   CppNPObject::hasMethod,
123   CppNPObject::invoke,
124   /* NPInvokeDefaultFunctionPtr */ NULL,
125   CppNPObject::hasProperty,
126   CppNPObject::getProperty,
127   CppNPObject::setProperty,
128   /* NPRemovePropertyFunctionPtr */ NULL
129 };
130 
allocate(NPP npp,NPClass * aClass)131 /* static */ NPObject* CppNPObject::allocate(NPP npp, NPClass* aClass) {
132   CppNPObject* obj = new CppNPObject;
133   // obj->parent will be initialized by the NPObject code calling this.
134   obj->bound_class = NULL;
135   return &obj->parent;
136 }
137 
deallocate(NPObject * np_obj)138 /* static */ void CppNPObject::deallocate(NPObject* np_obj) {
139   CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj);
140   delete obj;
141 }
142 
hasMethod(NPObject * np_obj,NPIdentifier ident)143 /* static */ bool CppNPObject::hasMethod(NPObject* np_obj,
144                                          NPIdentifier ident) {
145   CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj);
146   return obj->bound_class->HasMethod(ident);
147 }
148 
hasProperty(NPObject * np_obj,NPIdentifier ident)149 /* static */ bool CppNPObject::hasProperty(NPObject* np_obj,
150                                            NPIdentifier ident) {
151   CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj);
152   return obj->bound_class->HasProperty(ident);
153 }
154 
invoke(NPObject * np_obj,NPIdentifier ident,const NPVariant * args,uint32_t arg_count,NPVariant * result)155 /* static */ bool CppNPObject::invoke(NPObject* np_obj, NPIdentifier ident,
156                                       const NPVariant* args, uint32_t arg_count,
157                                       NPVariant* result) {
158   CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj);
159   return obj->bound_class->Invoke(ident, args, arg_count, result);
160 }
161 
getProperty(NPObject * np_obj,NPIdentifier ident,NPVariant * result)162 /* static */ bool CppNPObject::getProperty(NPObject* np_obj,
163                                            NPIdentifier ident,
164                                            NPVariant* result) {
165   CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj);
166   return obj->bound_class->GetProperty(ident, result);
167 }
168 
setProperty(NPObject * np_obj,NPIdentifier ident,const NPVariant * value)169 /* static */ bool CppNPObject::setProperty(NPObject* np_obj,
170                                            NPIdentifier ident,
171                                            const NPVariant* value) {
172   CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj);
173   return obj->bound_class->SetProperty(ident, value);
174 }
175 
CppBoundClass()176 CppBoundClass::CppBoundClass()
177     : bound_to_frame_(false) {
178 }
179 
~CppBoundClass()180 CppBoundClass::~CppBoundClass() {
181   for (MethodList::iterator i = methods_.begin(); i != methods_.end(); ++i)
182     delete i->second;
183 
184   for (PropertyList::iterator i = properties_.begin(); i != properties_.end();
185       ++i) {
186     delete i->second;
187   }
188 
189   // Unregister ourselves if we were bound to a frame.
190   if (bound_to_frame_)
191     WebBindings::unregisterObject(NPVARIANT_TO_OBJECT(self_variant_));
192 }
193 
HasMethod(NPIdentifier ident) const194 bool CppBoundClass::HasMethod(NPIdentifier ident) const {
195   return (methods_.find(ident) != methods_.end());
196 }
197 
HasProperty(NPIdentifier ident) const198 bool CppBoundClass::HasProperty(NPIdentifier ident) const {
199   return (properties_.find(ident) != properties_.end());
200 }
201 
Invoke(NPIdentifier ident,const NPVariant * args,size_t arg_count,NPVariant * result)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_.get()) {
210       callback = fallback_callback_.get();
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 
GetProperty(NPIdentifier ident,NPVariant * result) const231 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 
SetProperty(NPIdentifier ident,const NPVariant * value)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 
BindCallback(const std::string & name,Callback * callback)256 void CppBoundClass::BindCallback(const std::string& name, Callback* callback) {
257   NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str());
258   MethodList::iterator old_callback = methods_.find(ident);
259   if (old_callback != methods_.end()) {
260     delete old_callback->second;
261     if (callback == NULL) {
262       methods_.erase(old_callback);
263       return;
264     }
265   }
266 
267   methods_[ident] = callback;
268 }
269 
BindGetterCallback(const std::string & name,GetterCallback * callback)270 void CppBoundClass::BindGetterCallback(const std::string& name,
271                                        GetterCallback* callback) {
272   PropertyCallback* property_callback = callback == NULL ?
273       NULL : new GetterPropertyCallback(callback);
274 
275   BindProperty(name, property_callback);
276 }
277 
BindProperty(const std::string & name,CppVariant * prop)278 void CppBoundClass::BindProperty(const std::string& name, CppVariant* prop) {
279   PropertyCallback* property_callback = prop == NULL ?
280       NULL : new CppVariantPropertyCallback(prop);
281 
282   BindProperty(name, property_callback);
283 }
284 
BindProperty(const std::string & name,PropertyCallback * callback)285 void CppBoundClass::BindProperty(const std::string& name,
286                                  PropertyCallback* callback) {
287   NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str());
288   PropertyList::iterator old_callback = properties_.find(ident);
289   if (old_callback != properties_.end()) {
290     delete old_callback->second;
291     if (callback == NULL) {
292       properties_.erase(old_callback);
293       return;
294     }
295   }
296 
297   properties_[ident] = callback;
298 }
299 
IsMethodRegistered(const std::string & name) const300 bool CppBoundClass::IsMethodRegistered(const std::string& name) const {
301   NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str());
302   MethodList::const_iterator callback = methods_.find(ident);
303   return (callback != methods_.end());
304 }
305 
GetAsCppVariant()306 CppVariant* CppBoundClass::GetAsCppVariant() {
307   if (!self_variant_.isObject()) {
308     // Create an NPObject using our static NPClass.  The first argument (a
309     // plugin's instance handle) is passed through to the allocate function
310     // directly, and we don't use it, so it's ok to be 0.
311     NPObject* np_obj = WebBindings::createObject(0, &CppNPObject::np_class_);
312     CppNPObject* obj = reinterpret_cast<CppNPObject*>(np_obj);
313     obj->bound_class = this;
314     self_variant_.Set(np_obj);
315     WebBindings::releaseObject(np_obj);  // CppVariant takes the reference.
316   }
317   DCHECK(self_variant_.isObject());
318   return &self_variant_;
319 }
320 
BindToJavascript(WebFrame * frame,const std::string & classname)321 void CppBoundClass::BindToJavascript(WebFrame* frame,
322                                      const std::string& classname) {
323 #if WEBKIT_USING_JSC
324 #error "This is not going to work anymore...but it's not clear what the solution is...or if it's still necessary."
325   JSC::JSLock lock(false);
326 #endif
327 
328   // BindToWindowObject will take its own reference to the NPObject, and clean
329   // up after itself.  It will also (indirectly) register the object with V8,
330   // so we must remember this so we can unregister it when we're destroyed.
331   frame->bindToWindowObject(ASCIIToUTF16(classname),
332                             NPVARIANT_TO_OBJECT(*GetAsCppVariant()));
333   bound_to_frame_ = true;
334 }
335