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/WebFrame.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 WebKit::WebArrayBuffer; 32using WebKit::WebBindings; 33using WebKit::WebPluginContainer; 34 35namespace content { 36 37namespace { 38 39const char kInvalidPluginValue[] = "Error: Plugin returned invalid value."; 40 41PP_Var NPObjectToPPVarImpl(PepperPluginInstanceImpl* instance, 42 NPObject* object, 43 v8::Local<v8::Context> context) { 44 DCHECK(object); 45 if (context.IsEmpty()) 46 return PP_MakeUndefined(); 47 v8::Context::Scope context_scope(context); 48 49 WebArrayBuffer buffer; 50 // TODO(dmichael): Should I protect against duplicate Vars representing the 51 // same array buffer? It's probably not worth the trouble, since it will only 52 // affect in-process plugins. 53 if (WebBindings::getArrayBuffer(object, &buffer)) { 54 scoped_refptr<HostArrayBufferVar> buffer_var( 55 new HostArrayBufferVar(buffer)); 56 return buffer_var->GetPPVar(); 57 } 58 scoped_refptr<NPObjectVar> object_var( 59 HostGlobals::Get()->host_var_tracker()->NPObjectVarForNPObject( 60 instance->pp_instance(), object)); 61 if (!object_var.get()) { // No object for this module yet, make a new one. 62 object_var = new NPObjectVar(instance->pp_instance(), object); 63 } 64 return object_var->GetPPVar(); 65} 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 VOID_TO_NPVARIANT(*result); 119 break; 120 } 121 return true; 122} 123 124PP_Var NPVariantToPPVar(PepperPluginInstanceImpl* instance, 125 const NPVariant* variant) { 126 switch (variant->type) { 127 case NPVariantType_Void: 128 return PP_MakeUndefined(); 129 case NPVariantType_Null: 130 return PP_MakeNull(); 131 case NPVariantType_Bool: 132 return PP_MakeBool(PP_FromBool(NPVARIANT_TO_BOOLEAN(*variant))); 133 case NPVariantType_Int32: 134 return PP_MakeInt32(NPVARIANT_TO_INT32(*variant)); 135 case NPVariantType_Double: 136 return PP_MakeDouble(NPVARIANT_TO_DOUBLE(*variant)); 137 case NPVariantType_String: 138 return StringVar::StringToPPVar( 139 NPVARIANT_TO_STRING(*variant).UTF8Characters, 140 NPVARIANT_TO_STRING(*variant).UTF8Length); 141 case NPVariantType_Object: 142 return NPObjectToPPVar(instance, NPVARIANT_TO_OBJECT(*variant)); 143 } 144 NOTREACHED(); 145 return PP_MakeUndefined(); 146} 147 148NPIdentifier PPVarToNPIdentifier(PP_Var var) { 149 switch (var.type) { 150 case PP_VARTYPE_STRING: { 151 StringVar* string = StringVar::FromPPVar(var); 152 if (!string) 153 return NULL; 154 return WebBindings::getStringIdentifier(string->value().c_str()); 155 } 156 case PP_VARTYPE_INT32: 157 return WebBindings::getIntIdentifier(var.value.as_int); 158 default: 159 return NULL; 160 } 161} 162 163PP_Var NPIdentifierToPPVar(NPIdentifier id) { 164 const NPUTF8* string_value = NULL; 165 int32_t int_value = 0; 166 bool is_string = false; 167 WebBindings::extractIdentifierData(id, string_value, int_value, is_string); 168 if (is_string) 169 return StringVar::StringToPPVar(string_value); 170 171 return PP_MakeInt32(int_value); 172} 173 174PP_Var NPObjectToPPVar(PepperPluginInstanceImpl* instance, NPObject* object) { 175 WebPluginContainer* container = instance->container(); 176 // It's possible that container() is NULL if the plugin has been removed from 177 // the DOM (but the PluginInstance is not destroyed yet). 178 if (!container) 179 return PP_MakeUndefined(); 180 v8::HandleScope scope(instance->GetIsolate()); 181 v8::Local<v8::Context> context = 182 container->element().document().frame()->mainWorldScriptContext(); 183 return NPObjectToPPVarImpl(instance, object, context); 184} 185 186PP_Var NPObjectToPPVarForTest(PepperPluginInstanceImpl* instance, 187 NPObject* object) { 188 v8::Isolate* test_isolate = v8::Isolate::New(); 189 PP_Var result = PP_MakeUndefined(); 190 { 191 v8::HandleScope scope(test_isolate); 192 v8::Isolate::Scope isolate_scope(test_isolate); 193 v8::Local<v8::Context> context = v8::Context::New(test_isolate); 194 result = NPObjectToPPVarImpl(instance, object, context); 195 } 196 test_isolate->Dispose(); 197 return result; 198} 199 200// PPResultAndExceptionToNPResult ---------------------------------------------- 201 202PPResultAndExceptionToNPResult::PPResultAndExceptionToNPResult( 203 NPObject* object_var, 204 NPVariant* np_result) 205 : object_var_(object_var), 206 np_result_(np_result), 207 exception_(PP_MakeUndefined()), 208 success_(false), 209 checked_exception_(false) { 210} 211 212PPResultAndExceptionToNPResult::~PPResultAndExceptionToNPResult() { 213 // The user should have called SetResult or CheckExceptionForNoResult 214 // before letting this class go out of scope, or the exception will have 215 // been lost. 216 DCHECK(checked_exception_); 217 218 PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(exception_); 219} 220 221// Call this with the return value of the PPAPI function. It will convert 222// the result to the NPVariant output parameter and pass any exception on to 223// the JS engine. It will update the success flag and return it. 224bool PPResultAndExceptionToNPResult::SetResult(PP_Var result) { 225 DCHECK(!checked_exception_); // Don't call more than once. 226 DCHECK(np_result_); // Should be expecting a result. 227 228 checked_exception_ = true; 229 230 if (has_exception()) { 231 ThrowException(); 232 success_ = false; 233 } else if (!PPVarToNPVariant(result, np_result_)) { 234 WebBindings::setException(object_var_, kInvalidPluginValue); 235 success_ = false; 236 } else { 237 success_ = true; 238 } 239 240 // No matter what happened, we need to release the reference to the 241 // value passed in. On success, a reference to this value will be in 242 // the np_result_. 243 PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(result); 244 return success_; 245} 246 247// Call this after calling a PPAPI function that could have set the 248// exception. It will pass the exception on to the JS engine and update 249// the success flag. 250// 251// The success flag will be returned. 252bool PPResultAndExceptionToNPResult::CheckExceptionForNoResult() { 253 DCHECK(!checked_exception_); // Don't call more than once. 254 DCHECK(!np_result_); // Can't have a result when doing this. 255 256 checked_exception_ = true; 257 258 if (has_exception()) { 259 ThrowException(); 260 success_ = false; 261 return false; 262 } 263 success_ = true; 264 return true; 265} 266 267// Call this to ignore any exception. This prevents the DCHECK from failing 268// in the destructor. 269void PPResultAndExceptionToNPResult::IgnoreException() { 270 checked_exception_ = true; 271} 272 273// Throws the current exception to JS. The exception must be set. 274void PPResultAndExceptionToNPResult::ThrowException() { 275 StringVar* string = StringVar::FromPPVar(exception_); 276 if (string) 277 WebBindings::setException(object_var_, string->value().c_str()); 278} 279 280// PPVarArrayFromNPVariantArray ------------------------------------------------ 281 282PPVarArrayFromNPVariantArray::PPVarArrayFromNPVariantArray( 283 PepperPluginInstanceImpl* instance, 284 size_t size, 285 const NPVariant* variants) 286 : size_(size) { 287 if (size_ > 0) { 288 array_.reset(new PP_Var[size_]); 289 for (size_t i = 0; i < size_; i++) 290 array_[i] = NPVariantToPPVar(instance, &variants[i]); 291 } 292} 293 294PPVarArrayFromNPVariantArray::~PPVarArrayFromNPVariantArray() { 295 ppapi::VarTracker* var_tracker = PpapiGlobals::Get()->GetVarTracker(); 296 for (size_t i = 0; i < size_; i++) 297 var_tracker->ReleaseVar(array_[i]); 298} 299 300// PPVarFromNPObject ----------------------------------------------------------- 301 302PPVarFromNPObject::PPVarFromNPObject(PepperPluginInstanceImpl* instance, 303 NPObject* object) 304 : var_(NPObjectToPPVar(instance, object)) { 305} 306 307PPVarFromNPObject::~PPVarFromNPObject() { 308 PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(var_); 309} 310 311// NPObjectAccessorWithIdentifier ---------------------------------------------- 312 313NPObjectAccessorWithIdentifier::NPObjectAccessorWithIdentifier( 314 NPObject* object, 315 NPIdentifier identifier, 316 bool allow_integer_identifier) 317 : object_(PluginObject::FromNPObject(object)), 318 identifier_(PP_MakeUndefined()) { 319 if (object_) { 320 identifier_ = NPIdentifierToPPVar(identifier); 321 if (identifier_.type == PP_VARTYPE_INT32 && !allow_integer_identifier) 322 identifier_.type = PP_VARTYPE_UNDEFINED; // Mark it invalid. 323 } 324} 325 326NPObjectAccessorWithIdentifier::~NPObjectAccessorWithIdentifier() { 327 PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(identifier_); 328} 329 330// TryCatch -------------------------------------------------------------------- 331 332TryCatch::TryCatch(PP_Var* exception) 333 : has_exception_(exception && exception->type != PP_VARTYPE_UNDEFINED), 334 exception_(exception) { 335 WebBindings::pushExceptionHandler(&TryCatch::Catch, this); 336} 337 338TryCatch::~TryCatch() { 339 WebBindings::popExceptionHandler(); 340} 341 342void TryCatch::SetException(const char* message) { 343 if (!has_exception()) { 344 has_exception_ = true; 345 if (exception_) { 346 *exception_ = ppapi::StringVar::StringToPPVar(message, strlen(message)); 347 } 348 } 349} 350 351// static 352void TryCatch::Catch(void* self, const char* message) { 353 static_cast<TryCatch*>(self)->SetException(message); 354} 355 356} // namespace content 357