diff options
21 files changed, 1402 insertions, 647 deletions
@@ -167,7 +167,7 @@ deps = { Var("libvpx_revision"), "src/third_party/ppapi": - (Var("googlecode_url") % "ppapi") + "/trunk@238", + (Var("googlecode_url") % "ppapi") + "/trunk@244", "src/third_party/libjingle/source": (Var("googlecode_url") % "libjingle") + "/branches/nextsnap@" + diff --git a/webkit/glue/plugins/pepper_file_ref.cc b/webkit/glue/plugins/pepper_file_ref.cc index 3438c95..9fe7568 100644 --- a/webkit/glue/plugins/pepper_file_ref.cc +++ b/webkit/glue/plugins/pepper_file_ref.cc @@ -76,7 +76,7 @@ PP_Var GetName(PP_Resource file_ref_id) { scoped_refptr<FileRef> file_ref(Resource::GetAs<FileRef>(file_ref_id)); if (!file_ref) return PP_MakeVoid(); - return StringToPPVar(file_ref->GetName()); + return StringVar::StringToPPVar(file_ref->module(), file_ref->GetName()); } PP_Var GetPath(PP_Resource file_ref_id) { @@ -87,7 +87,7 @@ PP_Var GetPath(PP_Resource file_ref_id) { if (file_ref->file_system_type() == PP_FILESYSTEMTYPE_EXTERNAL) return PP_MakeVoid(); - return StringToPPVar(file_ref->path()); + return StringVar::StringToPPVar(file_ref->module(), file_ref->path()); } PP_Resource GetParent(PP_Resource file_ref_id) { diff --git a/webkit/glue/plugins/pepper_font.cc b/webkit/glue/plugins/pepper_font.cc index fbf334fa..82bf369 100644 --- a/webkit/glue/plugins/pepper_font.cc +++ b/webkit/glue/plugins/pepper_font.cc @@ -81,7 +81,7 @@ WebFontDescription PPFontDescToWebFontDesc(const PP_FontDescription_Dev& font) { MonospaceFamily); WebFontDescription result; - String* face_name = GetString(font.face); + scoped_refptr<StringVar> face_name(StringVar::FromPPVar(font.face)); if (face_name) result.family = UTF8ToUTF16(face_name->value()); result.genericFamily = PP_FONTFAMILY_TO_WEB_FONTFAMILY(font.family); @@ -97,7 +97,7 @@ WebFontDescription PPFontDescToWebFontDesc(const PP_FontDescription_Dev& font) { // Converts the given PP_TextRun to a WebTextRun, returning true on success. // False means the input was invalid. bool PPTextRunToWebTextRun(const PP_TextRun_Dev* run, WebTextRun* output) { - String* text_string = GetString(run->text); + scoped_refptr<StringVar> text_string(StringVar::FromPPVar(run->text)); if (!text_string) return false; *output = WebTextRun(UTF8ToUTF16(text_string->value()), @@ -205,7 +205,8 @@ bool Font::Describe(PP_FontDescription_Dev* description, // While converting the other way in PPFontDescToWebFontDesc we validated // that the enums can be casted. - description->face = StringToPPVar(UTF16ToUTF8(web_desc.family)); + description->face = StringVar::StringToPPVar(module(), + UTF16ToUTF8(web_desc.family)); description->family = static_cast<PP_FontFamily_Dev>(web_desc.genericFamily); description->size = static_cast<uint32_t>(web_desc.size); description->weight = static_cast<PP_FontWeight_Dev>(web_desc.weight); diff --git a/webkit/glue/plugins/pepper_image_data.h b/webkit/glue/plugins/pepper_image_data.h index 7652b80..c5937b2 100644 --- a/webkit/glue/plugins/pepper_image_data.h +++ b/webkit/glue/plugins/pepper_image_data.h @@ -42,7 +42,7 @@ class ImageData : public Resource { static const PPB_ImageData* GetInterface(); // Resource overrides. - ImageData* AsImageData() { return this; } + virtual ImageData* AsImageData() { return this; } // PPB_ImageData implementation. bool Init(PP_ImageDataFormat format, diff --git a/webkit/glue/plugins/pepper_plugin_instance.cc b/webkit/glue/plugins/pepper_plugin_instance.cc index 02dc515..e6b1c18 100644 --- a/webkit/glue/plugins/pepper_plugin_instance.cc +++ b/webkit/glue/plugins/pepper_plugin_instance.cc @@ -280,14 +280,14 @@ PP_Var PluginInstance::GetWindowObject() { if (!frame) return PP_MakeVoid(); - return NPObjectToPPVar(frame->windowObject()); + return ObjectVar::NPObjectToPPVar(module(), frame->windowObject()); } PP_Var PluginInstance::GetOwnerElementObject() { if (!container_) return PP_MakeVoid(); - - return NPObjectToPPVar(container_->scriptableObjectForElement()); + return ObjectVar::NPObjectToPPVar(module(), + container_->scriptableObjectForElement()); } bool PluginInstance::BindGraphics(PP_Resource device_id) { @@ -339,12 +339,12 @@ bool PluginInstance::SetCursor(PP_CursorType_Dev type) { } PP_Var PluginInstance::ExecuteScript(PP_Var script, PP_Var* exception) { - TryCatch try_catch(exception); - if (try_catch.HasException()) + TryCatch try_catch(module(), exception); + if (try_catch.has_exception()) return PP_MakeVoid(); // Convert the script into an inconvenient NPString object. - String* script_string = GetString(script); + scoped_refptr<StringVar> script_string(StringVar::FromPPVar(script)); if (!script_string) { try_catch.SetException("Script param to ExecuteScript must be a string."); return PP_MakeVoid(); @@ -371,7 +371,7 @@ PP_Var PluginInstance::ExecuteScript(PP_Var script, PP_Var* exception) { return PP_MakeVoid(); } - PP_Var ret = NPVariantToPPVar(&result); + PP_Var ret = Var::NPVariantToPPVar(module_, &result); WebBindings::releaseVariantValue(&result); return ret; } @@ -457,7 +457,8 @@ void PluginInstance::ViewFlushedPaint() { string16 PluginInstance::GetSelectedText(bool html) { PP_Var rv = instance_interface_->GetSelectedText(GetPPInstance(), html); - String* string = GetString(rv); + scoped_refptr<StringVar> string(StringVar::FromPPVar(rv)); + Var::PluginReleasePPVar(rv); // Release the ref the plugin transfered to us. if (!string) return string16(); return UTF8ToUTF16(string->value()); diff --git a/webkit/glue/plugins/pepper_plugin_module.cc b/webkit/glue/plugins/pepper_plugin_module.cc index 8068a72..4cbf192 100644 --- a/webkit/glue/plugins/pepper_plugin_module.cc +++ b/webkit/glue/plugins/pepper_plugin_module.cc @@ -54,6 +54,7 @@ #include "webkit/glue/plugins/pepper_graphics_2d.h" #include "webkit/glue/plugins/pepper_image_data.h" #include "webkit/glue/plugins/pepper_plugin_instance.h" +#include "webkit/glue/plugins/pepper_plugin_object.h" #include "webkit/glue/plugins/pepper_private.h" #include "webkit/glue/plugins/pepper_resource_tracker.h" #include "webkit/glue/plugins/pepper_scrollbar.h" @@ -161,10 +162,18 @@ void QuitMessageLoop() { MessageLoop::current()->Quit(); } +uint32_t GetLiveObjectCount(PP_Module module_id) { + PluginModule* module = PluginModule::FromPPModule(module_id); + if (!module) + return static_cast<uint32_t>(-1); + return ResourceTracker::Get()->GetLiveObjectsForModule(module); +} + const PPB_Testing_Dev testing_interface = { &ReadImageData, &RunMessageLoop, &QuitMessageLoop, + &GetLiveObjectCount }; // GetInterface ---------------------------------------------------------------- @@ -173,7 +182,7 @@ const void* GetInterface(const char* name) { if (strcmp(name, PPB_CORE_INTERFACE) == 0) return &core_interface; if (strcmp(name, PPB_VAR_INTERFACE) == 0) - return GetVarInterface(); + return Var::GetInterface(); if (strcmp(name, PPB_INSTANCE_INTERFACE) == 0) return PluginInstance::GetInterface(); if (strcmp(name, PPB_IMAGEDATA_INTERFACE) == 0) @@ -251,6 +260,17 @@ PluginModule::PluginModule() } PluginModule::~PluginModule() { + // Free all the plugin objects. This will automatically clear the back- + // pointer from the NPObject so WebKit can't call into the plugin any more. + // + // Swap out the set so we can delete from it (the objects will try to + // unregister themselves inside the delete call). + PluginObjectSet plugin_object_copy; + live_plugin_objects_.swap(plugin_object_copy); + for (PluginObjectSet::iterator i = live_plugin_objects_.begin(); + i != live_plugin_objects_.end(); ++i) + delete *i; + // When the module is being deleted, there should be no more instances still // holding a reference to us. DCHECK(instances_.empty()); @@ -403,4 +423,44 @@ void PluginModule::InstanceDeleted(PluginInstance* instance) { instances_.erase(instance); } +void PluginModule::AddNPObjectVar(ObjectVar* object_var) { + DCHECK(np_object_to_object_var_.find(object_var->np_object()) == + np_object_to_object_var_.end()) << "ObjectVar already in map"; + np_object_to_object_var_[object_var->np_object()] = object_var; +} + +void PluginModule::RemoveNPObjectVar(ObjectVar* object_var) { + NPObjectToObjectVarMap::iterator found = + np_object_to_object_var_.find(object_var->np_object()); + if (found == np_object_to_object_var_.end()) { + NOTREACHED() << "ObjectVar not registered."; + return; + } + if (found->second != object_var) { + NOTREACHED() << "ObjectVar doesn't match."; + return; + } + np_object_to_object_var_.erase(found); +} + +ObjectVar* PluginModule::ObjectVarForNPObject(NPObject* np_object) const { + NPObjectToObjectVarMap::const_iterator found = + np_object_to_object_var_.find(np_object); + if (found == np_object_to_object_var_.end()) + return NULL; + return found->second; +} + +void PluginModule::AddPluginObject(PluginObject* plugin_object) { + DCHECK(live_plugin_objects_.find(plugin_object) == + live_plugin_objects_.end()); + live_plugin_objects_.insert(plugin_object); +} + +void PluginModule::RemovePluginObject(PluginObject* plugin_object) { + // Don't actually verify that the object is in the set since during module + // deletion we'll be in the process of freeing them. + live_plugin_objects_.erase(plugin_object); +} + } // namespace pepper diff --git a/webkit/glue/plugins/pepper_plugin_module.h b/webkit/glue/plugins/pepper_plugin_module.h index ab089a1..e543562 100644 --- a/webkit/glue/plugins/pepper_plugin_module.h +++ b/webkit/glue/plugins/pepper_plugin_module.h @@ -5,6 +5,7 @@ #ifndef WEBKIT_GLUE_PLUGINS_PEPPER_PLUGIN_MODULE_H_ #define WEBKIT_GLUE_PLUGINS_PEPPER_PLUGIN_MODULE_H_ +#include <map> #include <set> #include "base/basictypes.h" @@ -14,13 +15,16 @@ #include "third_party/ppapi/c/ppb.h" class FilePath; - +typedef struct NPObject NPObject; struct PPB_Core; +typedef void* NPIdentifier; namespace pepper { +class ObjectVar; class PluginDelegate; class PluginInstance; +class PluginObject; class PluginModule : public base::RefCounted<PluginModule> { public: @@ -70,6 +74,22 @@ class PluginModule : public base::RefCounted<PluginModule> { void InstanceCreated(PluginInstance* instance); void InstanceDeleted(PluginInstance* instance); + // Tracks all live ObjectVar. This is so we can map between PluginModule + + // NPObject and get the ObjectVar corresponding to it. This Add/Remove + // function should be called by the ObjectVar when it is created and + // destroyed. + void AddNPObjectVar(ObjectVar* object_var); + void RemoveNPObjectVar(ObjectVar* object_var); + + // Looks up a previously registered ObjectVar for the given NPObject and + // module. Returns NULL if there is no ObjectVar corresponding to the given + // NPObject for the given module. See AddNPObjectVar above. + ObjectVar* ObjectVarForNPObject(NPObject* np_object) const; + + // Tracks all live PluginObjects. + void AddPluginObject(PluginObject* plugin_object); + void RemovePluginObject(PluginObject* plugin_object); + private: PluginModule(); @@ -95,6 +115,14 @@ class PluginModule : public base::RefCounted<PluginModule> { typedef std::set<PluginInstance*> PluginInstanceSet; PluginInstanceSet instances_; + // Tracks all live ObjectVars used by this module so we can map NPObjects to + // the corresponding object. These are non-owning references. + typedef std::map<NPObject*, ObjectVar*> NPObjectToObjectVarMap;; + NPObjectToObjectVarMap np_object_to_object_var_; + + typedef std::set<PluginObject*> PluginObjectSet; + PluginObjectSet live_plugin_objects_; + DISALLOW_COPY_AND_ASSIGN(PluginModule); }; diff --git a/webkit/glue/plugins/pepper_plugin_object.cc b/webkit/glue/plugins/pepper_plugin_object.cc new file mode 100644 index 0000000..fb8081b --- /dev/null +++ b/webkit/glue/plugins/pepper_plugin_object.cc @@ -0,0 +1,601 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "webkit/glue/plugins/pepper_plugin_object.h" + +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "base/string_number_conversions.h" +#include "base/string_util.h" +#include "third_party/npapi/bindings/npapi.h" +#include "third_party/npapi/bindings/npruntime.h" +#include "third_party/ppapi/c/pp_var.h" +#include "third_party/ppapi/c/ppb_var.h" +#include "third_party/ppapi/c/ppp_class.h" +#include "third_party/WebKit/WebKit/chromium/public/WebBindings.h" +#include "webkit/glue/plugins/pepper_plugin_module.h" +#include "webkit/glue/plugins/pepper_string.h" +#include "webkit/glue/plugins/pepper_var.h" + +using WebKit::WebBindings; + +namespace pepper { + +namespace { + +const char kInvalidValueException[] = "Error: Invalid value"; +const char kInvalidPluginValue[] = "Error: Plugin returned invalid value."; + +// --------------------------------------------------------------------------- +// Utilities + +// Converts the given PP_Var to an NPVariant, returning true on success. +// False means that the given variant is invalid. In this case, the result +// NPVariant will be set to a void one. +// +// The contents of the PP_Var will be copied unless the PP_Var corresponds to +// an object. +bool PPVarToNPVariant(PP_Var var, NPVariant* result) { + switch (var.type) { + case PP_VARTYPE_VOID: + VOID_TO_NPVARIANT(*result); + break; + case PP_VARTYPE_NULL: + NULL_TO_NPVARIANT(*result); + break; + case PP_VARTYPE_BOOL: + BOOLEAN_TO_NPVARIANT(var.value.as_bool, *result); + break; + case PP_VARTYPE_INT32: + INT32_TO_NPVARIANT(var.value.as_int, *result); + break; + case PP_VARTYPE_DOUBLE: + DOUBLE_TO_NPVARIANT(var.value.as_double, *result); + break; + case PP_VARTYPE_STRING: { + scoped_refptr<StringVar> string(StringVar::FromPPVar(var)); + if (!string) { + VOID_TO_NPVARIANT(*result); + return false; + } + const std::string& value = string->value(); + STRINGN_TO_NPVARIANT(base::strdup(value.c_str()), value.size(), *result); + break; + } + case PP_VARTYPE_OBJECT: { + scoped_refptr<ObjectVar> object(ObjectVar::FromPPVar(var)); + if (!object) { + VOID_TO_NPVARIANT(*result); + return false; + } + OBJECT_TO_NPVARIANT(WebBindings::retainObject(object->np_object()), + *result); + break; + } + } + return true; +} + +// PPVarArrayFromNPVariantArray ------------------------------------------------ + +// Converts an array of NPVariants to an array of PP_Var, and scopes the +// ownership of the PP_Var. This is used when converting argument lists from +// WebKit to the plugin. +class PPVarArrayFromNPVariantArray { + public: + PPVarArrayFromNPVariantArray(PluginModule* module, + size_t size, + const NPVariant* variants) + : size_(size) { + if (size_ > 0) { + array_.reset(new PP_Var[size_]); + for (size_t i = 0; i < size_; i++) + array_[i] = Var::NPVariantToPPVar(module, &variants[i]); + } + } + + ~PPVarArrayFromNPVariantArray() { + for (size_t i = 0; i < size_; i++) + Var::PluginReleasePPVar(array_[i]); + } + + PP_Var* array() { return array_.get(); } + + private: + size_t size_; + scoped_array<PP_Var> array_; + + DISALLOW_COPY_AND_ASSIGN(PPVarArrayFromNPVariantArray); +}; + +// PPResultAndExceptionToNPResult ---------------------------------------------- + +// Convenience object for converting a PPAPI call that can throw an exception +// and optionally return a value, back to the NPAPI layer which expects a +// NPVariant as a result. +// +// Normal usage is that you will pass the result of exception() to the +// PPAPI function as the exception output parameter. Then you will either +// call SetResult with the result of the PPAPI call, or +// CheckExceptionForNoResult if the PPAPI call doesn't return a PP_Var. +// +// Both SetResult and CheckExceptionForNoResult will throw an exception to +// the JavaScript library if the plugin reported an exception. SetResult +// will additionally convert the result to an NPVariant and write it to the +// output parameter given in the constructor. +class PPResultAndExceptionToNPResult { + public: + // The object_var parameter is the object to associate any exception with. + // It may not be NULL. This class does not take a ref, so it must remain + // valid for the lifetime of this object. + // + // The np_result parameter is the NPAPI result output parameter. This may be + // NULL if there is no NPVariant result (like for HasProperty). If this is + // specified, you must call SetResult() to set it. If it is not, you must + // call CheckExceptionForNoResult to do the exception checking with no result + // conversion. + PPResultAndExceptionToNPResult(PluginObject* object_var, + NPVariant* np_result) + : object_var_(object_var), + np_result_(np_result), + exception_(PP_MakeVoid()), + success_(false), + checked_exception_(false) { + } + + ~PPResultAndExceptionToNPResult() { + // The user should have called SetResult or CheckExceptionForNoResult + // before letting this class go out of scope, or the exception will have + // been lost. + DCHECK(checked_exception_); + + ObjectVar::PluginReleasePPVar(exception_); + } + + // Returns true if an exception has been set. + bool has_exception() const { return exception_.type != PP_VARTYPE_VOID; } + + // Returns a pointer to the exception. You would pass this to the PPAPI + // function as the exception parameter. If it is set to non-void, this object + // will take ownership of destroying it. + PP_Var* exception() { return &exception_; } + + // Returns true if everything succeeded with no exception. This is valid only + // after calling SetResult/CheckExceptionForNoResult. + bool success() const { + DCHECK(checked_exception_); + return success_; + } + + // Call this with the return value of the PPAPI function. It will convert + // the result to the NPVariant output parameter and pass any exception on to + // the JS engine. It will update the success flag and return it. + bool SetResult(PP_Var result) { + DCHECK(!checked_exception_); // Don't call more than once. + DCHECK(np_result_); // Should be expecting a result. + + checked_exception_ = true; + + if (has_exception()) { + ThrowException(); + success_ = false; + } else if (!PPVarToNPVariant(result, np_result_)) { + WebBindings::setException(object_var_->GetNPObject(), + kInvalidPluginValue); + success_ = false; + } else { + success_ = true; + } + + // No matter what happened, we need to release the reference to the + // value passed in. On success, a reference to this value will be in + // the np_result_. + Var::PluginReleasePPVar(result); + return success_; + } + + // Call this after calling a PPAPI function that could have set the + // exception. It will pass the exception on to the JS engine and update + // the success flag. + // + // The success flag will be returned. + bool CheckExceptionForNoResult() { + DCHECK(!checked_exception_); // Don't call more than once. + DCHECK(!np_result_); // Can't have a result when doing this. + + checked_exception_ = true; + + if (has_exception()) { + ThrowException(); + success_ = false; + return false; + } + success_ = true; + return true; + } + + // Call this to ignore any exception. This prevents the DCHECK from failing + // in the destructor. + void IgnoreException() { + checked_exception_ = true; + } + + private: + // Throws the current exception to JS. The exception must be set. + void ThrowException() { + scoped_refptr<StringVar> string(StringVar::FromPPVar(exception_)); + if (string) { + WebBindings::setException(object_var_->GetNPObject(), + string->value().c_str()); + } + } + + PluginObject* object_var_; // Non-owning ref (see constructor). + NPVariant* np_result_; // Output value, possibly NULL (see constructor). + PP_Var exception_; // Exception set by the PPAPI call. We own a ref to it. + bool success_; // See the success() function above. + bool checked_exception_; // SetResult/CheckExceptionForNoResult was called. + + DISALLOW_COPY_AND_ASSIGN(PPResultAndExceptionToNPResult); +}; + +// NPObjectAccessorWithIdentifier ---------------------------------------------- + +// Helper class for our NPObject wrapper. This converts a call from WebKit +// where it gives us an NPObject and an NPIdentifier to an easily-accessible +// ObjectVar (corresponding to the NPObject) and PP_Var (corresponding to the +// NPIdentifier). +// +// If the NPObject or identifier is invalid, we'll set is_valid() to false. +// The caller should check is_valid() before doing anything with the class. +// +// JS can't have integer functions, so when dealing with these, we don't want +// to allow integer identifiers. The calling code can decode if it wants to +// allow integer identifiers (like for property access) or prohibit them +// (like for method calling) by setting |allow_integer_identifier|. If this +// is false and the identifier is an integer, we'll set is_valid() to false. +// +// Getting an integer identifier in this case should be impossible. V8 +// shouldn't be allowing this, and the Pepper Var calls from the plugin are +// supposed to error out before calling into V8 (which will then call us back). +// Aside from an egregious error, the only time this could happen is an NPAPI +// plugin calling us. +class NPObjectAccessorWithIdentifier { + public: + NPObjectAccessorWithIdentifier(NPObject* object, + NPIdentifier identifier, + bool allow_integer_identifier) + : object_(PluginObject::FromNPObject(object)), + identifier_(PP_MakeVoid()) { + if (object_) { + identifier_ = Var::NPIdentifierToPPVar(object_->module(), identifier); + if (identifier_.type == PP_VARTYPE_INT32 && !allow_integer_identifier) + identifier_.type = PP_VARTYPE_VOID; // Make the identifier invalid. + } + } + + ~NPObjectAccessorWithIdentifier() { + Var::PluginReleasePPVar(identifier_); + } + + // Returns true if both the object and identifier are valid. + bool is_valid() const { + return object_ && identifier_.type != PP_VARTYPE_VOID; + } + + PluginObject* object() { return object_; } + PP_Var identifier() const { return identifier_; } + + private: + PluginObject* object_; + PP_Var identifier_; + + DISALLOW_COPY_AND_ASSIGN(NPObjectAccessorWithIdentifier); +}; + +// NPObject implementation in terms of PPP_Class ------------------------------- + +NPObject* WrapperClass_Allocate(NPP npp, NPClass* unused) { + return PluginObject::AllocateObjectWrapper(); +} + +void WrapperClass_Deallocate(NPObject* np_object) { + PluginObject* plugin_object = PluginObject::FromNPObject(np_object); + if (!plugin_object) + return; + plugin_object->ppp_class()->Deallocate(plugin_object->ppp_class_data()); + delete plugin_object; +} + +void WrapperClass_Invalidate(NPObject* object) { +} + +bool WrapperClass_HasMethod(NPObject* object, NPIdentifier method_name) { + NPObjectAccessorWithIdentifier accessor(object, method_name, false); + if (!accessor.is_valid()) + return false; + + PPResultAndExceptionToNPResult result_converter(accessor.object(), NULL); + bool rv = accessor.object()->ppp_class()->HasMethod( + accessor.object()->ppp_class_data(), accessor.identifier(), + result_converter.exception()); + result_converter.CheckExceptionForNoResult(); + return rv; +} + +bool WrapperClass_Invoke(NPObject* object, NPIdentifier method_name, + const NPVariant* argv, uint32_t argc, + NPVariant* result) { + NPObjectAccessorWithIdentifier accessor(object, method_name, false); + if (!accessor.is_valid()) + return false; + + PPResultAndExceptionToNPResult result_converter(accessor.object(), result); + PPVarArrayFromNPVariantArray args(accessor.object()->module(), argc, argv); + + return result_converter.SetResult(accessor.object()->ppp_class()->Call( + accessor.object()->ppp_class_data(), accessor.identifier(), + argc, args.array(), result_converter.exception())); +} + +bool WrapperClass_InvokeDefault(NPObject* np_object, const NPVariant* argv, + uint32_t argc, NPVariant* result) { + PluginObject* obj = PluginObject::FromNPObject(np_object); + if (!obj) + return false; + + PPVarArrayFromNPVariantArray args(obj->module(), argc, argv); + PPResultAndExceptionToNPResult result_converter(obj, result); + + result_converter.SetResult(obj->ppp_class()->Call( + obj->ppp_class_data(), PP_MakeVoid(), argc, args.array(), + result_converter.exception())); + return result_converter.success(); +} + + + + + + + + + + + + + + + + +bool WrapperClass_HasProperty(NPObject* object, NPIdentifier property_name) { + NPObjectAccessorWithIdentifier accessor(object, property_name, true); + if (!accessor.is_valid()) + return false; + + PPResultAndExceptionToNPResult result_converter(accessor.object(), NULL); + bool rv = accessor.object()->ppp_class()->HasProperty( + accessor.object()->ppp_class_data(), accessor.identifier(), + result_converter.exception()); + result_converter.CheckExceptionForNoResult(); + return rv; +} + +bool WrapperClass_GetProperty(NPObject* object, NPIdentifier property_name, + NPVariant* result) { + NPObjectAccessorWithIdentifier accessor(object, property_name, true); + if (!accessor.is_valid()) + return false; + + PPResultAndExceptionToNPResult result_converter(accessor.object(), result); + return result_converter.SetResult(accessor.object()->ppp_class()->GetProperty( + accessor.object()->ppp_class_data(), accessor.identifier(), + result_converter.exception())); +} + +bool WrapperClass_SetProperty(NPObject* object, NPIdentifier property_name, + const NPVariant* value) { + NPObjectAccessorWithIdentifier accessor(object, property_name, true); + if (!accessor.is_valid()) + return false; + + PPResultAndExceptionToNPResult result_converter(accessor.object(), NULL); + PP_Var value_var = Var::NPVariantToPPVar(accessor.object()->module(), value); + accessor.object()->ppp_class()->SetProperty( + accessor.object()->ppp_class_data(), accessor.identifier(), value_var, + result_converter.exception()); + Var::PluginReleasePPVar(value_var); + return result_converter.CheckExceptionForNoResult(); +} + +bool WrapperClass_RemoveProperty(NPObject* object, NPIdentifier property_name) { + NPObjectAccessorWithIdentifier accessor(object, property_name, true); + if (!accessor.is_valid()) + return false; + + PPResultAndExceptionToNPResult result_converter(accessor.object(), NULL); + accessor.object()->ppp_class()->RemoveProperty( + accessor.object()->ppp_class_data(), accessor.identifier(), + result_converter.exception()); + return result_converter.CheckExceptionForNoResult(); +} + +bool WrapperClass_Enumerate(NPObject* object, NPIdentifier** values, + uint32_t* count) { + *values = NULL; + *count = 0; + PluginObject* obj = PluginObject::FromNPObject(object); + if (!obj) + return false; + + uint32_t property_count = 0; + PP_Var* properties = NULL; // Must be freed! + PPResultAndExceptionToNPResult result_converter(obj, NULL); + obj->ppp_class()->GetAllPropertyNames(obj->ppp_class_data(), + &property_count, &properties, + result_converter.exception()); + + // Convert the array of PP_Var to an array of NPIdentifiers. If any + // conversions fail, we will set the exception. + if (!result_converter.has_exception()) { + if (property_count > 0) { + *values = static_cast<NPIdentifier*>( + malloc(sizeof(NPIdentifier) * property_count)); + *count = 0; // Will be the number of items successfully converted. + for (uint32_t i = 0; i < property_count; ++i) { + if (!((*values)[i] = Var::PPVarToNPIdentifier(properties[i]))) { + // Throw an exception for the failed convertion. + *result_converter.exception() = StringVar::StringToPPVar( + obj->module(), kInvalidValueException); + break; + } + (*count)++; + } + + if (result_converter.has_exception()) { + // We don't actually have to free the identifiers we converted since + // all identifiers leak anyway :( . + free(*values); + *values = NULL; + *count = 0; + } + } + } + + // This will actually throw the exception, either from GetAllPropertyNames, + // or if anything was set during the conversion process. + result_converter.CheckExceptionForNoResult(); + + // Release the PP_Var that the plugin allocated. On success, they will all + // be converted to NPVariants, and on failure, we want them to just go away. + for (uint32_t i = 0; i < property_count; ++i) + Var::PluginReleasePPVar(properties[i]); + free(properties); + return result_converter.success(); +} + +bool WrapperClass_Construct(NPObject* object, const NPVariant* argv, + uint32_t argc, NPVariant* result) { + PluginObject* obj = PluginObject::FromNPObject(object); + if (!obj) + return false; + + PPVarArrayFromNPVariantArray args(obj->module(), argc, argv); + PPResultAndExceptionToNPResult result_converter(obj, result); + return result_converter.SetResult(obj->ppp_class()->Construct( + obj->ppp_class_data(), argc, args.array(), + result_converter.exception())); +} + +const NPClass wrapper_class = { + NP_CLASS_STRUCT_VERSION, + WrapperClass_Allocate, + WrapperClass_Deallocate, + WrapperClass_Invalidate, + WrapperClass_HasMethod, + WrapperClass_Invoke, + WrapperClass_InvokeDefault, + WrapperClass_HasProperty, + WrapperClass_GetProperty, + WrapperClass_SetProperty, + WrapperClass_RemoveProperty, + WrapperClass_Enumerate, + WrapperClass_Construct +}; + +} // namespace + +// PluginObject ------------------------------------------------------------- + +struct PluginObject::NPObjectWrapper : public NPObject { + // Points to the var object that owns this wrapper. This value may be NULL + // if there is no var owning this wrapper. This can happen if the plugin + // releases all references to the var, but a reference to the underlying + // NPObject is still held by script on the page. + PluginObject* obj; +}; + +PluginObject::PluginObject(PluginModule* module, + NPObjectWrapper* object_wrapper, + const PPP_Class* ppp_class, + void* ppp_class_data) + : module_(module), + object_wrapper_(object_wrapper), + ppp_class_(ppp_class), + ppp_class_data_(ppp_class_data) { + // Make the object wrapper refer back to this class so our NPObject + // implementation can call back into the Pepper layer. + object_wrapper_->obj = this; + module_->AddPluginObject(this); +} + +PluginObject::~PluginObject() { + // The wrapper we made for this NPObject may still have a reference to it + // from JavaScript, so we clear out its ObjectVar back pointer which will + // cause all calls "up" to the plugin to become NOPs. Our ObjectVar base + // class will release our reference to the object, which may or may not + // delete the NPObject. + DCHECK(object_wrapper_->obj == this); + object_wrapper_->obj = NULL; + module_->RemovePluginObject(this); +} + +PP_Var PluginObject::Create(PluginModule* module, + const PPP_Class* ppp_class, + void* ppp_class_data) { + // This will internally end up calling our AllocateObjectWrapper via the + // WrapperClass_Allocated function which will have created an object wrapper + // appropriate for this class (derived from NPObject). + NPObjectWrapper* wrapper = static_cast<NPObjectWrapper*>( + WebBindings::createObject(NULL, const_cast<NPClass*>(&wrapper_class))); + + // This object will register itself both with the NPObject and with the + // PluginModule. The NPObject will normally handle its lifetime, and it + // will get deleted in the destroy method. It may also get deleted when the + // plugin module is deallocated. + new PluginObject(module, wrapper, ppp_class, ppp_class_data); + + // We can just use a normal ObjectVar to refer to this object from the + // plugin. It will hold a ref to the underlying NPObject which will in turn + // hold our pluginObject. + return ObjectVar::NPObjectToPPVar(module, wrapper); +} + +NPObject* PluginObject::GetNPObject() const { + return object_wrapper_; +} + +// static +bool PluginObject::IsInstanceOf(NPObject* np_object, + const PPP_Class* ppp_class, + void** ppp_class_data) { + // Validate that this object is implemented by our wrapper class before + // trying to get the PluginObject. + if (np_object->_class != &wrapper_class) + return false; + + PluginObject* plugin_object = FromNPObject(np_object); + if (!plugin_object) + return false; // Object is no longer alive. + + if (plugin_object->ppp_class() != ppp_class) + return false; + if (ppp_class_data) + *ppp_class_data = plugin_object->ppp_class_data(); + return true; +} + +// static +PluginObject* PluginObject::FromNPObject(NPObject* object) { + return static_cast<NPObjectWrapper*>(object)->obj; +} + +// static +NPObject* PluginObject::AllocateObjectWrapper() { + NPObjectWrapper* wrapper = new NPObjectWrapper; + memset(wrapper, sizeof(NPObjectWrapper), 0); + return wrapper; +} + +} // namespace pepper diff --git a/webkit/glue/plugins/pepper_plugin_object.h b/webkit/glue/plugins/pepper_plugin_object.h new file mode 100644 index 0000000..7715a81 --- /dev/null +++ b/webkit/glue/plugins/pepper_plugin_object.h @@ -0,0 +1,89 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WEBKIT_GLUE_PLUGINS_PEPPER_PLUGIN_OBJECT_H_ +#define WEBKIT_GLUE_PLUGINS_PEPPER_PLUGIN_OBJECT_H_ + +#include <string> + +#include "base/basictypes.h" + +struct PP_Var; +struct PPP_Class; +typedef struct NPObject NPObject; +typedef struct _NPVariant NPVariant; + +namespace pepper { + +class PluginModule; + +class PluginObject { + public: + virtual ~PluginObject(); + + // Allocates a new PluginObject and returns it as a PP_Var with a + // refcount of 1. + static PP_Var Create(PluginModule* module, + const PPP_Class* ppp_class, + void* ppp_class_data); + + PluginModule* module() const { return module_; } + + const PPP_Class* ppp_class() { return ppp_class_; } + void* ppp_class_data() { return ppp_class_data_; }; + + NPObject* GetNPObject() const; + + // Returns true if the given var is an object implemented by the same plugin + // that owns the var object, and that the class matches. If it matches, + // returns true and places the class data into |*ppp_class_data| (which can + // optionally be NULL if no class data is desired). + static bool IsInstanceOf(NPObject* np_object, + const PPP_Class* ppp_class, + void** ppp_class_data); + + // Converts the given NPObject to the corresponding ObjectVar. + // + // The given NPObject must be one corresponding to a PluginObject or this + // will crash. If the object is a PluginObject but the plugin has gone + // away (the object could still be alive because of a reference from JS), + // then the return value will be NULL. + static PluginObject* FromNPObject(NPObject* object); + + // Allocates a plugin wrapper object and returns it as an NPObject. This is + // used internally only. + static NPObject* AllocateObjectWrapper(); + + private: + struct NPObjectWrapper; + + // This object must be created using the CreateObject function of the which + // will set up the correct NPObject. + // + // The NPObjectWrapper (an NPObject) should already have the reference + // incremented on it, and this class will take ownership of that reference. + PluginObject(PluginModule* module, + NPObjectWrapper* object_wrapper, + const PPP_Class* ppp_class, + void* ppp_class_data); + + PluginModule* module_; + + // Holds a pointer to the NPObject wrapper backing the var. This class + // derives from NPObject and we hold a reference to it, so it must be + // refcounted. When the type is not an object, this value will be NULL. + // + // We don't actually own this pointer, it's the NPObject that actually + // owns us. + NPObjectWrapper* object_wrapper_; + + const PPP_Class* ppp_class_; + void* ppp_class_data_; + + DISALLOW_COPY_AND_ASSIGN(PluginObject); +}; + +} // namespace pepper + +#endif // WEBKIT_GLUE_PLUGINS_PEPPER_PLUGIN_OBJECT_H_ diff --git a/webkit/glue/plugins/pepper_private.cc b/webkit/glue/plugins/pepper_private.cc index 551b8ac..3a09656 100644 --- a/webkit/glue/plugins/pepper_private.cc +++ b/webkit/glue/plugins/pepper_private.cc @@ -73,12 +73,16 @@ static const ResourceImageInfo kResourceImageMap[] = { IDR_PDF_THUMBNAIL_NUM_BACKGROUND }, }; -PP_Var GetLocalizedString(PP_ResourceString string_id) { +PP_Var GetLocalizedString(PP_Module module_id, PP_ResourceString string_id) { + PluginModule* module = PluginModule::FromPPModule(module_id); + if (!module) + return PP_MakeVoid(); + std::string rv; if (string_id == PP_RESOURCESTRING_PDFGETPASSWORD) rv = UTF16ToUTF8(webkit_glue::GetLocalizedString(IDS_PDF_NEED_PASSWORD)); - return StringToPPVar(rv); + return StringVar::StringToPPVar(module, rv); } PP_Resource GetResourceImage(PP_Module module_id, PP_ResourceImage image_id) { diff --git a/webkit/glue/plugins/pepper_resource.h b/webkit/glue/plugins/pepper_resource.h index 3bea5b5..1eeb4ec 100644 --- a/webkit/glue/plugins/pepper_resource.h +++ b/webkit/glue/plugins/pepper_resource.h @@ -23,12 +23,15 @@ class Font; class Graphics2D; class Graphics3D; class ImageData; +class ObjectVar; class PluginModule; class PrivateFontFile; class Scrollbar; +class StringVar; class URLLoader; class URLRequestInfo; class URLResponseInfo; +class Var; class VideoDecoder; class Widget; @@ -87,11 +90,14 @@ class Resource : public base::RefCountedThreadSafe<Resource> { virtual Graphics2D* AsGraphics2D() { return NULL; } virtual Graphics3D* AsGraphics3D() { return NULL; } virtual ImageData* AsImageData() { return NULL; } + virtual ObjectVar* AsObjectVar() { return NULL; } virtual PrivateFontFile* AsPrivateFontFile() { return NULL; } virtual Scrollbar* AsScrollbar() { return NULL; } + virtual StringVar* AsStringVar() { return NULL; } virtual URLLoader* AsURLLoader() { return NULL; } virtual URLRequestInfo* AsURLRequestInfo() { return NULL; } virtual URLResponseInfo* AsURLResponseInfo() { return NULL; } + virtual Var* AsVar() { return NULL; } virtual VideoDecoder* AsVideoDecoder() { return NULL; } virtual Widget* AsWidget() { return NULL; } @@ -132,11 +138,14 @@ DEFINE_RESOURCE_CAST(Font) DEFINE_RESOURCE_CAST(Graphics2D) DEFINE_RESOURCE_CAST(Graphics3D) DEFINE_RESOURCE_CAST(ImageData) +DEFINE_RESOURCE_CAST(ObjectVar) DEFINE_RESOURCE_CAST(PrivateFontFile) DEFINE_RESOURCE_CAST(Scrollbar) +DEFINE_RESOURCE_CAST(StringVar); DEFINE_RESOURCE_CAST(URLLoader) DEFINE_RESOURCE_CAST(URLRequestInfo) DEFINE_RESOURCE_CAST(URLResponseInfo) +DEFINE_RESOURCE_CAST(Var) DEFINE_RESOURCE_CAST(VideoDecoder) DEFINE_RESOURCE_CAST(Widget) diff --git a/webkit/glue/plugins/pepper_resource_tracker.cc b/webkit/glue/plugins/pepper_resource_tracker.cc index 8aa94d2..869874e 100644 --- a/webkit/glue/plugins/pepper_resource_tracker.cc +++ b/webkit/glue/plugins/pepper_resource_tracker.cc @@ -58,4 +58,18 @@ bool ResourceTracker::UnrefResource(PP_Resource res) { } } +uint32 ResourceTracker::GetLiveObjectsForModule(PluginModule* module) const { + // Since this is for testing only, we'll just go through all of them and + // count. + // + // TODO(brettw) we will eventually need to implement more efficient + // module->resource lookup to free resources when a module is unloaded. In + // this case, this function can be implemented using that system. + uint32 count = 0; + for (ResourceMap::const_iterator i = live_resources_.begin(); + i != live_resources_.end(); ++i) + count++; + return count; +} + } // namespace pepper diff --git a/webkit/glue/plugins/pepper_resource_tracker.h b/webkit/glue/plugins/pepper_resource_tracker.h index 1363dde..20ba6db 100644 --- a/webkit/glue/plugins/pepper_resource_tracker.h +++ b/webkit/glue/plugins/pepper_resource_tracker.h @@ -13,8 +13,11 @@ #include "base/singleton.h" #include "third_party/ppapi/c/pp_resource.h" +typedef struct NPObject NPObject; + namespace pepper { +class PluginModule; class Resource; // This class maintains a global list of all live pepper resources. It allows @@ -40,6 +43,11 @@ class ResourceTracker { bool AddRefResource(PP_Resource res); bool UnrefResource(PP_Resource res); + // Returns the number of resources associated with this module. + // + // This is slow, use only for testing. + uint32 GetLiveObjectsForModule(PluginModule* module) const; + private: friend struct DefaultSingletonTraits<ResourceTracker>; friend class Resource; diff --git a/webkit/glue/plugins/pepper_url_request_info.cc b/webkit/glue/plugins/pepper_url_request_info.cc index 3c26767..d5130f2 100644 --- a/webkit/glue/plugins/pepper_url_request_info.cc +++ b/webkit/glue/plugins/pepper_url_request_info.cc @@ -72,8 +72,11 @@ bool SetProperty(PP_Resource request_id, if (var.type == PP_VARTYPE_BOOL) return request->SetBooleanProperty(property, var.value.as_bool); - if (var.type == PP_VARTYPE_STRING) - return request->SetStringProperty(property, GetString(var)->value()); + if (var.type == PP_VARTYPE_STRING) { + scoped_refptr<StringVar> string(StringVar::FromPPVar(var)); + if (string) + return request->SetStringProperty(property, string->value()); + } return false; } @@ -84,7 +87,7 @@ bool AppendDataToBody(PP_Resource request_id, PP_Var var) { if (!request) return false; - String* data = GetString(var); + scoped_refptr<StringVar> data(StringVar::FromPPVar(var)); if (!data) return false; diff --git a/webkit/glue/plugins/pepper_url_response_info.cc b/webkit/glue/plugins/pepper_url_response_info.cc index 3e80994..79042ba 100644 --- a/webkit/glue/plugins/pepper_url_response_info.cc +++ b/webkit/glue/plugins/pepper_url_response_info.cc @@ -90,11 +90,11 @@ const PPB_URLResponseInfo_Dev* URLResponseInfo::GetInterface() { PP_Var URLResponseInfo::GetProperty(PP_URLResponseProperty_Dev property) { switch (property) { case PP_URLRESPONSEPROPERTY_URL: - return StringToPPVar(url_); + return StringVar::StringToPPVar(module(), url_); case PP_URLRESPONSEPROPERTY_STATUSCODE: return PP_MakeInt32(status_code_); case PP_URLRESPONSEPROPERTY_HEADERS: - return StringToPPVar(headers_); + return StringVar::StringToPPVar(module(), headers_); default: NOTIMPLEMENTED(); // TODO(darin): Implement me! return PP_MakeVoid(); diff --git a/webkit/glue/plugins/pepper_url_util.cc b/webkit/glue/plugins/pepper_url_util.cc index 07a0691..3f9a54f 100644 --- a/webkit/glue/plugins/pepper_url_util.cc +++ b/webkit/glue/plugins/pepper_url_util.cc @@ -46,11 +46,12 @@ void ConvertComponents(const url_parse::Parsed& input, // Used for returning the given GURL from a PPAPI function, with an optional // out param indicating the components. -PP_Var GenerateUrlReturn(const GURL& url, PP_UrlComponents_Dev* components) { +PP_Var GenerateUrlReturn(PluginModule* module, const GURL& url, + PP_UrlComponents_Dev* components) { if (!url.is_valid()) return PP_MakeNull(); ConvertComponents(url.parsed_for_possibly_invalid_spec(), components); - return StringToPPVar(url.possibly_invalid_spec()); + return StringVar::StringToPPVar(module, url.possibly_invalid_spec()); } // Sets |*security_origin| to be the WebKit security origin associated with the @@ -73,24 +74,26 @@ bool SecurityOriginForInstance(PP_Instance instance_id, } PP_Var Canonicalize(PP_Var url, PP_UrlComponents_Dev* components) { - String* url_string = GetString(url); + scoped_refptr<StringVar> url_string(StringVar::FromPPVar(url)); if (!url_string) return PP_MakeNull(); - return GenerateUrlReturn(GURL(url_string->value()), components); + return GenerateUrlReturn(url_string->module(), + GURL(url_string->value()), components); } PP_Var ResolveRelativeToUrl(PP_Var base_url, PP_Var relative, PP_UrlComponents_Dev* components) { - String* base_url_string = GetString(base_url); - String* relative_string = GetString(relative); + scoped_refptr<StringVar> base_url_string(StringVar::FromPPVar(base_url)); + scoped_refptr<StringVar> relative_string(StringVar::FromPPVar(relative)); if (!base_url_string || !relative_string) return PP_MakeNull(); GURL base_gurl(base_url_string->value()); if (!base_gurl.is_valid()) return PP_MakeNull(); - return GenerateUrlReturn(base_gurl.Resolve(relative_string->value()), + return GenerateUrlReturn(base_url_string->module(), + base_gurl.Resolve(relative_string->value()), components); } @@ -101,19 +104,20 @@ PP_Var ResolveRelativeToDocument(PP_Instance instance_id, if (!instance) return PP_MakeNull(); - String* relative_string = GetString(relative); + scoped_refptr<StringVar> relative_string(StringVar::FromPPVar(relative)); if (!relative_string) return PP_MakeNull(); WebKit::WebElement plugin_element = instance->container()->element(); GURL document_url = plugin_element.document().baseURL(); - return GenerateUrlReturn(document_url.Resolve(relative_string->value()), + return GenerateUrlReturn(instance->module(), + document_url.Resolve(relative_string->value()), components); } bool IsSameSecurityOrigin(PP_Var url_a, PP_Var url_b) { - String* url_a_string = GetString(url_a); - String* url_b_string = GetString(url_b); + scoped_refptr<StringVar> url_a_string(StringVar::FromPPVar(url_a)); + scoped_refptr<StringVar> url_b_string(StringVar::FromPPVar(url_b)); if (!url_a_string || !url_b_string) return false; @@ -126,7 +130,7 @@ bool IsSameSecurityOrigin(PP_Var url_a, PP_Var url_b) { } bool DocumentCanRequest(PP_Instance instance, PP_Var url) { - String* url_string = GetString(url); + scoped_refptr<StringVar> url_string(StringVar::FromPPVar(url)); if (!url_string) return false; diff --git a/webkit/glue/plugins/pepper_var.cc b/webkit/glue/plugins/pepper_var.cc index f34ac6d..b4ba014 100644 --- a/webkit/glue/plugins/pepper_var.cc +++ b/webkit/glue/plugins/pepper_var.cc @@ -6,13 +6,12 @@ #include "base/logging.h" #include "base/scoped_ptr.h" -#include "base/string_number_conversions.h" #include "base/string_util.h" #include "third_party/ppapi/c/pp_var.h" #include "third_party/ppapi/c/ppb_var.h" -#include "third_party/ppapi/c/ppp_class.h" #include "third_party/WebKit/WebKit/chromium/public/WebBindings.h" -#include "webkit/glue/plugins/pepper_string.h" +#include "webkit/glue/plugins/pepper_plugin_module.h" +#include "webkit/glue/plugins/pepper_plugin_object.h" #include "v8/include/v8.h" using WebKit::WebBindings; @@ -21,11 +20,9 @@ namespace pepper { namespace { -void Release(PP_Var var); -PP_Var VarFromUtf8(const char* data, uint32_t len); - const char kInvalidObjectException[] = "Error: Invalid object"; const char kInvalidPropertyException[] = "Error: Invalid property"; +const char kInvalidValueException[] = "Error: Invalid value"; const char kUnableToGetPropertyException[] = "Error: Unable to get property"; const char kUnableToSetPropertyException[] = "Error: Unable to set property"; const char kUnableToRemovePropertyException[] = @@ -38,514 +35,182 @@ const char kUnableToConstructException[] = "Error: Unable to construct"; // --------------------------------------------------------------------------- // Utilities -String* GetStringUnchecked(PP_Var var) { - return reinterpret_cast<String*>(var.value.as_id); -} - -NPObject* GetNPObjectUnchecked(PP_Var var) { - return reinterpret_cast<NPObject*>(var.value.as_id); -} - -// Returns a NPVariant that corresponds to the given PP_Var. The contents of -// the PP_Var will be copied unless the PP_Var corresponds to an object. -NPVariant PPVarToNPVariant(PP_Var var) { - NPVariant ret; - switch (var.type) { - case PP_VARTYPE_VOID: - VOID_TO_NPVARIANT(ret); - break; - case PP_VARTYPE_NULL: - NULL_TO_NPVARIANT(ret); - break; - case PP_VARTYPE_BOOL: - BOOLEAN_TO_NPVARIANT(var.value.as_bool, ret); - break; - case PP_VARTYPE_INT32: - INT32_TO_NPVARIANT(var.value.as_int, ret); - break; - case PP_VARTYPE_DOUBLE: - DOUBLE_TO_NPVARIANT(var.value.as_double, ret); - break; - case PP_VARTYPE_STRING: { - const std::string& value = GetStringUnchecked(var)->value(); - STRINGN_TO_NPVARIANT(base::strdup(value.c_str()), value.size(), ret); - break; - } - case PP_VARTYPE_OBJECT: { - NPObject* object = GetNPObjectUnchecked(var); - OBJECT_TO_NPVARIANT(WebBindings::retainObject(object), ret); - break; - } - } - return ret; -} - -// Returns a NPVariant that corresponds to the given PP_Var. The contents of -// the PP_Var will NOT be copied, so you need to ensure that the PP_Var remains -// valid while the resultant NPVariant is in use. -NPVariant PPVarToNPVariantNoCopy(PP_Var var) { - NPVariant ret; +// Converts the given PP_Var to an NPVariant, returning true on success. +// False means that the given variant is invalid. In this case, the result +// NPVariant will be set to a void one. +// +// The contents of the PP_Var will NOT be copied, so you need to ensure that +// the PP_Var remains valid while the resultant NPVariant is in use. +bool PPVarToNPVariantNoCopy(PP_Var var, NPVariant* result) { switch (var.type) { case PP_VARTYPE_VOID: - VOID_TO_NPVARIANT(ret); + VOID_TO_NPVARIANT(*result); break; case PP_VARTYPE_NULL: - NULL_TO_NPVARIANT(ret); + NULL_TO_NPVARIANT(*result); break; case PP_VARTYPE_BOOL: - BOOLEAN_TO_NPVARIANT(var.value.as_bool, ret); + BOOLEAN_TO_NPVARIANT(var.value.as_bool, *result); break; case PP_VARTYPE_INT32: - INT32_TO_NPVARIANT(var.value.as_int, ret); + INT32_TO_NPVARIANT(var.value.as_int, *result); break; case PP_VARTYPE_DOUBLE: - DOUBLE_TO_NPVARIANT(var.value.as_double, ret); + DOUBLE_TO_NPVARIANT(var.value.as_double, *result); break; case PP_VARTYPE_STRING: { - const std::string& value = GetStringUnchecked(var)->value(); - STRINGN_TO_NPVARIANT(value.c_str(), value.size(), ret); + scoped_refptr<StringVar> string(StringVar::FromPPVar(var)); + if (!string) { + VOID_TO_NPVARIANT(*result); + return false; + } + const std::string& value = string->value(); + STRINGN_TO_NPVARIANT(value.c_str(), value.size(), *result); break; } case PP_VARTYPE_OBJECT: { - OBJECT_TO_NPVARIANT(GetNPObjectUnchecked(var), ret); + scoped_refptr<ObjectVar> object(ObjectVar::FromPPVar(var)); + if (!object) { + VOID_TO_NPVARIANT(*result); + return false; + } + OBJECT_TO_NPVARIANT(object->np_object(), *result); break; } - } - return ret; -} - -// Returns a NPIdentifier that corresponds to the given PP_Var. The contents -// of the PP_Var will be copied. Returns NULL if the given PP_Var is not a a -// string or integer type. -NPIdentifier PPVarToNPIdentifier(PP_Var var) { - switch (var.type) { - case PP_VARTYPE_STRING: - return WebBindings::getStringIdentifier( - GetStringUnchecked(var)->value().c_str()); - case PP_VARTYPE_INT32: - return WebBindings::getIntIdentifier(var.value.as_int); default: - return NULL; - } -} - -PP_Var NPIdentifierToPPVar(NPIdentifier id) { - const NPUTF8* string_value = NULL; - int32_t int_value = 0; - bool is_string = false; - WebBindings::extractIdentifierData(id, string_value, int_value, is_string); - if (is_string) - return VarFromUtf8(string_value, strlen(string_value)); - - return PP_MakeInt32(int_value); -} - -PP_Var NPIdentifierToPPVarString(NPIdentifier id) { - PP_Var var = NPIdentifierToPPVar(id); - if (var.type == PP_VARTYPE_STRING) - return var; - DCHECK(var.type == PP_VARTYPE_INT32); - const std::string& str = base::IntToString(var.value.as_int); - return VarFromUtf8(str.data(), str.size()); -} - -void ThrowException(NPObject* object, PP_Var exception) { - String* str = GetString(exception); - if (str) - WebBindings::setException(object, str->value().c_str()); -} - -// --------------------------------------------------------------------------- -// NPObject implementation in terms of PPP_Class - -struct WrapperObject : NPObject { - const PPP_Class* ppp_class; - void* ppp_class_data; -}; - -static WrapperObject* ToWrapper(NPObject* object) { - return static_cast<WrapperObject*>(object); -} - -NPObject* WrapperClass_Allocate(NPP npp, NPClass* unused) { - return new WrapperObject; -} - -void WrapperClass_Deallocate(NPObject* object) { - WrapperObject* wrapper = ToWrapper(object); - wrapper->ppp_class->Deallocate(wrapper->ppp_class_data); - delete object; -} - -void WrapperClass_Invalidate(NPObject* object) { - // TODO(darin): Do I need to do something here? -} - -bool WrapperClass_HasMethod(NPObject* object, NPIdentifier method_name) { - WrapperObject* wrapper = ToWrapper(object); - - PP_Var method_name_var = NPIdentifierToPPVarString(method_name); - PP_Var exception = PP_MakeVoid(); - bool rv = wrapper->ppp_class->HasMethod(wrapper->ppp_class_data, - method_name_var, - &exception); - Release(method_name_var); - - if (exception.type != PP_VARTYPE_VOID) { - ThrowException(object, exception); - Release(exception); - return false; - } - return rv; -} - -bool WrapperClass_Invoke(NPObject* object, NPIdentifier method_name, - const NPVariant* argv, uint32_t argc, - NPVariant* result) { - WrapperObject* wrapper = ToWrapper(object); - - scoped_array<PP_Var> args; - if (argc) { - args.reset(new PP_Var[argc]); - for (uint32_t i = 0; i < argc; ++i) - args[i] = NPVariantToPPVar(&argv[i]); - } - PP_Var method_name_var = NPIdentifierToPPVarString(method_name); - PP_Var exception = PP_MakeVoid(); - PP_Var result_var = wrapper->ppp_class->Call(wrapper->ppp_class_data, - method_name_var, argc, - args.get(), &exception); - Release(method_name_var); - for (uint32_t i = 0; i < argc; ++i) - Release(args[i]); - - bool rv; - if (exception.type == PP_VARTYPE_VOID) { - rv = true; - *result = PPVarToNPVariant(result_var); - } else { - rv = false; - ThrowException(object, exception); - Release(exception); - } - Release(result_var); - return rv; -} - -bool WrapperClass_InvokeDefault(NPObject* object, const NPVariant* argv, - uint32_t argc, NPVariant* result) { - WrapperObject* wrapper = ToWrapper(object); - - scoped_array<PP_Var> args; - if (argc) { - args.reset(new PP_Var[argc]); - for (uint32_t i = 0; i < argc; ++i) - args[i] = NPVariantToPPVar(&argv[i]); - } - PP_Var exception = PP_MakeVoid(); - PP_Var result_var = wrapper->ppp_class->Call(wrapper->ppp_class_data, - PP_MakeVoid(), argc, args.get(), - &exception); - for (uint32_t i = 0; i < argc; ++i) - Release(args[i]); - - bool rv; - if (exception.type == PP_VARTYPE_VOID) { - rv = true; - *result = PPVarToNPVariant(result_var); - } else { - rv = false; - ThrowException(object, exception); - Release(exception); - } - Release(result_var); - return rv; -} - -bool WrapperClass_HasProperty(NPObject* object, NPIdentifier property_name) { - WrapperObject* wrapper = ToWrapper(object); - - PP_Var property_name_var = NPIdentifierToPPVar(property_name); - PP_Var exception = PP_MakeVoid(); - bool rv = wrapper->ppp_class->HasProperty(wrapper->ppp_class_data, - property_name_var, - &exception); - Release(property_name_var); - - if (exception.type != PP_VARTYPE_VOID) { - ThrowException(object, exception); - Release(exception); - return false; - } - return rv; -} - -bool WrapperClass_GetProperty(NPObject* object, NPIdentifier property_name, - NPVariant* result) { - WrapperObject* wrapper = ToWrapper(object); - - PP_Var property_name_var = NPIdentifierToPPVar(property_name); - PP_Var exception = PP_MakeVoid(); - PP_Var result_var = wrapper->ppp_class->GetProperty(wrapper->ppp_class_data, - property_name_var, - &exception); - Release(property_name_var); - - bool rv; - if (exception.type == PP_VARTYPE_VOID) { - rv = true; - *result = PPVarToNPVariant(result_var); - } else { - rv = false; - ThrowException(object, exception); - Release(exception); - } - Release(result_var); - return rv; -} - -bool WrapperClass_SetProperty(NPObject* object, NPIdentifier property_name, - const NPVariant* value) { - WrapperObject* wrapper = ToWrapper(object); - - PP_Var property_name_var = NPIdentifierToPPVar(property_name); - PP_Var value_var = NPVariantToPPVar(value); - PP_Var exception = PP_MakeVoid(); - wrapper->ppp_class->SetProperty(wrapper->ppp_class_data, property_name_var, - value_var, &exception); - Release(value_var); - Release(property_name_var); - - if (exception.type != PP_VARTYPE_VOID) { - ThrowException(object, exception); - Release(exception); - return false; - } - return true; -} - -bool WrapperClass_RemoveProperty(NPObject* object, NPIdentifier property_name) { - WrapperObject* wrapper = ToWrapper(object); - - PP_Var property_name_var = NPIdentifierToPPVar(property_name); - PP_Var exception = PP_MakeVoid(); - wrapper->ppp_class->RemoveProperty(wrapper->ppp_class_data, property_name_var, - &exception); - Release(property_name_var); - - if (exception.type != PP_VARTYPE_VOID) { - ThrowException(object, exception); - Release(exception); - return false; + VOID_TO_NPVARIANT(*result); + return false; } return true; } -bool WrapperClass_Enumerate(NPObject* object, NPIdentifier** values, - uint32_t* count) { - WrapperObject* wrapper = ToWrapper(object); - - uint32_t property_count = 0; - PP_Var* properties = NULL; - PP_Var exception = PP_MakeVoid(); - wrapper->ppp_class->GetAllPropertyNames(wrapper->ppp_class_data, - &property_count, - &properties, - &exception); - - bool rv; - if (exception.type == PP_VARTYPE_VOID) { - rv = true; - if (property_count == 0) { - *values = NULL; - *count = 0; +// ObjectAccessorTryCatch ------------------------------------------------------ + +// Automatically sets up a TryCatch for accessing the object identified by the +// given PP_Var. The module from the object will be used for the exception +// strings generated by the TryCatch. +// +// This will automatically retrieve the ObjectVar from the object and throw +// an exception if it's invalid. At the end of construction, if there is no +// exception, you know that there is no previously set exception, that the +// object passed in is valid and ready to use (via the object() getter), and +// that the TryCatch's module() getter is also set up properly and ready to +// use. +class ObjectAccessorTryCatch : public TryCatch { + public: + ObjectAccessorTryCatch(PP_Var object, PP_Var* exception) + : TryCatch(NULL, exception), + object_(ObjectVar::FromPPVar(object)) { + if (!object_) { + // No object or an invalid object was given. This means we have no module + // to associated with the exception text, so use the magic invalid object + // exception. + SetInvalidObjectException(); } else { - *values = static_cast<NPIdentifier*>( - malloc(sizeof(NPIdentifier) * property_count)); - *count = property_count; - for (uint32_t i = 0; i < property_count; ++i) - (*values)[i] = PPVarToNPIdentifier(properties[i]); + // When the object is valid, we have a valid module to associate + set_module(object_->module()); } - } else { - rv = false; - ThrowException(object, exception); - Release(exception); } - for (uint32_t i = 0; i < property_count; ++i) - Release(properties[i]); - free(properties); - return rv; -} - -bool WrapperClass_Construct(NPObject* object, const NPVariant* argv, - uint32_t argc, NPVariant* result) { - WrapperObject* wrapper = ToWrapper(object); + ObjectVar* object() { return object_.get(); } - scoped_array<PP_Var> args; - if (argc) { - args.reset(new PP_Var[argc]); - for (uint32_t i = 0; i < argc; ++i) - args[i] = NPVariantToPPVar(&argv[i]); - } + protected: + scoped_refptr<ObjectVar> object_; - PP_Var exception = PP_MakeVoid(); - PP_Var result_var = wrapper->ppp_class->Construct(wrapper->ppp_class_data, - argc, args.get(), - &exception); - for (uint32_t i = 0; i < argc; ++i) - Release(args[i]); - - bool rv; - if (exception.type == PP_VARTYPE_VOID) { - rv = true; - *result = PPVarToNPVariant(result_var); - } else { - rv = false; - ThrowException(object, exception); - Release(exception); - } - Release(result_var); - return rv; -} - -const NPClass wrapper_class = { - NP_CLASS_STRUCT_VERSION, - WrapperClass_Allocate, - WrapperClass_Deallocate, - WrapperClass_Invalidate, - WrapperClass_HasMethod, - WrapperClass_Invoke, - WrapperClass_InvokeDefault, - WrapperClass_HasProperty, - WrapperClass_GetProperty, - WrapperClass_SetProperty, - WrapperClass_RemoveProperty, - WrapperClass_Enumerate, - WrapperClass_Construct + DISALLOW_COPY_AND_ASSIGN(ObjectAccessorTryCatch); }; -// --------------------------------------------------------------------------- -// PPB_Var methods - -void AddRef(PP_Var var) { - if (var.type == PP_VARTYPE_STRING) { - GetStringUnchecked(var)->AddRef(); - } else if (var.type == PP_VARTYPE_OBJECT) { - // TODO(darin): Add thread safety check - WebBindings::retainObject(GetNPObjectUnchecked(var)); - } -} - -void Release(PP_Var var) { - if (var.type == PP_VARTYPE_STRING) { - GetStringUnchecked(var)->Release(); - } else if (var.type == PP_VARTYPE_OBJECT) { - // TODO(darin): Add thread safety check - WebBindings::releaseObject(GetNPObjectUnchecked(var)); +// ObjectAccessiorWithIdentifierTryCatch --------------------------------------- + +// Automatically sets up a TryCatch for accessing the identifier on the given +// object. This just extends ObjectAccessorTryCatch to additionally convert +// the given identifier to an NPIdentifier and validate it, throwing an +// exception if it's invalid. +// +// At the end of construction, if there is no exception, you know that there is +// no previously set exception, that the object passed in is valid and ready to +// use (via the object() getter), that the identifier is valid and ready to +// use (via the identifier() getter), and that the TryCatch's module() getter +// is also set up properly and ready to use. +class ObjectAccessorWithIdentifierTryCatch : public ObjectAccessorTryCatch { + public: + ObjectAccessorWithIdentifierTryCatch(PP_Var object, + PP_Var identifier, + PP_Var* exception) + : ObjectAccessorTryCatch(object, exception), + identifier_(0) { + if (!has_exception()) { + identifier_ = Var::PPVarToNPIdentifier(identifier); + if (!identifier_) + SetException(kInvalidPropertyException); + } } -} -PP_Var VarFromUtf8(const char* data, uint32_t len) { - scoped_refptr<String> str = new String(data, len); + NPIdentifier identifier() const { return identifier_; } - if (!str || !IsStringUTF8(str->value())) { - return PP_MakeNull(); - } + private: + NPIdentifier identifier_; - PP_Var ret; - ret.type = PP_VARTYPE_STRING; + DISALLOW_COPY_AND_ASSIGN(ObjectAccessorWithIdentifierTryCatch); +}; - // The caller takes ownership now. - ret.value.as_id = reinterpret_cast<intptr_t>(str.release()); +// PPB_Var methods ------------------------------------------------------------- - return ret; +PP_Var VarFromUtf8(PP_Module module_id, const char* data, uint32_t len) { + PluginModule* module = PluginModule::FromPPModule(module_id); + if (!module) + return PP_MakeNull(); + return StringVar::StringToPPVar(module, data, len); } const char* VarToUtf8(PP_Var var, uint32_t* len) { - if (var.type != PP_VARTYPE_STRING) { + scoped_refptr<StringVar> str(StringVar::FromPPVar(var)); + if (!str) { *len = 0; return NULL; } - const std::string& str = GetStringUnchecked(var)->value(); - *len = static_cast<uint32_t>(str.size()); - if (str.empty()) + *len = static_cast<uint32_t>(str->value().size()); + if (str->value().empty()) return ""; // Don't return NULL on success. - return str.data(); + return str->value().data(); } bool HasProperty(PP_Var var, PP_Var name, PP_Var* exception) { - TryCatch try_catch(exception); - if (try_catch.HasException()) - return false; - - NPObject* object = GetNPObject(var); - if (!object) { - try_catch.SetException(kInvalidObjectException); - return false; - } - - NPIdentifier identifier = PPVarToNPIdentifier(name); - if (!identifier) { - try_catch.SetException(kInvalidPropertyException); + ObjectAccessorWithIdentifierTryCatch accessor(var, name, exception); + if (accessor.has_exception()) return false; - } - - return WebBindings::hasProperty(NULL, object, identifier); + return WebBindings::hasProperty(NULL, accessor.object()->np_object(), + accessor.identifier()); } bool HasMethod(PP_Var var, PP_Var name, PP_Var* exception) { - TryCatch try_catch(exception); - if (try_catch.HasException()) - return false; - - NPObject* object = GetNPObject(var); - if (!object) { - try_catch.SetException(kInvalidObjectException); - return false; - } - - NPIdentifier identifier = PPVarToNPIdentifier(name); - if (!identifier) { - try_catch.SetException(kInvalidPropertyException); + ObjectAccessorWithIdentifierTryCatch accessor(var, name, exception); + if (accessor.has_exception()) return false; - } - - return WebBindings::hasMethod(NULL, object, identifier); + return WebBindings::hasMethod(NULL, accessor.object()->np_object(), + accessor.identifier()); } PP_Var GetProperty(PP_Var var, PP_Var name, PP_Var* exception) { - TryCatch try_catch(exception); - if (try_catch.HasException()) - return PP_MakeVoid(); - - NPObject* object = GetNPObject(var); - if (!object) { - try_catch.SetException(kInvalidObjectException); - return PP_MakeVoid(); - } - - NPIdentifier identifier = PPVarToNPIdentifier(name); - if (!identifier) { - try_catch.SetException(kInvalidPropertyException); + ObjectAccessorWithIdentifierTryCatch accessor(var, name, exception); + if (accessor.has_exception()) return PP_MakeVoid(); - } NPVariant result; - if (!WebBindings::getProperty(NULL, object, identifier, &result)) { + if (!WebBindings::getProperty(NULL, accessor.object()->np_object(), + accessor.identifier(), &result)) { // An exception may have been raised. - if (!try_catch.HasException()) - try_catch.SetException(kUnableToGetPropertyException); + accessor.SetException(kUnableToGetPropertyException); return PP_MakeVoid(); } - PP_Var ret = NPVariantToPPVar(&result); + PP_Var ret = Var::NPVariantToPPVar(accessor.object()->module(), &result); WebBindings::releaseVariantValue(&result); return ret; } @@ -557,21 +222,15 @@ void GetAllPropertyNames(PP_Var var, *properties = NULL; *property_count = 0; - TryCatch try_catch(exception); - if (try_catch.HasException()) - return; - - NPObject* object = GetNPObject(var); - if (!object) { - try_catch.SetException(kInvalidObjectException); + ObjectAccessorTryCatch accessor(var, exception); + if (accessor.has_exception()) return; - } NPIdentifier* identifiers = NULL; uint32_t count = 0; - if (!WebBindings::enumerate(NULL, object, &identifiers, &count)) { - if (!try_catch.HasException()) - try_catch.SetException(kUnableToGetAllPropertiesException); + if (!WebBindings::enumerate(NULL, accessor.object()->np_object(), + &identifiers, &count)) { + accessor.SetException(kUnableToGetAllPropertiesException); return; } @@ -580,8 +239,10 @@ void GetAllPropertyNames(PP_Var var, *property_count = count; *properties = static_cast<PP_Var*>(malloc(sizeof(PP_Var) * count)); - for (uint32_t i = 0; i < count; ++i) - (*properties)[i] = NPIdentifierToPPVar(identifiers[i]); + for (uint32_t i = 0; i < count; ++i) { + (*properties)[i] = Var::NPIdentifierToPPVar(accessor.object()->module(), + identifiers[i]); + } free(identifiers); } @@ -589,52 +250,30 @@ void SetProperty(PP_Var var, PP_Var name, PP_Var value, PP_Var* exception) { - TryCatch try_catch(exception); - if (try_catch.HasException()) - return; - - NPObject* object = GetNPObject(var); - if (!object) { - try_catch.SetException(kInvalidObjectException); + ObjectAccessorWithIdentifierTryCatch accessor(var, name, exception); + if (accessor.has_exception()) return; - } - NPIdentifier identifier = PPVarToNPIdentifier(name); - if (!identifier) { - try_catch.SetException(kInvalidPropertyException); + NPVariant variant; + if (!PPVarToNPVariantNoCopy(value, &variant)) { + accessor.SetException(kInvalidValueException); return; } - - NPVariant variant = PPVarToNPVariantNoCopy(value); - if (!WebBindings::setProperty(NULL, object, identifier, &variant)) { - if (!try_catch.HasException()) - try_catch.SetException(kUnableToSetPropertyException); - } + if (!WebBindings::setProperty(NULL, accessor.object()->np_object(), + accessor.identifier(), &variant)) + accessor.SetException(kUnableToSetPropertyException); } void RemoveProperty(PP_Var var, PP_Var name, PP_Var* exception) { - TryCatch try_catch(exception); - if (try_catch.HasException()) - return; - - NPObject* object = GetNPObject(var); - if (!object) { - try_catch.SetException(kInvalidObjectException); + ObjectAccessorWithIdentifierTryCatch accessor(var, name, exception); + if (accessor.has_exception()) return; - } - - NPIdentifier identifier = PPVarToNPIdentifier(name); - if (!identifier) { - try_catch.SetException(kInvalidPropertyException); - return; - } - if (!WebBindings::removeProperty(NULL, object, identifier)) { - if (!try_catch.HasException()) - try_catch.SetException(kUnableToRemovePropertyException); - } + if (!WebBindings::removeProperty(NULL, accessor.object()->np_object(), + accessor.identifier())) + accessor.SetException(kUnableToRemovePropertyException); } PP_Var Call(PP_Var var, @@ -642,56 +281,55 @@ PP_Var Call(PP_Var var, uint32_t argc, PP_Var* argv, PP_Var* exception) { - TryCatch try_catch(exception); - if (try_catch.HasException()) - return PP_MakeVoid(); - - NPObject* object = GetNPObject(var); - if (!object) { - try_catch.SetException(kInvalidObjectException); + ObjectAccessorTryCatch accessor(var, exception); + if (accessor.has_exception()) return PP_MakeVoid(); - } NPIdentifier identifier; if (method_name.type == PP_VARTYPE_VOID) { identifier = NULL; } else if (method_name.type == PP_VARTYPE_STRING) { // Specifically allow only string functions to be called. - identifier = PPVarToNPIdentifier(method_name); + identifier = Var::PPVarToNPIdentifier(method_name); if (!identifier) { - try_catch.SetException(kInvalidPropertyException); + accessor.SetException(kInvalidPropertyException); return PP_MakeVoid(); } } else { - try_catch.SetException(kInvalidPropertyException); + accessor.SetException(kInvalidPropertyException); return PP_MakeVoid(); } scoped_array<NPVariant> args; if (argc) { args.reset(new NPVariant[argc]); - for (uint32_t i = 0; i < argc; ++i) - args[i] = PPVarToNPVariantNoCopy(argv[i]); + for (uint32_t i = 0; i < argc; ++i) { + if (!PPVarToNPVariantNoCopy(argv[i], &args[i])) { + // This argument was invalid, throw an exception & give up. + accessor.SetException(kInvalidValueException); + return PP_MakeVoid(); + } + } } bool ok; NPVariant result; if (identifier) { - ok = WebBindings::invoke(NULL, object, identifier, args.get(), argc, - &result); + ok = WebBindings::invoke(NULL, accessor.object()->np_object(), + identifier, args.get(), argc, &result); } else { - ok = WebBindings::invokeDefault(NULL, object, args.get(), argc, &result); + ok = WebBindings::invokeDefault(NULL, accessor.object()->np_object(), + args.get(), argc, &result); } if (!ok) { // An exception may have been raised. - if (!try_catch.HasException()) - try_catch.SetException(kUnableToCallMethodException); + accessor.SetException(kUnableToCallMethodException); return PP_MakeVoid(); } - PP_Var ret = NPVariantToPPVar(&result); + PP_Var ret = Var::NPVariantToPPVar(accessor.module(), &result); WebBindings::releaseVariantValue(&result); return ret; } @@ -700,67 +338,58 @@ PP_Var Construct(PP_Var var, uint32_t argc, PP_Var* argv, PP_Var* exception) { - TryCatch try_catch(exception); - if (try_catch.HasException()) - return PP_MakeVoid(); - - NPObject* object = GetNPObject(var); - if (!object) { - try_catch.SetException(kInvalidObjectException); + ObjectAccessorTryCatch accessor(var, exception); + if (accessor.has_exception()) return PP_MakeVoid(); - } scoped_array<NPVariant> args; if (argc) { args.reset(new NPVariant[argc]); - for (uint32_t i = 0; i < argc; ++i) - args[i] = PPVarToNPVariantNoCopy(argv[i]); + for (uint32_t i = 0; i < argc; ++i) { + if (!PPVarToNPVariantNoCopy(argv[i], &args[i])) { + // This argument was invalid, throw an exception & give up. + accessor.SetException(kInvalidValueException); + return PP_MakeVoid(); + } + } } NPVariant result; - if (!WebBindings::construct(NULL, object, args.get(), argc, &result)) { + if (!WebBindings::construct(NULL, accessor.object()->np_object(), + args.get(), argc, &result)) { // An exception may have been raised. - if (!try_catch.HasException()) - try_catch.SetException(kUnableToConstructException); + accessor.SetException(kUnableToConstructException); return PP_MakeVoid(); } - PP_Var ret = NPVariantToPPVar(&result); + PP_Var ret = Var::NPVariantToPPVar(accessor.module(), &result); WebBindings::releaseVariantValue(&result); return ret; } -bool IsInstanceOf(PP_Var var, const PPP_Class* ppp_class, +bool IsInstanceOf(PP_Var var, + const PPP_Class* ppp_class, void** ppp_class_data) { - NPObject* object = GetNPObject(var); + scoped_refptr<ObjectVar> object(ObjectVar::FromPPVar(var)); if (!object) - return false; - - if (object->_class != &wrapper_class) - return false; + return false; // Not an object at all. - WrapperObject* wrapper = ToWrapper(object); - if (wrapper->ppp_class != ppp_class) - return false; - - if (ppp_class_data) - *ppp_class_data = wrapper->ppp_class_data; - return true; + return PluginObject::IsInstanceOf(object->np_object(), + ppp_class, ppp_class_data); } -PP_Var CreateObject(const PPP_Class* ppp_class, void* ppp_class_data) { - NPObject* object = - WebBindings::createObject(NULL, const_cast<NPClass*>(&wrapper_class)); - static_cast<WrapperObject*>(object)->ppp_class = ppp_class; - static_cast<WrapperObject*>(object)->ppp_class_data = ppp_class_data; - PP_Var ret = NPObjectToPPVar(object); - WebBindings::releaseObject(object); // Release reference from createObject - return ret; +PP_Var CreateObject(PP_Module module_id, + const PPP_Class* ppp_class, + void* ppp_class_data) { + PluginModule* module = PluginModule::FromPPModule(module_id); + if (!module) + return PP_MakeNull(); + return PluginObject::Create(module, ppp_class, ppp_class_data); } const PPB_Var var_interface = { - &AddRef, - &Release, + &Var::PluginAddRefPPVar, + &Var::PluginReleasePPVar, &VarFromUtf8, &VarToUtf8, &HasProperty, @@ -777,19 +406,16 @@ const PPB_Var var_interface = { } // namespace -const PPB_Var* GetVarInterface() { - return &var_interface; +// Var ------------------------------------------------------------------------- + +Var::Var(PluginModule* module) : Resource(module) { } -PP_Var NPObjectToPPVar(NPObject* object) { - PP_Var ret; - ret.type = PP_VARTYPE_OBJECT; - ret.value.as_id = reinterpret_cast<intptr_t>(object); - WebBindings::retainObject(object); - return ret; +Var::~Var() { } -PP_Var NPVariantToPPVar(const NPVariant* variant) { +// static +PP_Var Var::NPVariantToPPVar(PluginModule* module, const NPVariant* variant) { switch (variant->type) { case NPVariantType_Void: return PP_MakeVoid(); @@ -802,34 +428,150 @@ PP_Var NPVariantToPPVar(const NPVariant* variant) { case NPVariantType_Double: return PP_MakeDouble(NPVARIANT_TO_DOUBLE(*variant)); case NPVariantType_String: - return VarFromUtf8(NPVARIANT_TO_STRING(*variant).UTF8Characters, - NPVARIANT_TO_STRING(*variant).UTF8Length); + return StringVar::StringToPPVar( + module, + NPVARIANT_TO_STRING(*variant).UTF8Characters, + NPVARIANT_TO_STRING(*variant).UTF8Length); case NPVariantType_Object: - return NPObjectToPPVar(NPVARIANT_TO_OBJECT(*variant)); + return ObjectVar::NPObjectToPPVar(module, NPVARIANT_TO_OBJECT(*variant)); } NOTREACHED(); return PP_MakeVoid(); } +// static +NPIdentifier Var::PPVarToNPIdentifier(PP_Var var) { + switch (var.type) { + case PP_VARTYPE_STRING: { + scoped_refptr<StringVar> string(StringVar::FromPPVar(var)); + if (!string) + return NULL; + return WebBindings::getStringIdentifier(string->value().c_str()); + } + case PP_VARTYPE_INT32: + return WebBindings::getIntIdentifier(var.value.as_int); + default: + return NULL; + } +} + +// static +PP_Var Var::NPIdentifierToPPVar(PluginModule* module, NPIdentifier id) { + const NPUTF8* string_value = NULL; + int32_t int_value = 0; + bool is_string = false; + WebBindings::extractIdentifierData(id, string_value, int_value, is_string); + if (is_string) + return StringVar::StringToPPVar(module, string_value); -NPObject* GetNPObject(PP_Var var) { - if (var.type != PP_VARTYPE_OBJECT) - return NULL; - return GetNPObjectUnchecked(var); + return PP_MakeInt32(int_value); +} + +// static +void Var::PluginAddRefPPVar(PP_Var var) { + if (var.type == PP_VARTYPE_STRING || var.type == PP_VARTYPE_OBJECT) { + // TODO(brettw) consider checking that the ID is actually a var ID rather + // than some random other resource ID. + if (!ResourceTracker::Get()->AddRefResource(var.value.as_id)) + DLOG(WARNING) << "AddRefVar()ing a nonexistant string/object var."; + } +} + +// static +void Var::PluginReleasePPVar(PP_Var var) { + if (var.type == PP_VARTYPE_STRING || var.type == PP_VARTYPE_OBJECT) { + // TODO(brettw) consider checking that the ID is actually a var ID rather + // than some random other resource ID. + if (!ResourceTracker::Get()->UnrefResource(var.value.as_id)) + DLOG(WARNING) << "ReleaseVar()ing a nonexistant string/object var."; + } +} + +// static +const PPB_Var* Var::GetInterface() { + return &var_interface; +} + +// StringVar ------------------------------------------------------------------- + +StringVar::StringVar(PluginModule* module, const char* str, uint32 len) + : Var(module), + value_(str, len) { +} + +StringVar::~StringVar() { +} + +// static +PP_Var StringVar::StringToPPVar(PluginModule* module, const std::string& var) { + return StringToPPVar(module, var.c_str(), var.size()); } -PP_Var StringToPPVar(const std::string& str) { - DCHECK(IsStringUTF8(str)); - return VarFromUtf8(str.data(), str.size()); +// static +PP_Var StringVar::StringToPPVar(PluginModule* module, + const char* data, uint32 len) { + scoped_refptr<StringVar> str(new StringVar(module, data, len)); + if (!str || !IsStringUTF8(str->value())) + return PP_MakeNull(); + + PP_Var ret; + ret.type = PP_VARTYPE_STRING; + + // The caller takes ownership now. + ret.value.as_id = str->GetReference(); + return ret; } -String* GetString(PP_Var var) { +// static +scoped_refptr<StringVar> StringVar::FromPPVar(PP_Var var) { if (var.type != PP_VARTYPE_STRING) - return NULL; - return GetStringUnchecked(var); + return scoped_refptr<StringVar>(NULL); + return Resource::GetAs<StringVar>(var.value.as_id); +} + +// ObjectVar ------------------------------------------------------------- + +ObjectVar::ObjectVar(PluginModule* module, NPObject* np_object) + : Var(module), + np_object_(np_object) { + WebBindings::retainObject(np_object_); + module->AddNPObjectVar(this); +} + +ObjectVar::~ObjectVar() { + module()->RemoveNPObjectVar(this); + WebBindings::releaseObject(np_object_); +} + +// static +PP_Var ObjectVar::NPObjectToPPVar(PluginModule* module, NPObject* object) { + scoped_refptr<ObjectVar> object_var(module->ObjectVarForNPObject(object)); + if (!object_var) // No object for this module yet, make a new one. + object_var = new ObjectVar(module, object); + + if (!object_var) + return PP_MakeVoid(); + + // Convert to a PP_Var, GetReference will AddRef for us. + PP_Var result; + result.type = PP_VARTYPE_OBJECT; + result.value.as_id = object_var->GetReference(); + return result; +} + +// static +scoped_refptr<ObjectVar> ObjectVar::FromPPVar(PP_Var var) { + if (var.type != PP_VARTYPE_OBJECT) + return scoped_refptr<ObjectVar>(NULL); + return Resource::GetAs<ObjectVar>(var.value.as_id); } -TryCatch::TryCatch(PP_Var* exception) : exception_(exception) { +// TryCatch -------------------------------------------------------------------- + +TryCatch::TryCatch(PluginModule* module, PP_Var* exception) + : module_(module), + has_exception_(exception && exception->type != PP_VARTYPE_VOID), + exception_(exception) { WebBindings::pushExceptionHandler(&TryCatch::Catch, this); } @@ -837,14 +579,28 @@ TryCatch::~TryCatch() { WebBindings::popExceptionHandler(); } -bool TryCatch::HasException() const { - return exception_ && exception_->type != PP_VARTYPE_VOID; +void TryCatch::SetException(const char* message) { + if (!module_) { + // Don't have a module to make the string. + SetInvalidObjectException(); + return; + } + + if (!has_exception()) { + has_exception_ = true; + if (exception_) + *exception_ = StringVar::StringToPPVar(module_, message, strlen(message)); + } } -void TryCatch::SetException(const char* message) { - DCHECK(!HasException()); - if (exception_) - *exception_ = VarFromUtf8(message, strlen(message)); +void TryCatch::SetInvalidObjectException() { + if (!has_exception()) { + has_exception_ = true; + // TODO(brettw) bug 54504: Have a global singleton string that can hold + // a generic error message. + if (exception_) + *exception_ = PP_MakeInt32(1); + } } // static @@ -852,5 +608,4 @@ void TryCatch::Catch(void* self, const char* message) { static_cast<TryCatch*>(self)->SetException(message); } - } // namespace pepper diff --git a/webkit/glue/plugins/pepper_var.h b/webkit/glue/plugins/pepper_var.h index a678d4d..b618029 100644 --- a/webkit/glue/plugins/pepper_var.h +++ b/webkit/glue/plugins/pepper_var.h @@ -7,6 +7,8 @@ #include <string> +#include "webkit/glue/plugins/pepper_resource.h" + struct PP_Var; struct PPB_Var; typedef struct NPObject NPObject; @@ -15,36 +17,177 @@ typedef void* NPIdentifier; namespace pepper { -class String; +// Var ------------------------------------------------------------------------- + +// Represents a non-POD var. This is derived from a resource even though it +// isn't a resource from the plugin's perspective. This allows us to re-use +// the refcounting and the association with the module from the resource code. +class Var : public Resource { + public: + virtual ~Var(); + + // Resource overrides. + virtual Var* AsVar() { return this; } + + // Returns a PP_Var that corresponds to the given NPVariant. The contents of + // the NPVariant will be copied unless the NPVariant corresponds to an + // object. This will handle all Variant types including POD, strings, and + // objects. + // + // The returned PP_Var will have a refcount of 1, this passing ownership of + // the reference to the caller. This is suitable for returning to a plugin. + static PP_Var NPVariantToPPVar(PluginModule* module, + const NPVariant* variant); + + // Returns a NPIdentifier that corresponds to the given PP_Var. The contents + // of the PP_Var will be copied. Returns 0 if the given PP_Var is not a a + // string or integer type. + static NPIdentifier PPVarToNPIdentifier(PP_Var var); + + // Returns a PP_Var corresponding to the given identifier. In the case of + // a string identifier, the string will be allocated associated with the + // given module. A returned string will have a reference count of 1. + static PP_Var NPIdentifierToPPVar(PluginModule* module, NPIdentifier id); + + // Provides access to the manual refcounting of a PP_Var from the plugin's + // perspective. This is different than the AddRef/Release on this scoped + // object. This uses the ResourceTracker, which keeps a separate "plugin + // refcount" that prevents the plugin from messing up our refcounting or + // freeing something out from under us. + // + // You should not generally need to use these functions. However, if you + // call a plugin function that returns a var, it will transfer a ref to us + // (the caller) which in the case of a string or object var will need to + // be released. + // + // Example, assuming we're expecting the plugin to return a string: + // PP_Var rv = some_ppp_interface->DoSomething(a, b, c); + // + // // Get the string value. This will take a reference to the object which + // // will prevent it from being deleted out from under us when we call + // // PluginReleasePPVar(). + // scoped_refptr<StringVar> string(StringVar::FromPPVar(rv)); + // + // // Release the reference the plugin gave us when returning the value. + // // This is legal to do for all types of vars. + // Var::PluginReleasePPVar(rv); + // + // // Use the string. + // if (!string) + // return false; // It didn't return a proper string. + // UseTheString(string->value()); + static void PluginAddRefPPVar(PP_Var var); + static void PluginReleasePPVar(PP_Var var); + + // Returns the PPB_Var interface for the plugin to use. + static const PPB_Var* GetInterface(); + + protected: + // This can only be constructed as a StringVar or an ObjectVar. + explicit Var(PluginModule* module); + + private: + DISALLOW_COPY_AND_ASSIGN(Var); +}; + +// StringVar ------------------------------------------------------------------- + +// Represents a string-based Var. +// +// Returning a given string as a PP_Var: +// return StringVar::StringToPPVar(module, my_string); +// +// Converting a PP_Var to a string: +// scoped_refptr<StringVar> string(StringVar::FromPPVar(var)); +// if (!string) +// return false; // Not a string or an invalid var. +// DoSomethingWithTheString(string->value()); +class StringVar : public Var { + public: + StringVar(PluginModule* module, const char* str, uint32 len); + virtual ~StringVar(); + + const std::string& value() const { return value_; } + + // Resource overrides. + virtual StringVar* AsStringVar() { return this; } + + // Helper function to create a PP_Var of type string that contains a copy of + // the given string. The input data must be valid UTF-8 encoded text, if it + // is not valid UTF-8, a NULL var will be returned. + // + // The return value will have a reference count of 1. Internally, this will + // create a StringVar, associate it with a module, and return the reference + // to it in the var. + static PP_Var StringToPPVar(PluginModule* module, const std::string& str); + static PP_Var StringToPPVar(PluginModule* module, + const char* str, uint32 len); -// There's no class implementing Var since it could represent a number of -// objects. Instead, we just expose a getter for the interface implemented in -// the .cc file here. -const PPB_Var* GetVarInterface(); + // Helper function that converts a PP_Var to a string. This will return NULL + // if the PP_Var is not of string type or the string is invalid. + static scoped_refptr<StringVar> FromPPVar(PP_Var var); -// Returns a PP_Var of type object that wraps the given NPObject. Calling this -// function multiple times given the same NPObject results in the same PP_Var. -PP_Var NPObjectToPPVar(NPObject* object); + private: + std::string value_; + + DISALLOW_COPY_AND_ASSIGN(StringVar); +}; -// Returns a PP_Var that corresponds to the given NPVariant. The contents of -// the NPVariant will be copied unless the NPVariant corresponds to an object. -PP_Var NPVariantToPPVar(const NPVariant* variant); +// ObjectVar ------------------------------------------------------------------- + +// Represents a JavaScript object Var. By itself, this represents random +// NPObjects that a given plugin (identified by the resource's module) wants to +// reference. If two different modules reference the same NPObject (like the +// "window" object), then there will be different ObjectVar's (and hence PP_Var +// IDs) for each module. This allows us to track all references owned by a +// given module and free them when the plugin exits independently of other +// plugins that may be running at the same time. +// +// See StringVar for examples, except obviously using NPObjects instead of +// strings. +class ObjectVar : public Var { + public: + virtual ~ObjectVar(); + + // Resource overrides. + virtual ObjectVar* AsObjectVar() { return this; } + + // Returns the underlying NPObject corresponding to this ObjectVar. + // Guaranteed non-NULL. + NPObject* np_object() const { return np_object_; } + + // Helper function to create a PP_Var of type object that contains the given + // NPObject for use byt he given module. Calling this function multiple times + // given the same module + NPObject results in the same PP_Var, assuming that + // there is still a PP_Var with a reference open to it from the previous + // call. + // + // The module is necessary because we can have different modules pointing to + // the same NPObject, and we want to keep their refs separate. + // + // If no ObjectVar currently exists corresponding to the NPObject, one is + // created associated with the given module. + static PP_Var NPObjectToPPVar(PluginModule* module, NPObject* object); + + // Helper function that converts a PP_Var to an object. This will return NULL + // if the PP_Var is not of object type or the object is invalid. + static scoped_refptr<ObjectVar> FromPPVar(PP_Var var); + + protected: + // You should always use FromNPObject to create an ObjectVar. This function + // guarantees that we maintain the 1:1 mapping between NPObject and + // ObjectVar. + ObjectVar(PluginModule* module, NPObject* np_object); -// Returns the NPObject corresponding to the PP_Var. This pointer has not been -// retained, so you should not call WebBindings::releaseObject unless you first -// call WebBindings::retainObject. Returns NULL if the PP_Var is not an object -// type. -NPObject* GetNPObject(PP_Var var); + private: + // Guaranteed non-NULL, this is the underlying object used by WebKit. We + // hold a reference to this object. + NPObject* np_object_; -// Returns a PP_Var of type string that contains a copy of the given string. -// The input data must be valid UTF-8 encoded text. The return value will -// have a reference count of 1. -PP_Var StringToPPVar(const std::string& str); + DISALLOW_COPY_AND_ASSIGN(ObjectVar); +}; -// Returns the String corresponding to the PP_Var. This pointer has not been -// AddRef'd, so you should not call Release! Returns NULL if the PP_Var is not -// a string type. -String* GetString(PP_Var var); +// TryCatch -------------------------------------------------------------------- // Instantiate this object on the stack to catch V8 exceptions and pass them // to an optional out parameter supplied by the plugin. @@ -53,19 +196,48 @@ class TryCatch { // The given exception may be NULL if the consumer isn't interested in // catching exceptions. If non-NULL, the given var will be updated if any // exception is thrown (so it must outlive the TryCatch object). - TryCatch(PP_Var* exception); + // + // The module associated with the exception is passed so we know which module + // to associate any exception string with. It may be NULL if you don't know + // the module at construction time, in which case you should set it later + // by calling set_module(). + // + // If an exception is thrown when the module is NULL, setting *any* exception + // will result in using the InvalidObjectException. + TryCatch(PluginModule* module, PP_Var* exception); ~TryCatch(); + // Get and set the module. This may be NULL (see the constructor). + PluginModule* module() { return module_; } + void set_module(PluginModule* module) { module_ = module; } + // Returns true is an exception has been thrown. This can be true immediately // after construction if the var passed to the constructor is non-void. - bool HasException() const; - - // Sets the given exception. + bool has_exception() const { return has_exception_; } + + // Sets the given exception. If no module has been set yet, the message will + // be ignored (since we have no module to associate the string with) and the + // SetInvalidObjectException() will be used instead. + // + // If an exception has been previously set, this function will do nothing + // (normally you want only the first exception). void SetException(const char* message); + // Sets the exception to be a generic message contained in a magic string + // not associated with any module. + void SetInvalidObjectException(); + private: static void Catch(void* self, const char* message); + PluginModule* module_; + + // True if an exception has been thrown. Since the exception itself may be + // NULL if the plugin isn't interested in getting the exception, this will + // always indicate if SetException has been called, regardless of whether + // the exception itself has been stored. + bool has_exception_; + // May be null if the consumer isn't interesting in catching exceptions. PP_Var* exception_; }; diff --git a/webkit/glue/plugins/pepper_webplugin_impl.cc b/webkit/glue/plugins/pepper_webplugin_impl.cc index 68fdb88..95b3012 100644 --- a/webkit/glue/plugins/pepper_webplugin_impl.cc +++ b/webkit/glue/plugins/pepper_webplugin_impl.cc @@ -73,7 +73,11 @@ void WebPluginImpl::destroy() { } NPObject* WebPluginImpl::scriptableObject() { - return GetNPObject(instance_->GetInstanceObject()); + scoped_refptr<ObjectVar> object( + ObjectVar::FromPPVar(instance_->GetInstanceObject())); + if (object) + return object->np_object(); + return NULL; } void WebPluginImpl::paint(WebCanvas* canvas, const WebRect& rect) { diff --git a/webkit/glue/plugins/ppb_private.h b/webkit/glue/plugins/ppb_private.h index 94c95bf..218f73a 100644 --- a/webkit/glue/plugins/ppb_private.h +++ b/webkit/glue/plugins/ppb_private.h @@ -84,7 +84,7 @@ struct PP_PrivateFontFileDescription { struct PPB_Private { // Returns a localized string. - PP_Var (*GetLocalizedString)(PP_ResourceString string_id); + PP_Var (*GetLocalizedString)(PP_Module module, PP_ResourceString string_id); // Returns a resource image. PP_Resource (*GetResourceImage)(PP_Module module, diff --git a/webkit/glue/webkit_glue.gypi b/webkit/glue/webkit_glue.gypi index c1d5677..84ea800 100644 --- a/webkit/glue/webkit_glue.gypi +++ b/webkit/glue/webkit_glue.gypi @@ -218,6 +218,8 @@ 'plugins/pepper_plugin_instance.h', 'plugins/pepper_plugin_module.cc', 'plugins/pepper_plugin_module.h', + 'plugins/pepper_plugin_object.cc', + 'plugins/pepper_plugin_object.h', 'plugins/pepper_private.cc', 'plugins/pepper_private.h', 'plugins/pepper_resource_tracker.cc', |