1// Copyright (c) 2011 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#include "content/renderer/pepper/npapi_glue.h" 6 7#include "base/logging.h" 8#include "base/memory/ref_counted.h" 9#include "base/strings/string_util.h" 10#include "content/renderer/pepper/host_array_buffer_var.h" 11#include "content/renderer/pepper/host_globals.h" 12#include "content/renderer/pepper/host_var_tracker.h" 13#include "content/renderer/pepper/npobject_var.h" 14#include "content/renderer/pepper/pepper_plugin_instance_impl.h" 15#include "content/renderer/pepper/plugin_module.h" 16#include "content/renderer/pepper/plugin_object.h" 17#include "ppapi/c/pp_var.h" 18#include "third_party/npapi/bindings/npapi.h" 19#include "third_party/npapi/bindings/npruntime.h" 20#include "third_party/WebKit/public/web/WebBindings.h" 21#include "third_party/WebKit/public/web/WebDocument.h" 22#include "third_party/WebKit/public/web/WebElement.h" 23#include "third_party/WebKit/public/web/WebLocalFrame.h" 24#include "third_party/WebKit/public/web/WebPluginContainer.h" 25#include "v8/include/v8.h" 26 27using ppapi::NPObjectVar; 28using ppapi::PpapiGlobals; 29using ppapi::StringVar; 30using ppapi::Var; 31using blink::WebArrayBuffer; 32using blink::WebBindings; 33using blink::WebLocalFrame; 34using blink::WebPluginContainer; 35 36namespace content { 37 38namespace { 39 40const char kInvalidPluginValue[] = "Error: Plugin returned invalid value."; 41 42PP_Var NPObjectToPPVarImpl(PepperPluginInstanceImpl* instance, 43 NPObject* object, 44 v8::Local<v8::Context> context) { 45 DCHECK(object); 46 if (context.IsEmpty()) 47 return PP_MakeUndefined(); 48 v8::Context::Scope context_scope(context); 49 50 WebArrayBuffer buffer; 51 // TODO(dmichael): Should I protect against duplicate Vars representing the 52 // same array buffer? It's probably not worth the trouble, since it will only 53 // affect in-process plugins. 54 if (WebBindings::getArrayBuffer(object, &buffer)) { 55 scoped_refptr<HostArrayBufferVar> buffer_var( 56 new HostArrayBufferVar(buffer)); 57 return buffer_var->GetPPVar(); 58 } 59 scoped_refptr<NPObjectVar> object_var( 60 HostGlobals::Get()->host_var_tracker()->NPObjectVarForNPObject( 61 instance->pp_instance(), object)); 62 if (!object_var.get()) { // No object for this module yet, make a new one. 63 object_var = new NPObjectVar(instance->pp_instance(), object); 64 } 65 return object_var->GetPPVar(); 66} 67 68} // namespace 69 70// Utilities ------------------------------------------------------------------- 71 72bool PPVarToNPVariant(PP_Var var, NPVariant* result) { 73 switch (var.type) { 74 case PP_VARTYPE_UNDEFINED: 75 VOID_TO_NPVARIANT(*result); 76 break; 77 case PP_VARTYPE_NULL: 78 NULL_TO_NPVARIANT(*result); 79 break; 80 case PP_VARTYPE_BOOL: 81 BOOLEAN_TO_NPVARIANT(var.value.as_bool, *result); 82 break; 83 case PP_VARTYPE_INT32: 84 INT32_TO_NPVARIANT(var.value.as_int, *result); 85 break; 86 case PP_VARTYPE_DOUBLE: 87 DOUBLE_TO_NPVARIANT(var.value.as_double, *result); 88 break; 89 case PP_VARTYPE_STRING: { 90 StringVar* string = StringVar::FromPPVar(var); 91 if (!string) { 92 VOID_TO_NPVARIANT(*result); 93 return false; 94 } 95 const std::string& value = string->value(); 96 char* c_string = static_cast<char*>(malloc(value.size())); 97 memcpy(c_string, value.data(), value.size()); 98 STRINGN_TO_NPVARIANT(c_string, value.size(), *result); 99 break; 100 } 101 case PP_VARTYPE_OBJECT: { 102 scoped_refptr<NPObjectVar> object(NPObjectVar::FromPPVar(var)); 103 if (!object.get()) { 104 VOID_TO_NPVARIANT(*result); 105 return false; 106 } 107 OBJECT_TO_NPVARIANT(WebBindings::retainObject(object->np_object()), 108 *result); 109 break; 110 } 111 // The following types are not supported for use with PPB_Var_Deprecated, 112 // because PPB_Var_Deprecated is only for trusted plugins, and the trusted 113 // plugins we have don't need these types. We can add support in the future 114 // if it becomes necessary. 115 case PP_VARTYPE_ARRAY: 116 case PP_VARTYPE_DICTIONARY: 117 case PP_VARTYPE_ARRAY_BUFFER: 118 case PP_VARTYPE_RESOURCE: 119 VOID_TO_NPVARIANT(*result); 120 break; 121 } 122 return true; 123} 124 125PP_Var NPVariantToPPVar(PepperPluginInstanceImpl* instance, 126 const NPVariant* variant) { 127 switch (variant->type) { 128 case NPVariantType_Void: 129 return PP_MakeUndefined(); 130 case NPVariantType_Null: 131 return PP_MakeNull(); 132 case NPVariantType_Bool: 133 return PP_MakeBool(PP_FromBool(NPVARIANT_TO_BOOLEAN(*variant))); 134 case NPVariantType_Int32: 135 return PP_MakeInt32(NPVARIANT_TO_INT32(*variant)); 136 case NPVariantType_Double: 137 return PP_MakeDouble(NPVARIANT_TO_DOUBLE(*variant)); 138 case NPVariantType_String: 139 return StringVar::StringToPPVar( 140 NPVARIANT_TO_STRING(*variant).UTF8Characters, 141 NPVARIANT_TO_STRING(*variant).UTF8Length); 142 case NPVariantType_Object: 143 return NPObjectToPPVar(instance, NPVARIANT_TO_OBJECT(*variant)); 144 } 145 NOTREACHED(); 146 return PP_MakeUndefined(); 147} 148 149NPIdentifier PPVarToNPIdentifier(PP_Var var) { 150 switch (var.type) { 151 case PP_VARTYPE_STRING: { 152 StringVar* string = StringVar::FromPPVar(var); 153 if (!string) 154 return NULL; 155 return WebBindings::getStringIdentifier(string->value().c_str()); 156 } 157 case PP_VARTYPE_INT32: 158 return WebBindings::getIntIdentifier(var.value.as_int); 159 default: 160 return NULL; 161 } 162} 163 164PP_Var NPIdentifierToPPVar(NPIdentifier id) { 165 const NPUTF8* string_value = NULL; 166 int32_t int_value = 0; 167 bool is_string = false; 168 WebBindings::extractIdentifierData(id, string_value, int_value, is_string); 169 if (is_string) 170 return StringVar::StringToPPVar(string_value); 171 172 return PP_MakeInt32(int_value); 173} 174 175PP_Var NPObjectToPPVar(PepperPluginInstanceImpl* instance, NPObject* object) { 176 WebPluginContainer* container = instance->container(); 177 // It's possible that container() is NULL if the plugin has been removed from 178 // the DOM (but the PluginInstance is not destroyed yet). 179 if (!container) 180 return PP_MakeUndefined(); 181 WebLocalFrame* frame = container->element().document().frame(); 182 if (!frame) 183 return PP_MakeUndefined(); 184 185 v8::HandleScope scope(instance->GetIsolate()); 186 v8::Local<v8::Context> context = frame->mainWorldScriptContext(); 187 return NPObjectToPPVarImpl(instance, object, context); 188} 189 190PP_Var NPObjectToPPVarForTest(PepperPluginInstanceImpl* instance, 191 NPObject* object) { 192 v8::Isolate* test_isolate = v8::Isolate::New(); 193 PP_Var result = PP_MakeUndefined(); 194 { 195 v8::HandleScope scope(test_isolate); 196 v8::Isolate::Scope isolate_scope(test_isolate); 197 v8::Local<v8::Context> context = v8::Context::New(test_isolate); 198 result = NPObjectToPPVarImpl(instance, object, context); 199 } 200 test_isolate->Dispose(); 201 return result; 202} 203 204// PPResultAndExceptionToNPResult ---------------------------------------------- 205 206PPResultAndExceptionToNPResult::PPResultAndExceptionToNPResult( 207 NPObject* object_var, 208 NPVariant* np_result) 209 : object_var_(object_var), 210 np_result_(np_result), 211 exception_(PP_MakeUndefined()), 212 success_(false), 213 checked_exception_(false) {} 214 215PPResultAndExceptionToNPResult::~PPResultAndExceptionToNPResult() { 216 // The user should have called SetResult or CheckExceptionForNoResult 217 // before letting this class go out of scope, or the exception will have 218 // been lost. 219 DCHECK(checked_exception_); 220 221 PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(exception_); 222} 223 224// Call this with the return value of the PPAPI function. It will convert 225// the result to the NPVariant output parameter and pass any exception on to 226// the JS engine. It will update the success flag and return it. 227bool PPResultAndExceptionToNPResult::SetResult(PP_Var result) { 228 DCHECK(!checked_exception_); // Don't call more than once. 229 DCHECK(np_result_); // Should be expecting a result. 230 231 checked_exception_ = true; 232 233 if (has_exception()) { 234 ThrowException(); 235 success_ = false; 236 } else if (!PPVarToNPVariant(result, np_result_)) { 237 WebBindings::setException(object_var_, kInvalidPluginValue); 238 success_ = false; 239 } else { 240 success_ = true; 241 } 242 243 // No matter what happened, we need to release the reference to the 244 // value passed in. On success, a reference to this value will be in 245 // the np_result_. 246 PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(result); 247 return success_; 248} 249 250// Call this after calling a PPAPI function that could have set the 251// exception. It will pass the exception on to the JS engine and update 252// the success flag. 253// 254// The success flag will be returned. 255bool PPResultAndExceptionToNPResult::CheckExceptionForNoResult() { 256 DCHECK(!checked_exception_); // Don't call more than once. 257 DCHECK(!np_result_); // Can't have a result when doing this. 258 259 checked_exception_ = true; 260 261 if (has_exception()) { 262 ThrowException(); 263 success_ = false; 264 return false; 265 } 266 success_ = true; 267 return true; 268} 269 270// Call this to ignore any exception. This prevents the DCHECK from failing 271// in the destructor. 272void PPResultAndExceptionToNPResult::IgnoreException() { 273 checked_exception_ = true; 274} 275 276// Throws the current exception to JS. The exception must be set. 277void PPResultAndExceptionToNPResult::ThrowException() { 278 StringVar* string = StringVar::FromPPVar(exception_); 279 if (string) 280 WebBindings::setException(object_var_, string->value().c_str()); 281} 282 283// PPVarArrayFromNPVariantArray ------------------------------------------------ 284 285PPVarArrayFromNPVariantArray::PPVarArrayFromNPVariantArray( 286 PepperPluginInstanceImpl* instance, 287 size_t size, 288 const NPVariant* variants) 289 : size_(size) { 290 if (size_ > 0) { 291 array_.reset(new PP_Var[size_]); 292 for (size_t i = 0; i < size_; i++) 293 array_[i] = NPVariantToPPVar(instance, &variants[i]); 294 } 295} 296 297PPVarArrayFromNPVariantArray::~PPVarArrayFromNPVariantArray() { 298 ppapi::VarTracker* var_tracker = PpapiGlobals::Get()->GetVarTracker(); 299 for (size_t i = 0; i < size_; i++) 300 var_tracker->ReleaseVar(array_[i]); 301} 302 303// PPVarFromNPObject ----------------------------------------------------------- 304 305PPVarFromNPObject::PPVarFromNPObject(PepperPluginInstanceImpl* instance, 306 NPObject* object) 307 : var_(NPObjectToPPVar(instance, object)) {} 308 309PPVarFromNPObject::~PPVarFromNPObject() { 310 PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(var_); 311} 312 313// NPObjectAccessorWithIdentifier ---------------------------------------------- 314 315NPObjectAccessorWithIdentifier::NPObjectAccessorWithIdentifier( 316 NPObject* object, 317 NPIdentifier identifier, 318 bool allow_integer_identifier) 319 : object_(PluginObject::FromNPObject(object)), 320 identifier_(PP_MakeUndefined()) { 321 if (object_) { 322 identifier_ = NPIdentifierToPPVar(identifier); 323 if (identifier_.type == PP_VARTYPE_INT32 && !allow_integer_identifier) 324 identifier_.type = PP_VARTYPE_UNDEFINED; // Mark it invalid. 325 } 326} 327 328NPObjectAccessorWithIdentifier::~NPObjectAccessorWithIdentifier() { 329 PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(identifier_); 330} 331 332// TryCatch -------------------------------------------------------------------- 333 334TryCatch::TryCatch(PP_Var* exception) 335 : has_exception_(exception && exception->type != PP_VARTYPE_UNDEFINED), 336 exception_(exception) { 337 WebBindings::pushExceptionHandler(&TryCatch::Catch, this); 338} 339 340TryCatch::~TryCatch() { WebBindings::popExceptionHandler(); } 341 342void TryCatch::SetException(const char* message) { 343 if (!has_exception()) { 344 has_exception_ = true; 345 if (exception_) { 346 if (!message) 347 message = "Unknown exception."; 348 *exception_ = ppapi::StringVar::StringToPPVar(message, strlen(message)); 349 } 350 } 351} 352 353// static 354void TryCatch::Catch(void* self, const char* message) { 355 static_cast<TryCatch*>(self)->SetException(message); 356} 357 358} // namespace content 359