// Copyright (c) 2011 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/plugins/ppapi/plugin_module.h" #include #include "base/command_line.h" #include "base/message_loop.h" #include "base/message_loop_proxy.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/time.h" #include "ppapi/c/dev/ppb_buffer_dev.h" #include "ppapi/c/dev/ppb_char_set_dev.h" #include "ppapi/c/dev/ppb_context_3d_dev.h" #include "ppapi/c/dev/ppb_context_3d_trusted_dev.h" #include "ppapi/c/dev/ppb_console_dev.h" #include "ppapi/c/dev/ppb_crypto_dev.h" #include "ppapi/c/dev/ppb_cursor_control_dev.h" #include "ppapi/c/dev/ppb_directory_reader_dev.h" #include "ppapi/c/dev/ppb_file_io_dev.h" #include "ppapi/c/dev/ppb_file_io_trusted_dev.h" #include "ppapi/c/dev/ppb_file_system_dev.h" #include "ppapi/c/dev/ppb_find_dev.h" #include "ppapi/c/dev/ppb_font_dev.h" #include "ppapi/c/dev/ppb_fullscreen_dev.h" #include "ppapi/c/dev/ppb_gles_chromium_texture_mapping_dev.h" #include "ppapi/c/dev/ppb_graphics_3d_dev.h" #include "ppapi/c/dev/ppb_opengles_dev.h" #include "ppapi/c/dev/ppb_scrollbar_dev.h" #include "ppapi/c/dev/ppb_testing_dev.h" #include "ppapi/c/dev/ppb_transport_dev.h" #include "ppapi/c/dev/ppb_url_util_dev.h" #include "ppapi/c/dev/ppb_var_deprecated.h" #include "ppapi/c/dev/ppb_video_decoder_dev.h" #include "ppapi/c/dev/ppb_widget_dev.h" #include "ppapi/c/dev/ppb_zoom_dev.h" #include "ppapi/c/pp_module.h" #include "ppapi/c/pp_resource.h" #include "ppapi/c/pp_var.h" #include "ppapi/c/ppb_core.h" #include "ppapi/c/ppb_graphics_2d.h" #include "ppapi/c/ppb_image_data.h" #include "ppapi/c/ppb_instance.h" #include "ppapi/c/ppb_messaging.h" #include "ppapi/c/ppb_url_loader.h" #include "ppapi/c/ppb_url_request_info.h" #include "ppapi/c/ppb_url_response_info.h" #include "ppapi/c/ppb_var.h" #include "ppapi/c/ppp.h" #include "ppapi/c/ppp_instance.h" #include "ppapi/c/private/ppb_flash.h" #include "ppapi/c/private/ppb_flash_clipboard.h" #include "ppapi/c/private/ppb_flash_file.h" #include "ppapi/c/private/ppb_flash_menu.h" #include "ppapi/c/private/ppb_flash_net_connector.h" #include "ppapi/c/private/ppb_instance_private.h" #include "ppapi/c/private/ppb_pdf.h" #include "ppapi/c/private/ppb_proxy_private.h" #include "ppapi/c/private/ppb_nacl_private.h" #include "ppapi/c/trusted/ppb_broker_trusted.h" #include "ppapi/c/trusted/ppb_image_data_trusted.h" #include "ppapi/c/trusted/ppb_url_loader_trusted.h" #include "webkit/plugins/ppapi/callbacks.h" #include "webkit/plugins/ppapi/common.h" #include "webkit/plugins/ppapi/ppapi_plugin_instance.h" #include "webkit/plugins/ppapi/ppb_audio_impl.h" #include "webkit/plugins/ppapi/ppb_broker_impl.h" #include "webkit/plugins/ppapi/ppb_buffer_impl.h" #include "webkit/plugins/ppapi/ppb_char_set_impl.h" #include "webkit/plugins/ppapi/ppb_console_impl.h" #include "webkit/plugins/ppapi/ppb_crypto_impl.h" #include "webkit/plugins/ppapi/ppb_cursor_control_impl.h" #include "webkit/plugins/ppapi/ppb_directory_reader_impl.h" #include "webkit/plugins/ppapi/ppb_file_chooser_impl.h" #include "webkit/plugins/ppapi/ppb_file_io_impl.h" #include "webkit/plugins/ppapi/ppb_file_ref_impl.h" #include "webkit/plugins/ppapi/ppb_file_system_impl.h" #include "webkit/plugins/ppapi/ppb_flash_clipboard_impl.h" #include "webkit/plugins/ppapi/ppb_flash_file_impl.h" #include "webkit/plugins/ppapi/ppb_flash_impl.h" #include "webkit/plugins/ppapi/ppb_flash_menu_impl.h" #include "webkit/plugins/ppapi/ppb_flash_net_connector_impl.h" #include "webkit/plugins/ppapi/ppb_font_impl.h" #include "webkit/plugins/ppapi/ppb_graphics_2d_impl.h" #include "webkit/plugins/ppapi/ppb_image_data_impl.h" #include "webkit/plugins/ppapi/ppb_nacl_private_impl.h" #include "webkit/plugins/ppapi/ppb_pdf_impl.h" #include "webkit/plugins/ppapi/ppb_proxy_impl.h" #include "webkit/plugins/ppapi/ppb_scrollbar_impl.h" #include "webkit/plugins/ppapi/ppb_transport_impl.h" #include "webkit/plugins/ppapi/ppb_url_loader_impl.h" #include "webkit/plugins/ppapi/ppb_url_request_info_impl.h" #include "webkit/plugins/ppapi/ppb_url_response_info_impl.h" #include "webkit/plugins/ppapi/ppb_url_util_impl.h" #include "webkit/plugins/ppapi/ppb_video_decoder_impl.h" #include "webkit/plugins/ppapi/ppb_widget_impl.h" #include "webkit/plugins/ppapi/resource_tracker.h" #include "webkit/plugins/ppapi/var.h" #ifdef ENABLE_GPU #include "webkit/plugins/ppapi/ppb_context_3d_impl.h" #include "webkit/plugins/ppapi/ppb_gles_chromium_texture_mapping_impl.h" #include "webkit/plugins/ppapi/ppb_graphics_3d_impl.h" #include "webkit/plugins/ppapi/ppb_opengles_impl.h" #include "webkit/plugins/ppapi/ppb_surface_3d_impl.h" #endif // ENABLE_GPU namespace webkit { namespace ppapi { namespace { // Maintains all currently loaded plugin libs for validating PP_Module // identifiers. typedef std::set PluginModuleSet; PluginModuleSet* GetLivePluginSet() { static PluginModuleSet live_plugin_libs; return &live_plugin_libs; } base::MessageLoopProxy* GetMainThreadMessageLoop() { static scoped_refptr proxy( base::MessageLoopProxy::CreateForCurrentThread()); return proxy.get(); } // PPB_Core -------------------------------------------------------------------- void AddRefResource(PP_Resource resource) { if (!ResourceTracker::Get()->AddRefResource(resource)) { DLOG(WARNING) << "AddRefResource()ing a nonexistent resource " << resource; } } void ReleaseResource(PP_Resource resource) { if (!ResourceTracker::Get()->UnrefResource(resource)) { DLOG(WARNING) << "ReleaseResource()ing a nonexistent resource " << resource; } } void* MemAlloc(uint32_t num_bytes) { return malloc(num_bytes); } void MemFree(void* ptr) { free(ptr); } double GetTime() { return base::Time::Now().ToDoubleT(); } double GetTickTime() { // TODO(brettw) http://code.google.com/p/chromium/issues/detail?id=57448 // This should be a tick timer rather than wall clock time, but needs to // match message times, which also currently use wall clock time. return GetTime(); } void CallOnMainThread(int delay_in_msec, PP_CompletionCallback callback, int32_t result) { GetMainThreadMessageLoop()->PostDelayedTask( FROM_HERE, NewRunnableFunction(callback.func, callback.user_data, result), delay_in_msec); } PP_Bool IsMainThread() { return BoolToPPBool(GetMainThreadMessageLoop()->BelongsToCurrentThread()); } const PPB_Core core_interface = { &AddRefResource, &ReleaseResource, &MemAlloc, &MemFree, &GetTime, &GetTickTime, &CallOnMainThread, &IsMainThread }; // PPB_Testing ----------------------------------------------------------------- PP_Bool ReadImageData(PP_Resource device_context_2d, PP_Resource image, const PP_Point* top_left) { scoped_refptr context( Resource::GetAs(device_context_2d)); if (!context.get()) return PP_FALSE; return BoolToPPBool(context->ReadImageData(image, top_left)); } void RunMessageLoop(PP_Instance instance) { bool old_state = MessageLoop::current()->NestableTasksAllowed(); MessageLoop::current()->SetNestableTasksAllowed(true); MessageLoop::current()->Run(); MessageLoop::current()->SetNestableTasksAllowed(old_state); } void QuitMessageLoop(PP_Instance instance) { MessageLoop::current()->QuitNow(); } uint32_t GetLiveObjectsForInstance(PP_Instance instance_id) { return ResourceTracker::Get()->GetLiveObjectsForInstance(instance_id); } const PPB_Testing_Dev testing_interface = { &ReadImageData, &RunMessageLoop, &QuitMessageLoop, &GetLiveObjectsForInstance }; // GetInterface ---------------------------------------------------------------- const void* GetInterface(const char* name) { // All interfaces should be used on the main thread. DCHECK(IsMainThread()); // Please keep alphabetized by interface macro name with "special" stuff at // the bottom. if (strcmp(name, PPB_AUDIO_CONFIG_INTERFACE) == 0) return PPB_AudioConfig_Impl::GetInterface(); if (strcmp(name, PPB_AUDIO_INTERFACE) == 0) return PPB_Audio_Impl::GetInterface(); if (strcmp(name, PPB_AUDIO_TRUSTED_INTERFACE) == 0) return PPB_Audio_Impl::GetTrustedInterface(); if (strcmp(name, PPB_BROKER_TRUSTED_INTERFACE) == 0) return PPB_Broker_Impl::GetTrustedInterface(); if (strcmp(name, PPB_BUFFER_DEV_INTERFACE) == 0) return PPB_Buffer_Impl::GetInterface(); if (strcmp(name, PPB_CHAR_SET_DEV_INTERFACE) == 0) return PPB_CharSet_Impl::GetInterface(); if (strcmp(name, PPB_CONSOLE_DEV_INTERFACE) == 0) return PPB_Console_Impl::GetInterface(); if (strcmp(name, PPB_CORE_INTERFACE) == 0) return &core_interface; if (strcmp(name, PPB_CRYPTO_DEV_INTERFACE) == 0) return PPB_Crypto_Impl::GetInterface(); if (strcmp(name, PPB_CURSOR_CONTROL_DEV_INTERFACE) == 0) return GetCursorControlInterface(); if (strcmp(name, PPB_DIRECTORYREADER_DEV_INTERFACE) == 0) return PPB_DirectoryReader_Impl::GetInterface(); if (strcmp(name, PPB_FILECHOOSER_DEV_INTERFACE) == 0) return PPB_FileChooser_Impl::GetInterface(); if (strcmp(name, PPB_FILEIO_DEV_INTERFACE) == 0) return PPB_FileIO_Impl::GetInterface(); if (strcmp(name, PPB_NACL_PRIVATE_INTERFACE) == 0) return PPB_NaCl_Private_Impl::GetInterface(); if (strcmp(name, PPB_FILEIOTRUSTED_DEV_INTERFACE) == 0) return PPB_FileIO_Impl::GetTrustedInterface(); if (strcmp(name, PPB_FILEREF_DEV_INTERFACE) == 0) return PPB_FileRef_Impl::GetInterface(); if (strcmp(name, PPB_FILESYSTEM_DEV_INTERFACE) == 0) return PPB_FileSystem_Impl::GetInterface(); if (strcmp(name, PPB_FIND_DEV_INTERFACE) == 0) return PluginInstance::GetFindInterface(); if (strcmp(name, PPB_FLASH_INTERFACE) == 0) return PPB_Flash_Impl::GetInterface(); if (strcmp(name, PPB_FLASH_CLIPBOARD_INTERFACE) == 0) return PPB_Flash_Clipboard_Impl::GetInterface(); if (strcmp(name, PPB_FLASH_FILE_FILEREF_INTERFACE) == 0) return PPB_Flash_File_FileRef_Impl::GetInterface(); if (strcmp(name, PPB_FLASH_FILE_MODULELOCAL_INTERFACE) == 0) return PPB_Flash_File_ModuleLocal_Impl::GetInterface(); if (strcmp(name, PPB_FLASH_MENU_INTERFACE) == 0) return PPB_Flash_Menu_Impl::GetInterface(); if (strcmp(name, PPB_FONT_DEV_INTERFACE) == 0) return PPB_Font_Impl::GetInterface(); if (strcmp(name, PPB_FULLSCREEN_DEV_INTERFACE) == 0) return PluginInstance::GetFullscreenInterface(); if (strcmp(name, PPB_GRAPHICS_2D_INTERFACE) == 0) return PPB_Graphics2D_Impl::GetInterface(); if (strcmp(name, PPB_IMAGEDATA_INTERFACE) == 0) return PPB_ImageData_Impl::GetInterface(); if (strcmp(name, PPB_IMAGEDATA_TRUSTED_INTERFACE) == 0) return PPB_ImageData_Impl::GetTrustedInterface(); if (strcmp(name, PPB_INSTANCE_INTERFACE) == 0) return PluginInstance::GetInterface(); if (strcmp(name, PPB_INSTANCE_PRIVATE_INTERFACE) == 0) return PluginInstance::GetPrivateInterface(); if (strcmp(name, PPB_MESSAGING_INTERFACE) == 0) return PluginInstance::GetMessagingInterface(); if (strcmp(name, PPB_PDF_INTERFACE) == 0) return PPB_PDF_Impl::GetInterface(); if (strcmp(name, PPB_PROXY_PRIVATE_INTERFACE) == 0) return PPB_Proxy_Impl::GetInterface(); if (strcmp(name, PPB_SCROLLBAR_DEV_INTERFACE) == 0) return PPB_Scrollbar_Impl::GetInterface(); if (strcmp(name, PPB_TRANSPORT_DEV_INTERFACE) == 0) return PPB_Transport_Impl::GetInterface(); if (strcmp(name, PPB_URLLOADER_INTERFACE) == 0) return PPB_URLLoader_Impl::GetInterface(); if (strcmp(name, PPB_URLLOADERTRUSTED_INTERFACE) == 0) return PPB_URLLoader_Impl::GetTrustedInterface(); if (strcmp(name, PPB_URLREQUESTINFO_INTERFACE) == 0) return PPB_URLRequestInfo_Impl::GetInterface(); if (strcmp(name, PPB_URLRESPONSEINFO_INTERFACE) == 0) return PPB_URLResponseInfo_Impl::GetInterface(); if (strcmp(name, PPB_URLUTIL_DEV_INTERFACE) == 0) return PPB_URLUtil_Impl::GetInterface(); if (strcmp(name, PPB_VAR_DEPRECATED_INTERFACE) == 0) return Var::GetDeprecatedInterface(); if (strcmp(name, PPB_VAR_INTERFACE) == 0) return Var::GetInterface(); if (strcmp(name, PPB_VIDEODECODER_DEV_INTERFACE) == 0) return PPB_VideoDecoder_Impl::GetInterface(); if (strcmp(name, PPB_WIDGET_DEV_INTERFACE) == 0) return PPB_Widget_Impl::GetInterface(); if (strcmp(name, PPB_ZOOM_DEV_INTERFACE) == 0) return PluginInstance::GetZoomInterface(); #ifdef ENABLE_GPU // This should really refer to switches::kDisable3DAPIs. if (!CommandLine::ForCurrentProcess()->HasSwitch("disable-3d-apis")) { if (strcmp(name, PPB_GRAPHICS_3D_DEV_INTERFACE) == 0) return PPB_Graphics3D_Impl::GetInterface(); if (strcmp(name, PPB_CONTEXT_3D_DEV_INTERFACE) == 0) return PPB_Context3D_Impl::GetInterface(); if (strcmp(name, PPB_CONTEXT_3D_TRUSTED_DEV_INTERFACE) == 0) return PPB_Context3D_Impl::GetTrustedInterface(); if (strcmp(name, PPB_GLES_CHROMIUM_TEXTURE_MAPPING_DEV_INTERFACE) == 0) return PPB_GLESChromiumTextureMapping_Impl::GetInterface(); if (strcmp(name, PPB_OPENGLES2_DEV_INTERFACE) == 0) return PPB_OpenGLES_Impl::GetInterface(); if (strcmp(name, PPB_SURFACE_3D_DEV_INTERFACE) == 0) return PPB_Surface3D_Impl::GetInterface(); } #endif // ENABLE_GPU #ifdef ENABLE_FLAPPER_HACKS if (strcmp(name, PPB_FLASH_NETCONNECTOR_INTERFACE) == 0) return PPB_Flash_NetConnector_Impl::GetInterface(); #endif // ENABLE_FLAPPER_HACKS // Only support the testing interface when the command line switch is // specified. This allows us to prevent people from (ab)using this interface // in production code. if (strcmp(name, PPB_TESTING_DEV_INTERFACE) == 0) { if (CommandLine::ForCurrentProcess()->HasSwitch("enable-pepper-testing")) return &testing_interface; } return NULL; } // Gets the PPAPI entry points from the given library and places them into the // given structure. Returns true on success. bool LoadEntryPointsFromLibrary(const base::NativeLibrary& library, PluginModule::EntryPoints* entry_points) { entry_points->get_interface = reinterpret_cast( base::GetFunctionPointerFromNativeLibrary(library, "PPP_GetInterface")); if (!entry_points->get_interface) { LOG(WARNING) << "No PPP_GetInterface in plugin library"; return false; } entry_points->initialize_module = reinterpret_cast( base::GetFunctionPointerFromNativeLibrary(library, "PPP_InitializeModule")); if (!entry_points->initialize_module) { LOG(WARNING) << "No PPP_InitializeModule in plugin library"; return false; } // It's okay for PPP_ShutdownModule to not be defined and shutdown_module to // be NULL. entry_points->shutdown_module = reinterpret_cast( base::GetFunctionPointerFromNativeLibrary(library, "PPP_ShutdownModule")); return true; } } // namespace PluginModule::EntryPoints::EntryPoints() : get_interface(NULL), initialize_module(NULL), shutdown_module(NULL) { } // PluginModule ---------------------------------------------------------------- PluginModule::PluginModule(const std::string& name, const FilePath& path, PluginDelegate::ModuleLifetime* lifetime_delegate) : lifetime_delegate_(lifetime_delegate), callback_tracker_(new CallbackTracker), is_crashed_(false), broker_(NULL), library_(NULL), name_(name), path_(path), reserve_instance_id_(NULL) { pp_module_ = ResourceTracker::Get()->AddModule(this); GetMainThreadMessageLoop(); // Initialize the main thread message loop. GetLivePluginSet()->insert(this); } PluginModule::~PluginModule() { // When the module is being deleted, there should be no more instances still // holding a reference to us. DCHECK(instances_.empty()); GetLivePluginSet()->erase(this); callback_tracker_->AbortAll(); if (entry_points_.shutdown_module) entry_points_.shutdown_module(); if (library_) base::UnloadNativeLibrary(library_); ResourceTracker::Get()->ModuleDeleted(pp_module_); // When the plugin crashes, we immediately tell the lifetime delegate that // we're gone, so we don't want to tell it again. if (!is_crashed_) lifetime_delegate_->PluginModuleDead(this); } bool PluginModule::InitAsInternalPlugin(const EntryPoints& entry_points) { entry_points_ = entry_points; return InitializeModule(); } bool PluginModule::InitAsLibrary(const FilePath& path) { base::NativeLibrary library = base::LoadNativeLibrary(path, NULL); if (!library) return false; if (!LoadEntryPointsFromLibrary(library, &entry_points_) || !InitializeModule()) { base::UnloadNativeLibrary(library); return false; } library_ = library; return true; } void PluginModule::InitAsProxied( PluginDelegate::OutOfProcessProxy* out_of_process_proxy) { DCHECK(!out_of_process_proxy_.get()); out_of_process_proxy_.reset(out_of_process_proxy); } // static const PPB_Core* PluginModule::GetCore() { return &core_interface; } // static PluginModule::GetInterfaceFunc PluginModule::GetLocalGetInterfaceFunc() { return &GetInterface; } PluginInstance* PluginModule::CreateInstance(PluginDelegate* delegate) { const PPP_Instance* plugin_instance_interface = reinterpret_cast(GetPluginInterface( PPP_INSTANCE_INTERFACE)); if (!plugin_instance_interface) { LOG(WARNING) << "Plugin doesn't support instance interface, failing."; return NULL; } PluginInstance* instance = new PluginInstance(delegate, this, plugin_instance_interface); if (out_of_process_proxy_.get()) out_of_process_proxy_->AddInstance(instance->pp_instance()); return instance; } PluginInstance* PluginModule::GetSomeInstance() const { // This will generally crash later if there is not actually any instance to // return, so we force a crash now to make bugs easier to track down. CHECK(!instances_.empty()); return *instances_.begin(); } const void* PluginModule::GetPluginInterface(const char* name) const { if (out_of_process_proxy_.get()) return out_of_process_proxy_->GetProxiedInterface(name); // In-process plugins. if (!entry_points_.get_interface) return NULL; return entry_points_.get_interface(name); } void PluginModule::InstanceCreated(PluginInstance* instance) { instances_.insert(instance); } void PluginModule::InstanceDeleted(PluginInstance* instance) { if (out_of_process_proxy_.get()) out_of_process_proxy_->RemoveInstance(instance->pp_instance()); instances_.erase(instance); } scoped_refptr PluginModule::GetCallbackTracker() { return callback_tracker_; } void PluginModule::PluginCrashed() { DCHECK(!is_crashed_); // Should only get one notification. is_crashed_ = true; // Notify all instances that they crashed. for (PluginInstanceSet::iterator i = instances_.begin(); i != instances_.end(); ++i) (*i)->InstanceCrashed(); lifetime_delegate_->PluginModuleDead(this); } void PluginModule::SetReserveInstanceIDCallback( PP_Bool (*reserve)(PP_Module, PP_Instance)) { DCHECK(!reserve_instance_id_) << "Only expect one set."; reserve_instance_id_ = reserve; } bool PluginModule::ReserveInstanceID(PP_Instance instance) { if (reserve_instance_id_) return PPBoolToBool(reserve_instance_id_(pp_module_, instance)); return true; // Instance ID is usable. } void PluginModule::SetBroker(PluginDelegate::PpapiBroker* broker) { DCHECK(!broker_ || !broker); broker_ = broker; } PluginDelegate::PpapiBroker* PluginModule::GetBroker(){ return broker_; } bool PluginModule::InitializeModule() { DCHECK(!out_of_process_proxy_.get()) << "Don't call for proxied modules."; int retval = entry_points_.initialize_module(pp_module(), &GetInterface); if (retval != 0) { LOG(WARNING) << "PPP_InitializeModule returned failure " << retval; return false; } return true; } } // namespace ppapi } // namespace webkit