OLD | NEW |
(Empty) | |
| 1 // Copyright 2013 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 /* |
| 6 * Copyright (C) 2010 Google Inc. All rights reserved. |
| 7 * Copyright (C) 2009 Pawel Hajdan (phajdan.jr@chromium.org) |
| 8 * |
| 9 * Redistribution and use in source and binary forms, with or without |
| 10 * modification, are permitted provided that the following conditions are |
| 11 * met: |
| 12 * |
| 13 * * Redistributions of source code must retain the above copyright |
| 14 * notice, this list of conditions and the following disclaimer. |
| 15 * * Redistributions in binary form must reproduce the above |
| 16 * copyright notice, this list of conditions and the following disclaimer |
| 17 * in the documentation and/or other materials provided with the |
| 18 * distribution. |
| 19 * * Neither the name of Google Inc. nor the names of its |
| 20 * contributors may be used to endorse or promote products derived from |
| 21 * this software without specific prior written permission. |
| 22 * |
| 23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 27 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 29 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 30 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 31 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 33 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 34 */ |
| 35 |
| 36 // This file contains definitions for CppBoundClass |
| 37 |
| 38 // Here's the control flow of a JS method getting forwarded to a class. |
| 39 // - Something calls our NPObject with a function like "Invoke". |
| 40 // - CppNPObject's static invoke() function forwards it to its attached |
| 41 // CppBoundClass's invoke() method. |
| 42 // - CppBoundClass has then overridden invoke() to look up the function |
| 43 // name in its internal map of methods, and then calls the appropriate |
| 44 // method. |
| 45 |
| 46 #include "content/shell/renderer/test_runner/CppBoundClass.h" |
| 47 |
| 48 #include "content/shell/renderer/test_runner/TestCommon.h" |
| 49 #include "third_party/WebKit/public/platform/WebString.h" |
| 50 #include "third_party/WebKit/public/web/WebBindings.h" |
| 51 #include "third_party/WebKit/public/web/WebFrame.h" |
| 52 |
| 53 using namespace blink; |
| 54 using namespace std; |
| 55 |
| 56 namespace WebTestRunner { |
| 57 |
| 58 namespace { |
| 59 |
| 60 class CppVariantPropertyCallback : public CppBoundClass::PropertyCallback { |
| 61 public: |
| 62 CppVariantPropertyCallback(CppVariant* value) : m_value(value) { } |
| 63 |
| 64 virtual bool getValue(CppVariant* value) |
| 65 { |
| 66 value->set(*m_value); |
| 67 return true; |
| 68 } |
| 69 |
| 70 virtual bool setValue(const CppVariant& value) |
| 71 { |
| 72 m_value->set(value); |
| 73 return true; |
| 74 } |
| 75 |
| 76 private: |
| 77 CppVariant* m_value; |
| 78 }; |
| 79 |
| 80 class GetterPropertyCallback : public CppBoundClass::PropertyCallback { |
| 81 public: |
| 82 GetterPropertyCallback(WebScopedPtr<CppBoundClass::GetterCallback> callback) |
| 83 : m_callback(callback) |
| 84 { |
| 85 } |
| 86 |
| 87 virtual bool getValue(CppVariant* value) |
| 88 { |
| 89 m_callback->run(value); |
| 90 return true; |
| 91 } |
| 92 |
| 93 virtual bool setValue(const CppVariant& value) { return false; } |
| 94 |
| 95 private: |
| 96 WebScopedPtr<CppBoundClass::GetterCallback> m_callback; |
| 97 }; |
| 98 |
| 99 } |
| 100 |
| 101 // Our special NPObject type. We extend an NPObject with a pointer to a |
| 102 // CppBoundClass, which is just a C++ interface that we forward all NPObject |
| 103 // callbacks to. |
| 104 struct CppNPObject { |
| 105 NPObject parent; // This must be the first field in the struct. |
| 106 CppBoundClass* boundClass; |
| 107 |
| 108 // |
| 109 // All following objects and functions are static, and just used to interfac
e |
| 110 // with NPObject/NPClass. |
| 111 // |
| 112 |
| 113 // An NPClass associates static functions of CppNPObject with the |
| 114 // function pointers used by the JS runtime. |
| 115 static NPClass npClass; |
| 116 |
| 117 // Allocate a new NPObject with the specified class. |
| 118 static NPObject* allocate(NPP, NPClass*); |
| 119 |
| 120 // Free an object. |
| 121 static void deallocate(NPObject*); |
| 122 |
| 123 // Returns true if the C++ class associated with this NPObject exposes the |
| 124 // given property. Called by the JS runtime. |
| 125 static bool hasProperty(NPObject*, NPIdentifier); |
| 126 |
| 127 // Returns true if the C++ class associated with this NPObject exposes the |
| 128 // given method. Called by the JS runtime. |
| 129 static bool hasMethod(NPObject*, NPIdentifier); |
| 130 |
| 131 // If the given method is exposed by the C++ class associated with this |
| 132 // NPObject, invokes it with the given arguments and returns a result. Other
wise, |
| 133 // returns "undefined" (in the JavaScript sense). Called by the JS runtime. |
| 134 static bool invoke(NPObject*, NPIdentifier, |
| 135 const NPVariant* arguments, uint32_t argumentCount, |
| 136 NPVariant* result); |
| 137 |
| 138 // If the given property is exposed by the C++ class associated with this |
| 139 // NPObject, returns its value. Otherwise, returns "undefined" (in the |
| 140 // JavaScript sense). Called by the JS runtime. |
| 141 static bool getProperty(NPObject*, NPIdentifier, NPVariant* result); |
| 142 |
| 143 // If the given property is exposed by the C++ class associated with this |
| 144 // NPObject, sets its value. Otherwise, does nothing. Called by the JS |
| 145 // runtime. |
| 146 static bool setProperty(NPObject*, NPIdentifier, const NPVariant* value); |
| 147 }; |
| 148 |
| 149 // Build CppNPObject's static function pointers into an NPClass, for use |
| 150 // in constructing NPObjects for the C++ classes. |
| 151 NPClass CppNPObject::npClass = { |
| 152 NP_CLASS_STRUCT_VERSION, |
| 153 CppNPObject::allocate, |
| 154 CppNPObject::deallocate, |
| 155 /* NPInvalidateFunctionPtr */ 0, |
| 156 CppNPObject::hasMethod, |
| 157 CppNPObject::invoke, |
| 158 /* NPInvokeDefaultFunctionPtr */ 0, |
| 159 CppNPObject::hasProperty, |
| 160 CppNPObject::getProperty, |
| 161 CppNPObject::setProperty, |
| 162 /* NPRemovePropertyFunctionPtr */ 0 |
| 163 }; |
| 164 |
| 165 NPObject* CppNPObject::allocate(NPP npp, NPClass* aClass) |
| 166 { |
| 167 CppNPObject* obj = new CppNPObject; |
| 168 // obj->parent will be initialized by the NPObject code calling this. |
| 169 obj->boundClass = 0; |
| 170 return &obj->parent; |
| 171 } |
| 172 |
| 173 void CppNPObject::deallocate(NPObject* npObj) |
| 174 { |
| 175 CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj); |
| 176 delete obj; |
| 177 } |
| 178 |
| 179 bool CppNPObject::hasMethod(NPObject* npObj, NPIdentifier ident) |
| 180 { |
| 181 CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj); |
| 182 return obj->boundClass->hasMethod(ident); |
| 183 } |
| 184 |
| 185 bool CppNPObject::hasProperty(NPObject* npObj, NPIdentifier ident) |
| 186 { |
| 187 CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj); |
| 188 return obj->boundClass->hasProperty(ident); |
| 189 } |
| 190 |
| 191 bool CppNPObject::invoke(NPObject* npObj, NPIdentifier ident, |
| 192 const NPVariant* arguments, uint32_t argumentCount, |
| 193 NPVariant* result) |
| 194 { |
| 195 CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj); |
| 196 return obj->boundClass->invoke(ident, arguments, argumentCount, result); |
| 197 } |
| 198 |
| 199 bool CppNPObject::getProperty(NPObject* npObj, NPIdentifier ident, NPVariant* re
sult) |
| 200 { |
| 201 CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj); |
| 202 return obj->boundClass->getProperty(ident, result); |
| 203 } |
| 204 |
| 205 bool CppNPObject::setProperty(NPObject* npObj, NPIdentifier ident, const NPVaria
nt* value) |
| 206 { |
| 207 CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj); |
| 208 return obj->boundClass->setProperty(ident, value); |
| 209 } |
| 210 |
| 211 CppBoundClass::~CppBoundClass() |
| 212 { |
| 213 for (MethodList::iterator i = m_methods.begin(); i != m_methods.end(); ++i) |
| 214 delete i->second; |
| 215 |
| 216 for (PropertyList::iterator i = m_properties.begin(); i != m_properties.end(
); ++i) |
| 217 delete i->second; |
| 218 |
| 219 // Unregister ourselves if we were bound to a frame. |
| 220 if (m_boundToFrame) |
| 221 WebBindings::unregisterObject(NPVARIANT_TO_OBJECT(m_selfVariant)); |
| 222 } |
| 223 |
| 224 bool CppBoundClass::hasMethod(NPIdentifier ident) const |
| 225 { |
| 226 return m_methods.find(ident) != m_methods.end(); |
| 227 } |
| 228 |
| 229 bool CppBoundClass::hasProperty(NPIdentifier ident) const |
| 230 { |
| 231 return m_properties.find(ident) != m_properties.end(); |
| 232 } |
| 233 |
| 234 bool CppBoundClass::invoke(NPIdentifier ident, |
| 235 const NPVariant* arguments, |
| 236 size_t argumentCount, |
| 237 NPVariant* result) { |
| 238 MethodList::const_iterator end = m_methods.end(); |
| 239 MethodList::const_iterator method = m_methods.find(ident); |
| 240 Callback* callback; |
| 241 if (method == end) { |
| 242 if (!m_fallbackCallback.get()) { |
| 243 VOID_TO_NPVARIANT(*result); |
| 244 return false; |
| 245 } |
| 246 callback = m_fallbackCallback.get(); |
| 247 } else |
| 248 callback = (*method).second; |
| 249 |
| 250 // Build a CppArgumentList argument vector from the NPVariants coming in. |
| 251 CppArgumentList cppArguments(argumentCount); |
| 252 for (size_t i = 0; i < argumentCount; i++) |
| 253 cppArguments[i].set(arguments[i]); |
| 254 |
| 255 CppVariant cppResult; |
| 256 callback->run(cppArguments, &cppResult); |
| 257 |
| 258 cppResult.copyToNPVariant(result); |
| 259 return true; |
| 260 } |
| 261 |
| 262 bool CppBoundClass::getProperty(NPIdentifier ident, NPVariant* result) const |
| 263 { |
| 264 PropertyList::const_iterator callback = m_properties.find(ident); |
| 265 if (callback == m_properties.end()) { |
| 266 VOID_TO_NPVARIANT(*result); |
| 267 return false; |
| 268 } |
| 269 |
| 270 CppVariant cppValue; |
| 271 if (!callback->second->getValue(&cppValue)) |
| 272 return false; |
| 273 cppValue.copyToNPVariant(result); |
| 274 return true; |
| 275 } |
| 276 |
| 277 bool CppBoundClass::setProperty(NPIdentifier ident, const NPVariant* value) |
| 278 { |
| 279 PropertyList::iterator callback = m_properties.find(ident); |
| 280 if (callback == m_properties.end()) |
| 281 return false; |
| 282 |
| 283 CppVariant cppValue; |
| 284 cppValue.set(*value); |
| 285 return (*callback).second->setValue(cppValue); |
| 286 } |
| 287 |
| 288 void CppBoundClass::bindCallback(const string& name, Callback* callback) |
| 289 { |
| 290 NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str()); |
| 291 MethodList::iterator oldCallback = m_methods.find(ident); |
| 292 if (oldCallback != m_methods.end()) { |
| 293 delete oldCallback->second; |
| 294 if (!callback) { |
| 295 m_methods.erase(oldCallback); |
| 296 return; |
| 297 } |
| 298 } |
| 299 |
| 300 m_methods[ident] = callback; |
| 301 } |
| 302 |
| 303 void CppBoundClass::bindGetterCallback(const string& name, WebScopedPtr<GetterCa
llback> callback) |
| 304 { |
| 305 PropertyCallback* propertyCallback = callback.get() ? new GetterPropertyCall
back(callback) : 0; |
| 306 bindProperty(name, propertyCallback); |
| 307 } |
| 308 |
| 309 void CppBoundClass::bindProperty(const string& name, CppVariant* prop) |
| 310 { |
| 311 PropertyCallback* propertyCallback = prop ? new CppVariantPropertyCallback(p
rop) : 0; |
| 312 bindProperty(name, propertyCallback); |
| 313 } |
| 314 |
| 315 void CppBoundClass::bindProperty(const string& name, PropertyCallback* callback) |
| 316 { |
| 317 NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str()); |
| 318 PropertyList::iterator oldCallback = m_properties.find(ident); |
| 319 if (oldCallback != m_properties.end()) { |
| 320 delete oldCallback->second; |
| 321 if (!callback) { |
| 322 m_properties.erase(oldCallback); |
| 323 return; |
| 324 } |
| 325 } |
| 326 |
| 327 m_properties[ident] = callback; |
| 328 } |
| 329 |
| 330 bool CppBoundClass::isMethodRegistered(const string& name) const |
| 331 { |
| 332 NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str()); |
| 333 MethodList::const_iterator callback = m_methods.find(ident); |
| 334 return callback != m_methods.end(); |
| 335 } |
| 336 |
| 337 CppVariant* CppBoundClass::getAsCppVariant() |
| 338 { |
| 339 if (!m_selfVariant.isObject()) { |
| 340 // Create an NPObject using our static NPClass. The first argument (a |
| 341 // plugin's instance handle) is passed through to the allocate function |
| 342 // directly, and we don't use it, so it's ok to be 0. |
| 343 NPObject* npObj = WebBindings::createObject(0, &CppNPObject::npClass); |
| 344 CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj); |
| 345 obj->boundClass = this; |
| 346 m_selfVariant.set(npObj); |
| 347 WebBindings::releaseObject(npObj); // CppVariant takes the reference. |
| 348 } |
| 349 BLINK_ASSERT(m_selfVariant.isObject()); |
| 350 return &m_selfVariant; |
| 351 } |
| 352 |
| 353 void CppBoundClass::bindToJavascript(WebFrame* frame, const WebString& classname
) |
| 354 { |
| 355 // BindToWindowObject will take its own reference to the NPObject, and clean |
| 356 // up after itself. It will also (indirectly) register the object with V8, |
| 357 // so we must remember this so we can unregister it when we're destroyed. |
| 358 frame->bindToWindowObject(classname, NPVARIANT_TO_OBJECT(*getAsCppVariant())
, 0); |
| 359 m_boundToFrame = true; |
| 360 } |
| 361 |
| 362 } |
OLD | NEW |