diff options
Diffstat (limited to 'content')
147 files changed, 19627 insertions, 106 deletions
diff --git a/content/common/DEPS b/content/common/DEPS index 2e11e26..d60a74e 100644 --- a/content/common/DEPS +++ b/content/common/DEPS @@ -6,6 +6,9 @@ include_rules = [ "-webkit/child", "-webkit/renderer", + # TODO(jam): remove this + "+content/renderer/pepper/plugin_module.h", + # TODO(ananta|jamesr|scottmg) http://crbug.com/237249 "!webkit/child/websocketstreamhandle_impl.h", diff --git a/content/common/pepper_plugin_registry.cc b/content/common/pepper_plugin_registry.cc index 44bc6a0..041c50d 100644 --- a/content/common/pepper_plugin_registry.cc +++ b/content/common/pepper_plugin_registry.cc @@ -184,7 +184,7 @@ void PepperPluginRegistry::PluginModuleDead( return; } } - NOTREACHED(); // Should have always found the module above. + // Can occur in tests. } PepperPluginRegistry::~PepperPluginRegistry() { @@ -209,7 +209,7 @@ PepperPluginRegistry::PepperPluginRegistry() { continue; // Out of process plugins need no special pre-initialization. scoped_refptr<webkit::ppapi::PluginModule> module = - new webkit::ppapi::PluginModule(current.name, current.path, this, + new webkit::ppapi::PluginModule(current.name, current.path, ppapi::PpapiPermissions(current.permissions)); AddLiveModule(current.path, module.get()); if (current.is_internal) { diff --git a/content/common/pepper_plugin_registry.h b/content/common/pepper_plugin_registry.h index b40f00b..6a94725 100644 --- a/content/common/pepper_plugin_registry.h +++ b/content/common/pepper_plugin_registry.h @@ -9,7 +9,9 @@ #include <map> #include "content/public/common/pepper_plugin_info.h" -#include "webkit/plugins/ppapi/plugin_delegate.h" + +// TODO(jam): refactor +#include "content/renderer/pepper/plugin_module.h" namespace content { @@ -24,8 +26,7 @@ bool MakePepperPluginInfo(const WebPluginInfo& webplugin_info, // It keeps two lists. One list of preloaded in-process modules, and one list // is a list of all live modules (some of which may be out-of-process and hence // not preloaded). -class PepperPluginRegistry - : public webkit::ppapi::PluginDelegate::ModuleLifetime { +class PepperPluginRegistry { public: ~PepperPluginRegistry(); @@ -64,9 +65,7 @@ class PepperPluginRegistry void AddLiveModule(const base::FilePath& path, webkit::ppapi::PluginModule* module); - // ModuleLifetime implementation. - virtual void PluginModuleDead( - webkit::ppapi::PluginModule* dead_module) OVERRIDE; + void PluginModuleDead(webkit::ppapi::PluginModule* dead_module); private: PepperPluginRegistry(); diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi index 01a7048..e260a50 100644 --- a/content/content_renderer.gypi +++ b/content/content_renderer.gypi @@ -46,6 +46,7 @@ 'public/renderer/navigation_state.cc', 'public/renderer/navigation_state.h', 'public/renderer/password_form_conversion_utils.h', + 'public/renderer/ppapi_plugin_instance.h', 'public/renderer/renderer_ppapi_host.h', 'public/renderer/render_frame.h', 'public/renderer/render_process_observer.cc', @@ -258,8 +259,29 @@ 'renderer/paint_aggregator.cc', 'renderer/paint_aggregator.h', 'renderer/password_form_conversion_utils.cc', + 'renderer/pepper/audio_helper.cc', + 'renderer/pepper/audio_helper.h', + 'renderer/pepper/common.h', + 'renderer/pepper/content_decryptor_delegate.cc', + 'renderer/pepper/content_decryptor_delegate.h', 'renderer/pepper/content_renderer_pepper_host_factory.cc', 'renderer/pepper/content_renderer_pepper_host_factory.h', + 'renderer/pepper/event_conversion.cc', + 'renderer/pepper/event_conversion.h', + 'renderer/pepper/fullscreen_container.h', + 'renderer/pepper/gfx_conversion.h', + 'renderer/pepper/host_array_buffer_var.cc', + 'renderer/pepper/host_array_buffer_var.h', + 'renderer/pepper/host_globals.cc', + 'renderer/pepper/host_globals.h', + 'renderer/pepper/host_var_tracker.cc', + 'renderer/pepper/host_var_tracker.h', + 'renderer/pepper/message_channel.cc', + 'renderer/pepper/message_channel.h', + 'renderer/pepper/npapi_glue.cc', + 'renderer/pepper/npapi_glue.h', + 'renderer/pepper/npobject_var.cc', + 'renderer/pepper/npobject_var.h', 'renderer/pepper/pepper_audio_input_host.cc', 'renderer/pepper/pepper_audio_input_host.h', 'renderer/pepper/pepper_broker_impl.cc', @@ -311,10 +333,72 @@ 'renderer/pepper/pepper_video_capture_host.h', 'renderer/pepper/pepper_websocket_host.cc', 'renderer/pepper/pepper_websocket_host.h', + 'renderer/pepper/plugin_delegate.h', + 'renderer/pepper/plugin_module.cc', + 'renderer/pepper/plugin_module.h', + 'renderer/pepper/plugin_object.cc', + 'renderer/pepper/plugin_object.h', + 'renderer/pepper/ppapi_interface_factory.cc', + 'renderer/pepper/ppapi_interface_factory.h', + 'renderer/pepper/ppapi_plugin_instance_impl.cc', + 'renderer/pepper/ppapi_plugin_instance_impl.h', + 'renderer/pepper/ppapi_webplugin_impl.cc', + 'renderer/pepper/ppapi_webplugin_impl.h', + 'renderer/pepper/ppb_audio_impl.cc', + 'renderer/pepper/ppb_audio_impl.h', + 'renderer/pepper/ppb_broker_impl.cc', + 'renderer/pepper/ppb_broker_impl.h', + 'renderer/pepper/ppb_buffer_impl.cc', + 'renderer/pepper/ppb_buffer_impl.h', + 'renderer/pepper/ppb_file_ref_impl.cc', + 'renderer/pepper/ppb_file_ref_impl.h', + 'renderer/pepper/ppb_flash_message_loop_impl.cc', + 'renderer/pepper/ppb_flash_message_loop_impl.h', + 'renderer/pepper/ppb_gpu_blacklist_private_impl.cc', + 'renderer/pepper/ppb_gpu_blacklist_private_impl.h', + 'renderer/pepper/ppb_graphics_3d_impl.cc', + 'renderer/pepper/ppb_graphics_3d_impl.h', + 'renderer/pepper/ppb_image_data_impl.cc', + 'renderer/pepper/ppb_image_data_impl.h', + 'renderer/pepper/ppb_network_monitor_private_impl.cc', + 'renderer/pepper/ppb_network_monitor_private_impl.h', + 'renderer/pepper/ppb_proxy_impl.cc', + 'renderer/pepper/ppb_proxy_impl.h', + 'renderer/pepper/ppb_scrollbar_impl.cc', + 'renderer/pepper/ppb_scrollbar_impl.h', + 'renderer/pepper/ppb_tcp_server_socket_private_impl.cc', + 'renderer/pepper/ppb_tcp_server_socket_private_impl.h', + 'renderer/pepper/ppb_tcp_socket_private_impl.cc', + 'renderer/pepper/ppb_tcp_socket_private_impl.h', + 'renderer/pepper/ppb_uma_private_impl.cc', + 'renderer/pepper/ppb_uma_private_impl.h', + 'renderer/pepper/ppb_var_deprecated_impl.cc', + 'renderer/pepper/ppb_var_deprecated_impl.h', + 'renderer/pepper/ppb_video_decoder_impl.cc', + 'renderer/pepper/ppb_video_decoder_impl.h', + 'renderer/pepper/ppb_widget_impl.cc', + 'renderer/pepper/ppb_widget_impl.h', + 'renderer/pepper/ppb_x509_certificate_private_impl.cc', + 'renderer/pepper/ppb_x509_certificate_private_impl.h', + 'renderer/pepper/quota_file_io.cc', + 'renderer/pepper/quota_file_io.h', 'renderer/pepper/renderer_ppapi_host_impl.cc', 'renderer/pepper/renderer_ppapi_host_impl.h', + 'renderer/pepper/resource_creation_impl.cc', + 'renderer/pepper/resource_creation_impl.h', + 'renderer/pepper/resource_helper.cc', + 'renderer/pepper/resource_helper.h', + 'renderer/pepper/url_request_info_util.cc', + 'renderer/pepper/url_request_info_util.h', 'renderer/pepper/url_response_info_util.cc', 'renderer/pepper/url_response_info_util.h', + 'renderer/pepper/usb_key_code_conversion.h', + 'renderer/pepper/usb_key_code_conversion.cc', + 'renderer/pepper/usb_key_code_conversion_linux.cc', + 'renderer/pepper/usb_key_code_conversion_mac.cc', + 'renderer/pepper/usb_key_code_conversion_win.cc', + 'renderer/pepper/v8_var_converter.cc', + 'renderer/pepper/v8_var_converter.h', 'renderer/plugin_channel_host.cc', 'renderer/plugin_channel_host.h', 'renderer/browser_plugin/browser_plugin.cc', @@ -601,6 +685,13 @@ 'renderer/media/crypto/ppapi_decryptor.h', ], }], + ['enable_gpu!=1', { + 'sources!': [ + 'renderer/pepper/ppb_graphics_3d_impl.cc', + 'renderer/pepper/ppb_graphics_3d_impl.h', + 'renderer/pepper/ppb_open_gl_es_impl.cc', + ], + }], ], 'target_conditions': [ ['OS=="android"', { diff --git a/content/content_tests.gypi b/content/content_tests.gypi index 9e2564d..6f1c0efe 100644 --- a/content/content_tests.gypi +++ b/content/content_tests.gypi @@ -430,10 +430,21 @@ 'renderer/media/video_capture_message_filter_unittest.cc', 'renderer/media/webaudiosourceprovider_impl_unittest.cc', 'renderer/paint_aggregator_unittest.cc', - 'renderer/skia_benchmarking_extension_unittest.cc', + 'renderer/pepper/host_var_tracker_unittest.cc', + 'renderer/pepper/mock_platform_image_2d.cc', + 'renderer/pepper/mock_platform_image_2d.h', + 'renderer/pepper/mock_plugin_delegate.cc', + 'renderer/pepper/mock_plugin_delegate.h', + 'renderer/pepper/mock_resource.h', + 'renderer/pepper/ppapi_plugin_instance_unittest.cc', + 'renderer/pepper/ppapi_unittest.cc', + 'renderer/pepper/ppapi_unittest.h', + 'renderer/pepper/quota_file_io_unittest.cc', + 'renderer/pepper/v8_var_converter_unittest.cc', 'renderer/pepper/pepper_broker_impl_unittest.cc', 'renderer/render_thread_impl_unittest.cc', 'renderer/render_view_impl_unittest.cc', + 'renderer/skia_benchmarking_extension_unittest.cc', 'renderer/v8_value_converter_impl_unittest.cc', 'renderer/webplugin_impl_unittest.cc', 'test/image_decoder_test.cc', @@ -531,17 +542,6 @@ '../webkit/mocks/mock_weburlloader.cc', '../webkit/mocks/mock_weburlloader.h', '../webkit/common/user_agent/user_agent_unittest.cc', - '../webkit/plugins/ppapi/host_var_tracker_unittest.cc', - '../webkit/plugins/ppapi/mock_platform_image_2d.cc', - '../webkit/plugins/ppapi/mock_platform_image_2d.h', - '../webkit/plugins/ppapi/mock_plugin_delegate.cc', - '../webkit/plugins/ppapi/mock_plugin_delegate.h', - '../webkit/plugins/ppapi/mock_resource.h', - '../webkit/plugins/ppapi/ppapi_plugin_instance_unittest.cc', - '../webkit/plugins/ppapi/ppapi_unittest.cc', - '../webkit/plugins/ppapi/ppapi_unittest.h', - '../webkit/plugins/ppapi/quota_file_io_unittest.cc', - '../webkit/plugins/ppapi/v8_var_converter_unittest.cc', '../webkit/browser/quota/mock_quota_manager.cc', '../webkit/browser/quota/mock_quota_manager.h', '../webkit/browser/quota/mock_quota_manager_unittest.cc', @@ -848,6 +848,10 @@ 'renderer/dom_serializer_browsertest.cc', 'renderer/mouse_lock_dispatcher_browsertest.cc', 'renderer/password_form_conversion_utils_browsertest.cc', + 'renderer/pepper/mock_platform_image_2d.cc', + 'renderer/pepper/mock_platform_image_2d.h', + 'renderer/pepper/mock_plugin_delegate.cc', + 'renderer/pepper/mock_plugin_delegate.h', 'renderer/pepper/mock_renderer_ppapi_host.cc', 'renderer/pepper/pepper_device_enumeration_host_helper_unittest.cc', 'renderer/pepper/pepper_file_chooser_host_unittest.cc', @@ -868,10 +872,6 @@ 'test/content_browser_test_utils_mac.mm', 'test/content_browser_test_test.cc', 'test/content_test_launcher.cc', - '../webkit/plugins/ppapi/mock_platform_image_2d.cc', - '../webkit/plugins/ppapi/mock_platform_image_2d.h', - '../webkit/plugins/ppapi/mock_plugin_delegate.cc', - '../webkit/plugins/ppapi/mock_plugin_delegate.h', '../webkit/renderer/cpp_binding_example.cc', '../webkit/renderer/cpp_binding_example.h', ], @@ -969,12 +969,12 @@ ], }], ['enable_plugins==0', { + 'sources/': [ + ['exclude', '^renderer/pepper/'], + ], 'sources!': [ 'browser/plugin_service_impl_browsertest.cc', 'browser/plugin_data_remover_impl_browsertest.cc', - 'renderer/pepper/pepper_device_enumeration_host_helper_unittest.cc', - 'renderer/pepper/pepper_file_chooser_host_unittest.cc', - 'renderer/pepper/pepper_graphics_2d_host_unittest.cc', ], }], ['enable_pepper_cdms==1', { diff --git a/content/ppapi_plugin/ppapi_thread.h b/content/ppapi_plugin/ppapi_thread.h index 9fb56c1..7726a98 100644 --- a/content/ppapi_plugin/ppapi_thread.h +++ b/content/ppapi_plugin/ppapi_thread.h @@ -15,13 +15,13 @@ #include "base/scoped_native_library.h" #include "build/build_config.h" #include "content/child/child_thread.h" +#include "content/public/common/pepper_plugin_info.h" #include "ipc/ipc_listener.h" #include "ppapi/c/pp_module.h" #include "ppapi/c/trusted/ppp_broker.h" #include "ppapi/proxy/plugin_dispatcher.h" #include "ppapi/proxy/plugin_globals.h" #include "ppapi/proxy/plugin_proxy_delegate.h" -#include "webkit/plugins/ppapi/plugin_module.h" #if defined(OS_WIN) #include "base/win/scoped_handle.h" @@ -138,7 +138,7 @@ class PpapiThread : public ChildThread, ppapi::proxy::PluginGlobals plugin_globals_; // Storage for plugin entry points. - webkit::ppapi::PluginModule::EntryPoints plugin_entry_points_; + PepperPluginInfo::EntryPoints plugin_entry_points_; // Callback to call when a new instance connects to the broker. // Used only when is_broker_. diff --git a/content/public/common/pepper_plugin_info.cc b/content/public/common/pepper_plugin_info.cc index f8e88b2..21ef4e8 100644 --- a/content/public/common/pepper_plugin_info.cc +++ b/content/public/common/pepper_plugin_info.cc @@ -8,6 +8,12 @@ namespace content { +PepperPluginInfo::EntryPoints::EntryPoints() + : get_interface(NULL), + initialize_module(NULL), + shutdown_module(NULL) { +} + PepperPluginInfo::PepperPluginInfo() : is_internal(false), is_out_of_process(false), diff --git a/content/public/common/pepper_plugin_info.h b/content/public/common/pepper_plugin_info.h index 8106c6e..3cd54bc 100644 --- a/content/public/common/pepper_plugin_info.h +++ b/content/public/common/pepper_plugin_info.h @@ -11,11 +11,25 @@ #include "base/files/file_path.h" #include "content/common/content_export.h" #include "content/public/common/webplugininfo.h" -#include "webkit/plugins/ppapi/plugin_module.h" +#include "ppapi/c/pp_module.h" +#include "ppapi/c/ppb.h" namespace content { struct CONTENT_EXPORT PepperPluginInfo { + typedef const void* (*GetInterfaceFunc)(const char*); + typedef int (*PPP_InitializeModuleFunc)(PP_Module, PPB_GetInterface); + typedef void (*PPP_ShutdownModuleFunc)(); + + struct EntryPoints { + // This structure is POD, with the constructor initializing to NULL. + CONTENT_EXPORT EntryPoints(); + + GetInterfaceFunc get_interface; + PPP_InitializeModuleFunc initialize_module; + PPP_ShutdownModuleFunc shutdown_module; // Optional, may be NULL. + }; + PepperPluginInfo(); ~PepperPluginInfo(); @@ -42,7 +56,7 @@ struct CONTENT_EXPORT PepperPluginInfo { // When is_internal is set, this contains the function pointers to the // entry points for the internal plugins. - webkit::ppapi::PluginModule::EntryPoints internal_entry_points; + EntryPoints internal_entry_points; // Permission bits from ppapi::Permission. uint32 permissions; diff --git a/content/public/renderer/ppapi_plugin_instance.h b/content/public/renderer/ppapi_plugin_instance.h new file mode 100644 index 0000000..1e56e4c --- /dev/null +++ b/content/public/renderer/ppapi_plugin_instance.h @@ -0,0 +1,105 @@ +// Copyright (c) 2013 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 CONTENT_PUBLIC_PPAPI_PLUGIN_INSTANCE_H_ +#define CONTENT_PUBLIC_PPAPI_PLUGIN_INSTANCE_H_ + +#include "base/basictypes.h" +#include "base/process/process_handle.h" +#include "content/common/content_export.h" +#include "ppapi/c/pp_resource.h" +#include "ppapi/c/private/ppb_instance_private.h" + +class GURL; + +namespace base { +class FilePath; +} + +namespace content { +class RenderView; +} + +namespace gfx { +class ImageSkia; +class Rect; +} + +namespace ppapi { +class PpapiPermissions; +class VarTracker; +struct URLRequestInfoData; +} + +namespace IPC { +struct ChannelHandle; +} + +namespace WebKit { +class WebPluginContainer; +} + +namespace webkit { +namespace ppapi { + +class PluginInstance { + public: + static CONTENT_EXPORT PluginInstance* Get(PP_Instance instance_id); + + virtual ~PluginInstance() {} + + virtual content::RenderView* GetRenderView() = 0; + + virtual WebKit::WebPluginContainer* GetContainer() = 0; + + virtual ::ppapi::VarTracker* GetVarTracker() = 0; + + virtual const GURL& GetPluginURL() = 0; + + // Returns the location of this module. + virtual base::FilePath GetModulePath() = 0; + + // Returns a reference to a file with the given path. + // The returned object will have a refcount of 0 (just like "new"). + virtual PP_Resource CreateExternalFileReference( + const base::FilePath& external_file_path) = 0; + + // Creates a PPB_ImageData given a Skia image. + virtual PP_Resource CreateImage(gfx::ImageSkia* source_image, + float scale) = 0; + + // Switches this instance with one that uses the out of process IPC proxy. + virtual PP_ExternalPluginResult SwitchToOutOfProcessProxy( + const base::FilePath& file_path, + ::ppapi::PpapiPermissions permissions, + const IPC::ChannelHandle& channel_handle, + base::ProcessId plugin_pid, + int plugin_child_id) = 0; + + // Set this to true if plugin thinks it will always be on top. This allows us + // to use a more optimized painting path in some cases. + virtual void SetAlwaysOnTop(bool on_top) = 0; + + // Returns true iff the plugin is a full-page plugin (i.e. not in an iframe + // or embedded in a page). + virtual bool IsFullPagePlugin() = 0; + + // Switches between fullscreen and normal mode. If |delay_report| is set to + // false, it may report the new state through DidChangeView immediately. If + // true, it will delay it. When called from the plugin, delay_report should + // be true to avoid re-entrancy. + virtual void FlashSetFullscreen(bool fullscreen, bool delay_report) = 0; + + virtual bool IsRectTopmost(const gfx::Rect& rect) = 0; + + virtual int32_t Navigate(const ::ppapi::URLRequestInfoData& request, + const char* target, + bool from_user_action) = 0; + +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_PUBLIC_PPAPI_PLUGIN_INSTANCE_H_ diff --git a/content/public/renderer/renderer_ppapi_host.h b/content/public/renderer/renderer_ppapi_host.h index 5b33f7b..5f3caac 100644 --- a/content/public/renderer/renderer_ppapi_host.h +++ b/content/public/renderer/renderer_ppapi_host.h @@ -5,13 +5,13 @@ #ifndef CONTENT_PUBLIC_RENDERER_RENDERER_PPAPI_HOST_H_ #define CONTENT_PUBLIC_RENDERER_RENDERER_PPAPI_HOST_H_ +#include "base/callback_forward.h" #include "base/memory/ref_counted.h" #include "base/platform_file.h" #include "base/process.h" #include "content/common/content_export.h" #include "ipc/ipc_platform_file.h" #include "ppapi/c/pp_instance.h" -#include "webkit/plugins/ppapi/plugin_delegate.h" namespace base { class FilePath; @@ -38,7 +38,6 @@ class WebPluginContainer; namespace webkit { namespace ppapi { class PluginInstance; -class PluginModule; } } diff --git a/content/renderer/media/crypto/content_decryption_module_factory.cc b/content/renderer/media/crypto/content_decryption_module_factory.cc index 71949fc..3138be2 100644 --- a/content/renderer/media/crypto/content_decryption_module_factory.cc +++ b/content/renderer/media/crypto/content_decryption_module_factory.cc @@ -10,11 +10,11 @@ #if defined(ENABLE_PEPPER_CDMS) #include "content/renderer/media/crypto/ppapi_decryptor.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" +#include "content/renderer/pepper/ppapi_webplugin_impl.h" #include "third_party/WebKit/public/platform/WebString.h" #include "third_party/WebKit/public/web/WebFrame.h" #include "third_party/WebKit/public/web/WebMediaPlayerClient.h" -#include "webkit/plugins/ppapi/ppapi_plugin_instance_impl.h" -#include "webkit/plugins/ppapi/ppapi_webplugin_impl.h" #elif defined(OS_ANDROID) #include "content/renderer/media/android/proxy_media_keys.h" #include "content/renderer/media/android/webmediaplayer_proxy_android.h" diff --git a/content/renderer/media/crypto/ppapi_decryptor.cc b/content/renderer/media/crypto/ppapi_decryptor.cc index 33f30c9..7bf2aab8 100644 --- a/content/renderer/media/crypto/ppapi_decryptor.cc +++ b/content/renderer/media/crypto/ppapi_decryptor.cc @@ -12,13 +12,13 @@ #include "base/logging.h" #include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop_proxy.h" +#include "content/renderer/pepper/content_decryptor_delegate.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" #include "media/base/audio_decoder_config.h" #include "media/base/data_buffer.h" #include "media/base/decoder_buffer.h" #include "media/base/video_decoder_config.h" #include "media/base/video_frame.h" -#include "webkit/plugins/ppapi/content_decryptor_delegate.h" -#include "webkit/plugins/ppapi/ppapi_plugin_instance_impl.h" namespace content { diff --git a/content/renderer/media/pepper_platform_video_decoder_impl.h b/content/renderer/media/pepper_platform_video_decoder_impl.h index 686974b..d3fc9a9 100644 --- a/content/renderer/media/pepper_platform_video_decoder_impl.h +++ b/content/renderer/media/pepper_platform_video_decoder_impl.h @@ -10,8 +10,8 @@ #include "base/compiler_specific.h" #include "base/memory/ref_counted.h" #include "base/message_loop/message_loop.h" +#include "content/renderer/pepper/plugin_delegate.h" #include "media/video/video_decode_accelerator.h" -#include "webkit/plugins/ppapi/plugin_delegate.h" namespace content { diff --git a/content/renderer/media/video_destination_handler.cc b/content/renderer/media/video_destination_handler.cc index 6745acc..39e6a39 100644 --- a/content/renderer/media/video_destination_handler.cc +++ b/content/renderer/media/video_destination_handler.cc @@ -11,10 +11,10 @@ #include "base/rand_util.h" #include "content/renderer/media/media_stream_dependency_factory.h" #include "content/renderer/media/media_stream_registry_interface.h" +#include "content/renderer/pepper/ppb_image_data_impl.h" #include "content/renderer/render_thread_impl.h" #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h" #include "third_party/WebKit/public/web/WebMediaStreamRegistry.h" -#include "webkit/plugins/ppapi/ppb_image_data_impl.h" using cricket::CaptureState; using cricket::VideoFormat; @@ -85,6 +85,7 @@ bool PpFrameWriter::IsScreencast() const { void PpFrameWriter::PutFrame(PPB_ImageData_Impl* image_data, int64 time_stamp_ns) { +#if defined(ENABLE_PLUGINS) base::AutoLock auto_lock(lock_); // This assumes the handler of the SignalFrameCaptured won't call Start/Stop. // TODO(ronghuawu): Avoid the using of lock. One way is to post this call to @@ -132,6 +133,7 @@ void PpFrameWriter::PutFrame(PPB_ImageData_Impl* image_data, // This signals to libJingle that a new VideoFrame is available. // libJingle have no assumptions on what thread this signal come from. SignalFrameCaptured(this, &frame); +#endif } // PpFrameWriterProxy is a helper class to make sure the user won't use diff --git a/content/renderer/media/webmediaplayer_impl.cc b/content/renderer/media/webmediaplayer_impl.cc index bffda93..dac94c6 100644 --- a/content/renderer/media/webmediaplayer_impl.cc +++ b/content/renderer/media/webmediaplayer_impl.cc @@ -28,6 +28,7 @@ #include "content/renderer/media/webmediaplayer_params.h" #include "content/renderer/media/webmediaplayer_util.h" #include "content/renderer/media/webmediasourceclient_impl.h" +#include "content/renderer/pepper/ppapi_webplugin_impl.h" #include "gpu/GLES2/gl2extchromium.h" #include "media/audio/null_audio_sink.h" #include "media/base/bind_to_loop.h" @@ -55,7 +56,6 @@ #include "third_party/WebKit/public/web/WebRuntimeFeatures.h" #include "third_party/WebKit/public/web/WebView.h" #include "v8/include/v8.h" -#include "webkit/plugins/ppapi/ppapi_webplugin_impl.h" #include "webkit/renderer/compositor_bindings/web_layer_impl.h" using WebKit::WebCanvas; diff --git a/content/renderer/pepper/DEPS b/content/renderer/pepper/DEPS new file mode 100644 index 0000000..0b8e33d --- /dev/null +++ b/content/renderer/pepper/DEPS @@ -0,0 +1,11 @@ +include_rules = [ + "+ppapi/c", + "+ppapi/shared_impl", + "+ppapi/thunk", + "+printing", + "+media/audio", + "+media/base", + "+media/video", + "+ui/base/ime", + "+ui/base/range", +] diff --git a/content/renderer/pepper/OWNERS b/content/renderer/pepper/OWNERS index dc19a03..c5db5bd 100644 --- a/content/renderer/pepper/OWNERS +++ b/content/renderer/pepper/OWNERS @@ -1,3 +1,6 @@ dmichael@chromium.org raymes@chromium.org yzshen@chromium.org + +per-file usb_key_code_*=garykac@chromium.org +per-file usb_key_code_*=wez@chromium.org diff --git a/content/renderer/pepper/audio_helper.cc b/content/renderer/pepper/audio_helper.cc new file mode 100644 index 0000000..7eb0d87 --- /dev/null +++ b/content/renderer/pepper/audio_helper.cc @@ -0,0 +1,88 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/audio_helper.h" + +#include "content/renderer/pepper/common.h" +#include "content/renderer/pepper/resource_helper.h" +#include "ppapi/c/pp_completion_callback.h" + +using ppapi::TrackedCallback; + +namespace webkit { +namespace ppapi { + +// AudioHelper ----------------------------------------------------------------- + +AudioHelper::AudioHelper() : shared_memory_size_for_create_callback_(0) { +} + +AudioHelper::~AudioHelper() { +} + +int32_t AudioHelper::GetSyncSocketImpl(int* sync_socket) { + if (socket_for_create_callback_) { +#if defined(OS_POSIX) + *sync_socket = socket_for_create_callback_->handle(); +#elif defined(OS_WIN) + *sync_socket = reinterpret_cast<int>(socket_for_create_callback_->handle()); +#else + #error "Platform not supported." +#endif + return PP_OK; + } + return PP_ERROR_FAILED; +} + +int32_t AudioHelper::GetSharedMemoryImpl(int* shm_handle, uint32_t* shm_size) { + if (shared_memory_for_create_callback_) { +#if defined(OS_POSIX) + *shm_handle = shared_memory_for_create_callback_->handle().fd; +#elif defined(OS_WIN) + *shm_handle = reinterpret_cast<int>( + shared_memory_for_create_callback_->handle()); +#else + #error "Platform not supported." +#endif + *shm_size = shared_memory_size_for_create_callback_; + return PP_OK; + } + return PP_ERROR_FAILED; +} + +void AudioHelper::StreamCreated( + base::SharedMemoryHandle shared_memory_handle, + size_t shared_memory_size, + base::SyncSocket::Handle socket_handle) { + if (TrackedCallback::IsPending(create_callback_)) { + // Trusted side of proxy can specify a callback to recieve handles. In + // this case we don't need to map any data or start the thread since it + // will be handled by the proxy. + shared_memory_for_create_callback_.reset( + new base::SharedMemory(shared_memory_handle, false)); + shared_memory_size_for_create_callback_ = shared_memory_size; + socket_for_create_callback_.reset(new base::SyncSocket(socket_handle)); + + create_callback_->Run(PP_OK); + + // It might be nice to close the handles here to free up some system + // resources, but we can't since there's a race condition. The handles must + // be valid until they're sent over IPC, which is done from the I/O thread + // which will often get done after this code executes. We could do + // something more elaborate like an ACK from the plugin or post a task to + // the I/O thread and back, but this extra complexity doesn't seem worth it + // just to clean up these handles faster. + } else { + OnSetStreamInfo(shared_memory_handle, shared_memory_size, socket_handle); + } +} + +void AudioHelper::SetCreateCallback( + scoped_refptr< ::ppapi::TrackedCallback> create_callback) { + DCHECK(!TrackedCallback::IsPending(create_callback_)); + create_callback_ = create_callback; +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/audio_helper.h b/content/renderer/pepper/audio_helper.h new file mode 100644 index 0000000..854ac9a --- /dev/null +++ b/content/renderer/pepper/audio_helper.h @@ -0,0 +1,63 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_PEPPER_AUDIO_HELPER_H_ +#define CONTENT_RENDERER_PEPPER_AUDIO_HELPER_H_ + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/shared_memory.h" +#include "base/sync_socket.h" +#include "content/renderer/pepper/plugin_delegate.h" +#include "ppapi/c/pp_completion_callback.h" +#include "ppapi/shared_impl/resource.h" +#include "ppapi/shared_impl/scoped_pp_resource.h" +#include "ppapi/shared_impl/tracked_callback.h" + +namespace webkit { +namespace ppapi { + +class AudioHelper : public PluginDelegate::PlatformAudioOutputClient { + public: + AudioHelper(); + virtual ~AudioHelper(); + + // |PluginDelegate::PlatformAudioOutputClient| implementation. + virtual void StreamCreated(base::SharedMemoryHandle shared_memory_handle, + size_t shared_memory_size_, + base::SyncSocket::Handle socket) OVERRIDE; + + void SetCreateCallback( + scoped_refptr< ::ppapi::TrackedCallback> create_callback); + + protected: + // TODO(viettrungluu): This is all very poorly thought out. Refactor. + + // To be called by implementations of |PPB_Audio_API|/|PPB_AudioInput_API|. + int32_t GetSyncSocketImpl(int* sync_socket); + int32_t GetSharedMemoryImpl(int* shm_handle, uint32_t* shm_size); + + // To be implemented by subclasses to call their |SetStreamInfo()|. + virtual void OnSetStreamInfo(base::SharedMemoryHandle shared_memory_handle, + size_t shared_memory_size, + base::SyncSocket::Handle socket_handle) = 0; + + private: + scoped_refptr< ::ppapi::TrackedCallback> create_callback_; + + // When a create callback is being issued, these will save the info for + // querying from the callback. The proxy uses this to get the handles to the + // other process instead of mapping them in the renderer. These will be + // invalid all other times. + scoped_ptr<base::SharedMemory> shared_memory_for_create_callback_; + size_t shared_memory_size_for_create_callback_; + scoped_ptr<base::SyncSocket> socket_for_create_callback_; + + DISALLOW_COPY_AND_ASSIGN(AudioHelper); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_AUDIO_HELPER_H_ diff --git a/content/renderer/pepper/common.h b/content/renderer/pepper/common.h new file mode 100644 index 0000000..ed51b62 --- /dev/null +++ b/content/renderer/pepper/common.h @@ -0,0 +1,26 @@ +// 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 CONTENT_RENDERER_PEPPER_COMMON_H_ +#define CONTENT_RENDERER_PEPPER_COMMON_H_ + +#include "ppapi/c/pp_bool.h" +#include "ppapi/c/pp_var.h" + +namespace webkit { +namespace ppapi { + +inline PP_Bool BoolToPPBool(bool value) { + return value ? PP_TRUE : PP_FALSE; +} + +inline bool PPBoolToBool(PP_Bool value) { + return (PP_TRUE == value); +} + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_COMMON_H_ + diff --git a/content/renderer/pepper/content_decryptor_delegate.cc b/content/renderer/pepper/content_decryptor_delegate.cc new file mode 100644 index 0000000..79d9cff --- /dev/null +++ b/content/renderer/pepper/content_decryptor_delegate.cc @@ -0,0 +1,1051 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/content_decryptor_delegate.h" + +#include "base/callback_helpers.h" +#include "base/debug/trace_event.h" +#include "base/message_loop/message_loop_proxy.h" +#include "base/safe_numerics.h" +#include "content/renderer/pepper/ppb_buffer_impl.h" +#include "media/base/audio_buffer.h" +#include "media/base/audio_decoder_config.h" +#include "media/base/bind_to_loop.h" +#include "media/base/channel_layout.h" +#include "media/base/data_buffer.h" +#include "media/base/decoder_buffer.h" +#include "media/base/decrypt_config.h" +#include "media/base/video_decoder_config.h" +#include "media/base/video_frame.h" +#include "media/base/video_util.h" +#include "ppapi/shared_impl/scoped_pp_resource.h" +#include "ppapi/shared_impl/var.h" +#include "ppapi/shared_impl/var_tracker.h" +#include "ppapi/thunk/enter.h" +#include "ppapi/thunk/ppb_buffer_api.h" +#include "ui/gfx/rect.h" + +using ppapi::ArrayBufferVar; +using ppapi::PpapiGlobals; +using ppapi::ScopedPPResource; +using ppapi::StringVar; +using ppapi::thunk::EnterResourceNoLock; +using ppapi::thunk::PPB_Buffer_API; + +namespace webkit { +namespace ppapi { + +namespace { + +// Fills |resource| with a PPB_Buffer_Impl and copies |data| into the buffer +// resource. The |*resource|, if valid, will be in the ResourceTracker with a +// reference-count of 0. If |data| is NULL, sets |*resource| to NULL. Returns +// true upon success and false if any error happened. +bool MakeBufferResource(PP_Instance instance, + const uint8* data, uint32_t size, + scoped_refptr<PPB_Buffer_Impl>* resource) { + TRACE_EVENT0("eme", "ContentDecryptorDelegate - MakeBufferResource"); + DCHECK(resource); + + if (!data || !size) { + DCHECK(!data && !size); + resource = NULL; + return true; + } + + scoped_refptr<PPB_Buffer_Impl> buffer( + PPB_Buffer_Impl::CreateResource(instance, size)); + if (!buffer.get()) + return false; + + BufferAutoMapper mapper(buffer.get()); + if (!mapper.data() || mapper.size() < size) + return false; + memcpy(mapper.data(), data, size); + + *resource = buffer; + return true; +} + +// Copies the content of |str| into |array|. +// Returns true if copy succeeded. Returns false if copy failed, e.g. if the +// |array_size| is smaller than the |str| length. +template <uint32_t array_size> +bool CopyStringToArray(const std::string& str, uint8 (&array)[array_size]) { + if (array_size < str.size()) + return false; + + memcpy(array, str.data(), str.size()); + return true; +} + +// Fills the |block_info| with information from |encrypted_buffer|. +// +// Returns true if |block_info| is successfully filled. Returns false +// otherwise. +static bool MakeEncryptedBlockInfo( + const scoped_refptr<media::DecoderBuffer>& encrypted_buffer, + uint32_t request_id, + PP_EncryptedBlockInfo* block_info) { + // TODO(xhwang): Fix initialization of PP_EncryptedBlockInfo here and + // anywhere else. + memset(block_info, 0, sizeof(*block_info)); + block_info->tracking_info.request_id = request_id; + + // EOS buffers need a request ID and nothing more. + if (encrypted_buffer->end_of_stream()) + return true; + + DCHECK(encrypted_buffer->data_size()) + << "DecryptConfig is set on an empty buffer"; + + block_info->tracking_info.timestamp = + encrypted_buffer->timestamp().InMicroseconds(); + block_info->data_size = encrypted_buffer->data_size(); + + const media::DecryptConfig* decrypt_config = + encrypted_buffer->decrypt_config(); + block_info->data_offset = decrypt_config->data_offset(); + + if (!CopyStringToArray(decrypt_config->key_id(), block_info->key_id) || + !CopyStringToArray(decrypt_config->iv(), block_info->iv)) + return false; + + block_info->key_id_size = decrypt_config->key_id().size(); + block_info->iv_size = decrypt_config->iv().size(); + + if (decrypt_config->subsamples().size() > arraysize(block_info->subsamples)) + return false; + + block_info->num_subsamples = decrypt_config->subsamples().size(); + for (uint32_t i = 0; i < block_info->num_subsamples; ++i) { + block_info->subsamples[i].clear_bytes = + decrypt_config->subsamples()[i].clear_bytes; + block_info->subsamples[i].cipher_bytes = + decrypt_config->subsamples()[i].cypher_bytes; + } + + return true; +} + +PP_AudioCodec MediaAudioCodecToPpAudioCodec(media::AudioCodec codec) { + switch (codec) { + case media::kCodecVorbis: + return PP_AUDIOCODEC_VORBIS; + case media::kCodecAAC: + return PP_AUDIOCODEC_AAC; + default: + return PP_AUDIOCODEC_UNKNOWN; + } +} + +PP_VideoCodec MediaVideoCodecToPpVideoCodec(media::VideoCodec codec) { + switch (codec) { + case media::kCodecVP8: + return PP_VIDEOCODEC_VP8; + case media::kCodecH264: + return PP_VIDEOCODEC_H264; + default: + return PP_VIDEOCODEC_UNKNOWN; + } +} + +PP_VideoCodecProfile MediaVideoCodecProfileToPpVideoCodecProfile( + media::VideoCodecProfile profile) { + switch (profile) { + case media::VP8PROFILE_MAIN: + return PP_VIDEOCODECPROFILE_VP8_MAIN; + case media::H264PROFILE_BASELINE: + return PP_VIDEOCODECPROFILE_H264_BASELINE; + case media::H264PROFILE_MAIN: + return PP_VIDEOCODECPROFILE_H264_MAIN; + case media::H264PROFILE_EXTENDED: + return PP_VIDEOCODECPROFILE_H264_EXTENDED; + case media::H264PROFILE_HIGH: + return PP_VIDEOCODECPROFILE_H264_HIGH; + case media::H264PROFILE_HIGH10PROFILE: + return PP_VIDEOCODECPROFILE_H264_HIGH_10; + case media::H264PROFILE_HIGH422PROFILE: + return PP_VIDEOCODECPROFILE_H264_HIGH_422; + case media::H264PROFILE_HIGH444PREDICTIVEPROFILE: + return PP_VIDEOCODECPROFILE_H264_HIGH_444_PREDICTIVE; + default: + return PP_VIDEOCODECPROFILE_UNKNOWN; + } +} + +PP_DecryptedFrameFormat MediaVideoFormatToPpDecryptedFrameFormat( + media::VideoFrame::Format format) { + switch (format) { + case media::VideoFrame::YV12: + return PP_DECRYPTEDFRAMEFORMAT_YV12; + case media::VideoFrame::I420: + return PP_DECRYPTEDFRAMEFORMAT_I420; + default: + return PP_DECRYPTEDFRAMEFORMAT_UNKNOWN; + } +} + +media::Decryptor::Status PpDecryptResultToMediaDecryptorStatus( + PP_DecryptResult result) { + switch (result) { + case PP_DECRYPTRESULT_SUCCESS: + return media::Decryptor::kSuccess; + case PP_DECRYPTRESULT_DECRYPT_NOKEY: + return media::Decryptor::kNoKey; + case PP_DECRYPTRESULT_NEEDMOREDATA: + return media::Decryptor::kNeedMoreData; + case PP_DECRYPTRESULT_DECRYPT_ERROR: + return media::Decryptor::kError; + case PP_DECRYPTRESULT_DECODE_ERROR: + return media::Decryptor::kError; + default: + NOTREACHED(); + return media::Decryptor::kError; + } +} + +PP_DecryptorStreamType MediaDecryptorStreamTypeToPpStreamType( + media::Decryptor::StreamType stream_type) { + switch (stream_type) { + case media::Decryptor::kAudio: + return PP_DECRYPTORSTREAMTYPE_AUDIO; + case media::Decryptor::kVideo: + return PP_DECRYPTORSTREAMTYPE_VIDEO; + default: + NOTREACHED(); + return PP_DECRYPTORSTREAMTYPE_VIDEO; + } +} + +} // namespace + +ContentDecryptorDelegate::ContentDecryptorDelegate( + PP_Instance pp_instance, + const PPP_ContentDecryptor_Private* plugin_decryption_interface) + : pp_instance_(pp_instance), + plugin_decryption_interface_(plugin_decryption_interface), + next_decryption_request_id_(1), + pending_audio_decrypt_request_id_(0), + pending_video_decrypt_request_id_(0), + pending_audio_decoder_init_request_id_(0), + pending_video_decoder_init_request_id_(0), + pending_audio_decode_request_id_(0), + pending_video_decode_request_id_(0), + weak_ptr_factory_(this), + weak_this_(weak_ptr_factory_.GetWeakPtr()), + audio_sample_format_(media::kUnknownSampleFormat), + audio_samples_per_second_(0), + audio_channel_count_(0), + audio_bytes_per_frame_(0) { +} + +ContentDecryptorDelegate::~ContentDecryptorDelegate() { +} + +void ContentDecryptorDelegate::Initialize(const std::string& key_system) { + // TODO(ddorwin): Add an Initialize method to PPP_ContentDecryptor_Private. + DCHECK(!key_system.empty()); + key_system_ = key_system; +} + +void ContentDecryptorDelegate::SetKeyEventCallbacks( + const media::KeyAddedCB& key_added_cb, + const media::KeyErrorCB& key_error_cb, + const media::KeyMessageCB& key_message_cb) { + key_added_cb_ = key_added_cb; + key_error_cb_ = key_error_cb; + key_message_cb_ = key_message_cb; +} + +bool ContentDecryptorDelegate::GenerateKeyRequest(const std::string& type, + const uint8* init_data, + int init_data_length) { + PP_Var init_data_array = + PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar( + init_data_length, init_data); + + plugin_decryption_interface_->GenerateKeyRequest( + pp_instance_, + StringVar::StringToPPVar(key_system_), // TODO(ddorwin): Remove. + StringVar::StringToPPVar(type), + init_data_array); + return true; +} + +bool ContentDecryptorDelegate::AddKey(const std::string& session_id, + const uint8* key, + int key_length, + const uint8* init_data, + int init_data_length) { + PP_Var key_array = + PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar(key_length, + key); + PP_Var init_data_array = + PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar( + init_data_length, init_data); + + plugin_decryption_interface_->AddKey( + pp_instance_, + StringVar::StringToPPVar(session_id), + key_array, + init_data_array); + return true; +} + +bool ContentDecryptorDelegate::CancelKeyRequest(const std::string& session_id) { + plugin_decryption_interface_->CancelKeyRequest( + pp_instance_, + StringVar::StringToPPVar(session_id)); + return true; +} + +// TODO(xhwang): Remove duplication of code in Decrypt(), +// DecryptAndDecodeAudio() and DecryptAndDecodeVideo(). +bool ContentDecryptorDelegate::Decrypt( + media::Decryptor::StreamType stream_type, + const scoped_refptr<media::DecoderBuffer>& encrypted_buffer, + const media::Decryptor::DecryptCB& decrypt_cb) { + DVLOG(3) << "Decrypt() - stream_type: " << stream_type; + // |{audio|video}_input_resource_| is not being used by the plugin + // now because there is only one pending audio/video decrypt request at any + // time. This is enforced by the media pipeline. + scoped_refptr<PPB_Buffer_Impl> encrypted_resource; + if (!MakeMediaBufferResource( + stream_type, encrypted_buffer, &encrypted_resource) || + !encrypted_resource.get()) { + return false; + } + ScopedPPResource pp_resource(encrypted_resource.get()); + + const uint32_t request_id = next_decryption_request_id_++; + DVLOG(2) << "Decrypt() - request_id " << request_id; + + PP_EncryptedBlockInfo block_info = {}; + DCHECK(encrypted_buffer->decrypt_config()); + if (!MakeEncryptedBlockInfo(encrypted_buffer, request_id, &block_info)) { + return false; + } + + // There is only one pending decrypt request at any time per stream. This is + // enforced by the media pipeline. + switch (stream_type) { + case media::Decryptor::kAudio: + DCHECK_EQ(pending_audio_decrypt_request_id_, 0u); + DCHECK(pending_audio_decrypt_cb_.is_null()); + pending_audio_decrypt_request_id_ = request_id; + pending_audio_decrypt_cb_ = decrypt_cb; + break; + case media::Decryptor::kVideo: + DCHECK_EQ(pending_video_decrypt_request_id_, 0u); + DCHECK(pending_video_decrypt_cb_.is_null()); + pending_video_decrypt_request_id_ = request_id; + pending_video_decrypt_cb_ = decrypt_cb; + break; + default: + NOTREACHED(); + return false; + } + + SetBufferToFreeInTrackingInfo(&block_info.tracking_info); + + plugin_decryption_interface_->Decrypt(pp_instance_, + pp_resource, + &block_info); + return true; +} + +bool ContentDecryptorDelegate::CancelDecrypt( + media::Decryptor::StreamType stream_type) { + DVLOG(3) << "CancelDecrypt() - stream_type: " << stream_type; + + media::Decryptor::DecryptCB decrypt_cb; + switch (stream_type) { + case media::Decryptor::kAudio: + // Release the shared memory as it can still be in use by the plugin. + // The next Decrypt() call will need to allocate a new shared memory + // buffer. + audio_input_resource_ = NULL; + pending_audio_decrypt_request_id_ = 0; + decrypt_cb = base::ResetAndReturn(&pending_audio_decrypt_cb_); + break; + case media::Decryptor::kVideo: + // Release the shared memory as it can still be in use by the plugin. + // The next Decrypt() call will need to allocate a new shared memory + // buffer. + video_input_resource_ = NULL; + pending_video_decrypt_request_id_ = 0; + decrypt_cb = base::ResetAndReturn(&pending_video_decrypt_cb_); + break; + default: + NOTREACHED(); + return false; + } + + if (!decrypt_cb.is_null()) + decrypt_cb.Run(media::Decryptor::kSuccess, NULL); + + return true; +} + +bool ContentDecryptorDelegate::InitializeAudioDecoder( + const media::AudioDecoderConfig& decoder_config, + const media::Decryptor::DecoderInitCB& init_cb) { + PP_AudioDecoderConfig pp_decoder_config; + pp_decoder_config.codec = + MediaAudioCodecToPpAudioCodec(decoder_config.codec()); + pp_decoder_config.channel_count = + media::ChannelLayoutToChannelCount(decoder_config.channel_layout()); + pp_decoder_config.bits_per_channel = decoder_config.bits_per_channel(); + pp_decoder_config.samples_per_second = decoder_config.samples_per_second(); + pp_decoder_config.request_id = next_decryption_request_id_++; + + audio_sample_format_ = decoder_config.sample_format(); + audio_samples_per_second_ = pp_decoder_config.samples_per_second; + audio_channel_count_ = pp_decoder_config.channel_count; + audio_bytes_per_frame_ = decoder_config.bytes_per_frame(); + + scoped_refptr<PPB_Buffer_Impl> extra_data_resource; + if (!MakeBufferResource(pp_instance_, + decoder_config.extra_data(), + decoder_config.extra_data_size(), + &extra_data_resource)) { + return false; + } + ScopedPPResource pp_resource(extra_data_resource.get()); + + DCHECK_EQ(pending_audio_decoder_init_request_id_, 0u); + DCHECK(pending_audio_decoder_init_cb_.is_null()); + pending_audio_decoder_init_request_id_ = pp_decoder_config.request_id; + pending_audio_decoder_init_cb_ = init_cb; + + plugin_decryption_interface_->InitializeAudioDecoder(pp_instance_, + &pp_decoder_config, + pp_resource); + return true; +} + +bool ContentDecryptorDelegate::InitializeVideoDecoder( + const media::VideoDecoderConfig& decoder_config, + const media::Decryptor::DecoderInitCB& init_cb) { + PP_VideoDecoderConfig pp_decoder_config; + pp_decoder_config.codec = + MediaVideoCodecToPpVideoCodec(decoder_config.codec()); + pp_decoder_config.profile = + MediaVideoCodecProfileToPpVideoCodecProfile(decoder_config.profile()); + pp_decoder_config.format = + MediaVideoFormatToPpDecryptedFrameFormat(decoder_config.format()); + pp_decoder_config.width = decoder_config.coded_size().width(); + pp_decoder_config.height = decoder_config.coded_size().height(); + pp_decoder_config.request_id = next_decryption_request_id_++; + + scoped_refptr<PPB_Buffer_Impl> extra_data_resource; + if (!MakeBufferResource(pp_instance_, + decoder_config.extra_data(), + decoder_config.extra_data_size(), + &extra_data_resource)) { + return false; + } + ScopedPPResource pp_resource(extra_data_resource.get()); + + DCHECK_EQ(pending_video_decoder_init_request_id_, 0u); + DCHECK(pending_video_decoder_init_cb_.is_null()); + pending_video_decoder_init_request_id_ = pp_decoder_config.request_id; + pending_video_decoder_init_cb_ = init_cb; + + natural_size_ = decoder_config.natural_size(); + + plugin_decryption_interface_->InitializeVideoDecoder(pp_instance_, + &pp_decoder_config, + pp_resource); + return true; +} + +bool ContentDecryptorDelegate::DeinitializeDecoder( + media::Decryptor::StreamType stream_type) { + CancelDecode(stream_type); + + natural_size_ = gfx::Size(); + + // TODO(tomfinegan): Add decoder deinitialize request tracking, and get + // stream type from media stack. + plugin_decryption_interface_->DeinitializeDecoder( + pp_instance_, MediaDecryptorStreamTypeToPpStreamType(stream_type), 0); + return true; +} + +bool ContentDecryptorDelegate::ResetDecoder( + media::Decryptor::StreamType stream_type) { + CancelDecode(stream_type); + + // TODO(tomfinegan): Add decoder reset request tracking. + plugin_decryption_interface_->ResetDecoder( + pp_instance_, MediaDecryptorStreamTypeToPpStreamType(stream_type), 0); + return true; +} + +bool ContentDecryptorDelegate::DecryptAndDecodeAudio( + const scoped_refptr<media::DecoderBuffer>& encrypted_buffer, + const media::Decryptor::AudioDecodeCB& audio_decode_cb) { + // |audio_input_resource_| is not being used by the plugin now + // because there is only one pending audio decode request at any time. + // This is enforced by the media pipeline. + scoped_refptr<PPB_Buffer_Impl> encrypted_resource; + if (!MakeMediaBufferResource(media::Decryptor::kAudio, + encrypted_buffer, + &encrypted_resource)) { + return false; + } + + // The resource should not be NULL for non-EOS buffer. + if (!encrypted_buffer->end_of_stream() && !encrypted_resource.get()) + return false; + + const uint32_t request_id = next_decryption_request_id_++; + DVLOG(2) << "DecryptAndDecodeAudio() - request_id " << request_id; + + PP_EncryptedBlockInfo block_info = {}; + if (!MakeEncryptedBlockInfo(encrypted_buffer, request_id, &block_info)) { + return false; + } + + SetBufferToFreeInTrackingInfo(&block_info.tracking_info); + + // There is only one pending audio decode request at any time. This is + // enforced by the media pipeline. If this DCHECK is violated, our buffer + // reuse policy is not valid, and we may have race problems for the shared + // buffer. + DCHECK_EQ(pending_audio_decode_request_id_, 0u); + DCHECK(pending_audio_decode_cb_.is_null()); + pending_audio_decode_request_id_ = request_id; + pending_audio_decode_cb_ = audio_decode_cb; + + ScopedPPResource pp_resource(encrypted_resource.get()); + plugin_decryption_interface_->DecryptAndDecode(pp_instance_, + PP_DECRYPTORSTREAMTYPE_AUDIO, + pp_resource, + &block_info); + return true; +} + +bool ContentDecryptorDelegate::DecryptAndDecodeVideo( + const scoped_refptr<media::DecoderBuffer>& encrypted_buffer, + const media::Decryptor::VideoDecodeCB& video_decode_cb) { + // |video_input_resource_| is not being used by the plugin now + // because there is only one pending video decode request at any time. + // This is enforced by the media pipeline. + scoped_refptr<PPB_Buffer_Impl> encrypted_resource; + if (!MakeMediaBufferResource(media::Decryptor::kVideo, + encrypted_buffer, + &encrypted_resource)) { + return false; + } + + // The resource should not be 0 for non-EOS buffer. + if (!encrypted_buffer->end_of_stream() && !encrypted_resource.get()) + return false; + + const uint32_t request_id = next_decryption_request_id_++; + DVLOG(2) << "DecryptAndDecodeVideo() - request_id " << request_id; + TRACE_EVENT_ASYNC_BEGIN0( + "eme", "ContentDecryptorDelegate::DecryptAndDecodeVideo", request_id); + + PP_EncryptedBlockInfo block_info = {}; + if (!MakeEncryptedBlockInfo(encrypted_buffer, request_id, &block_info)) { + return false; + } + + SetBufferToFreeInTrackingInfo(&block_info.tracking_info); + + // Only one pending video decode request at any time. This is enforced by the + // media pipeline. If this DCHECK is violated, our buffer + // reuse policy is not valid, and we may have race problems for the shared + // buffer. + DCHECK_EQ(pending_video_decode_request_id_, 0u); + DCHECK(pending_video_decode_cb_.is_null()); + pending_video_decode_request_id_ = request_id; + pending_video_decode_cb_ = video_decode_cb; + + // TODO(tomfinegan): Need to get stream type from media stack. + ScopedPPResource pp_resource(encrypted_resource.get()); + plugin_decryption_interface_->DecryptAndDecode(pp_instance_, + PP_DECRYPTORSTREAMTYPE_VIDEO, + pp_resource, + &block_info); + return true; +} + +void ContentDecryptorDelegate::NeedKey(PP_Var key_system_var, + PP_Var session_id_var, + PP_Var init_data_var) { + // TODO(ddorwin): Remove from PPB_ContentDecryptor_Private. + NOTREACHED(); +} + +void ContentDecryptorDelegate::KeyAdded(PP_Var key_system_var, + PP_Var session_id_var) { + if (key_added_cb_.is_null()) + return; + + StringVar* session_id_string = StringVar::FromPPVar(session_id_var); + if (!session_id_string) { + key_error_cb_.Run(std::string(), media::MediaKeys::kUnknownError, 0); + return; + } + + key_added_cb_.Run(session_id_string->value()); +} + +void ContentDecryptorDelegate::KeyMessage(PP_Var key_system_var, + PP_Var session_id_var, + PP_Var message_var, + PP_Var default_url_var) { + if (key_message_cb_.is_null()) + return; + + StringVar* session_id_string = StringVar::FromPPVar(session_id_var); + + ArrayBufferVar* message_array_buffer = + ArrayBufferVar::FromPPVar(message_var); + + std::vector<uint8> message; + if (message_array_buffer) { + const uint8* data = static_cast<const uint8*>(message_array_buffer->Map()); + message.assign(data, data + message_array_buffer->ByteLength()); + } + + StringVar* default_url_string = StringVar::FromPPVar(default_url_var); + + if (!session_id_string || !default_url_string) { + key_error_cb_.Run(std::string(), media::MediaKeys::kUnknownError, 0); + return; + } + + key_message_cb_.Run(session_id_string->value(), + message, + default_url_string->value()); +} + +void ContentDecryptorDelegate::KeyError(PP_Var key_system_var, + PP_Var session_id_var, + int32_t media_error, + int32_t system_code) { + if (key_error_cb_.is_null()) + return; + + StringVar* session_id_string = StringVar::FromPPVar(session_id_var); + if (!session_id_string) { + key_error_cb_.Run(std::string(), media::MediaKeys::kUnknownError, 0); + return; + } + + key_error_cb_.Run(session_id_string->value(), + static_cast<media::MediaKeys::KeyError>(media_error), + system_code); +} + +void ContentDecryptorDelegate::DecoderInitializeDone( + PP_DecryptorStreamType decoder_type, + uint32_t request_id, + PP_Bool success) { + if (decoder_type == PP_DECRYPTORSTREAMTYPE_AUDIO) { + // If the request ID is not valid or does not match what's saved, do + // nothing. + if (request_id == 0 || + request_id != pending_audio_decoder_init_request_id_) + return; + + DCHECK(!pending_audio_decoder_init_cb_.is_null()); + pending_audio_decoder_init_request_id_ = 0; + base::ResetAndReturn( + &pending_audio_decoder_init_cb_).Run(PP_ToBool(success)); + } else { + if (request_id == 0 || + request_id != pending_video_decoder_init_request_id_) + return; + + if (!success) + natural_size_ = gfx::Size(); + + DCHECK(!pending_video_decoder_init_cb_.is_null()); + pending_video_decoder_init_request_id_ = 0; + base::ResetAndReturn( + &pending_video_decoder_init_cb_).Run(PP_ToBool(success)); + } +} + +void ContentDecryptorDelegate::DecoderDeinitializeDone( + PP_DecryptorStreamType decoder_type, + uint32_t request_id) { + // TODO(tomfinegan): Add decoder stop completion handling. +} + +void ContentDecryptorDelegate::DecoderResetDone( + PP_DecryptorStreamType decoder_type, + uint32_t request_id) { + // TODO(tomfinegan): Add decoder reset completion handling. +} + +void ContentDecryptorDelegate::DeliverBlock( + PP_Resource decrypted_block, + const PP_DecryptedBlockInfo* block_info) { + DCHECK(block_info); + + FreeBuffer(block_info->tracking_info.buffer_id); + + const uint32_t request_id = block_info->tracking_info.request_id; + DVLOG(2) << "DeliverBlock() - request_id: " << request_id; + + // If the request ID is not valid or does not match what's saved, do nothing. + if (request_id == 0) { + DVLOG(1) << "DeliverBlock() - invalid request_id " << request_id; + return; + } + + media::Decryptor::DecryptCB decrypt_cb; + if (request_id == pending_audio_decrypt_request_id_) { + DCHECK(!pending_audio_decrypt_cb_.is_null()); + pending_audio_decrypt_request_id_ = 0; + decrypt_cb = base::ResetAndReturn(&pending_audio_decrypt_cb_); + } else if (request_id == pending_video_decrypt_request_id_) { + DCHECK(!pending_video_decrypt_cb_.is_null()); + pending_video_decrypt_request_id_ = 0; + decrypt_cb = base::ResetAndReturn(&pending_video_decrypt_cb_); + } else { + DVLOG(1) << "DeliverBlock() - request_id " << request_id << " not found"; + return; + } + + media::Decryptor::Status status = + PpDecryptResultToMediaDecryptorStatus(block_info->result); + if (status != media::Decryptor::kSuccess) { + decrypt_cb.Run(status, NULL); + return; + } + + EnterResourceNoLock<PPB_Buffer_API> enter(decrypted_block, true); + if (!enter.succeeded()) { + decrypt_cb.Run(media::Decryptor::kError, NULL); + return; + } + BufferAutoMapper mapper(enter.object()); + if (!mapper.data() || !mapper.size() || + mapper.size() < block_info->data_size) { + decrypt_cb.Run(media::Decryptor::kError, NULL); + return; + } + + // TODO(tomfinegan): Find a way to take ownership of the shared memory + // managed by the PPB_Buffer_Dev, and avoid the extra copy. + scoped_refptr<media::DecoderBuffer> decrypted_buffer( + media::DecoderBuffer::CopyFrom( + static_cast<uint8*>(mapper.data()), block_info->data_size)); + decrypted_buffer->set_timestamp(base::TimeDelta::FromMicroseconds( + block_info->tracking_info.timestamp)); + decrypt_cb.Run(media::Decryptor::kSuccess, decrypted_buffer); +} + +// Use a non-class-member function here so that if for some reason +// ContentDecryptorDelegate is destroyed before VideoFrame calls this callback, +// we can still get the shared memory unmapped. +static void BufferNoLongerNeeded( + const scoped_refptr<PPB_Buffer_Impl>& ppb_buffer, + base::Closure buffer_no_longer_needed_cb) { + ppb_buffer->Unmap(); + buffer_no_longer_needed_cb.Run(); +} + +// Enters |resource|, maps shared memory and returns pointer of mapped data. +// Returns NULL if any error occurs. +static uint8* GetMappedBuffer(PP_Resource resource, + scoped_refptr<PPB_Buffer_Impl>* ppb_buffer) { + EnterResourceNoLock<PPB_Buffer_API> enter(resource, true); + if (!enter.succeeded()) + return NULL; + + uint8* mapped_data = static_cast<uint8*>(enter.object()->Map()); + if (!enter.object()->IsMapped() || !mapped_data) + return NULL; + + uint32_t mapped_size = 0; + if (!enter.object()->Describe(&mapped_size) || !mapped_size) { + enter.object()->Unmap(); + return NULL; + } + + *ppb_buffer = static_cast<PPB_Buffer_Impl*>(enter.object()); + + return mapped_data; +} + +void ContentDecryptorDelegate::DeliverFrame( + PP_Resource decrypted_frame, + const PP_DecryptedFrameInfo* frame_info) { + DCHECK(frame_info); + + const uint32_t request_id = frame_info->tracking_info.request_id; + DVLOG(2) << "DeliverFrame() - request_id: " << request_id; + + // If the request ID is not valid or does not match what's saved, do nothing. + if (request_id == 0 || request_id != pending_video_decode_request_id_) { + DVLOG(1) << "DeliverFrame() - request_id " << request_id << " not found"; + FreeBuffer(frame_info->tracking_info.buffer_id); + return; + } + + TRACE_EVENT_ASYNC_END0( + "eme", "ContentDecryptorDelegate::DecryptAndDecodeVideo", request_id); + + DCHECK(!pending_video_decode_cb_.is_null()); + pending_video_decode_request_id_ = 0; + media::Decryptor::VideoDecodeCB video_decode_cb = + base::ResetAndReturn(&pending_video_decode_cb_); + + media::Decryptor::Status status = + PpDecryptResultToMediaDecryptorStatus(frame_info->result); + if (status != media::Decryptor::kSuccess) { + DCHECK(!frame_info->tracking_info.buffer_id); + video_decode_cb.Run(status, NULL); + return; + } + + scoped_refptr<PPB_Buffer_Impl> ppb_buffer; + uint8* frame_data = GetMappedBuffer(decrypted_frame, &ppb_buffer); + if (!frame_data) { + FreeBuffer(frame_info->tracking_info.buffer_id); + video_decode_cb.Run(media::Decryptor::kError, NULL); + return; + } + + gfx::Size frame_size(frame_info->width, frame_info->height); + DCHECK_EQ(frame_info->format, PP_DECRYPTEDFRAMEFORMAT_YV12); + + scoped_refptr<media::VideoFrame> decoded_frame = + media::VideoFrame::WrapExternalYuvData( + media::VideoFrame::YV12, + frame_size, gfx::Rect(frame_size), natural_size_, + frame_info->strides[PP_DECRYPTEDFRAMEPLANES_Y], + frame_info->strides[PP_DECRYPTEDFRAMEPLANES_U], + frame_info->strides[PP_DECRYPTEDFRAMEPLANES_V], + frame_data + frame_info->plane_offsets[PP_DECRYPTEDFRAMEPLANES_Y], + frame_data + frame_info->plane_offsets[PP_DECRYPTEDFRAMEPLANES_U], + frame_data + frame_info->plane_offsets[PP_DECRYPTEDFRAMEPLANES_V], + base::TimeDelta::FromMicroseconds( + frame_info->tracking_info.timestamp), + ppb_buffer->shared_memory()->handle(), + media::BindToLoop( + base::MessageLoopProxy::current(), + base::Bind(&BufferNoLongerNeeded, ppb_buffer, + base::Bind(&ContentDecryptorDelegate::FreeBuffer, + weak_this_, + frame_info->tracking_info.buffer_id)))); + + video_decode_cb.Run(media::Decryptor::kSuccess, decoded_frame); +} + +void ContentDecryptorDelegate::DeliverSamples( + PP_Resource audio_frames, + const PP_DecryptedBlockInfo* block_info) { + DCHECK(block_info); + + FreeBuffer(block_info->tracking_info.buffer_id); + + const uint32_t request_id = block_info->tracking_info.request_id; + DVLOG(2) << "DeliverSamples() - request_id: " << request_id; + + // If the request ID is not valid or does not match what's saved, do nothing. + if (request_id == 0 || request_id != pending_audio_decode_request_id_) { + DVLOG(1) << "DeliverSamples() - request_id " << request_id << " not found"; + return; + } + + DCHECK(!pending_audio_decode_cb_.is_null()); + pending_audio_decode_request_id_ = 0; + media::Decryptor::AudioDecodeCB audio_decode_cb = + base::ResetAndReturn(&pending_audio_decode_cb_); + + const media::Decryptor::AudioBuffers empty_frames; + + media::Decryptor::Status status = + PpDecryptResultToMediaDecryptorStatus(block_info->result); + if (status != media::Decryptor::kSuccess) { + audio_decode_cb.Run(status, empty_frames); + return; + } + + media::Decryptor::AudioBuffers audio_frame_list; + if (!DeserializeAudioFrames(audio_frames, + block_info->data_size, + &audio_frame_list)) { + NOTREACHED() << "CDM did not serialize the buffer correctly."; + audio_decode_cb.Run(media::Decryptor::kError, empty_frames); + return; + } + + audio_decode_cb.Run(media::Decryptor::kSuccess, audio_frame_list); +} + +// TODO(xhwang): Try to remove duplicate logic here and in CancelDecrypt(). +void ContentDecryptorDelegate::CancelDecode( + media::Decryptor::StreamType stream_type) { + switch (stream_type) { + case media::Decryptor::kAudio: + // Release the shared memory as it can still be in use by the plugin. + // The next DecryptAndDecode() call will need to allocate a new shared + // memory buffer. + audio_input_resource_ = NULL; + pending_audio_decode_request_id_ = 0; + if (!pending_audio_decode_cb_.is_null()) + base::ResetAndReturn(&pending_audio_decode_cb_).Run( + media::Decryptor::kSuccess, media::Decryptor::AudioBuffers()); + break; + case media::Decryptor::kVideo: + // Release the shared memory as it can still be in use by the plugin. + // The next DecryptAndDecode() call will need to allocate a new shared + // memory buffer. + video_input_resource_ = NULL; + pending_video_decode_request_id_ = 0; + if (!pending_video_decode_cb_.is_null()) + base::ResetAndReturn(&pending_video_decode_cb_).Run( + media::Decryptor::kSuccess, NULL); + break; + default: + NOTREACHED(); + } +} + +bool ContentDecryptorDelegate::MakeMediaBufferResource( + media::Decryptor::StreamType stream_type, + const scoped_refptr<media::DecoderBuffer>& encrypted_buffer, + scoped_refptr<PPB_Buffer_Impl>* resource) { + TRACE_EVENT0("eme", "ContentDecryptorDelegate::MakeMediaBufferResource"); + + // End of stream buffers are represented as null resources. + if (encrypted_buffer->end_of_stream()) { + *resource = NULL; + return true; + } + + DCHECK(stream_type == media::Decryptor::kAudio || + stream_type == media::Decryptor::kVideo); + scoped_refptr<PPB_Buffer_Impl>& media_resource = + (stream_type == media::Decryptor::kAudio) ? audio_input_resource_ : + video_input_resource_; + + const size_t data_size = static_cast<size_t>(encrypted_buffer->data_size()); + if (!media_resource.get() || media_resource->size() < data_size) { + // Either the buffer hasn't been created yet, or we have one that isn't big + // enough to fit |size| bytes. + + // Media resource size starts from |kMinimumMediaBufferSize| and grows + // exponentially to avoid frequent re-allocation of PPB_Buffer_Impl, + // which is usually expensive. Since input media buffers are compressed, + // they are usually small (compared to outputs). The over-allocated memory + // should be negligible. + const uint32_t kMinimumMediaBufferSize = 1024; + uint32_t media_resource_size = + media_resource.get() ? media_resource->size() : kMinimumMediaBufferSize; + while (media_resource_size < data_size) + media_resource_size *= 2; + + DVLOG(2) << "Size of media buffer for " + << ((stream_type == media::Decryptor::kAudio) ? "audio" : "video") + << " stream bumped to " << media_resource_size + << " bytes to fit input."; + media_resource = PPB_Buffer_Impl::CreateResource(pp_instance_, + media_resource_size); + if (!media_resource.get()) + return false; + } + + BufferAutoMapper mapper(media_resource.get()); + if (!mapper.data() || mapper.size() < data_size) { + media_resource = NULL; + return false; + } + memcpy(mapper.data(), encrypted_buffer->data(), data_size); + + *resource = media_resource; + return true; +} + +void ContentDecryptorDelegate::FreeBuffer(uint32_t buffer_id) { + if (buffer_id) + free_buffers_.push(buffer_id); +} + +void ContentDecryptorDelegate::SetBufferToFreeInTrackingInfo( + PP_DecryptTrackingInfo* tracking_info) { + DCHECK_EQ(tracking_info->buffer_id, 0u); + + if (free_buffers_.empty()) + return; + + tracking_info->buffer_id = free_buffers_.front(); + free_buffers_.pop(); +} + +bool ContentDecryptorDelegate::DeserializeAudioFrames( + PP_Resource audio_frames, + size_t data_size, + media::Decryptor::AudioBuffers* frames) { + DCHECK(frames); + EnterResourceNoLock<PPB_Buffer_API> enter(audio_frames, true); + if (!enter.succeeded()) + return false; + + BufferAutoMapper mapper(enter.object()); + if (!mapper.data() || !mapper.size() || + mapper.size() < static_cast<uint32_t>(data_size)) + return false; + + // TODO(jrummell): Pass ownership of data() directly to AudioBuffer to avoid + // the copy. Since it is possible to get multiple buffers, it would need to be + // sliced and ref counted appropriately. http://crbug.com/255576. + const uint8* cur = static_cast<uint8*>(mapper.data()); + size_t bytes_left = data_size; + + do { + int64 timestamp = 0; + int64 frame_size = -1; + const size_t kHeaderSize = sizeof(timestamp) + sizeof(frame_size); + + if (bytes_left < kHeaderSize) + return false; + + memcpy(×tamp, cur, sizeof(timestamp)); + cur += sizeof(timestamp); + bytes_left -= sizeof(timestamp); + + memcpy(&frame_size, cur, sizeof(frame_size)); + cur += sizeof(frame_size); + bytes_left -= sizeof(frame_size); + + // We should *not* have empty frames in the list. + if (frame_size <= 0 || + bytes_left < base::checked_numeric_cast<size_t>(frame_size)) { + return false; + } + + const uint8* data[] = {cur}; + int frame_count = frame_size / audio_bytes_per_frame_; + scoped_refptr<media::AudioBuffer> frame = media::AudioBuffer::CopyFrom( + audio_sample_format_, + audio_channel_count_, + frame_count, + data, + base::TimeDelta::FromMicroseconds(timestamp), + base::TimeDelta::FromMicroseconds(audio_samples_per_second_ / + frame_count)); + frames->push_back(frame); + + cur += frame_size; + bytes_left -= frame_size; + } while (bytes_left > 0); + + return true; +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/content_decryptor_delegate.h b/content/renderer/pepper/content_decryptor_delegate.h new file mode 100644 index 0000000..8f7ad31 --- /dev/null +++ b/content/renderer/pepper/content_decryptor_delegate.h @@ -0,0 +1,195 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_PEPPER_CONTENT_DECRYPTOR_DELEGATE_H_ +#define CONTENT_RENDERER_PEPPER_CONTENT_DECRYPTOR_DELEGATE_H_ + +#include <queue> +#include <string> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "media/base/decryptor.h" +#include "media/base/media_keys.h" +#include "media/base/sample_format.h" +#include "ppapi/c/private/pp_content_decryptor.h" +#include "ppapi/c/private/ppp_content_decryptor_private.h" +#include "ui/gfx/size.h" + +namespace media { +class AudioDecoderConfig; +class DecoderBuffer; +class VideoDecoderConfig; +} + +namespace webkit { +namespace ppapi { + +class PPB_Buffer_Impl; + +class ContentDecryptorDelegate { + public: + // ContentDecryptorDelegate does not take ownership of + // |plugin_decryption_interface|. Therefore |plugin_decryption_interface| + // must outlive this object. + ContentDecryptorDelegate( + PP_Instance pp_instance, + const PPP_ContentDecryptor_Private* plugin_decryption_interface); + ~ContentDecryptorDelegate(); + + void Initialize(const std::string& key_system); + + void SetKeyEventCallbacks(const media::KeyAddedCB& key_added_cb, + const media::KeyErrorCB& key_error_cb, + const media::KeyMessageCB& key_message_cb); + + // Provides access to PPP_ContentDecryptor_Private. + bool GenerateKeyRequest(const std::string& type, + const uint8* init_data, + int init_data_length); + bool AddKey(const std::string& session_id, + const uint8* key, + int key_length, + const uint8* init_data, + int init_data_length); + bool CancelKeyRequest(const std::string& session_id); + bool Decrypt(media::Decryptor::StreamType stream_type, + const scoped_refptr<media::DecoderBuffer>& encrypted_buffer, + const media::Decryptor::DecryptCB& decrypt_cb); + bool CancelDecrypt(media::Decryptor::StreamType stream_type); + bool InitializeAudioDecoder( + const media::AudioDecoderConfig& decoder_config, + const media::Decryptor::DecoderInitCB& decoder_init_cb); + bool InitializeVideoDecoder( + const media::VideoDecoderConfig& decoder_config, + const media::Decryptor::DecoderInitCB& decoder_init_cb); + // TODO(tomfinegan): Add callback args for DeinitializeDecoder() and + // ResetDecoder() + bool DeinitializeDecoder(media::Decryptor::StreamType stream_type); + bool ResetDecoder(media::Decryptor::StreamType stream_type); + // Note: These methods can be used with unencrypted data. + bool DecryptAndDecodeAudio( + const scoped_refptr<media::DecoderBuffer>& encrypted_buffer, + const media::Decryptor::AudioDecodeCB& audio_decode_cb); + bool DecryptAndDecodeVideo( + const scoped_refptr<media::DecoderBuffer>& encrypted_buffer, + const media::Decryptor::VideoDecodeCB& video_decode_cb); + + // PPB_ContentDecryptor_Private dispatching methods. + // TODO(ddorwin): Remove this method. + void NeedKey(PP_Var key_system, PP_Var session_id, PP_Var init_data); + // TODO(ddorwin): Remove key_system_var parameter from these methods. + void KeyAdded(PP_Var key_system, PP_Var session_id); + void KeyMessage(PP_Var key_system, + PP_Var session_id, + PP_Var message, + PP_Var default_url); + void KeyError(PP_Var key_system, + PP_Var session_id, + int32_t media_error, + int32_t system_code); + void DeliverBlock(PP_Resource decrypted_block, + const PP_DecryptedBlockInfo* block_info); + void DecoderInitializeDone(PP_DecryptorStreamType decoder_type, + uint32_t request_id, + PP_Bool success); + void DecoderDeinitializeDone(PP_DecryptorStreamType decoder_type, + uint32_t request_id); + void DecoderResetDone(PP_DecryptorStreamType decoder_type, + uint32_t request_id); + void DeliverFrame(PP_Resource decrypted_frame, + const PP_DecryptedFrameInfo* frame_info); + void DeliverSamples(PP_Resource audio_frames, + const PP_DecryptedBlockInfo* block_info); + + private: + // Cancels the pending decrypt-and-decode callback for |stream_type|. + void CancelDecode(media::Decryptor::StreamType stream_type); + + // Fills |resource| with a PPB_Buffer_Impl and copies the data from + // |encrypted_buffer| into the buffer resource. This method reuses + // |audio_input_resource_| and |video_input_resource_| to reduce the latency + // in requesting new PPB_Buffer_Impl resources. The caller must make sure that + // |audio_input_resource_| or |video_input_resource_| is available before + // calling this method. + // + // An end of stream |encrypted_buffer| is represented as a null |resource|. + // + // Returns true upon success and false if any error happened. + bool MakeMediaBufferResource( + media::Decryptor::StreamType stream_type, + const scoped_refptr<media::DecoderBuffer>& encrypted_buffer, + scoped_refptr<PPB_Buffer_Impl>* resource); + + void FreeBuffer(uint32_t buffer_id); + + void SetBufferToFreeInTrackingInfo(PP_DecryptTrackingInfo* tracking_info); + + // Deserializes audio data stored in |audio_frames| into individual audio + // buffers in |frames|. Returns true upon success. + bool DeserializeAudioFrames(PP_Resource audio_frames, + size_t data_size, + media::Decryptor::AudioBuffers* frames); + + const PP_Instance pp_instance_; + const PPP_ContentDecryptor_Private* const plugin_decryption_interface_; + + // TODO(ddorwin): Remove after updating the Pepper API to not use key system. + std::string key_system_; + + // Callbacks for firing key events. + media::KeyAddedCB key_added_cb_; + media::KeyErrorCB key_error_cb_; + media::KeyMessageCB key_message_cb_; + + gfx::Size natural_size_; + + // Request ID for tracking pending content decryption callbacks. + // Note that zero indicates an invalid request ID. + // TODO(xhwang): Add completion callbacks for Reset/Stop and remove the use + // of request IDs. + uint32_t next_decryption_request_id_; + + uint32_t pending_audio_decrypt_request_id_; + media::Decryptor::DecryptCB pending_audio_decrypt_cb_; + + uint32_t pending_video_decrypt_request_id_; + media::Decryptor::DecryptCB pending_video_decrypt_cb_; + + uint32_t pending_audio_decoder_init_request_id_; + media::Decryptor::DecoderInitCB pending_audio_decoder_init_cb_; + + uint32_t pending_video_decoder_init_request_id_; + media::Decryptor::DecoderInitCB pending_video_decoder_init_cb_; + + uint32_t pending_audio_decode_request_id_; + media::Decryptor::AudioDecodeCB pending_audio_decode_cb_; + + uint32_t pending_video_decode_request_id_; + media::Decryptor::VideoDecodeCB pending_video_decode_cb_; + + // Cached audio and video input buffers. See MakeMediaBufferResource. + scoped_refptr<PPB_Buffer_Impl> audio_input_resource_; + scoped_refptr<PPB_Buffer_Impl> video_input_resource_; + + std::queue<uint32_t> free_buffers_; + + base::WeakPtrFactory<ContentDecryptorDelegate> weak_ptr_factory_; + base::WeakPtr<ContentDecryptorDelegate> weak_this_; + + // Keep track of audio parameters. + media::SampleFormat audio_sample_format_; + int audio_samples_per_second_; + int audio_channel_count_; + int audio_bytes_per_frame_; + + DISALLOW_COPY_AND_ASSIGN(ContentDecryptorDelegate); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_CONTENT_DECRYPTOR_DELEGATE_H_ diff --git a/content/renderer/pepper/event_conversion.cc b/content/renderer/pepper/event_conversion.cc new file mode 100644 index 0000000..f912ae5 --- /dev/null +++ b/content/renderer/pepper/event_conversion.cc @@ -0,0 +1,735 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/event_conversion.h" + +#include <map> + +#include "base/basictypes.h" +#include "base/i18n/char_iterator.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversion_utils.h" +#include "base/strings/utf_string_conversions.h" +#include "content/renderer/pepper/common.h" +#include "content/renderer/pepper/usb_key_code_conversion.h" +#include "ppapi/c/pp_input_event.h" +#include "ppapi/shared_impl/ppb_input_event_shared.h" +#include "ppapi/shared_impl/time_conversion.h" +#include "third_party/WebKit/public/platform/WebGamepads.h" +#include "third_party/WebKit/public/platform/WebString.h" +#include "third_party/WebKit/public/web/WebInputEvent.h" + +using ppapi::EventTimeToPPTimeTicks; +using ppapi::InputEventData; +using ppapi::PPTimeTicksToEventTime; +using WebKit::WebInputEvent; +using WebKit::WebKeyboardEvent; +using WebKit::WebMouseEvent; +using WebKit::WebMouseWheelEvent; +using WebKit::WebString; +using WebKit::WebTouchEvent; +using WebKit::WebTouchPoint; +using WebKit::WebUChar; + +namespace webkit { +namespace ppapi { + +namespace { + +// Verify the modifier flags WebKit uses match the Pepper ones. If these start +// not matching, we'll need to write conversion code to preserve the Pepper +// values (since plugins will be depending on them). +COMPILE_ASSERT(static_cast<int>(PP_INPUTEVENT_MODIFIER_SHIFTKEY) == + static_cast<int>(WebInputEvent::ShiftKey), + ShiftKeyMatches); +COMPILE_ASSERT(static_cast<int>(PP_INPUTEVENT_MODIFIER_CONTROLKEY) == + static_cast<int>(WebInputEvent::ControlKey), + ControlKeyMatches); +COMPILE_ASSERT(static_cast<int>(PP_INPUTEVENT_MODIFIER_ALTKEY) == + static_cast<int>(WebInputEvent::AltKey), + AltKeyMatches); +COMPILE_ASSERT(static_cast<int>(PP_INPUTEVENT_MODIFIER_METAKEY) == + static_cast<int>(WebInputEvent::MetaKey), + MetaKeyMatches); +COMPILE_ASSERT(static_cast<int>(PP_INPUTEVENT_MODIFIER_ISKEYPAD) == + static_cast<int>(WebInputEvent::IsKeyPad), + KeyPadMatches); +COMPILE_ASSERT(static_cast<int>(PP_INPUTEVENT_MODIFIER_ISAUTOREPEAT) == + static_cast<int>(WebInputEvent::IsAutoRepeat), + AutoRepeatMatches); +COMPILE_ASSERT(static_cast<int>(PP_INPUTEVENT_MODIFIER_LEFTBUTTONDOWN) == + static_cast<int>(WebInputEvent::LeftButtonDown), + LeftButtonMatches); +COMPILE_ASSERT(static_cast<int>(PP_INPUTEVENT_MODIFIER_MIDDLEBUTTONDOWN) == + static_cast<int>(WebInputEvent::MiddleButtonDown), + MiddleButtonMatches); +COMPILE_ASSERT(static_cast<int>(PP_INPUTEVENT_MODIFIER_RIGHTBUTTONDOWN) == + static_cast<int>(WebInputEvent::RightButtonDown), + RightButtonMatches); +COMPILE_ASSERT(static_cast<int>(PP_INPUTEVENT_MODIFIER_CAPSLOCKKEY) == + static_cast<int>(WebInputEvent::CapsLockOn), + CapsLockMatches); +COMPILE_ASSERT(static_cast<int>(PP_INPUTEVENT_MODIFIER_NUMLOCKKEY) == + static_cast<int>(WebInputEvent::NumLockOn), + NumLockMatches); +COMPILE_ASSERT(static_cast<int>(PP_INPUTEVENT_MODIFIER_ISLEFT) == + static_cast<int>(WebInputEvent::IsLeft), + LeftMatches); +COMPILE_ASSERT(static_cast<int>(PP_INPUTEVENT_MODIFIER_ISRIGHT) == + static_cast<int>(WebInputEvent::IsRight), + RightMatches); + +PP_InputEvent_Type ConvertEventTypes(WebInputEvent::Type wetype) { + switch (wetype) { + case WebInputEvent::MouseDown: + return PP_INPUTEVENT_TYPE_MOUSEDOWN; + case WebInputEvent::MouseUp: + return PP_INPUTEVENT_TYPE_MOUSEUP; + case WebInputEvent::MouseMove: + return PP_INPUTEVENT_TYPE_MOUSEMOVE; + case WebInputEvent::MouseEnter: + return PP_INPUTEVENT_TYPE_MOUSEENTER; + case WebInputEvent::MouseLeave: + return PP_INPUTEVENT_TYPE_MOUSELEAVE; + case WebInputEvent::ContextMenu: + return PP_INPUTEVENT_TYPE_CONTEXTMENU; + case WebInputEvent::MouseWheel: + return PP_INPUTEVENT_TYPE_WHEEL; + case WebInputEvent::RawKeyDown: + return PP_INPUTEVENT_TYPE_RAWKEYDOWN; + case WebInputEvent::KeyDown: + return PP_INPUTEVENT_TYPE_KEYDOWN; + case WebInputEvent::KeyUp: + return PP_INPUTEVENT_TYPE_KEYUP; + case WebInputEvent::Char: + return PP_INPUTEVENT_TYPE_CHAR; + case WebInputEvent::TouchStart: + return PP_INPUTEVENT_TYPE_TOUCHSTART; + case WebInputEvent::TouchMove: + return PP_INPUTEVENT_TYPE_TOUCHMOVE; + case WebInputEvent::TouchEnd: + return PP_INPUTEVENT_TYPE_TOUCHEND; + case WebInputEvent::TouchCancel: + return PP_INPUTEVENT_TYPE_TOUCHCANCEL; + case WebInputEvent::Undefined: + default: + return PP_INPUTEVENT_TYPE_UNDEFINED; + } +} + +// Generates a PP_InputEvent with the fields common to all events, as well as +// the event type from the given web event. Event-specific fields will be zero +// initialized. +InputEventData GetEventWithCommonFieldsAndType(const WebInputEvent& web_event) { + InputEventData result; + result.event_type = ConvertEventTypes(web_event.type); + result.event_time_stamp = EventTimeToPPTimeTicks(web_event.timeStampSeconds); + result.usb_key_code = 0; + return result; +} + +void AppendKeyEvent(const WebInputEvent& event, + std::vector<InputEventData>* result_events) { + const WebKeyboardEvent& key_event = + static_cast<const WebKeyboardEvent&>(event); + InputEventData result = GetEventWithCommonFieldsAndType(event); + result.event_modifiers = key_event.modifiers; + result.key_code = key_event.windowsKeyCode; + result.usb_key_code = UsbKeyCodeForKeyboardEvent(key_event); + result_events->push_back(result); +} + +void AppendCharEvent(const WebInputEvent& event, + std::vector<InputEventData>* result_events) { + const WebKeyboardEvent& key_event = + static_cast<const WebKeyboardEvent&>(event); + + // This is a bit complex, the input event will normally just have one 16-bit + // character in it, but may be zero or more than one. The text array is + // just padded with 0 values for the unused ones, but is not necessarily + // null-terminated. + // + // Here we see how many UTF-16 characters we have. + size_t utf16_char_count = 0; + while (utf16_char_count < WebKeyboardEvent::textLengthCap && + key_event.text[utf16_char_count]) + utf16_char_count++; + + // Make a separate InputEventData for each Unicode character in the input. + base::i18n::UTF16CharIterator iter(key_event.text, utf16_char_count); + while (!iter.end()) { + InputEventData result = GetEventWithCommonFieldsAndType(event); + result.event_modifiers = key_event.modifiers; + base::WriteUnicodeCharacter(iter.get(), &result.character_text); + + result_events->push_back(result); + iter.Advance(); + } +} + +void AppendMouseEvent(const WebInputEvent& event, + std::vector<InputEventData>* result_events) { + COMPILE_ASSERT(static_cast<int>(WebMouseEvent::ButtonNone) == + static_cast<int>(PP_INPUTEVENT_MOUSEBUTTON_NONE), + MouseNone); + COMPILE_ASSERT(static_cast<int>(WebMouseEvent::ButtonLeft) == + static_cast<int>(PP_INPUTEVENT_MOUSEBUTTON_LEFT), + MouseLeft); + COMPILE_ASSERT(static_cast<int>(WebMouseEvent::ButtonRight) == + static_cast<int>(PP_INPUTEVENT_MOUSEBUTTON_RIGHT), + MouseRight); + COMPILE_ASSERT(static_cast<int>(WebMouseEvent::ButtonMiddle) == + static_cast<int>(PP_INPUTEVENT_MOUSEBUTTON_MIDDLE), + MouseMiddle); + + const WebMouseEvent& mouse_event = + static_cast<const WebMouseEvent&>(event); + InputEventData result = GetEventWithCommonFieldsAndType(event); + result.event_modifiers = mouse_event.modifiers; + if (mouse_event.type == WebInputEvent::MouseDown || + mouse_event.type == WebInputEvent::MouseMove || + mouse_event.type == WebInputEvent::MouseUp) { + result.mouse_button = + static_cast<PP_InputEvent_MouseButton>(mouse_event.button); + } + result.mouse_position.x = mouse_event.x; + result.mouse_position.y = mouse_event.y; + result.mouse_click_count = mouse_event.clickCount; + result.mouse_movement.x = mouse_event.movementX; + result.mouse_movement.y = mouse_event.movementY; + result_events->push_back(result); +} + +void AppendMouseWheelEvent(const WebInputEvent& event, + std::vector<InputEventData>* result_events) { + const WebMouseWheelEvent& mouse_wheel_event = + static_cast<const WebMouseWheelEvent&>(event); + InputEventData result = GetEventWithCommonFieldsAndType(event); + result.event_modifiers = mouse_wheel_event.modifiers; + result.wheel_delta.x = mouse_wheel_event.deltaX; + result.wheel_delta.y = mouse_wheel_event.deltaY; + result.wheel_ticks.x = mouse_wheel_event.wheelTicksX; + result.wheel_ticks.y = mouse_wheel_event.wheelTicksY; + result.wheel_scroll_by_page = !!mouse_wheel_event.scrollByPage; + result_events->push_back(result); +} + +void SetPPTouchPoints(const WebTouchPoint* touches, uint32_t touches_length, + std::vector<PP_TouchPoint>* result) { + for (uint32_t i = 0; i < touches_length; i++) { + const WebTouchPoint& touch_point = touches[i]; + PP_TouchPoint pp_pt; + pp_pt.id = touch_point.id; + pp_pt.position.x = touch_point.position.x; + pp_pt.position.y = touch_point.position.y; + pp_pt.radius.x = touch_point.radiusX; + pp_pt.radius.y = touch_point.radiusY; + pp_pt.rotation_angle = touch_point.rotationAngle; + pp_pt.pressure = touch_point.force; + result->push_back(pp_pt); + } +} + +void AppendTouchEvent(const WebInputEvent& event, + std::vector<InputEventData>* result_events) { + const WebTouchEvent& touch_event = + reinterpret_cast<const WebTouchEvent&>(event); + + InputEventData result = GetEventWithCommonFieldsAndType(event); + SetPPTouchPoints(touch_event.touches, touch_event.touchesLength, + &result.touches); + SetPPTouchPoints(touch_event.changedTouches, touch_event.changedTouchesLength, + &result.changed_touches); + SetPPTouchPoints(touch_event.targetTouches, touch_event.targetTouchesLength, + &result.target_touches); + + result_events->push_back(result); +} + +// Structure used to map touch point id's to touch states. Since the pepper +// touch event structure does not have states for individual touch points and +// instead relies on the event type in combination with the set of touch lists, +// we have to set the state for the changed touches to be the same as the event +// type and all others to be 'stationary.' +typedef std::map<uint32_t, WebTouchPoint::State> TouchStateMap; + +void SetWebTouchPoints(const std::vector<PP_TouchPoint>& pp_touches, + const TouchStateMap& states_map, + WebTouchPoint* web_touches, + uint32_t* web_touches_length) { + + for (uint32_t i = 0; i < pp_touches.size() && + i < WebTouchEvent::touchesLengthCap; i++) { + WebTouchPoint pt; + const PP_TouchPoint& pp_pt = pp_touches[i]; + pt.id = pp_pt.id; + + if (states_map.find(pt.id) == states_map.end()) + pt.state = WebTouchPoint::StateStationary; + else + pt.state = states_map.find(pt.id)->second; + + pt.position.x = pp_pt.position.x; + pt.position.y = pp_pt.position.y; + // TODO bug:http://code.google.com/p/chromium/issues/detail?id=93902 + pt.screenPosition.x = 0; + pt.screenPosition.y = 0; + pt.force = pp_pt.pressure; + pt.radiusX = pp_pt.radius.x; + pt.radiusY = pp_pt.radius.y; + pt.rotationAngle = pp_pt.rotation_angle; + web_touches[i] = pt; + (*web_touches_length)++; + } +} + +WebTouchEvent* BuildTouchEvent(const InputEventData& event) { + WebTouchEvent* web_event = new WebTouchEvent(); + WebTouchPoint::State state = WebTouchPoint::StateUndefined; + switch (event.event_type) { + case PP_INPUTEVENT_TYPE_TOUCHSTART: + web_event->type = WebInputEvent::TouchStart; + state = WebTouchPoint::StatePressed; + break; + case PP_INPUTEVENT_TYPE_TOUCHMOVE: + web_event->type = WebInputEvent::TouchMove; + state = WebTouchPoint::StateMoved; + break; + case PP_INPUTEVENT_TYPE_TOUCHEND: + web_event->type = WebInputEvent::TouchEnd; + state = WebTouchPoint::StateReleased; + break; + case PP_INPUTEVENT_TYPE_TOUCHCANCEL: + web_event->type = WebInputEvent::TouchCancel; + state = WebTouchPoint::StateCancelled; + break; + default: + NOTREACHED(); + } + + TouchStateMap states_map; + for (uint32_t i = 0; i < event.changed_touches.size(); i++) + states_map[event.changed_touches[i].id] = state; + + web_event->timeStampSeconds = PPTimeTicksToEventTime(event.event_time_stamp); + + SetWebTouchPoints(event.changed_touches, states_map, + web_event->changedTouches, + &web_event->changedTouchesLength); + + SetWebTouchPoints(event.touches, states_map, web_event->touches, + &web_event->touchesLength); + + SetWebTouchPoints(event.target_touches, states_map, web_event->targetTouches, + &web_event->targetTouchesLength); + + if (web_event->type == WebInputEvent::TouchEnd || + web_event->type == WebInputEvent::TouchCancel) { + SetWebTouchPoints(event.changed_touches, states_map, + web_event->touches, &web_event->touchesLength); + SetWebTouchPoints(event.changed_touches, states_map, + web_event->targetTouches, + &web_event->targetTouchesLength); + } + + return web_event; +} + +WebKeyboardEvent* BuildKeyEvent(const InputEventData& event) { + WebKeyboardEvent* key_event = new WebKeyboardEvent(); + switch (event.event_type) { + case PP_INPUTEVENT_TYPE_RAWKEYDOWN: + key_event->type = WebInputEvent::RawKeyDown; + break; + case PP_INPUTEVENT_TYPE_KEYDOWN: + key_event->type = WebInputEvent::KeyDown; + break; + case PP_INPUTEVENT_TYPE_KEYUP: + key_event->type = WebInputEvent::KeyUp; + break; + default: + NOTREACHED(); + } + key_event->timeStampSeconds = PPTimeTicksToEventTime(event.event_time_stamp); + key_event->modifiers = event.event_modifiers; + key_event->windowsKeyCode = event.key_code; + key_event->setKeyIdentifierFromWindowsKeyCode(); + return key_event; +} + +WebKeyboardEvent* BuildCharEvent(const InputEventData& event) { + WebKeyboardEvent* key_event = new WebKeyboardEvent(); + key_event->type = WebInputEvent::Char; + key_event->timeStampSeconds = PPTimeTicksToEventTime(event.event_time_stamp); + key_event->modifiers = event.event_modifiers; + + // Make sure to not read beyond the buffer in case some bad code doesn't + // NULL-terminate it (this is called from plugins). + size_t text_length_cap = WebKeyboardEvent::textLengthCap; + base::string16 text16 = UTF8ToUTF16(event.character_text); + + memset(key_event->text, 0, text_length_cap); + memset(key_event->unmodifiedText, 0, text_length_cap); + for (size_t i = 0; + i < std::min(text_length_cap, text16.size()); + ++i) + key_event->text[i] = text16[i]; + return key_event; +} + +WebMouseEvent* BuildMouseEvent(const InputEventData& event) { + WebMouseEvent* mouse_event = new WebMouseEvent(); + switch (event.event_type) { + case PP_INPUTEVENT_TYPE_MOUSEDOWN: + mouse_event->type = WebInputEvent::MouseDown; + break; + case PP_INPUTEVENT_TYPE_MOUSEUP: + mouse_event->type = WebInputEvent::MouseUp; + break; + case PP_INPUTEVENT_TYPE_MOUSEMOVE: + mouse_event->type = WebInputEvent::MouseMove; + break; + case PP_INPUTEVENT_TYPE_MOUSEENTER: + mouse_event->type = WebInputEvent::MouseEnter; + break; + case PP_INPUTEVENT_TYPE_MOUSELEAVE: + mouse_event->type = WebInputEvent::MouseLeave; + break; + case PP_INPUTEVENT_TYPE_CONTEXTMENU: + mouse_event->type = WebInputEvent::ContextMenu; + break; + default: + NOTREACHED(); + } + mouse_event->timeStampSeconds = + PPTimeTicksToEventTime(event.event_time_stamp); + mouse_event->modifiers = event.event_modifiers; + mouse_event->button = + static_cast<WebMouseEvent::Button>(event.mouse_button); + if (mouse_event->type == WebInputEvent::MouseMove) { + if (mouse_event->modifiers & WebInputEvent::LeftButtonDown) + mouse_event->button = WebMouseEvent::ButtonLeft; + else if (mouse_event->modifiers & WebInputEvent::MiddleButtonDown) + mouse_event->button = WebMouseEvent::ButtonMiddle; + else if (mouse_event->modifiers & WebInputEvent::RightButtonDown) + mouse_event->button = WebMouseEvent::ButtonRight; + } + mouse_event->x = event.mouse_position.x; + mouse_event->y = event.mouse_position.y; + mouse_event->clickCount = event.mouse_click_count; + mouse_event->movementX = event.mouse_movement.x; + mouse_event->movementY = event.mouse_movement.y; + return mouse_event; +} + +WebMouseWheelEvent* BuildMouseWheelEvent(const InputEventData& event) { + WebMouseWheelEvent* mouse_wheel_event = new WebMouseWheelEvent(); + mouse_wheel_event->type = WebInputEvent::MouseWheel; + mouse_wheel_event->timeStampSeconds = + PPTimeTicksToEventTime(event.event_time_stamp); + mouse_wheel_event->modifiers = event.event_modifiers; + mouse_wheel_event->deltaX = event.wheel_delta.x; + mouse_wheel_event->deltaY = event.wheel_delta.y; + mouse_wheel_event->wheelTicksX = event.wheel_ticks.x; + mouse_wheel_event->wheelTicksY = event.wheel_ticks.y; + mouse_wheel_event->scrollByPage = event.wheel_scroll_by_page; + return mouse_wheel_event; +} + +#if !defined(OS_WIN) +#define VK_RETURN 0x0D + +#define VK_PRIOR 0x21 +#define VK_NEXT 0x22 +#define VK_END 0x23 +#define VK_HOME 0x24 +#define VK_LEFT 0x25 +#define VK_UP 0x26 +#define VK_RIGHT 0x27 +#define VK_DOWN 0x28 +#define VK_SNAPSHOT 0x2C +#define VK_INSERT 0x2D +#define VK_DELETE 0x2E + +#define VK_APPS 0x5D + +#define VK_F1 0x70 +#endif + +// Convert a character string to a Windows virtual key code. Adapted from +// src/third_party/WebKit/Tools/DumpRenderTree/chromium/EventSender.cpp. This +// is used by CreateSimulatedWebInputEvents to convert keyboard events. +void GetKeyCode(const std::string& char_text, + WebUChar* code, + WebUChar* text, + bool* needs_shift_modifier, + bool* generate_char) { + WebUChar vk_code = 0; + WebUChar vk_text = 0; + *needs_shift_modifier = false; + *generate_char = false; + if ("\n" == char_text) { + vk_text = vk_code = VK_RETURN; + *generate_char = true; + } else if ("rightArrow" == char_text) { + vk_code = VK_RIGHT; + } else if ("downArrow" == char_text) { + vk_code = VK_DOWN; + } else if ("leftArrow" == char_text) { + vk_code = VK_LEFT; + } else if ("upArrow" == char_text) { + vk_code = VK_UP; + } else if ("insert" == char_text) { + vk_code = VK_INSERT; + } else if ("delete" == char_text) { + vk_code = VK_DELETE; + } else if ("pageUp" == char_text) { + vk_code = VK_PRIOR; + } else if ("pageDown" == char_text) { + vk_code = VK_NEXT; + } else if ("home" == char_text) { + vk_code = VK_HOME; + } else if ("end" == char_text) { + vk_code = VK_END; + } else if ("printScreen" == char_text) { + vk_code = VK_SNAPSHOT; + } else if ("menu" == char_text) { + vk_code = VK_APPS; + } else { + // Compare the input string with the function-key names defined by the + // DOM spec (i.e. "F1",...,"F24"). + for (int i = 1; i <= 24; ++i) { + std::string functionKeyName = base::StringPrintf("F%d", i); + if (functionKeyName == char_text) { + vk_code = VK_F1 + (i - 1); + break; + } + } + if (!vk_code) { + WebString web_char_text = + WebString::fromUTF8(char_text.data(), char_text.size()); + DCHECK_EQ(web_char_text.length(), 1U); + vk_text = vk_code = web_char_text.at(0); + *needs_shift_modifier = + (vk_code & 0xFF) >= 'A' && (vk_code & 0xFF) <= 'Z'; + if ((vk_code & 0xFF) >= 'a' && (vk_code & 0xFF) <= 'z') + vk_code -= 'a' - 'A'; + *generate_char = true; + } + } + + *code = vk_code; + *text = vk_text; +} + +} // namespace + +void CreateInputEventData(const WebInputEvent& event, + std::vector<InputEventData>* result) { + result->clear(); + + switch (event.type) { + case WebInputEvent::MouseDown: + case WebInputEvent::MouseUp: + case WebInputEvent::MouseMove: + case WebInputEvent::MouseEnter: + case WebInputEvent::MouseLeave: + case WebInputEvent::ContextMenu: + AppendMouseEvent(event, result); + break; + case WebInputEvent::MouseWheel: + AppendMouseWheelEvent(event, result); + break; + case WebInputEvent::RawKeyDown: + case WebInputEvent::KeyDown: + case WebInputEvent::KeyUp: + AppendKeyEvent(event, result); + break; + case WebInputEvent::Char: + AppendCharEvent(event, result); + break; + case WebInputEvent::TouchStart: + case WebInputEvent::TouchMove: + case WebInputEvent::TouchEnd: + case WebInputEvent::TouchCancel: + AppendTouchEvent(event, result); + break; + case WebInputEvent::Undefined: + default: + break; + } +} + +WebInputEvent* CreateWebInputEvent(const InputEventData& event) { + scoped_ptr<WebInputEvent> web_input_event; + switch (event.event_type) { + case PP_INPUTEVENT_TYPE_UNDEFINED: + return NULL; + case PP_INPUTEVENT_TYPE_MOUSEDOWN: + case PP_INPUTEVENT_TYPE_MOUSEUP: + case PP_INPUTEVENT_TYPE_MOUSEMOVE: + case PP_INPUTEVENT_TYPE_MOUSEENTER: + case PP_INPUTEVENT_TYPE_MOUSELEAVE: + case PP_INPUTEVENT_TYPE_CONTEXTMENU: + web_input_event.reset(BuildMouseEvent(event)); + break; + case PP_INPUTEVENT_TYPE_WHEEL: + web_input_event.reset(BuildMouseWheelEvent(event)); + break; + case PP_INPUTEVENT_TYPE_RAWKEYDOWN: + case PP_INPUTEVENT_TYPE_KEYDOWN: + case PP_INPUTEVENT_TYPE_KEYUP: + web_input_event.reset(BuildKeyEvent(event)); + break; + case PP_INPUTEVENT_TYPE_CHAR: + web_input_event.reset(BuildCharEvent(event)); + break; + case PP_INPUTEVENT_TYPE_IME_COMPOSITION_START: + case PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE: + case PP_INPUTEVENT_TYPE_IME_COMPOSITION_END: + case PP_INPUTEVENT_TYPE_IME_TEXT: + // TODO(kinaba) implement in WebKit an event structure to handle + // composition events. + NOTREACHED(); + break; + case PP_INPUTEVENT_TYPE_TOUCHSTART: + case PP_INPUTEVENT_TYPE_TOUCHMOVE: + case PP_INPUTEVENT_TYPE_TOUCHEND: + case PP_INPUTEVENT_TYPE_TOUCHCANCEL: + web_input_event.reset(BuildTouchEvent(event)); + break; + } + + return web_input_event.release(); +} + +// Generate a coherent sequence of input events to simulate a user event. +// From src/third_party/WebKit/Tools/DumpRenderTree/chromium/EventSender.cpp. +std::vector<linked_ptr<WebInputEvent> > CreateSimulatedWebInputEvents( + const ::ppapi::InputEventData& event, + int plugin_x, + int plugin_y) { + std::vector<linked_ptr<WebInputEvent> > events; + linked_ptr<WebInputEvent> original_event(CreateWebInputEvent(event)); + + switch (event.event_type) { + case PP_INPUTEVENT_TYPE_MOUSEDOWN: + case PP_INPUTEVENT_TYPE_MOUSEUP: + case PP_INPUTEVENT_TYPE_MOUSEMOVE: + case PP_INPUTEVENT_TYPE_MOUSEENTER: + case PP_INPUTEVENT_TYPE_MOUSELEAVE: + case PP_INPUTEVENT_TYPE_TOUCHSTART: + case PP_INPUTEVENT_TYPE_TOUCHMOVE: + case PP_INPUTEVENT_TYPE_TOUCHEND: + case PP_INPUTEVENT_TYPE_TOUCHCANCEL: + events.push_back(original_event); + break; + + case PP_INPUTEVENT_TYPE_WHEEL: { + WebMouseWheelEvent* web_mouse_wheel_event = + static_cast<WebMouseWheelEvent*>(original_event.get()); + web_mouse_wheel_event->x = plugin_x; + web_mouse_wheel_event->y = plugin_y; + events.push_back(original_event); + break; + } + + case PP_INPUTEVENT_TYPE_RAWKEYDOWN: + case PP_INPUTEVENT_TYPE_KEYDOWN: + case PP_INPUTEVENT_TYPE_KEYUP: { + // Windows key down events should always be "raw" to avoid an ASSERT. +#if defined(OS_WIN) + WebKeyboardEvent* web_keyboard_event = + static_cast<WebKeyboardEvent*>(original_event.get()); + if (web_keyboard_event->type == WebInputEvent::KeyDown) + web_keyboard_event->type = WebInputEvent::RawKeyDown; +#endif + events.push_back(original_event); + break; + } + + case PP_INPUTEVENT_TYPE_CHAR: { + WebKeyboardEvent* web_char_event = + static_cast<WebKeyboardEvent*>(original_event.get()); + + WebUChar code = 0, text = 0; + bool needs_shift_modifier = false, generate_char = false; + GetKeyCode(event.character_text, + &code, + &text, + &needs_shift_modifier, + &generate_char); + + // Synthesize key down and key up events in all cases. + scoped_ptr<WebKeyboardEvent> key_down_event(new WebKeyboardEvent()); + scoped_ptr<WebKeyboardEvent> key_up_event(new WebKeyboardEvent()); + + key_down_event->type = WebInputEvent::RawKeyDown; + key_down_event->windowsKeyCode = code; + key_down_event->nativeKeyCode = code; + if (needs_shift_modifier) + key_down_event->modifiers |= WebInputEvent::ShiftKey; + + // If a char event is needed, set the text fields. + if (generate_char) { + key_down_event->text[0] = text; + key_down_event->unmodifiedText[0] = text; + } + // Convert the key code to a string identifier. + key_down_event->setKeyIdentifierFromWindowsKeyCode(); + + *key_up_event = *web_char_event = *key_down_event; + + events.push_back(linked_ptr<WebInputEvent>(key_down_event.release())); + + if (generate_char) { + web_char_event->type = WebInputEvent::Char; + web_char_event->keyIdentifier[0] = '\0'; + events.push_back(original_event); + } + + key_up_event->type = WebInputEvent::KeyUp; + events.push_back(linked_ptr<WebInputEvent>(key_up_event.release())); + break; + } + + default: + break; + } + return events; +} + +PP_InputEvent_Class ClassifyInputEvent(WebInputEvent::Type type) { + switch (type) { + case WebInputEvent::MouseDown: + case WebInputEvent::MouseUp: + case WebInputEvent::MouseMove: + case WebInputEvent::MouseEnter: + case WebInputEvent::MouseLeave: + case WebInputEvent::ContextMenu: + return PP_INPUTEVENT_CLASS_MOUSE; + case WebInputEvent::MouseWheel: + return PP_INPUTEVENT_CLASS_WHEEL; + case WebInputEvent::RawKeyDown: + case WebInputEvent::KeyDown: + case WebInputEvent::KeyUp: + case WebInputEvent::Char: + return PP_INPUTEVENT_CLASS_KEYBOARD; + case WebInputEvent::TouchCancel: + case WebInputEvent::TouchEnd: + case WebInputEvent::TouchMove: + case WebInputEvent::TouchStart: + return PP_INPUTEVENT_CLASS_TOUCH; + case WebInputEvent::Undefined: + default: + NOTREACHED(); + return PP_InputEvent_Class(0); + } +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/event_conversion.h b/content/renderer/pepper/event_conversion.h new file mode 100644 index 0000000..0fa849c --- /dev/null +++ b/content/renderer/pepper/event_conversion.h @@ -0,0 +1,54 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_PEPPER_EVENT_CONVERSION_H_ +#define CONTENT_RENDERER_PEPPER_EVENT_CONVERSION_H_ + +#include <vector> + +#include "base/memory/linked_ptr.h" +#include "ppapi/c/ppb_input_event.h" +#include "third_party/WebKit/public/web/WebInputEvent.h" + +struct PP_InputEvent; + +namespace ppapi { +struct InputEventData; +} + +namespace WebKit { +class WebGamepads; +class WebInputEvent; +} + +namespace webkit { +namespace ppapi { + +// Converts the given WebKit event to one or possibly multiple PP_InputEvents. +// The generated events will be filled into the given vector. On failure, no +// events will ge generated and the vector will be empty. +void CreateInputEventData(const WebKit::WebInputEvent& event, + std::vector< ::ppapi::InputEventData >* pp_events); + +// Creates a WebInputEvent from the given PP_InputEvent. If it fails, returns +// NULL. The caller owns the created object on success. +WebKit::WebInputEvent* CreateWebInputEvent( + const ::ppapi::InputEventData& event); + +// Creates an array of WebInputEvents to make the given event look like a user +// input event on all platforms. |plugin_x| and |plugin_y| should be the +// coordinates of a point within the plugin's area on the page. +std::vector<linked_ptr<WebKit::WebInputEvent> > CreateSimulatedWebInputEvents( + const ::ppapi::InputEventData& event, + int plugin_x, + int plugin_y); + +// Returns the PPAPI event class for the given WebKit event type. The given +// type should not be "Undefined" since there's no corresponding PPAPI class. +PP_InputEvent_Class ClassifyInputEvent(WebKit::WebInputEvent::Type type); + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_EVENT_CONVERSION_H_ diff --git a/content/renderer/pepper/fullscreen_container.h b/content/renderer/pepper/fullscreen_container.h new file mode 100644 index 0000000..805c187 --- /dev/null +++ b/content/renderer/pepper/fullscreen_container.h @@ -0,0 +1,48 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_PEPPER_PPB_FULLSCREEN_CONTAINER_IMPL_H_ +#define CONTENT_RENDERER_PEPPER_PPB_FULLSCREEN_CONTAINER_IMPL_H_ + +#include "content/renderer/pepper/plugin_delegate.h" + +namespace WebKit { +class WebLayer; +struct WebCursorInfo; +struct WebRect; +} // namespace WebKit + +namespace webkit { +namespace ppapi { + +// This class is like a lightweight WebPluginContainer for fullscreen PPAPI +// plugins, that only handles painting. +class FullscreenContainer { + public: + // Invalidates the full plugin region. + virtual void Invalidate() = 0; + + // Invalidates a partial region of the plugin. + virtual void InvalidateRect(const WebKit::WebRect&) = 0; + + // Scrolls a partial region of the plugin in the given direction. + virtual void ScrollRect(int dx, int dy, const WebKit::WebRect&) = 0; + + // Destroys the fullscreen window. This also destroys the FullscreenContainer + // instance. + virtual void Destroy() = 0; + + // Notifies the container that the mouse cursor has changed. + virtual void DidChangeCursor(const WebKit::WebCursorInfo& cursor) = 0; + + virtual void SetLayer(WebKit::WebLayer* layer) = 0; + + protected: + virtual ~FullscreenContainer() {} +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_PPB_FULLSCREEN_CONTAINER_IMPL_H_ diff --git a/content/renderer/pepper/gfx_conversion.h b/content/renderer/pepper/gfx_conversion.h new file mode 100644 index 0000000..77e7001 --- /dev/null +++ b/content/renderer/pepper/gfx_conversion.h @@ -0,0 +1,48 @@ +// 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. + +#ifndef CONTENT_RENDERER_PEPPER_GFX_CONVERSION_H_ +#define CONTENT_RENDERER_PEPPER_GFX_CONVERSION_H_ + +#include "ppapi/c/pp_point.h" +#include "ppapi/c/pp_rect.h" +#include "ppapi/c/pp_size.h" +#include "ui/gfx/point.h" +#include "ui/gfx/rect.h" +#include "ui/gfx/size.h" + +// Conversions for graphics types between our gfx library and PPAPI. +// The style of naming is to match the PP_Bool conversions. + +namespace webkit { +namespace ppapi { + +inline gfx::Point PP_ToGfxPoint(const PP_Point& p) { + return gfx::Point(p.x, p.y); +} + +inline PP_Point PP_FromGfxPoint(const gfx::Point& p) { + return PP_MakePoint(p.x(), p.y()); +} + +inline gfx::Rect PP_ToGfxRect(const PP_Rect& r) { + return gfx::Rect(r.point.x, r.point.y, r.size.width, r.size.height); +} + +inline PP_Rect PP_FromGfxRect(const gfx::Rect& r) { + return PP_MakeRectFromXYWH(r.x(), r.y(), r.width(), r.height()); +} + +inline gfx::Size PP_ToGfxSize(const PP_Size& s) { + return gfx::Size(s.width, s.height); +} + +inline PP_Size PP_FromGfxSize(const gfx::Size& s) { + return PP_MakeSize(s.width(), s.height()); +} + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_GFX_CONVERSION_H_ diff --git a/content/renderer/pepper/host_array_buffer_var.cc b/content/renderer/pepper/host_array_buffer_var.cc new file mode 100644 index 0000000..565db42 --- /dev/null +++ b/content/renderer/pepper/host_array_buffer_var.cc @@ -0,0 +1,103 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/host_array_buffer_var.h" + +#include <stdio.h> +#include <string.h> + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/process_util.h" +#include "content/renderer/pepper/host_globals.h" +#include "content/renderer/pepper/plugin_module.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" +#include "ppapi/c/pp_instance.h" + +using ppapi::ArrayBufferVar; +using WebKit::WebArrayBuffer; + +namespace webkit { +namespace ppapi { + +HostArrayBufferVar::HostArrayBufferVar(uint32 size_in_bytes) + : buffer_(WebArrayBuffer::create(size_in_bytes, 1 /* element_size */)), + valid_(true) { +} + +HostArrayBufferVar::HostArrayBufferVar(const WebArrayBuffer& buffer) + : buffer_(buffer), + valid_(true) { +} + +HostArrayBufferVar::HostArrayBufferVar(uint32 size_in_bytes, + base::SharedMemoryHandle handle) + : buffer_(WebArrayBuffer::create(size_in_bytes, 1 /* element_size */)) { + base::SharedMemory s(handle, true); + valid_ = s.Map(size_in_bytes); + if (valid_) { + memcpy(buffer_.data(), s.memory(), size_in_bytes); + s.Unmap(); + } +} + +HostArrayBufferVar::~HostArrayBufferVar() { +} + +void* HostArrayBufferVar::Map() { + if (!valid_) + return NULL; + return buffer_.data(); +} + +void HostArrayBufferVar::Unmap() { + // We do not used shared memory on the host side. Nothing to do. +} + +uint32 HostArrayBufferVar::ByteLength() { + return buffer_.byteLength(); +} + +bool HostArrayBufferVar::CopyToNewShmem( + PP_Instance instance, + int* host_shm_handle_id, + base::SharedMemoryHandle* plugin_shm_handle) { + webkit::ppapi::PluginInstanceImpl* i = + webkit::ppapi::HostGlobals::Get()->GetInstance(instance); + scoped_ptr<base::SharedMemory> shm(i->delegate()->CreateAnonymousSharedMemory( + ByteLength())); + if (!shm) + return false; + + shm->Map(ByteLength()); + memcpy(shm->memory(), Map(), ByteLength()); + shm->Unmap(); + + // Duplicate the handle here; the SharedMemory destructor closes + // its handle on us. + HostGlobals* hg = HostGlobals::Get(); + PluginModule* pm = hg->GetModule(hg->GetModuleForInstance(instance)); + base::ProcessId p = pm->GetPeerProcessId(); + if (p == base::kNullProcessId) { + // In-process, clone for ourselves. + p = base::GetCurrentProcId(); + } + + base::PlatformFile platform_file = +#if defined(OS_WIN) + shm->handle(); +#elif defined(OS_POSIX) + shm->handle().fd; +#else +#error Not implemented. +#endif + + *plugin_shm_handle = + i->delegate()->ShareHandleWithRemote(platform_file, p, false); + *host_shm_handle_id = -1; + return true; +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/host_array_buffer_var.h b/content/renderer/pepper/host_array_buffer_var.h new file mode 100644 index 0000000..b1bd16a --- /dev/null +++ b/content/renderer/pepper/host_array_buffer_var.h @@ -0,0 +1,49 @@ +// Copyright (c) 2012 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 PPAPI_CONTENT_RENDERER_PEPPER_HOST_ARRAY_BUFFER_VAR_H_ +#define PPAPI_CONTENT_RENDERER_PEPPER_HOST_ARRAY_BUFFER_VAR_H_ + +#include "base/memory/shared_memory.h" +#include "ppapi/c/pp_instance.h" +#include "ppapi/shared_impl/host_resource.h" +#include "ppapi/shared_impl/var.h" +#include "third_party/WebKit/public/platform/WebArrayBuffer.h" + +namespace webkit { +namespace ppapi { + +// Represents a host-side ArrayBufferVar. +class HostArrayBufferVar : public ::ppapi::ArrayBufferVar { + public: + explicit HostArrayBufferVar(uint32 size_in_bytes); + explicit HostArrayBufferVar(const WebKit::WebArrayBuffer& buffer); + explicit HostArrayBufferVar(uint32 size_in_bytes, + base::SharedMemoryHandle handle); + + // ArrayBufferVar implementation. + virtual void* Map() OVERRIDE; + virtual void Unmap() OVERRIDE; + virtual uint32 ByteLength() OVERRIDE; + virtual bool CopyToNewShmem( + PP_Instance instance, + int* host_shm_handle_id, + base::SharedMemoryHandle* plugin_shm_handle) OVERRIDE; + + WebKit::WebArrayBuffer& webkit_buffer() { return buffer_; } + + private: + virtual ~HostArrayBufferVar(); + + WebKit::WebArrayBuffer buffer_; + // Tracks whether the data in the buffer is valid. + bool valid_; + + DISALLOW_COPY_AND_ASSIGN(HostArrayBufferVar); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // PPAPI_CONTENT_RENDERER_PEPPER_HOST_ARRAY_BUFFER_VAR_H_ diff --git a/content/renderer/pepper/host_globals.cc b/content/renderer/pepper/host_globals.cc new file mode 100644 index 0000000..ccc9f84 --- /dev/null +++ b/content/renderer/pepper/host_globals.cc @@ -0,0 +1,285 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/host_globals.h" + +#include <limits> + +#include "base/command_line.h" +#include "base/logging.h" +#include "base/rand_util.h" +#include "base/strings/utf_string_conversions.h" +#include "base/task_runner.h" +#include "content/renderer/pepper/plugin_module.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" +#include "ppapi/shared_impl/api_id.h" +#include "ppapi/shared_impl/id_assignment.h" +#include "third_party/WebKit/public/platform/WebString.h" +#include "third_party/WebKit/public/web/WebConsoleMessage.h" +#include "third_party/WebKit/public/web/WebDocument.h" +#include "third_party/WebKit/public/web/WebElement.h" +#include "third_party/WebKit/public/web/WebFrame.h" +#include "third_party/WebKit/public/web/WebPluginContainer.h" +#include "webkit/plugins/plugin_switches.h" + +using ppapi::CheckIdType; +using ppapi::MakeTypedId; +using ppapi::PPIdType; +using ppapi::ResourceTracker; +using WebKit::WebConsoleMessage; +using WebKit::WebString; + +namespace webkit { +namespace ppapi { + +namespace { + +typedef std::set<WebKit::WebPluginContainer*> ContainerSet; + +// Adds all WebPluginContainers associated with the given module to the set. +void GetAllContainersForModule(PluginModule* module, + ContainerSet* containers) { + const PluginModule::PluginInstanceSet& instances = + module->GetAllInstances(); + for (PluginModule::PluginInstanceSet::const_iterator i = instances.begin(); + i != instances.end(); ++i) + containers->insert((*i)->container()); +} + +WebConsoleMessage::Level LogLevelToWebLogLevel(PP_LogLevel level) { + switch (level) { + case PP_LOGLEVEL_TIP: + return WebConsoleMessage::LevelDebug; + case PP_LOGLEVEL_LOG: + return WebConsoleMessage::LevelLog; + case PP_LOGLEVEL_WARNING: + return WebConsoleMessage::LevelWarning; + case PP_LOGLEVEL_ERROR: + default: + return WebConsoleMessage::LevelError; + } +} + +WebConsoleMessage MakeLogMessage(PP_LogLevel level, + const std::string& source, + const std::string& message) { + std::string result = source; + if (!result.empty()) + result.append(": "); + result.append(message); + return WebConsoleMessage(LogLevelToWebLogLevel(level), + WebString(UTF8ToUTF16(result))); +} + +} // namespace + +HostGlobals* HostGlobals::host_globals_ = NULL; + +HostGlobals::HostGlobals() + : ::ppapi::PpapiGlobals(), + resource_tracker_(ResourceTracker::SINGLE_THREADED) { + DCHECK(!host_globals_); + host_globals_ = this; +} + +HostGlobals::HostGlobals( + ::ppapi::PpapiGlobals::PerThreadForTest per_thread_for_test) + : ::ppapi::PpapiGlobals(per_thread_for_test), + resource_tracker_(ResourceTracker::SINGLE_THREADED) { + DCHECK(!host_globals_); +} + +HostGlobals::~HostGlobals() { + DCHECK(host_globals_ == this || !host_globals_); + host_globals_ = NULL; +} + +::ppapi::ResourceTracker* HostGlobals::GetResourceTracker() { + return &resource_tracker_; +} + +::ppapi::VarTracker* HostGlobals::GetVarTracker() { + return &host_var_tracker_; +} + +::ppapi::CallbackTracker* HostGlobals::GetCallbackTrackerForInstance( + PP_Instance instance) { + InstanceMap::iterator found = instance_map_.find(instance); + if (found == instance_map_.end()) + return NULL; + return found->second->module()->GetCallbackTracker().get(); +} + +::ppapi::thunk::PPB_Instance_API* HostGlobals::GetInstanceAPI( + PP_Instance instance) { + // The InstanceAPI is just implemented by the PluginInstance object. + return GetInstance(instance); +} + +::ppapi::thunk::ResourceCreationAPI* HostGlobals::GetResourceCreationAPI( + PP_Instance pp_instance) { + PluginInstanceImpl* instance = GetInstance(pp_instance); + if (!instance) + return NULL; + return &instance->resource_creation(); +} + +PP_Module HostGlobals::GetModuleForInstance(PP_Instance instance) { + PluginInstanceImpl* inst = GetInstance(instance); + if (!inst) + return 0; + return inst->module()->pp_module(); +} + +std::string HostGlobals::GetCmdLine() { + return CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kPpapiFlashArgs); +} + +void HostGlobals::PreCacheFontForFlash(const void* logfontw) { + // Not implemented in-process. +} + +base::Lock* HostGlobals::GetProxyLock() { + // We do not lock on the host side. + return NULL; +} + +void HostGlobals::LogWithSource(PP_Instance instance, + PP_LogLevel level, + const std::string& source, + const std::string& value) { + PluginInstanceImpl* instance_object = + HostGlobals::Get()->GetInstance(instance); + if (instance_object) { + instance_object->container()->element().document().frame()-> + addMessageToConsole(MakeLogMessage(level, source, value)); + } else { + BroadcastLogWithSource(0, level, source, value); + } +} + +void HostGlobals::BroadcastLogWithSource(PP_Module pp_module, + PP_LogLevel level, + const std::string& source, + const std::string& value) { + // Get the unique containers associated with the broadcast. This prevents us + // from sending the same message to the same console when there are two + // instances on the page. + ContainerSet containers; + PluginModule* module = GetModule(pp_module); + if (module) { + GetAllContainersForModule(module, &containers); + } else { + // Unknown module, get containers for all modules. + for (ModuleMap::const_iterator i = module_map_.begin(); + i != module_map_.end(); ++i) { + GetAllContainersForModule(i->second, &containers); + } + } + + WebConsoleMessage message = MakeLogMessage(level, source, value); + for (ContainerSet::iterator i = containers.begin(); + i != containers.end(); ++i) + (*i)->element().document().frame()->addMessageToConsole(message); +} + +base::TaskRunner* HostGlobals::GetFileTaskRunner(PP_Instance instance) { + scoped_refptr<PluginInstanceImpl> plugin_instance = GetInstance(instance); + DCHECK(plugin_instance.get()); + scoped_refptr<base::MessageLoopProxy> message_loop = + plugin_instance->delegate()->GetFileThreadMessageLoopProxy(); + return message_loop.get(); +} + +::ppapi::MessageLoopShared* HostGlobals::GetCurrentMessageLoop() { + return NULL; +} + +PP_Module HostGlobals::AddModule(PluginModule* module) { +#ifndef NDEBUG + // Make sure we're not adding one more than once. + for (ModuleMap::const_iterator i = module_map_.begin(); + i != module_map_.end(); ++i) + DCHECK(i->second != module); +#endif + + // See AddInstance. + PP_Module new_module; + do { + new_module = MakeTypedId(static_cast<PP_Module>(base::RandUint64()), + ::ppapi::PP_ID_TYPE_MODULE); + } while (!new_module || + module_map_.find(new_module) != module_map_.end()); + module_map_[new_module] = module; + return new_module; +} + +void HostGlobals::ModuleDeleted(PP_Module module) { + DLOG_IF(ERROR, !CheckIdType(module, ::ppapi::PP_ID_TYPE_MODULE)) + << module << " is not a PP_Module."; + ModuleMap::iterator found = module_map_.find(module); + if (found == module_map_.end()) { + NOTREACHED(); + return; + } + module_map_.erase(found); +} + +PluginModule* HostGlobals::GetModule(PP_Module module) { + DLOG_IF(ERROR, !CheckIdType(module, ::ppapi::PP_ID_TYPE_MODULE)) + << module << " is not a PP_Module."; + ModuleMap::iterator found = module_map_.find(module); + if (found == module_map_.end()) + return NULL; + return found->second; +} + +PP_Instance HostGlobals::AddInstance(PluginInstanceImpl* instance) { + DCHECK(instance_map_.find(instance->pp_instance()) == instance_map_.end()); + + // Use a random number for the instance ID. This helps prevent some + // accidents. See also AddModule below. + // + // Need to make sure the random number isn't a duplicate or 0. + PP_Instance new_instance; + do { + new_instance = MakeTypedId(static_cast<PP_Instance>(base::RandUint64()), + ::ppapi::PP_ID_TYPE_INSTANCE); + } while (!new_instance || + instance_map_.find(new_instance) != instance_map_.end() || + !instance->module()->ReserveInstanceID(new_instance)); + + instance_map_[new_instance] = instance; + + resource_tracker_.DidCreateInstance(new_instance); + return new_instance; +} + +void HostGlobals::InstanceDeleted(PP_Instance instance) { + resource_tracker_.DidDeleteInstance(instance); + host_var_tracker_.DidDeleteInstance(instance); + instance_map_.erase(instance); +} + +void HostGlobals::InstanceCrashed(PP_Instance instance) { + resource_tracker_.DidDeleteInstance(instance); + host_var_tracker_.DidDeleteInstance(instance); +} + +PluginInstanceImpl* HostGlobals::GetInstance(PP_Instance instance) { + DLOG_IF(ERROR, !CheckIdType(instance, ::ppapi::PP_ID_TYPE_INSTANCE)) + << instance << " is not a PP_Instance."; + InstanceMap::iterator found = instance_map_.find(instance); + if (found == instance_map_.end()) + return NULL; + return found->second; +} + +bool HostGlobals::IsHostGlobals() const { + return true; +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/host_globals.h b/content/renderer/pepper/host_globals.h new file mode 100644 index 0000000..1689259 --- /dev/null +++ b/content/renderer/pepper/host_globals.h @@ -0,0 +1,118 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_PEPPER_HOST_GLOBALS_H_ +#define CONTENT_RENDERER_PEPPER_HOST_GLOBALS_H_ + +#include "base/compiler_specific.h" +#include "content/renderer/pepper/host_var_tracker.h" +#include "ppapi/shared_impl/callback_tracker.h" +#include "ppapi/shared_impl/ppapi_globals.h" +#include "ppapi/shared_impl/resource_tracker.h" +#include "ppapi/shared_impl/var_tracker.h" + +namespace webkit { +namespace ppapi { + +class PluginInstanceImpl; +class PluginModule; + +class HostGlobals : public ::ppapi::PpapiGlobals { + public: + HostGlobals(); + explicit HostGlobals(::ppapi::PpapiGlobals::PerThreadForTest); + virtual ~HostGlobals(); + + // Getter for the global singleton. Generally, you should use + // PpapiGlobals::Get() when possible. Use this only when you need some + // host-specific functionality. + inline static HostGlobals* Get() { + DCHECK(PpapiGlobals::Get()->IsHostGlobals()); + return static_cast<HostGlobals*>(PpapiGlobals::Get()); + } + + // PpapiGlobals implementation. + virtual ::ppapi::ResourceTracker* GetResourceTracker() OVERRIDE; + virtual ::ppapi::VarTracker* GetVarTracker() OVERRIDE; + virtual ::ppapi::CallbackTracker* GetCallbackTrackerForInstance( + PP_Instance instance) OVERRIDE; + virtual ::ppapi::thunk::PPB_Instance_API* GetInstanceAPI( + PP_Instance instance) OVERRIDE; + virtual ::ppapi::thunk::ResourceCreationAPI* GetResourceCreationAPI( + PP_Instance instance) OVERRIDE; + virtual PP_Module GetModuleForInstance(PP_Instance instance) OVERRIDE; + virtual std::string GetCmdLine() OVERRIDE; + virtual void PreCacheFontForFlash(const void* logfontw) OVERRIDE; + virtual base::Lock* GetProxyLock() OVERRIDE; + virtual void LogWithSource(PP_Instance instance, + PP_LogLevel level, + const std::string& source, + const std::string& value) OVERRIDE; + virtual void BroadcastLogWithSource(PP_Module module, + PP_LogLevel level, + const std::string& source, + const std::string& value) OVERRIDE; + virtual ::ppapi::MessageLoopShared* GetCurrentMessageLoop() OVERRIDE; + virtual base::TaskRunner* GetFileTaskRunner(PP_Instance instance) OVERRIDE; + + HostVarTracker* host_var_tracker() { + return &host_var_tracker_; + } + + // PP_Modules ---------------------------------------------------------------- + + // Adds a new plugin module to the list of tracked module, and returns a new + // module handle to identify it. + PP_Module AddModule(PluginModule* module); + + // Called when a plugin modulde was deleted and should no longer be tracked. + // The given handle should be one generated by AddModule. + void ModuleDeleted(PP_Module module); + + // Returns a pointer to the plugin modulde object associated with the given + // modulde handle. The return value will be NULL if the handle is invalid. + PluginModule* GetModule(PP_Module module); + + // PP_Instances -------------------------------------------------------------- + + // Adds a new plugin instance to the list of tracked instances, and returns a + // new instance handle to identify it. + PP_Instance AddInstance(PluginInstanceImpl* instance); + + // Called when a plugin instance was deleted and should no longer be tracked. + // The given handle should be one generated by AddInstance. + void InstanceDeleted(PP_Instance instance); + + void InstanceCrashed(PP_Instance instance); + + // Returns a pointer to the plugin instance object associated with the given + // instance handle. The return value will be NULL if the handle is invalid or + // if the instance has crashed. + PluginInstanceImpl* GetInstance(PP_Instance instance); + + private: + // PpapiGlobals overrides. + virtual bool IsHostGlobals() const OVERRIDE; + + static HostGlobals* host_globals_; + + ::ppapi::ResourceTracker resource_tracker_; + HostVarTracker host_var_tracker_; + + // Tracks all live instances and their associated object. + typedef std::map<PP_Instance, PluginInstanceImpl*> InstanceMap; + InstanceMap instance_map_; + + // Tracks all live modules. The pointers are non-owning, the PluginModule + // destructor will notify us when the module is deleted. + typedef std::map<PP_Module, PluginModule*> ModuleMap; + ModuleMap module_map_; + + DISALLOW_COPY_AND_ASSIGN(HostGlobals); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_HOST_GLOBALS_H_ diff --git a/content/renderer/pepper/host_var_tracker.cc b/content/renderer/pepper/host_var_tracker.cc new file mode 100644 index 0000000..b61af73 --- /dev/null +++ b/content/renderer/pepper/host_var_tracker.cc @@ -0,0 +1,173 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/host_var_tracker.h" + +#include "base/logging.h" +#include "content/renderer/pepper/host_array_buffer_var.h" +#include "content/renderer/pepper/npobject_var.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" +#include "ppapi/c/pp_var.h" + +using ppapi::ArrayBufferVar; +using ppapi::NPObjectVar; + +namespace webkit { +namespace ppapi { + +HostVarTracker::HostVarTracker() + : VarTracker(SINGLE_THREADED), + last_shared_memory_map_id_(0) { +} + +HostVarTracker::~HostVarTracker() { +} + +ArrayBufferVar* HostVarTracker::CreateArrayBuffer(uint32 size_in_bytes) { + return new HostArrayBufferVar(size_in_bytes); +} + +ArrayBufferVar* HostVarTracker::CreateShmArrayBuffer( + uint32 size_in_bytes, + base::SharedMemoryHandle handle) { + return new HostArrayBufferVar(size_in_bytes, handle); +} + +void HostVarTracker::AddNPObjectVar(NPObjectVar* object_var) { + CheckThreadingPreconditions(); + + InstanceMap::iterator found_instance = instance_map_.find( + object_var->pp_instance()); + if (found_instance == instance_map_.end()) { + // Lazily create the instance map. + DCHECK(object_var->pp_instance() != 0); + found_instance = instance_map_.insert(std::make_pair( + object_var->pp_instance(), + linked_ptr<NPObjectToNPObjectVarMap>(new NPObjectToNPObjectVarMap))). + first; + } + NPObjectToNPObjectVarMap* np_object_map = found_instance->second.get(); + + DCHECK(np_object_map->find(object_var->np_object()) == + np_object_map->end()) << "NPObjectVar already in map"; + np_object_map->insert(std::make_pair(object_var->np_object(), object_var)); +} + +void HostVarTracker::RemoveNPObjectVar(NPObjectVar* object_var) { + CheckThreadingPreconditions(); + + InstanceMap::iterator found_instance = instance_map_.find( + object_var->pp_instance()); + if (found_instance == instance_map_.end()) { + NOTREACHED() << "NPObjectVar has invalid instance."; + return; + } + NPObjectToNPObjectVarMap* np_object_map = found_instance->second.get(); + + NPObjectToNPObjectVarMap::iterator found_object = + np_object_map->find(object_var->np_object()); + if (found_object == np_object_map->end()) { + NOTREACHED() << "NPObjectVar not registered."; + return; + } + if (found_object->second != object_var) { + NOTREACHED() << "NPObjectVar doesn't match."; + return; + } + np_object_map->erase(found_object); +} + +NPObjectVar* HostVarTracker::NPObjectVarForNPObject(PP_Instance instance, + NPObject* np_object) { + CheckThreadingPreconditions(); + + InstanceMap::iterator found_instance = instance_map_.find(instance); + if (found_instance == instance_map_.end()) + return NULL; // No such instance. + NPObjectToNPObjectVarMap* np_object_map = found_instance->second.get(); + + NPObjectToNPObjectVarMap::iterator found_object = + np_object_map->find(np_object); + if (found_object == np_object_map->end()) + return NULL; // No such object. + return found_object->second; +} + +int HostVarTracker::GetLiveNPObjectVarsForInstance(PP_Instance instance) const { + CheckThreadingPreconditions(); + + InstanceMap::const_iterator found = instance_map_.find(instance); + if (found == instance_map_.end()) + return 0; + return static_cast<int>(found->second->size()); +} + +void HostVarTracker::DidDeleteInstance(PP_Instance instance) { + CheckThreadingPreconditions(); + + InstanceMap::iterator found_instance = instance_map_.find(instance); + if (found_instance == instance_map_.end()) + return; // Nothing to do. + NPObjectToNPObjectVarMap* np_object_map = found_instance->second.get(); + + // Force delete all var references. ForceReleaseNPObject() will cause + // this object, and potentially others it references, to be removed from + // |np_object_map|. + while (!np_object_map->empty()) { + ForceReleaseNPObject(np_object_map->begin()->second); + } + + // Remove the record for this instance since it should be empty. + DCHECK(np_object_map->empty()); + instance_map_.erase(found_instance); +} + +void HostVarTracker::ForceReleaseNPObject(::ppapi::NPObjectVar* object_var) { + object_var->InstanceDeleted(); + VarMap::iterator iter = live_vars_.find(object_var->GetExistingVarID()); + if (iter == live_vars_.end()) { + NOTREACHED(); + return; + } + iter->second.ref_count = 0; + DCHECK(iter->second.track_with_no_reference_count == 0); + DeleteObjectInfoIfNecessary(iter); +} + +int HostVarTracker::TrackSharedMemoryHandle(PP_Instance instance, + base::SharedMemoryHandle handle, + uint32 size_in_bytes) { + SharedMemoryMapEntry entry; + entry.instance = instance; + entry.handle = handle; + entry.size_in_bytes = size_in_bytes; + + // Find a free id for our map. + while (shared_memory_map_.find(last_shared_memory_map_id_) != + shared_memory_map_.end()) { + ++last_shared_memory_map_id_; + } + shared_memory_map_[last_shared_memory_map_id_] = entry; + return last_shared_memory_map_id_; +} + +bool HostVarTracker::StopTrackingSharedMemoryHandle( + int id, + PP_Instance instance, + base::SharedMemoryHandle* handle, + uint32* size_in_bytes) { + SharedMemoryMap::iterator it = shared_memory_map_.find(id); + if (it == shared_memory_map_.end()) + return false; + if (it->second.instance != instance) + return false; + + *handle = it->second.handle; + *size_in_bytes = it->second.size_in_bytes; + shared_memory_map_.erase(it); + return true; +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/host_var_tracker.h b/content/renderer/pepper/host_var_tracker.h new file mode 100644 index 0000000..9ff0dbd --- /dev/null +++ b/content/renderer/pepper/host_var_tracker.h @@ -0,0 +1,110 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_PEPPER_HOST_VAR_TRACKER_H_ +#define CONTENT_RENDERER_PEPPER_HOST_VAR_TRACKER_H_ + +#include <map> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/containers/hash_tables.h" +#include "base/gtest_prod_util.h" +#include "base/memory/linked_ptr.h" +#include "base/memory/ref_counted.h" +#include "content/common/content_export.h" +#include "ppapi/c/pp_instance.h" +#include "ppapi/shared_impl/host_resource.h" +#include "ppapi/shared_impl/resource_tracker.h" +#include "ppapi/shared_impl/var_tracker.h" + +typedef struct NPObject NPObject; + +namespace ppapi { +class ArrayBufferVar; +class NPObjectVar; +class Var; +} + +namespace webkit { +namespace ppapi { + +// Adds NPObject var tracking to the standard PPAPI VarTracker for use in the +// renderer. +class HostVarTracker : public ::ppapi::VarTracker { + public: + HostVarTracker(); + virtual ~HostVarTracker(); + + // Tracks all live NPObjectVar. This is so we can map between instance + + // NPObject and get the NPObjectVar corresponding to it. This Add/Remove + // function is called by the NPObjectVar when it is created and + // destroyed. + void AddNPObjectVar(::ppapi::NPObjectVar* object_var); + void RemoveNPObjectVar(::ppapi::NPObjectVar* object_var); + + // Looks up a previously registered NPObjectVar for the given NPObject and + // instance. Returns NULL if there is no NPObjectVar corresponding to the + // given NPObject for the given instance. See AddNPObjectVar above. + ::ppapi::NPObjectVar* NPObjectVarForNPObject(PP_Instance instance, + NPObject* np_object); + + // Returns the number of NPObjectVar's associated with the given instance. + // Returns 0 if the instance isn't known. + CONTENT_EXPORT int GetLiveNPObjectVarsForInstance( + PP_Instance instance) const; + + // VarTracker public implementation. + virtual void DidDeleteInstance(PP_Instance instance) OVERRIDE; + + virtual int TrackSharedMemoryHandle(PP_Instance instance, + base::SharedMemoryHandle file, + uint32 size_in_bytes) OVERRIDE; + virtual bool StopTrackingSharedMemoryHandle(int id, + PP_Instance instance, + base::SharedMemoryHandle* handle, + uint32* size_in_bytes) OVERRIDE; + + private: + // VarTracker private implementation. + virtual ::ppapi::ArrayBufferVar* CreateArrayBuffer( + uint32 size_in_bytes) OVERRIDE; + virtual ::ppapi::ArrayBufferVar* CreateShmArrayBuffer( + uint32 size_in_bytes, base::SharedMemoryHandle handle) OVERRIDE; + + // Clear the reference count of the given object and remove it from + // live_vars_. + void ForceReleaseNPObject(::ppapi::NPObjectVar* object_var); + + typedef std::map<NPObject*, ::ppapi::NPObjectVar*> + NPObjectToNPObjectVarMap; + + // Lists all known NPObjects, first indexed by the corresponding instance, + // then by the NPObject*. This allows us to look up an NPObjectVar given + // these two pieces of information. + // + // The instance map is lazily managed, so we'll add the + // NPObjectToNPObjectVarMap lazily when the first NPObject var is created, + // and delete it when it's empty. + typedef std::map<PP_Instance, linked_ptr<NPObjectToNPObjectVarMap> > + InstanceMap; + InstanceMap instance_map_; + + // Tracks all shared memory handles used for transmitting array buffers. + struct SharedMemoryMapEntry { + PP_Instance instance; + base::SharedMemoryHandle handle; + uint32 size_in_bytes; + }; + typedef std::map<int, SharedMemoryMapEntry> SharedMemoryMap; + SharedMemoryMap shared_memory_map_; + uint32_t last_shared_memory_map_id_; + + DISALLOW_COPY_AND_ASSIGN(HostVarTracker); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_HOST_VAR_TRACKER_H_ diff --git a/content/renderer/pepper/host_var_tracker_unittest.cc b/content/renderer/pepper/host_var_tracker_unittest.cc new file mode 100644 index 0000000..037f059e --- /dev/null +++ b/content/renderer/pepper/host_var_tracker_unittest.cc @@ -0,0 +1,138 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/ppapi_unittest.h" + +#include "base/memory/scoped_ptr.h" +#include "content/renderer/pepper/host_globals.h" +#include "content/renderer/pepper/host_var_tracker.h" +#include "content/renderer/pepper/mock_plugin_delegate.h" +#include "content/renderer/pepper/mock_resource.h" +#include "content/renderer/pepper/npapi_glue.h" +#include "content/renderer/pepper/npobject_var.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" +#include "ppapi/c/pp_var.h" +#include "ppapi/c/ppp_instance.h" +#include "third_party/npapi/bindings/npruntime.h" +#include "third_party/WebKit/public/web/WebBindings.h" + +using ppapi::NPObjectVar; + +namespace webkit { +namespace ppapi { + +namespace { + +// Tracked NPObjects ----------------------------------------------------------- + +int g_npobjects_alive = 0; + +void TrackedClassDeallocate(NPObject* npobject) { + g_npobjects_alive--; + delete npobject; +} + +NPClass g_tracked_npclass = { + NP_CLASS_STRUCT_VERSION, + NULL, + &TrackedClassDeallocate, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, +}; + +// Returns a new tracked NPObject with a refcount of 1. You'll want to put this +// in a NPObjectReleaser to free this ref when the test completes. +NPObject* NewTrackedNPObject() { + NPObject* object = new NPObject; + object->_class = &g_tracked_npclass; + object->referenceCount = 1; + + g_npobjects_alive++; + return object; +} + +class ReleaseNPObject { + public: + void operator()(NPObject* o) const { + WebKit::WebBindings::releaseObject(o); + } +}; + +// Handles automatically releasing a reference to the NPObject on destruction. +// It's assumed the input has a ref already taken. +typedef scoped_ptr_malloc<NPObject, ReleaseNPObject> NPObjectReleaser; + +} // namespace + +class HostVarTrackerTest : public PpapiUnittest { + public: + HostVarTrackerTest() { + } + + HostVarTracker& tracker() { + return *HostGlobals::Get()->host_var_tracker(); + } +}; + +TEST_F(HostVarTrackerTest, DeleteObjectVarWithInstance) { + // Make a second instance (the test harness already creates & manages one). + scoped_refptr<PluginInstanceImpl> instance2( + PluginInstanceImpl::Create(delegate(), NULL, module(), NULL, GURL())); + PP_Instance pp_instance2 = instance2->pp_instance(); + + // Make an object var. + NPObjectReleaser npobject(NewTrackedNPObject()); + NPObjectToPPVarForTest(instance2.get(), npobject.get()); + + EXPECT_EQ(1, g_npobjects_alive); + EXPECT_EQ(1, tracker().GetLiveNPObjectVarsForInstance(pp_instance2)); + + // Free the instance, this should release the ObjectVar. + instance2 = NULL; + EXPECT_EQ(0, tracker().GetLiveNPObjectVarsForInstance(pp_instance2)); +} + +// Make sure that using the same NPObject should give the same PP_Var +// each time. +TEST_F(HostVarTrackerTest, ReuseVar) { + NPObjectReleaser npobject(NewTrackedNPObject()); + + PP_Var pp_object1 = NPObjectToPPVarForTest(instance(), npobject.get()); + PP_Var pp_object2 = NPObjectToPPVarForTest(instance(), npobject.get()); + + // The two results should be the same. + EXPECT_EQ(pp_object1.value.as_id, pp_object2.value.as_id); + + // The objects should be able to get us back to the associated NPObject. + // This ObjectVar must be released before we do NPObjectToPPVarForTest again + // below so it gets freed and we get a new identifier. + { + scoped_refptr<NPObjectVar> check_object(NPObjectVar::FromPPVar(pp_object1)); + ASSERT_TRUE(check_object.get()); + EXPECT_EQ(instance()->pp_instance(), check_object->pp_instance()); + EXPECT_EQ(npobject.get(), check_object->np_object()); + } + + // Remove both of the refs we made above. + ::ppapi::VarTracker* var_tracker = + ::ppapi::PpapiGlobals::Get()->GetVarTracker(); + var_tracker->ReleaseVar(static_cast<int32_t>(pp_object2.value.as_id)); + var_tracker->ReleaseVar(static_cast<int32_t>(pp_object1.value.as_id)); + + // Releasing the resource should free the internal ref, and so making a new + // one now should generate a new ID. + PP_Var pp_object3 = NPObjectToPPVarForTest(instance(), npobject.get()); + EXPECT_NE(pp_object1.value.as_id, pp_object3.value.as_id); + var_tracker->ReleaseVar(static_cast<int32_t>(pp_object3.value.as_id)); +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/message_channel.cc b/content/renderer/pepper/message_channel.cc new file mode 100644 index 0000000..ea3bdf5 --- /dev/null +++ b/content/renderer/pepper/message_channel.cc @@ -0,0 +1,520 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/message_channel.h" + +#include <cstdlib> +#include <string> + +#include "base/bind.h" +#include "base/logging.h" +#include "base/message_loop/message_loop.h" +#include "content/renderer/pepper/host_array_buffer_var.h" +#include "content/renderer/pepper/npapi_glue.h" +#include "content/renderer/pepper/plugin_module.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" +#include "content/renderer/pepper/v8_var_converter.h" +#include "ppapi/shared_impl/ppapi_globals.h" +#include "ppapi/shared_impl/var.h" +#include "ppapi/shared_impl/var_tracker.h" +#include "third_party/WebKit/public/web/WebBindings.h" +#include "third_party/WebKit/public/web/WebDocument.h" +#include "third_party/WebKit/public/web/WebDOMMessageEvent.h" +#include "third_party/WebKit/public/web/WebElement.h" +#include "third_party/WebKit/public/web/WebFrame.h" +#include "third_party/WebKit/public/web/WebNode.h" +#include "third_party/WebKit/public/web/WebPluginContainer.h" +#include "third_party/WebKit/public/web/WebSerializedScriptValue.h" +#include "v8/include/v8.h" + +using ppapi::ArrayBufferVar; +using ppapi::PpapiGlobals; +using ppapi::StringVar; +using WebKit::WebBindings; +using WebKit::WebElement; +using WebKit::WebDOMEvent; +using WebKit::WebDOMMessageEvent; +using WebKit::WebPluginContainer; +using WebKit::WebSerializedScriptValue; + +namespace webkit { + +namespace ppapi { + +namespace { + +const char kPostMessage[] = "postMessage"; +const char kV8ToVarConversionError[] = "Failed to convert a PostMessage " + "argument from a JavaScript value to a PP_Var. It may have cycles or be of " + "an unsupported type."; +const char kVarToV8ConversionError[] = "Failed to convert a PostMessage " + "argument from a PP_Var to a Javascript value. It may have cycles or be of " + "an unsupported type."; + +// Helper function to get the MessageChannel that is associated with an +// NPObject*. +MessageChannel* ToMessageChannel(NPObject* object) { + return static_cast<MessageChannel::MessageChannelNPObject*>(object)-> + message_channel.get(); +} + +NPObject* ToPassThroughObject(NPObject* object) { + MessageChannel* channel = ToMessageChannel(object); + return channel ? channel->passthrough_object() : NULL; +} + +// Helper function to determine if a given identifier is equal to kPostMessage. +bool IdentifierIsPostMessage(NPIdentifier identifier) { + return WebBindings::getStringIdentifier(kPostMessage) == identifier; +} + +bool NPVariantToPPVar(const NPVariant* variant, PP_Var* result) { + switch (variant->type) { + case NPVariantType_Void: + *result = PP_MakeUndefined(); + return true; + case NPVariantType_Null: + *result = PP_MakeNull(); + return true; + case NPVariantType_Bool: + *result = PP_MakeBool(PP_FromBool(NPVARIANT_TO_BOOLEAN(*variant))); + return true; + case NPVariantType_Int32: + *result = PP_MakeInt32(NPVARIANT_TO_INT32(*variant)); + return true; + case NPVariantType_Double: + *result = PP_MakeDouble(NPVARIANT_TO_DOUBLE(*variant)); + return true; + case NPVariantType_String: + *result = StringVar::StringToPPVar( + NPVARIANT_TO_STRING(*variant).UTF8Characters, + NPVARIANT_TO_STRING(*variant).UTF8Length); + return true; + case NPVariantType_Object: { + // Calling WebBindings::toV8Value creates a wrapper around NPVariant so it + // shouldn't result in a deep copy. + v8::Handle<v8::Value> v8_value = WebBindings::toV8Value(variant); + if (!V8VarConverter::FromV8Value(v8_value, v8::Context::GetCurrent(), + result)) { + return false; + } + return true; + } + } + return false; +} + +// Copy a PP_Var in to a PP_Var that is appropriate for sending via postMessage. +// This currently just copies the value. For a string Var, the result is a +// PP_Var with the a copy of |var|'s string contents and a reference count of 1. +PP_Var CopyPPVar(const PP_Var& var) { + switch (var.type) { + case PP_VARTYPE_UNDEFINED: + case PP_VARTYPE_NULL: + case PP_VARTYPE_BOOL: + case PP_VARTYPE_INT32: + case PP_VARTYPE_DOUBLE: + return var; + case PP_VARTYPE_STRING: { + StringVar* string = StringVar::FromPPVar(var); + if (!string) + return PP_MakeUndefined(); + return StringVar::StringToPPVar(string->value()); + } + case PP_VARTYPE_ARRAY_BUFFER: { + ArrayBufferVar* buffer = ArrayBufferVar::FromPPVar(var); + if (!buffer) + return PP_MakeUndefined(); + PP_Var new_buffer_var = PpapiGlobals::Get()->GetVarTracker()-> + MakeArrayBufferPPVar(buffer->ByteLength()); + DCHECK(new_buffer_var.type == PP_VARTYPE_ARRAY_BUFFER); + if (new_buffer_var.type != PP_VARTYPE_ARRAY_BUFFER) + return PP_MakeUndefined(); + ArrayBufferVar* new_buffer = ArrayBufferVar::FromPPVar(new_buffer_var); + DCHECK(new_buffer); + if (!new_buffer) + return PP_MakeUndefined(); + memcpy(new_buffer->Map(), buffer->Map(), buffer->ByteLength()); + return new_buffer_var; + } + case PP_VARTYPE_OBJECT: + case PP_VARTYPE_ARRAY: + case PP_VARTYPE_DICTIONARY: + // Objects/Arrays/Dictionaries not supported by PostMessage in-process. + NOTREACHED(); + return PP_MakeUndefined(); + } + NOTREACHED(); + return PP_MakeUndefined(); +} + +//------------------------------------------------------------------------------ +// Implementations of NPClass functions. These are here to: +// - Implement postMessage behavior. +// - Forward calls to the 'passthrough' object to allow backwards-compatibility +// with GetInstanceObject() objects. +//------------------------------------------------------------------------------ +NPObject* MessageChannelAllocate(NPP npp, NPClass* the_class) { + return new MessageChannel::MessageChannelNPObject; +} + +void MessageChannelDeallocate(NPObject* object) { + MessageChannel::MessageChannelNPObject* instance = + static_cast<MessageChannel::MessageChannelNPObject*>(object); + delete instance; +} + +bool MessageChannelHasMethod(NPObject* np_obj, NPIdentifier name) { + if (!np_obj) + return false; + + // We only handle a function called postMessage. + if (IdentifierIsPostMessage(name)) + return true; + + // Other method names we will pass to the passthrough object, if we have one. + NPObject* passthrough = ToPassThroughObject(np_obj); + if (passthrough) + return WebBindings::hasMethod(NULL, passthrough, name); + return false; +} + +bool MessageChannelInvoke(NPObject* np_obj, NPIdentifier name, + const NPVariant* args, uint32 arg_count, + NPVariant* result) { + if (!np_obj) + return false; + + // We only handle a function called postMessage. + if (IdentifierIsPostMessage(name) && (arg_count == 1)) { + MessageChannel* message_channel = ToMessageChannel(np_obj); + if (message_channel) { + PP_Var argument = PP_MakeUndefined(); + if (!NPVariantToPPVar(&args[0], &argument)) { + PpapiGlobals::Get()->LogWithSource( + message_channel->instance()->pp_instance(), + PP_LOGLEVEL_ERROR, std::string(), kV8ToVarConversionError); + return false; + } + message_channel->PostMessageToNative(argument); + PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(argument); + return true; + } else { + return false; + } + } + // Other method calls we will pass to the passthrough object, if we have one. + NPObject* passthrough = ToPassThroughObject(np_obj); + if (passthrough) { + return WebBindings::invoke(NULL, passthrough, name, args, arg_count, + result); + } + return false; +} + +bool MessageChannelInvokeDefault(NPObject* np_obj, + const NPVariant* args, + uint32 arg_count, + NPVariant* result) { + if (!np_obj) + return false; + + // Invoke on the passthrough object, if we have one. + NPObject* passthrough = ToPassThroughObject(np_obj); + if (passthrough) { + return WebBindings::invokeDefault(NULL, passthrough, args, arg_count, + result); + } + return false; +} + +bool MessageChannelHasProperty(NPObject* np_obj, NPIdentifier name) { + if (!np_obj) + return false; + + // Invoke on the passthrough object, if we have one. + NPObject* passthrough = ToPassThroughObject(np_obj); + if (passthrough) + return WebBindings::hasProperty(NULL, passthrough, name); + return false; +} + +bool MessageChannelGetProperty(NPObject* np_obj, NPIdentifier name, + NPVariant* result) { + if (!np_obj) + return false; + + // Don't allow getting the postMessage function. + if (IdentifierIsPostMessage(name)) + return false; + + // Invoke on the passthrough object, if we have one. + NPObject* passthrough = ToPassThroughObject(np_obj); + if (passthrough) + return WebBindings::getProperty(NULL, passthrough, name, result); + return false; +} + +bool MessageChannelSetProperty(NPObject* np_obj, NPIdentifier name, + const NPVariant* variant) { + if (!np_obj) + return false; + + // Don't allow setting the postMessage function. + if (IdentifierIsPostMessage(name)) + return false; + + // Invoke on the passthrough object, if we have one. + NPObject* passthrough = ToPassThroughObject(np_obj); + if (passthrough) + return WebBindings::setProperty(NULL, passthrough, name, variant); + return false; +} + +bool MessageChannelEnumerate(NPObject *np_obj, NPIdentifier **value, + uint32_t *count) { + if (!np_obj) + return false; + + // Invoke on the passthrough object, if we have one, to enumerate its + // properties. + NPObject* passthrough = ToPassThroughObject(np_obj); + if (passthrough) { + bool success = WebBindings::enumerate(NULL, passthrough, value, count); + if (success) { + // Add postMessage to the list and return it. + if (std::numeric_limits<size_t>::max() / sizeof(NPIdentifier) <= + static_cast<size_t>(*count) + 1) // Else, "always false" x64 warning. + return false; + NPIdentifier* new_array = static_cast<NPIdentifier*>( + std::malloc(sizeof(NPIdentifier) * (*count + 1))); + std::memcpy(new_array, *value, sizeof(NPIdentifier)*(*count)); + new_array[*count] = WebBindings::getStringIdentifier(kPostMessage); + std::free(*value); + *value = new_array; + ++(*count); + return true; + } + } + + // Otherwise, build an array that includes only postMessage. + *value = static_cast<NPIdentifier*>(malloc(sizeof(NPIdentifier))); + (*value)[0] = WebBindings::getStringIdentifier(kPostMessage); + *count = 1; + return true; +} + +NPClass message_channel_class = { + NP_CLASS_STRUCT_VERSION, + &MessageChannelAllocate, + &MessageChannelDeallocate, + NULL, + &MessageChannelHasMethod, + &MessageChannelInvoke, + &MessageChannelInvokeDefault, + &MessageChannelHasProperty, + &MessageChannelGetProperty, + &MessageChannelSetProperty, + NULL, + &MessageChannelEnumerate, +}; + +} // namespace + +// MessageChannel -------------------------------------------------------------- +MessageChannel::MessageChannelNPObject::MessageChannelNPObject() { +} + +MessageChannel::MessageChannelNPObject::~MessageChannelNPObject() {} + +MessageChannel::MessageChannel(PluginInstanceImpl* instance) + : instance_(instance), + passthrough_object_(NULL), + np_object_(NULL), + weak_ptr_factory_(this), + early_message_queue_state_(QUEUE_MESSAGES) { + // Now create an NPObject for receiving calls to postMessage. This sets the + // reference count to 1. We release it in the destructor. + NPObject* obj = WebBindings::createObject(instance_->instanceNPP(), + &message_channel_class); + DCHECK(obj); + np_object_ = static_cast<MessageChannel::MessageChannelNPObject*>(obj); + np_object_->message_channel = weak_ptr_factory_.GetWeakPtr(); +} + +void MessageChannel::PostMessageToJavaScript(PP_Var message_data) { + v8::HandleScope scope; + + // Because V8 is probably not on the stack for Native->JS calls, we need to + // enter the appropriate context for the plugin. + WebPluginContainer* container = instance_->container(); + // It's possible that container() is NULL if the plugin has been removed from + // the DOM (but the PluginInstance is not destroyed yet). + if (!container) + return; + + v8::Local<v8::Context> context = + container->element().document().frame()->mainWorldScriptContext(); + // If the page is being destroyed, the context may be empty. + if (context.IsEmpty()) + return; + v8::Context::Scope context_scope(context); + + v8::Handle<v8::Value> v8_val; + if (!V8VarConverter::ToV8Value(message_data, context, &v8_val)) { + PpapiGlobals::Get()->LogWithSource(instance_->pp_instance(), + PP_LOGLEVEL_ERROR, std::string(), kVarToV8ConversionError); + return; + } + + // This is for backward compatibility. It usually makes sense for us to return + // a string object rather than a string primitive because it allows multiple + // references to the same string (as with PP_Var strings). However, prior to + // implementing dictionary and array, vars we would return a string primitive + // here. Changing it to an object now will break existing code that uses + // strict comparisons for strings returned from PostMessage. e.g. x === "123" + // will no longer return true. So if the only value to return is a string + // object, just return the string primitive. + if (v8_val->IsStringObject()) + v8_val = v8_val->ToString(); + + WebSerializedScriptValue serialized_val = + WebSerializedScriptValue::serialize(v8_val); + + if (instance_->module()->IsProxied()) { + if (early_message_queue_state_ != SEND_DIRECTLY) { + // We can't just PostTask here; the messages would arrive out of + // order. Instead, we queue them up until we're ready to post + // them. + early_message_queue_.push_back(serialized_val); + } else { + // The proxy sent an asynchronous message, so the plugin is already + // unblocked. Therefore, there's no need to PostTask. + DCHECK(early_message_queue_.size() == 0); + PostMessageToJavaScriptImpl(serialized_val); + } + } else { + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&MessageChannel::PostMessageToJavaScriptImpl, + weak_ptr_factory_.GetWeakPtr(), + serialized_val)); + } +} + +void MessageChannel::StopQueueingJavaScriptMessages() { + // We PostTask here instead of draining the message queue directly + // since we haven't finished initializing the WebPluginImpl yet, so + // the plugin isn't available in the DOM. + early_message_queue_state_ = DRAIN_PENDING; + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&MessageChannel::DrainEarlyMessageQueue, + weak_ptr_factory_.GetWeakPtr())); +} + +void MessageChannel::QueueJavaScriptMessages() { + if (early_message_queue_state_ == DRAIN_PENDING) + early_message_queue_state_ = DRAIN_CANCELLED; + else + early_message_queue_state_ = QUEUE_MESSAGES; +} + +void MessageChannel::DrainEarlyMessageQueue() { + // Take a reference on the PluginInstance. This is because JavaScript code + // may delete the plugin, which would destroy the PluginInstance and its + // corresponding MessageChannel. + scoped_refptr<PluginInstanceImpl> instance_ref(instance_); + + if (early_message_queue_state_ == DRAIN_CANCELLED) { + early_message_queue_state_ = QUEUE_MESSAGES; + return; + } + DCHECK(early_message_queue_state_ == DRAIN_PENDING); + + while (!early_message_queue_.empty()) { + PostMessageToJavaScriptImpl(early_message_queue_.front()); + early_message_queue_.pop_front(); + } + early_message_queue_state_ = SEND_DIRECTLY; +} + +void MessageChannel::PostMessageToJavaScriptImpl( + const WebSerializedScriptValue& message_data) { + DCHECK(instance_); + + WebPluginContainer* container = instance_->container(); + // It's possible that container() is NULL if the plugin has been removed from + // the DOM (but the PluginInstance is not destroyed yet). + if (!container) + return; + + WebDOMEvent event = + container->element().document().createEvent("MessageEvent"); + WebDOMMessageEvent msg_event = event.to<WebDOMMessageEvent>(); + msg_event.initMessageEvent("message", // type + false, // canBubble + false, // cancelable + message_data, // data + "", // origin [*] + NULL, // source [*] + ""); // lastEventId + // [*] Note that the |origin| is only specified for cross-document and server- + // sent messages, while |source| is only specified for cross-document + // messages: + // http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html + // This currently behaves like Web Workers. On Firefox, Chrome, and Safari + // at least, postMessage on Workers does not provide the origin or source. + // TODO(dmichael): Add origin if we change to a more iframe-like origin + // policy (see crbug.com/81537) + + container->element().dispatchEvent(msg_event); +} + +void MessageChannel::PostMessageToNative(PP_Var message_data) { + if (instance_->module()->IsProxied()) { + // In the proxied case, the copy will happen via serializiation, and the + // message is asynchronous. Therefore there's no need to copy the Var, nor + // to PostTask. + PostMessageToNativeImpl(message_data); + } else { + // Make a copy of the message data for the Task we will run. + PP_Var var_copy(CopyPPVar(message_data)); + + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&MessageChannel::PostMessageToNativeImpl, + weak_ptr_factory_.GetWeakPtr(), + var_copy)); + } +} + +void MessageChannel::PostMessageToNativeImpl(PP_Var message_data) { + instance_->HandleMessage(message_data); +} + +MessageChannel::~MessageChannel() { + WebBindings::releaseObject(np_object_); + if (passthrough_object_) + WebBindings::releaseObject(passthrough_object_); +} + +void MessageChannel::SetPassthroughObject(NPObject* passthrough) { + // Retain the passthrough object; We need to ensure it lives as long as this + // MessageChannel. + if (passthrough) + WebBindings::retainObject(passthrough); + + // If we had a passthrough set already, release it. Note that we retain the + // incoming passthrough object first, so that we behave correctly if anyone + // invokes: + // SetPassthroughObject(passthrough_object()); + if (passthrough_object_) + WebBindings::releaseObject(passthrough_object_); + + passthrough_object_ = passthrough; +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/message_channel.h b/content/renderer/pepper/message_channel.h new file mode 100644 index 0000000..322c4b4 --- /dev/null +++ b/content/renderer/pepper/message_channel.h @@ -0,0 +1,123 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_PEPPER_MESSAGE_CHANNEL_H_ +#define CONTENT_RENDERER_PEPPER_MESSAGE_CHANNEL_H_ + +#include <deque> + +#include "base/memory/weak_ptr.h" +#include "ppapi/shared_impl/resource.h" +#include "third_party/WebKit/public/web/WebSerializedScriptValue.h" +#include "third_party/npapi/bindings/npruntime.h" + +struct PP_Var; + +namespace webkit { +namespace ppapi { + +class PluginInstanceImpl; + +// MessageChannel implements bidirectional postMessage functionality, allowing +// calls from JavaScript to plugins and vice-versa. See +// PPB_Messaging::PostMessage and PPP_Messaging::HandleMessage for more +// information. +// +// Currently, only 1 MessageChannel can exist, to implement postMessage +// functionality for the instance interfaces. In the future, when we create a +// MessagePort type in PPAPI, those may be implemented here as well with some +// refactoring. +// - Separate message ports won't require the passthrough object. +// - The message target won't be limited to instance, and should support +// either plugin-provided or JS objects. +// TODO(dmichael): Add support for separate MessagePorts. +class MessageChannel { + public: + // MessageChannelNPObject is a simple struct that adds a pointer back to a + // MessageChannel instance. This way, we can use an NPObject to allow + // JavaScript interactions without forcing MessageChannel to inherit from + // NPObject. + struct MessageChannelNPObject : public NPObject { + MessageChannelNPObject(); + ~MessageChannelNPObject(); + + base::WeakPtr<MessageChannel> message_channel; + }; + + explicit MessageChannel(PluginInstanceImpl* instance); + ~MessageChannel(); + + // Post a message to the onmessage handler for this channel's instance + // asynchronously. + void PostMessageToJavaScript(PP_Var message_data); + // Post a message to the PPP_Instance HandleMessage function for this + // channel's instance. + void PostMessageToNative(PP_Var message_data); + + // Return the NPObject* to which we should forward any calls which aren't + // related to postMessage. Note that this can be NULL; it only gets set if + // there is a scriptable 'InstanceObject' associated with this channel's + // instance. + NPObject* passthrough_object() { + return passthrough_object_; + } + void SetPassthroughObject(NPObject* passthrough); + + NPObject* np_object() { return np_object_; } + + PluginInstanceImpl* instance() { + return instance_; + } + + // Messages sent to JavaScript are queued by default. After the DOM is + // set up for the plugin, users of MessageChannel should call + // StopQueueingJavaScriptMessages to start dispatching messages to JavaScript. + void QueueJavaScriptMessages(); + void StopQueueingJavaScriptMessages(); + + private: + PluginInstanceImpl* instance_; + + // We pass all non-postMessage calls through to the passthrough_object_. + // This way, a plugin can use PPB_Class or PPP_Class_Deprecated and also + // postMessage. This is necessary to support backwards-compatibility, and + // also trusted plugins for which we will continue to support synchronous + // scripting. + NPObject* passthrough_object_; + + // The NPObject we use to expose postMessage to JavaScript. + MessageChannelNPObject* np_object_; + + // Post a message to the onmessage handler for this channel's instance + // synchronously. This is used by PostMessageToJavaScript. + void PostMessageToJavaScriptImpl( + const WebKit::WebSerializedScriptValue& message_data); + // Post a message to the PPP_Instance HandleMessage function for this + // channel's instance. This is used by PostMessageToNative. + void PostMessageToNativeImpl(PP_Var message_data); + + void DrainEarlyMessageQueue(); + + // This is used to ensure pending tasks will not fire after this object is + // destroyed. + base::WeakPtrFactory<MessageChannel> weak_ptr_factory_; + + // TODO(teravest): Remove all the tricky DRAIN_CANCELLED logic once + // webkit::ppapi::PluginInstance::ResetAsProxied() is gone. + std::deque<WebKit::WebSerializedScriptValue> early_message_queue_; + enum EarlyMessageQueueState { + QUEUE_MESSAGES, // Queue JS messages. + SEND_DIRECTLY, // Post JS messages directly. + DRAIN_PENDING, // Drain queue, then transition to DIRECT. + DRAIN_CANCELLED // Preempt drain, go back to QUEUE. + }; + EarlyMessageQueueState early_message_queue_state_; + + DISALLOW_COPY_AND_ASSIGN(MessageChannel); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_MESSAGE_CHANNEL_H_ diff --git a/content/renderer/pepper/mock_platform_image_2d.cc b/content/renderer/pepper/mock_platform_image_2d.cc new file mode 100644 index 0000000..2aba190 --- /dev/null +++ b/content/renderer/pepper/mock_platform_image_2d.cc @@ -0,0 +1,34 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/mock_platform_image_2d.h" + +#include "skia/ext/platform_canvas.h" + +namespace webkit { +namespace ppapi { + +MockPlatformImage2D::MockPlatformImage2D(int width, int height) + : width_(width), + height_(height) { +} + +MockPlatformImage2D::~MockPlatformImage2D() { +} + +skia::PlatformCanvas* MockPlatformImage2D::Map() { + return skia::CreatePlatformCanvas(width_, height_, true); +} + +intptr_t MockPlatformImage2D::GetSharedMemoryHandle(uint32* byte_count) const { + *byte_count = 0; + return 0; +} + +TransportDIB* MockPlatformImage2D::GetTransportDIB() const { + return NULL; +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/mock_platform_image_2d.h b/content/renderer/pepper/mock_platform_image_2d.h new file mode 100644 index 0000000..eedd4af --- /dev/null +++ b/content/renderer/pepper/mock_platform_image_2d.h @@ -0,0 +1,32 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_PEPPER_MOCK_PLATFORM_IMAGE_2D_H_ +#define CONTENT_RENDERER_PEPPER_MOCK_PLATFORM_IMAGE_2D_H_ + +#include "content/renderer/pepper/plugin_delegate.h" +#include "skia/ext/platform_canvas.h" + +namespace webkit { +namespace ppapi { + +class MockPlatformImage2D : public PluginDelegate::PlatformImage2D { + public: + MockPlatformImage2D(int width, int height); + virtual ~MockPlatformImage2D(); + + // PlatformImage2D implementation. + virtual skia::PlatformCanvas* Map() OVERRIDE; + virtual intptr_t GetSharedMemoryHandle(uint32* byte_count) const OVERRIDE; + virtual TransportDIB* GetTransportDIB() const OVERRIDE; + + private: + int width_; + int height_; +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_MOCK_PLATFORM_IMAGE_2D_H_ diff --git a/content/renderer/pepper/mock_plugin_delegate.cc b/content/renderer/pepper/mock_plugin_delegate.cc new file mode 100644 index 0000000..aae195e --- /dev/null +++ b/content/renderer/pepper/mock_plugin_delegate.cc @@ -0,0 +1,415 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/mock_plugin_delegate.h" + +#include "base/logging.h" +#include "base/message_loop/message_loop_proxy.h" +#include "content/renderer/pepper/mock_platform_image_2d.h" +#include "content/renderer/pepper/plugin_delegate.h" +#include "content/renderer/pepper/plugin_module.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" +#include "ppapi/c/pp_errors.h" +#include "ppapi/shared_impl/ppapi_permissions.h" +#include "ppapi/shared_impl/ppapi_preferences.h" +#include "third_party/WebKit/public/platform/WebGamepads.h" + +namespace webkit { +namespace ppapi { + +MockPluginDelegate::MockPluginDelegate() { +} + +MockPluginDelegate::~MockPluginDelegate() { +} + +void MockPluginDelegate::PluginFocusChanged(PluginInstanceImpl* instance, + bool focused) { +} + +void MockPluginDelegate::PluginTextInputTypeChanged( + PluginInstanceImpl* instance) { +} + +void MockPluginDelegate::PluginCaretPositionChanged( + PluginInstanceImpl* instance) { +} + +void MockPluginDelegate::PluginRequestedCancelComposition( + PluginInstanceImpl* instance) { +} + +void MockPluginDelegate::PluginSelectionChanged(PluginInstanceImpl* instance) { +} + +void MockPluginDelegate::SimulateImeSetComposition( + const base::string16& text, + const std::vector<WebKit::WebCompositionUnderline>& underlines, + int selection_start, + int selection_end) { +} + +void MockPluginDelegate::SimulateImeConfirmComposition( + const base::string16& text) { +} + +void MockPluginDelegate::PluginCrashed(PluginInstanceImpl* instance) { +} + +void MockPluginDelegate::InstanceCreated(PluginInstanceImpl* instance) { +} + +void MockPluginDelegate::InstanceDeleted(PluginInstanceImpl* instance) { +} + +scoped_ptr< ::ppapi::thunk::ResourceCreationAPI> +MockPluginDelegate::CreateResourceCreationAPI(PluginInstanceImpl* instance) { + return scoped_ptr< ::ppapi::thunk::ResourceCreationAPI>(); +} + +SkBitmap* MockPluginDelegate::GetSadPluginBitmap() { + return NULL; +} + +WebKit::WebPlugin* MockPluginDelegate::CreatePluginReplacement( + const base::FilePath& file_path) { + return NULL; +} + +MockPluginDelegate::PlatformImage2D* MockPluginDelegate::CreateImage2D( + int width, + int height) { + return new MockPlatformImage2D(width, height); +} + +PluginDelegate::PlatformGraphics2D* MockPluginDelegate::GetGraphics2D( + PluginInstanceImpl* instance, + PP_Resource graphics_2d) { + return NULL; +} + +MockPluginDelegate::PlatformContext3D* MockPluginDelegate::CreateContext3D() { + return NULL; +} + +MockPluginDelegate::PlatformVideoDecoder* +MockPluginDelegate::CreateVideoDecoder( + media::VideoDecodeAccelerator::Client* client, + int32 command_buffer_route_id) { + return NULL; +} + +MockPluginDelegate::PlatformVideoCapture* +MockPluginDelegate::CreateVideoCapture( + const std::string& device_id, + const GURL& document_url, + PlatformVideoCaptureEventHandler* handler){ + return NULL; +} + +uint32_t MockPluginDelegate::GetAudioHardwareOutputSampleRate() { + return 0; +} + +uint32_t MockPluginDelegate::GetAudioHardwareOutputBufferSize() { + return 0; +} + +MockPluginDelegate::PlatformAudioOutput* MockPluginDelegate::CreateAudioOutput( + uint32_t sample_rate, + uint32_t sample_count, + PlatformAudioOutputClient* client) { + return NULL; +} + +MockPluginDelegate::PlatformAudioInput* MockPluginDelegate::CreateAudioInput( + const std::string& device_id, + const GURL& document_url, + uint32_t sample_rate, + uint32_t sample_count, + PlatformAudioInputClient* client) { + return NULL; +} + +MockPluginDelegate::Broker* MockPluginDelegate::ConnectToBroker( + PPB_Broker_Impl* client) { + return NULL; +} + +void MockPluginDelegate::NumberOfFindResultsChanged(int identifier, + int total, + bool final_result) { +} + +void MockPluginDelegate::SelectedFindResultChanged(int identifier, int index) { +} + +bool MockPluginDelegate::AsyncOpenFile(const base::FilePath& path, + int flags, + const AsyncOpenFileCallback& callback) { + return false; +} + +void MockPluginDelegate::AsyncOpenFileSystemURL( + const GURL& path, + int flags, + const AsyncOpenFileSystemURLCallback& callback) { +} + +bool MockPluginDelegate::IsFileSystemOpened( + PP_Instance instance, + PP_Resource resource) const { + return false; +} + +PP_FileSystemType MockPluginDelegate::GetFileSystemType( + PP_Instance instance, + PP_Resource resource) const { + return PP_FILESYSTEMTYPE_INVALID; +} + +GURL MockPluginDelegate::GetFileSystemRootUrl( + PP_Instance instance, + PP_Resource resource) const { + return GURL(); +} + +void MockPluginDelegate::MakeDirectory( + const GURL& path, + bool recursive, + const StatusCallback& callback) { +} + +void MockPluginDelegate::Query( + const GURL& path, + const MetadataCallback& success_callback, + const StatusCallback& error_callback) { +} + +void MockPluginDelegate::ReadDirectoryEntries( + const GURL& path, + const ReadDirectoryCallback& success_callback, + const StatusCallback& error_callback) { +} + +void MockPluginDelegate::Touch( + const GURL& path, + const base::Time& last_access_time, + const base::Time& last_modified_time, + const StatusCallback& callback) { +} + +void MockPluginDelegate::SetLength( + const GURL& path, + int64_t length, + const StatusCallback& callback) { +} + +void MockPluginDelegate::Delete( + const GURL& path, + const StatusCallback& callback) { +} + +void MockPluginDelegate::Rename( + const GURL& file_path, + const GURL& new_file_path, + const StatusCallback& callback) { +} + +void MockPluginDelegate::ReadDirectory( + const GURL& directory_path, + const ReadDirectoryCallback& success_callback, + const StatusCallback& error_callback) { +} + +void MockPluginDelegate::QueryAvailableSpace( + const GURL& origin, quota::StorageType type, + const AvailableSpaceCallback& callback) { +} + +void MockPluginDelegate::WillUpdateFile(const GURL& file_path) { +} + +void MockPluginDelegate::DidUpdateFile(const GURL& file_path, int64_t delta) { +} + +void MockPluginDelegate::SyncGetFileSystemPlatformPath( + const GURL& url, + base::FilePath* platform_path) { + DCHECK(platform_path); + *platform_path = base::FilePath(); +} + +scoped_refptr<base::MessageLoopProxy> +MockPluginDelegate::GetFileThreadMessageLoopProxy() { + return scoped_refptr<base::MessageLoopProxy>(); +} + +uint32 MockPluginDelegate::TCPSocketCreate() { + return 0; +} + +void MockPluginDelegate::TCPSocketConnect(PPB_TCPSocket_Private_Impl* socket, + uint32 socket_id, + const std::string& host, + uint16_t port) { +} + +void MockPluginDelegate::TCPSocketConnectWithNetAddress( + PPB_TCPSocket_Private_Impl* socket, + uint32 socket_id, + const PP_NetAddress_Private& addr) { +} + +void MockPluginDelegate::TCPSocketSSLHandshake( + uint32 socket_id, + const std::string& server_name, + uint16_t server_port, + const std::vector<std::vector<char> >& trusted_certs, + const std::vector<std::vector<char> >& untrusted_certs) { +} + +void MockPluginDelegate::TCPSocketRead(uint32 socket_id, + int32_t bytes_to_read) { +} + +void MockPluginDelegate::TCPSocketWrite(uint32 socket_id, + const std::string& buffer) { +} + +void MockPluginDelegate::TCPSocketSetOption( + uint32 socket_id, + PP_TCPSocket_Option name, + const ::ppapi::SocketOptionData& value) { +} + +void MockPluginDelegate::TCPSocketDisconnect(uint32 socket_id) { +} + +void MockPluginDelegate::RegisterTCPSocket(PPB_TCPSocket_Private_Impl* socket, + uint32 socket_id) { +} + +void MockPluginDelegate::TCPServerSocketListen( + PP_Resource socket_resource, + const PP_NetAddress_Private& addr, + int32_t backlog) { +} + +void MockPluginDelegate::TCPServerSocketAccept(uint32 server_socket_id) { +} + +void MockPluginDelegate::TCPServerSocketStopListening( + PP_Resource socket_resource, + uint32 socket_id) { +} + +bool MockPluginDelegate::AddNetworkListObserver( + webkit_glue::NetworkListObserver* observer) { + return false; +} + +void MockPluginDelegate::RemoveNetworkListObserver( + webkit_glue::NetworkListObserver* observer) { +} + +bool MockPluginDelegate::X509CertificateParseDER( + const std::vector<char>& der, + ::ppapi::PPB_X509Certificate_Fields* fields) { + return false; +} + +FullscreenContainer* MockPluginDelegate::CreateFullscreenContainer( + PluginInstanceImpl* instance) { + return NULL; +} + +gfx::Size MockPluginDelegate::GetScreenSize() { + return gfx::Size(1024, 768); +} + +std::string MockPluginDelegate::GetDefaultEncoding() { + return "iso-8859-1"; +} + +void MockPluginDelegate::ZoomLimitsChanged(double minimum_factor, + double maximum_factor) { +} + +base::SharedMemory* MockPluginDelegate::CreateAnonymousSharedMemory( + size_t size) { + return NULL; +} + +::ppapi::Preferences MockPluginDelegate::GetPreferences() { + return ::ppapi::Preferences(); +} + +bool MockPluginDelegate::LockMouse(PluginInstanceImpl* instance) { + return false; +} + +void MockPluginDelegate::UnlockMouse(PluginInstanceImpl* instance) { +} + +bool MockPluginDelegate::IsMouseLocked(PluginInstanceImpl* instance) { + return false; +} + +void MockPluginDelegate::DidChangeCursor(PluginInstanceImpl* instance, + const WebKit::WebCursorInfo& cursor) { +} + +void MockPluginDelegate::DidReceiveMouseEvent(PluginInstanceImpl* instance) { +} + +void MockPluginDelegate::SampleGamepads(WebKit::WebGamepads* data) { + data->length = 0; +} + +bool MockPluginDelegate::IsInFullscreenMode() { + return false; +} + +bool MockPluginDelegate::IsPageVisible() const { + return true; +} + +int MockPluginDelegate::EnumerateDevices( + PP_DeviceType_Dev type, + const EnumerateDevicesCallback& callback) { + return -1; +} + +void MockPluginDelegate::StopEnumerateDevices(int request_id) { +} + +IPC::PlatformFileForTransit MockPluginDelegate::ShareHandleWithRemote( + base::PlatformFile handle, + base::ProcessId target_process_id, + bool should_close_source) const { + return IPC::InvalidPlatformFileForTransit(); +} + +bool MockPluginDelegate::IsRunningInProcess(PP_Instance instance) const { + return false; +} + +void MockPluginDelegate::HandleDocumentLoad( + PluginInstanceImpl* instance, + const WebKit::WebURLResponse& response) { +} + +content::RendererPpapiHost* MockPluginDelegate::CreateExternalPluginModule( + scoped_refptr<PluginModule> module, + const base::FilePath& path, + ::ppapi::PpapiPermissions permissions, + const IPC::ChannelHandle& channel_handle, + base::ProcessId plugin_pid, + int plugin_child_id) { + return NULL; +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/mock_plugin_delegate.h b/content/renderer/pepper/mock_plugin_delegate.h new file mode 100644 index 0000000..8631dd7 --- /dev/null +++ b/content/renderer/pepper/mock_plugin_delegate.h @@ -0,0 +1,205 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_PEPPER_MOCK_PLUGIN_DELEGATE_H_ +#define CONTENT_RENDERER_PEPPER_MOCK_PLUGIN_DELEGATE_H_ + +#include "content/renderer/pepper/plugin_delegate.h" + +struct PP_NetAddress_Private; +namespace ppapi { class PPB_X509Certificate_Fields; } + +namespace webkit { +namespace ppapi { + +class MockPluginDelegate : public PluginDelegate { + public: + MockPluginDelegate(); + virtual ~MockPluginDelegate(); + + virtual void PluginFocusChanged(PluginInstanceImpl* instance, + bool focused) OVERRIDE; + virtual void PluginTextInputTypeChanged( + PluginInstanceImpl* instance) OVERRIDE; + virtual void PluginCaretPositionChanged( + PluginInstanceImpl* instance) OVERRIDE; + virtual void PluginRequestedCancelComposition( + PluginInstanceImpl* instance) OVERRIDE; + virtual void PluginSelectionChanged(PluginInstanceImpl* instance) OVERRIDE; + virtual void SimulateImeSetComposition( + const base::string16& text, + const std::vector<WebKit::WebCompositionUnderline>& underlines, + int selection_start, + int selection_end) OVERRIDE; + virtual void SimulateImeConfirmComposition( + const base::string16& text) OVERRIDE; + virtual void PluginCrashed(PluginInstanceImpl* instance) OVERRIDE; + virtual void InstanceCreated(PluginInstanceImpl* instance) OVERRIDE; + virtual void InstanceDeleted(PluginInstanceImpl* instance) OVERRIDE; + virtual scoped_ptr< ::ppapi::thunk::ResourceCreationAPI> + CreateResourceCreationAPI(PluginInstanceImpl* instance) OVERRIDE; + virtual SkBitmap* GetSadPluginBitmap() OVERRIDE; + virtual WebKit::WebPlugin* CreatePluginReplacement( + const base::FilePath& file_path) OVERRIDE; + virtual PlatformImage2D* CreateImage2D(int width, int height) OVERRIDE; + virtual PlatformGraphics2D* GetGraphics2D(PluginInstanceImpl* instance, + PP_Resource graphics_2d) OVERRIDE; + virtual PlatformContext3D* CreateContext3D() OVERRIDE; + virtual PlatformVideoDecoder* CreateVideoDecoder( + media::VideoDecodeAccelerator::Client* client, + int32 command_buffer_route_id) OVERRIDE; + virtual PlatformVideoCapture* CreateVideoCapture( + const std::string& device_id, + const GURL& document_url, + PlatformVideoCaptureEventHandler* handler) OVERRIDE; + virtual uint32_t GetAudioHardwareOutputSampleRate() OVERRIDE; + virtual uint32_t GetAudioHardwareOutputBufferSize() OVERRIDE; + virtual PlatformAudioOutput* CreateAudioOutput( + uint32_t sample_rate, + uint32_t sample_count, + PlatformAudioOutputClient* client) OVERRIDE; + virtual PlatformAudioInput* CreateAudioInput( + const std::string& device_id, + const GURL& document_url, + uint32_t sample_rate, + uint32_t sample_count, + PlatformAudioInputClient* client) OVERRIDE; + virtual Broker* ConnectToBroker(PPB_Broker_Impl* client) OVERRIDE; + virtual void NumberOfFindResultsChanged(int identifier, + int total, + bool final_result) OVERRIDE; + virtual void SelectedFindResultChanged(int identifier, int index) OVERRIDE; + virtual bool AsyncOpenFile(const base::FilePath& path, + int flags, + const AsyncOpenFileCallback& callback) OVERRIDE; + virtual void AsyncOpenFileSystemURL( + const GURL& path, + int flags, + const AsyncOpenFileSystemURLCallback& callback) OVERRIDE; + virtual bool IsFileSystemOpened(PP_Instance instance, + PP_Resource resource) const OVERRIDE; + virtual PP_FileSystemType GetFileSystemType( + PP_Instance instance, + PP_Resource resource) const OVERRIDE; + virtual GURL GetFileSystemRootUrl(PP_Instance instance, + PP_Resource resource) const OVERRIDE; + virtual void MakeDirectory( + const GURL& path, + bool recursive, + const StatusCallback& callback) OVERRIDE; + virtual void Query(const GURL& path, + const MetadataCallback& success_callback, + const StatusCallback& error_callback) OVERRIDE; + virtual void ReadDirectoryEntries( + const GURL& path, + const ReadDirectoryCallback& success_callback, + const StatusCallback& error_callback) OVERRIDE; + virtual void Touch(const GURL& path, + const base::Time& last_access_time, + const base::Time& last_modified_time, + const StatusCallback& callback) OVERRIDE; + virtual void SetLength(const GURL& path, + int64_t length, + const StatusCallback& callback) OVERRIDE; + virtual void Delete(const GURL& path, + const StatusCallback& callback) OVERRIDE; + virtual void Rename(const GURL& file_path, + const GURL& new_file_path, + const StatusCallback& callback) OVERRIDE; + virtual void ReadDirectory( + const GURL& directory_path, + const ReadDirectoryCallback& success_callback, + const StatusCallback& error_callback) OVERRIDE; + virtual void QueryAvailableSpace( + const GURL& origin, + quota::StorageType type, + const AvailableSpaceCallback& callback) OVERRIDE; + virtual void WillUpdateFile(const GURL& file_path) OVERRIDE; + virtual void DidUpdateFile(const GURL& file_path, int64_t delta) OVERRIDE; + virtual void SyncGetFileSystemPlatformPath( + const GURL& url, + base::FilePath* platform_path) OVERRIDE; + virtual scoped_refptr<base::MessageLoopProxy> + GetFileThreadMessageLoopProxy() OVERRIDE; + virtual uint32 TCPSocketCreate() OVERRIDE; + virtual void TCPSocketConnect(PPB_TCPSocket_Private_Impl* socket, + uint32 socket_id, + const std::string& host, + uint16_t port) OVERRIDE; + virtual void TCPSocketConnectWithNetAddress( + PPB_TCPSocket_Private_Impl* socket, + uint32 socket_id, + const PP_NetAddress_Private& addr) OVERRIDE; + virtual void TCPSocketSSLHandshake( + uint32 socket_id, + const std::string& server_name, + uint16_t server_port, + const std::vector<std::vector<char> >& trusted_certs, + const std::vector<std::vector<char> >& untrusted_certs) OVERRIDE; + virtual void TCPSocketRead(uint32 socket_id, int32_t bytes_to_read) OVERRIDE; + virtual void TCPSocketWrite(uint32 socket_id, + const std::string& buffer) OVERRIDE; + virtual void TCPSocketDisconnect(uint32 socket_id) OVERRIDE; + virtual void TCPSocketSetOption( + uint32 socket_id, + PP_TCPSocket_Option name, + const ::ppapi::SocketOptionData& value) OVERRIDE; + virtual void RegisterTCPSocket(PPB_TCPSocket_Private_Impl* socket, + uint32 socket_id) OVERRIDE; + virtual void TCPServerSocketListen(PP_Resource socket_resource, + const PP_NetAddress_Private& addr, + int32_t backlog) OVERRIDE; + virtual void TCPServerSocketAccept(uint32 server_socket_id) OVERRIDE; + virtual void TCPServerSocketStopListening(PP_Resource socket_resource, + uint32 socket_id) OVERRIDE; + // Add/remove a network list observer. + virtual bool AddNetworkListObserver( + webkit_glue::NetworkListObserver* observer) OVERRIDE; + virtual void RemoveNetworkListObserver( + webkit_glue::NetworkListObserver* observer) OVERRIDE; + virtual bool X509CertificateParseDER( + const std::vector<char>& der, + ::ppapi::PPB_X509Certificate_Fields* fields) OVERRIDE; + virtual FullscreenContainer* CreateFullscreenContainer( + PluginInstanceImpl* instance) OVERRIDE; + virtual gfx::Size GetScreenSize() OVERRIDE; + virtual std::string GetDefaultEncoding() OVERRIDE; + virtual void ZoomLimitsChanged(double minimum_factor, + double maximum_factor) OVERRIDE; + virtual base::SharedMemory* CreateAnonymousSharedMemory(size_t size) OVERRIDE; + virtual ::ppapi::Preferences GetPreferences() OVERRIDE; + virtual bool LockMouse(PluginInstanceImpl* instance) OVERRIDE; + virtual void UnlockMouse(PluginInstanceImpl* instance) OVERRIDE; + virtual bool IsMouseLocked(PluginInstanceImpl* instance) OVERRIDE; + virtual void DidChangeCursor(PluginInstanceImpl* instance, + const WebKit::WebCursorInfo& cursor) OVERRIDE; + virtual void DidReceiveMouseEvent(PluginInstanceImpl* instance) OVERRIDE; + virtual void SampleGamepads(WebKit::WebGamepads* data) OVERRIDE; + virtual bool IsInFullscreenMode() OVERRIDE; + virtual bool IsPageVisible() const OVERRIDE; + virtual int EnumerateDevices( + PP_DeviceType_Dev type, + const EnumerateDevicesCallback& callback) OVERRIDE; + virtual void StopEnumerateDevices(int request_id) OVERRIDE; + virtual IPC::PlatformFileForTransit ShareHandleWithRemote( + base::PlatformFile handle, + base::ProcessId target_process_id, + bool should_close_source) const OVERRIDE; + virtual bool IsRunningInProcess(PP_Instance instance) const OVERRIDE; + virtual void HandleDocumentLoad( + PluginInstanceImpl* instance, + const WebKit::WebURLResponse& response) OVERRIDE; + virtual content::RendererPpapiHost* CreateExternalPluginModule( + scoped_refptr<PluginModule> module, + const base::FilePath& path, + ::ppapi::PpapiPermissions permissions, + const IPC::ChannelHandle& channel_handle, + base::ProcessId plugin_pid, + int plugin_child_id) OVERRIDE; +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_MOCK_PLUGIN_DELEGATE_H_ diff --git a/content/renderer/pepper/mock_resource.h b/content/renderer/pepper/mock_resource.h new file mode 100644 index 0000000..60531f0 --- /dev/null +++ b/content/renderer/pepper/mock_resource.h @@ -0,0 +1,28 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_PEPPER_MOCK_RESOURCE_H_ +#define CONTENT_RENDERER_PEPPER_MOCK_RESOURCE_H_ + +#include "ppapi/shared_impl/resource.h" + +namespace webkit { +namespace ppapi { + +// Tests can derive from this to implement special test-specific resources. +// It's assumed that a test will only need one mock resource, so it can +// static_cast to get its own implementation. +class MockResource : public ::ppapi::Resource { + public: + MockResource(PP_Instance instance) + : Resource(::ppapi::OBJECT_IS_IMPL, instance) {} + + private: + virtual ~MockResource() {} +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_MOCK_RESOURCE_H_ diff --git a/content/renderer/pepper/npapi_glue.cc b/content/renderer/pepper/npapi_glue.cc new file mode 100644 index 0000000..f8b0c52 --- /dev/null +++ b/content/renderer/pepper/npapi_glue.cc @@ -0,0 +1,357 @@ +// 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 "content/renderer/pepper/npapi_glue.h" + +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/strings/string_util.h" +#include "content/renderer/pepper/host_array_buffer_var.h" +#include "content/renderer/pepper/host_globals.h" +#include "content/renderer/pepper/host_var_tracker.h" +#include "content/renderer/pepper/npobject_var.h" +#include "content/renderer/pepper/plugin_module.h" +#include "content/renderer/pepper/plugin_object.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" +#include "ppapi/c/pp_var.h" +#include "third_party/npapi/bindings/npapi.h" +#include "third_party/npapi/bindings/npruntime.h" +#include "third_party/WebKit/public/web/WebBindings.h" +#include "third_party/WebKit/public/web/WebDocument.h" +#include "third_party/WebKit/public/web/WebElement.h" +#include "third_party/WebKit/public/web/WebFrame.h" +#include "third_party/WebKit/public/web/WebPluginContainer.h" +#include "v8/include/v8.h" + +using ppapi::NPObjectVar; +using ppapi::PpapiGlobals; +using ppapi::StringVar; +using ppapi::Var; +using WebKit::WebArrayBuffer; +using WebKit::WebBindings; +using WebKit::WebPluginContainer; + +namespace webkit { +namespace ppapi { + +namespace { + +const char kInvalidPluginValue[] = "Error: Plugin returned invalid value."; + +PP_Var NPObjectToPPVarImpl(PluginInstanceImpl* instance, + NPObject* object, + v8::Local<v8::Context> context) { + DCHECK(object); + if (context.IsEmpty()) + return PP_MakeUndefined(); + v8::Context::Scope context_scope(context); + + WebArrayBuffer buffer; + // TODO(dmichael): Should I protect against duplicate Vars representing the + // same array buffer? It's probably not worth the trouble, since it will only + // affect in-process plugins. + if (WebBindings::getArrayBuffer(object, &buffer)) { + scoped_refptr<HostArrayBufferVar> buffer_var( + new HostArrayBufferVar(buffer)); + return buffer_var->GetPPVar(); + } + scoped_refptr<NPObjectVar> object_var( + HostGlobals::Get()->host_var_tracker()->NPObjectVarForNPObject( + instance->pp_instance(), object)); + if (!object_var.get()) { // No object for this module yet, make a new one. + object_var = new NPObjectVar(instance->pp_instance(), object); + } + return object_var->GetPPVar(); +} + + +} // namespace + +// Utilities ------------------------------------------------------------------- + +bool PPVarToNPVariant(PP_Var var, NPVariant* result) { + switch (var.type) { + case PP_VARTYPE_UNDEFINED: + 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: { + StringVar* string = StringVar::FromPPVar(var); + if (!string) { + VOID_TO_NPVARIANT(*result); + return false; + } + const std::string& value = string->value(); + char* c_string = static_cast<char*>(malloc(value.size())); + memcpy(c_string, value.data(), value.size()); + STRINGN_TO_NPVARIANT(c_string, value.size(), *result); + break; + } + case PP_VARTYPE_OBJECT: { + scoped_refptr<NPObjectVar> object(NPObjectVar::FromPPVar(var)); + if (!object.get()) { + VOID_TO_NPVARIANT(*result); + return false; + } + OBJECT_TO_NPVARIANT(WebBindings::retainObject(object->np_object()), + *result); + break; + } + // The following types are not supported for use with PPB_Var_Deprecated, + // because PPB_Var_Deprecated is only for trusted plugins, and the trusted + // plugins we have don't need these types. We can add support in the future + // if it becomes necessary. + case PP_VARTYPE_ARRAY: + case PP_VARTYPE_DICTIONARY: + case PP_VARTYPE_ARRAY_BUFFER: + VOID_TO_NPVARIANT(*result); + break; + } + return true; +} + +PP_Var NPVariantToPPVar(PluginInstanceImpl* instance, + const NPVariant* variant) { + switch (variant->type) { + case NPVariantType_Void: + return PP_MakeUndefined(); + case NPVariantType_Null: + return PP_MakeNull(); + case NPVariantType_Bool: + return PP_MakeBool(PP_FromBool(NPVARIANT_TO_BOOLEAN(*variant))); + case NPVariantType_Int32: + return PP_MakeInt32(NPVARIANT_TO_INT32(*variant)); + case NPVariantType_Double: + return PP_MakeDouble(NPVARIANT_TO_DOUBLE(*variant)); + case NPVariantType_String: + return StringVar::StringToPPVar( + NPVARIANT_TO_STRING(*variant).UTF8Characters, + NPVARIANT_TO_STRING(*variant).UTF8Length); + case NPVariantType_Object: + return NPObjectToPPVar(instance, NPVARIANT_TO_OBJECT(*variant)); + } + NOTREACHED(); + return PP_MakeUndefined(); +} + +NPIdentifier PPVarToNPIdentifier(PP_Var var) { + switch (var.type) { + case PP_VARTYPE_STRING: { + 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; + } +} + +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 StringVar::StringToPPVar(string_value); + + return PP_MakeInt32(int_value); +} + +PP_Var NPObjectToPPVar(PluginInstanceImpl* instance, NPObject* object) { + WebPluginContainer* container = instance->container(); + // It's possible that container() is NULL if the plugin has been removed from + // the DOM (but the PluginInstance is not destroyed yet). + if (!container) + return PP_MakeUndefined(); + v8::HandleScope scope(instance->GetIsolate()); + v8::Local<v8::Context> context = + container->element().document().frame()->mainWorldScriptContext(); + return NPObjectToPPVarImpl(instance, object, context); +} + +PP_Var NPObjectToPPVarForTest(PluginInstanceImpl* instance, NPObject* object) { + v8::Isolate* test_isolate = v8::Isolate::New(); + PP_Var result = PP_MakeUndefined(); + { + v8::HandleScope scope(test_isolate); + v8::Isolate::Scope isolate_scope(test_isolate); + v8::Local<v8::Context> context = v8::Context::New(test_isolate); + result = NPObjectToPPVarImpl(instance, object, context); + } + test_isolate->Dispose(); + return result; +} + +// PPResultAndExceptionToNPResult ---------------------------------------------- + +PPResultAndExceptionToNPResult::PPResultAndExceptionToNPResult( + NPObject* object_var, + NPVariant* np_result) + : object_var_(object_var), + np_result_(np_result), + exception_(PP_MakeUndefined()), + success_(false), + checked_exception_(false) { +} + +PPResultAndExceptionToNPResult::~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_); + + PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(exception_); +} + +// 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 PPResultAndExceptionToNPResult::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_, 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_. + PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(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 PPResultAndExceptionToNPResult::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 PPResultAndExceptionToNPResult::IgnoreException() { + checked_exception_ = true; +} + +// Throws the current exception to JS. The exception must be set. +void PPResultAndExceptionToNPResult::ThrowException() { + StringVar* string = StringVar::FromPPVar(exception_); + if (string) + WebBindings::setException(object_var_, string->value().c_str()); +} + +// PPVarArrayFromNPVariantArray ------------------------------------------------ + +PPVarArrayFromNPVariantArray::PPVarArrayFromNPVariantArray( + PluginInstanceImpl* instance, + 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] = NPVariantToPPVar(instance, &variants[i]); + } +} + +PPVarArrayFromNPVariantArray::~PPVarArrayFromNPVariantArray() { + ::ppapi::VarTracker* var_tracker = PpapiGlobals::Get()->GetVarTracker(); + for (size_t i = 0; i < size_; i++) + var_tracker->ReleaseVar(array_[i]); +} + +// PPVarFromNPObject ----------------------------------------------------------- + +PPVarFromNPObject::PPVarFromNPObject(PluginInstanceImpl* instance, + NPObject* object) + : var_(NPObjectToPPVar(instance, object)) { +} + +PPVarFromNPObject::~PPVarFromNPObject() { + PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(var_); +} + +// NPObjectAccessorWithIdentifier ---------------------------------------------- + +NPObjectAccessorWithIdentifier::NPObjectAccessorWithIdentifier( + NPObject* object, + NPIdentifier identifier, + bool allow_integer_identifier) + : object_(PluginObject::FromNPObject(object)), + identifier_(PP_MakeUndefined()) { + if (object_) { + identifier_ = NPIdentifierToPPVar(identifier); + if (identifier_.type == PP_VARTYPE_INT32 && !allow_integer_identifier) + identifier_.type = PP_VARTYPE_UNDEFINED; // Mark it invalid. + } +} + +NPObjectAccessorWithIdentifier::~NPObjectAccessorWithIdentifier() { + PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(identifier_); +} + +// TryCatch -------------------------------------------------------------------- + +TryCatch::TryCatch(PP_Var* exception) + : has_exception_(exception && exception->type != PP_VARTYPE_UNDEFINED), + exception_(exception) { + WebBindings::pushExceptionHandler(&TryCatch::Catch, this); +} + +TryCatch::~TryCatch() { + WebBindings::popExceptionHandler(); +} + +void TryCatch::SetException(const char* message) { + if (!has_exception()) { + has_exception_ = true; + if (exception_) { + *exception_ = ::ppapi::StringVar::StringToPPVar(message, strlen(message)); + } + } +} + +// static +void TryCatch::Catch(void* self, const char* message) { + static_cast<TryCatch*>(self)->SetException(message); +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/npapi_glue.h b/content/renderer/pepper/npapi_glue.h new file mode 100644 index 0000000..5cc745b --- /dev/null +++ b/content/renderer/pepper/npapi_glue.h @@ -0,0 +1,265 @@ +// 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. + +#ifndef CONTENT_RENDERER_PEPPER_NPAPI_GLUE_H_ +#define CONTENT_RENDERER_PEPPER_NPAPI_GLUE_H_ + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "content/common/content_export.h" +#include "ppapi/c/pp_module.h" +#include "ppapi/c/pp_var.h" + +struct NPObject; +typedef struct _NPVariant NPVariant; +typedef void* NPIdentifier; + +namespace webkit { +namespace ppapi { + +class PluginInstanceImpl; +class PluginObject; + +// 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); + +// 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. +PP_Var NPVariantToPPVar(PluginInstanceImpl* instance, 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. +NPIdentifier PPVarToNPIdentifier(PP_Var var); + +// Returns a PP_Var corresponding to the given identifier. In the case of +// a string identifier, the returned string will have a reference count of 1. +PP_Var NPIdentifierToPPVar(NPIdentifier id); + +// 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 instance is necessary because we can have different instances 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. +// +// Note: this could easily be changed to take a PP_Instance instead if that +// makes certain calls in the future easier. Currently all callers have a +// PluginInstance so that's what we use here. +CONTENT_EXPORT PP_Var NPObjectToPPVar(PluginInstanceImpl* instance, + NPObject* object); + +// This version creates a default v8::Context rather than using the one from +// the container of |instance|. It is only for use in unit tests, where we don't +// have a real container for |instance|. +CONTENT_EXPORT PP_Var NPObjectToPPVarForTest(PluginInstanceImpl* instance, + NPObject* object); + +// 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. + // + // 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(NPObject* object_var, NPVariant* np_result); + + ~PPResultAndExceptionToNPResult(); + + // Returns true if an exception has been set. + bool has_exception() const { return exception_.type != PP_VARTYPE_UNDEFINED; } + + // 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 { + 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); + + // 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(); + + // Call this to ignore any exception. This prevents the DCHECK from failing + // in the destructor. + void IgnoreException(); + + private: + // Throws the current exception to JS. The exception must be set. + void ThrowException(); + + NPObject* 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); +}; + +// 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(PluginInstanceImpl* instance, + size_t size, + const NPVariant* variants); + ~PPVarArrayFromNPVariantArray(); + + PP_Var* array() { return array_.get(); } + + private: + size_t size_; + scoped_ptr<PP_Var[]> array_; + + DISALLOW_COPY_AND_ASSIGN(PPVarArrayFromNPVariantArray); +}; + +// PPVarFromNPObject ----------------------------------------------------------- + +// Converts an NPObject tp PP_Var, and scopes the ownership of the PP_Var. This +// is used when converting 'this' pointer from WebKit to the plugin. +class PPVarFromNPObject { + public: + PPVarFromNPObject(PluginInstanceImpl* instance, NPObject* object); + ~PPVarFromNPObject(); + + PP_Var var() const { return var_; } + + private: + const PP_Var var_; + + DISALLOW_COPY_AND_ASSIGN(PPVarFromNPObject); +}; + +// 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); + ~NPObjectAccessorWithIdentifier(); + + // Returns true if both the object and identifier are valid. + bool is_valid() const { + return object_ && identifier_.type != PP_VARTYPE_UNDEFINED; + } + + PluginObject* object() { return object_; } + PP_Var identifier() const { return identifier_; } + + private: + PluginObject* object_; + PP_Var identifier_; + + DISALLOW_COPY_AND_ASSIGN(NPObjectAccessorWithIdentifier); +}; + +// TryCatch -------------------------------------------------------------------- + +// Instantiate this object on the stack to catch V8 exceptions and pass them +// to an optional out parameter supplied by the plugin. +class TryCatch { + public: + // 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); + ~TryCatch(); + + // 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 has_exception() const { return has_exception_; } + + // Sets the given exception. If an exception has been previously set, this + // function will do nothing (normally you want only the first exception). + void SetException(const char* message); + + private: + static void Catch(void* self, const char* message); + + // 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_; +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_NPAPI_GLUE_H_ diff --git a/content/renderer/pepper/npobject_var.cc b/content/renderer/pepper/npobject_var.cc new file mode 100644 index 0000000..0ab1755 --- /dev/null +++ b/content/renderer/pepper/npobject_var.cc @@ -0,0 +1,59 @@ +// 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 "content/renderer/pepper/npobject_var.h" + +#include "base/logging.h" +#include "content/renderer/pepper/host_globals.h" +#include "content/renderer/pepper/host_var_tracker.h" +#include "ppapi/c/pp_var.h" +#include "third_party/WebKit/public/web/WebBindings.h" + +using webkit::ppapi::HostGlobals; +using WebKit::WebBindings; + +namespace ppapi { + +// NPObjectVar ----------------------------------------------------------------- + +NPObjectVar::NPObjectVar(PP_Instance instance, + NPObject* np_object) + : pp_instance_(instance), + np_object_(np_object) { + WebBindings::retainObject(np_object_); + HostGlobals::Get()->host_var_tracker()->AddNPObjectVar(this); +} + +NPObjectVar::~NPObjectVar() { + if (pp_instance()) + HostGlobals::Get()->host_var_tracker()->RemoveNPObjectVar(this); + WebBindings::releaseObject(np_object_); +} + +NPObjectVar* NPObjectVar::AsNPObjectVar() { + return this; +} + +PP_VarType NPObjectVar::GetType() const { + return PP_VARTYPE_OBJECT; +} + +void NPObjectVar::InstanceDeleted() { + DCHECK(pp_instance_); + HostGlobals::Get()->host_var_tracker()->RemoveNPObjectVar(this); + pp_instance_ = 0; +} + +// static +scoped_refptr<NPObjectVar> NPObjectVar::FromPPVar(PP_Var var) { + if (var.type != PP_VARTYPE_OBJECT) + return scoped_refptr<NPObjectVar>(NULL); + scoped_refptr<Var> var_object( + PpapiGlobals::Get()->GetVarTracker()->GetVar(var)); + if (!var_object.get()) + return scoped_refptr<NPObjectVar>(); + return scoped_refptr<NPObjectVar>(var_object->AsNPObjectVar()); +} + +} // namespace ppapi diff --git a/content/renderer/pepper/npobject_var.h b/content/renderer/pepper/npobject_var.h new file mode 100644 index 0000000..6cafbb4 --- /dev/null +++ b/content/renderer/pepper/npobject_var.h @@ -0,0 +1,71 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_PEPPER_NPOBJECT_VAR_H_ +#define CONTENT_RENDERER_PEPPER_NPOBJECT_VAR_H_ + +#include <string> + +#include "base/compiler_specific.h" +#include "ppapi/c/pp_instance.h" +#include "ppapi/shared_impl/var.h" +#include "content/common/content_export.h" + +typedef struct NPObject NPObject; +typedef struct _NPVariant NPVariant; +typedef void* NPIdentifier; + +namespace ppapi { + +// NPObjectVar ----------------------------------------------------------------- + +// 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 NPObjectVar'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. +class NPObjectVar : public Var { + public: + // You should always use FromNPObject to create an NPObjectVar. This function + // guarantees that we maintain the 1:1 mapping between NPObject and + // NPObjectVar. + NPObjectVar(PP_Instance instance, NPObject* np_object); + + // Var overrides. + virtual NPObjectVar* AsNPObjectVar() OVERRIDE; + virtual PP_VarType GetType() const OVERRIDE; + + // Returns the underlying NPObject corresponding to this NPObjectVar. + // Guaranteed non-NULL. + NPObject* np_object() const { return np_object_; } + + // Notification that the instance was deleted, the internal reference will be + // zeroed out. + void InstanceDeleted(); + + // Possibly 0 if the object has outlived its instance. + PP_Instance pp_instance() const { return pp_instance_; } + + // 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. + CONTENT_EXPORT static scoped_refptr<NPObjectVar> FromPPVar(PP_Var var); + + private: + virtual ~NPObjectVar(); + + // Possibly 0 if the object has outlived its instance. + PP_Instance pp_instance_; + + // Guaranteed non-NULL, this is the underlying object used by WebKit. We + // hold a reference to this object. + NPObject* np_object_; + + DISALLOW_COPY_AND_ASSIGN(NPObjectVar); +}; + +} // namespace + +#endif // CONTENT_RENDERER_PEPPER_NPOBJECT_VAR_H_ diff --git a/content/renderer/pepper/pepper_audio_input_host.cc b/content/renderer/pepper/pepper_audio_input_host.cc index 280d07e..6e74126 100644 --- a/content/renderer/pepper/pepper_audio_input_host.cc +++ b/content/renderer/pepper/pepper_audio_input_host.cc @@ -6,6 +6,7 @@ #include "base/logging.h" #include "build/build_config.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" #include "content/renderer/pepper/renderer_ppapi_host_impl.h" #include "ipc/ipc_message.h" #include "media/audio/shared_memory_util.h" @@ -17,7 +18,6 @@ #include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebElement.h" #include "third_party/WebKit/public/web/WebPluginContainer.h" -#include "webkit/plugins/ppapi/ppapi_plugin_instance_impl.h" namespace content { diff --git a/content/renderer/pepper/pepper_audio_input_host.h b/content/renderer/pepper/pepper_audio_input_host.h index 97f9013..8316ec8 100644 --- a/content/renderer/pepper/pepper_audio_input_host.h +++ b/content/renderer/pepper/pepper_audio_input_host.h @@ -13,11 +13,11 @@ #include "base/memory/shared_memory.h" #include "base/sync_socket.h" #include "content/renderer/pepper/pepper_device_enumeration_host_helper.h" +#include "content/renderer/pepper/plugin_delegate.h" #include "ipc/ipc_platform_file.h" #include "ppapi/c/ppb_audio_config.h" #include "ppapi/host/host_message_context.h" #include "ppapi/host/resource_host.h" -#include "webkit/plugins/ppapi/plugin_delegate.h" namespace content { diff --git a/content/renderer/pepper/pepper_broker_impl.cc b/content/renderer/pepper/pepper_broker_impl.cc index 842c56f..aca9f32 100644 --- a/content/renderer/pepper/pepper_broker_impl.cc +++ b/content/renderer/pepper/pepper_broker_impl.cc @@ -8,12 +8,12 @@ #include "content/public/renderer/renderer_restrict_dispatch_group.h" #include "content/renderer/pepper/pepper_plugin_delegate_impl.h" #include "content/renderer/pepper/pepper_proxy_channel_delegate_impl.h" +#include "content/renderer/pepper/plugin_module.h" +#include "content/renderer/pepper/ppb_broker_impl.h" #include "ipc/ipc_channel_handle.h" #include "ppapi/proxy/broker_dispatcher.h" #include "ppapi/proxy/ppapi_messages.h" #include "ppapi/shared_impl/platform_file.h" -#include "webkit/plugins/ppapi/plugin_module.h" -#include "webkit/plugins/ppapi/ppb_broker_impl.h" #if defined(OS_WIN) #include <windows.h> diff --git a/content/renderer/pepper/pepper_broker_impl.h b/content/renderer/pepper/pepper_broker_impl.h index 26f5619..ce81d96 100644 --- a/content/renderer/pepper/pepper_broker_impl.h +++ b/content/renderer/pepper/pepper_broker_impl.h @@ -8,9 +8,9 @@ #include "base/memory/ref_counted.h" #include "base/process/process.h" #include "content/common/content_export.h" +#include "content/renderer/pepper/plugin_delegate.h" +#include "content/renderer/pepper/ppb_broker_impl.h" #include "ppapi/proxy/proxy_channel.h" -#include "webkit/plugins/ppapi/plugin_delegate.h" -#include "webkit/plugins/ppapi/ppb_broker_impl.h" namespace IPC { struct ChannelHandle; diff --git a/content/renderer/pepper/pepper_device_enumeration_event_handler.h b/content/renderer/pepper/pepper_device_enumeration_event_handler.h index b390c80..543bcdb 100644 --- a/content/renderer/pepper/pepper_device_enumeration_event_handler.h +++ b/content/renderer/pepper/pepper_device_enumeration_event_handler.h @@ -10,7 +10,7 @@ #include "base/memory/weak_ptr.h" #include "content/renderer/media/media_stream_dispatcher_eventhandler.h" #include "content/renderer/pepper/pepper_plugin_delegate_impl.h" -#include "webkit/plugins/ppapi/plugin_delegate.h" +#include "content/renderer/pepper/plugin_delegate.h" namespace content { diff --git a/content/renderer/pepper/pepper_device_enumeration_host_helper.cc b/content/renderer/pepper/pepper_device_enumeration_host_helper.cc index fca8ac5..136c946 100644 --- a/content/renderer/pepper/pepper_device_enumeration_host_helper.cc +++ b/content/renderer/pepper/pepper_device_enumeration_host_helper.cc @@ -8,6 +8,7 @@ #include "base/logging.h" #include "base/memory/weak_ptr.h" #include "base/message_loop/message_loop.h" +#include "content/renderer/pepper/plugin_delegate.h" #include "ipc/ipc_message.h" #include "ppapi/c/pp_errors.h" #include "ppapi/host/dispatch_host_message.h" @@ -16,7 +17,6 @@ #include "ppapi/host/resource_host.h" #include "ppapi/proxy/ppapi_messages.h" #include "ppapi/shared_impl/ppb_device_ref_shared.h" -#include "webkit/plugins/ppapi/plugin_delegate.h" using ppapi::host::HostMessageContext; using webkit::ppapi::PluginDelegate; diff --git a/content/renderer/pepper/pepper_device_enumeration_host_helper_unittest.cc b/content/renderer/pepper/pepper_device_enumeration_host_helper_unittest.cc index c5616f5..f4e5714 100644 --- a/content/renderer/pepper/pepper_device_enumeration_host_helper_unittest.cc +++ b/content/renderer/pepper/pepper_device_enumeration_host_helper_unittest.cc @@ -7,6 +7,7 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" #include "base/logging.h" +#include "content/renderer/pepper/mock_plugin_delegate.h" #include "content/renderer/pepper/pepper_device_enumeration_host_helper.h" #include "ppapi/c/pp_errors.h" #include "ppapi/host/host_message_context.h" @@ -18,7 +19,6 @@ #include "ppapi/proxy/resource_message_test_sink.h" #include "ppapi/shared_impl/ppapi_permissions.h" #include "testing/gtest/include/gtest/gtest.h" -#include "webkit/plugins/ppapi/mock_plugin_delegate.h" namespace content { diff --git a/content/renderer/pepper/pepper_file_chooser_host.cc b/content/renderer/pepper/pepper_file_chooser_host.cc index 6f9b453..8eecb07 100644 --- a/content/renderer/pepper/pepper_file_chooser_host.cc +++ b/content/renderer/pepper/pepper_file_chooser_host.cc @@ -7,6 +7,7 @@ #include "base/files/file_path.h" #include "base/strings/utf_string_conversions.h" #include "content/public/renderer/renderer_ppapi_host.h" +#include "content/renderer/pepper/ppb_file_ref_impl.h" #include "content/renderer/render_view_impl.h" #include "ppapi/c/pp_errors.h" #include "ppapi/host/dispatch_host_message.h" @@ -18,7 +19,6 @@ #include "third_party/WebKit/public/platform/WebVector.h" #include "third_party/WebKit/public/web/WebFileChooserCompletion.h" #include "third_party/WebKit/public/web/WebFileChooserParams.h" -#include "webkit/plugins/ppapi/ppb_file_ref_impl.h" namespace content { diff --git a/content/renderer/pepper/pepper_file_io_host.cc b/content/renderer/pepper/pepper_file_io_host.cc index f5ef26e..96f0e82 100644 --- a/content/renderer/pepper/pepper_file_io_host.cc +++ b/content/renderer/pepper/pepper_file_io_host.cc @@ -11,6 +11,10 @@ #include "base/files/file_util_proxy.h" #include "content/public/common/content_client.h" #include "content/public/renderer/content_renderer_client.h" +#include "content/renderer/pepper/host_globals.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" +#include "content/renderer/pepper/ppb_file_ref_impl.h" +#include "content/renderer/pepper/quota_file_io.h" #include "ppapi/c/pp_errors.h" #include "ppapi/host/dispatch_host_message.h" #include "ppapi/host/ppapi_host.h" @@ -19,10 +23,6 @@ #include "ppapi/shared_impl/time_conversion.h" #include "ppapi/thunk/enter.h" #include "third_party/WebKit/public/web/WebPluginContainer.h" -#include "webkit/plugins/ppapi/host_globals.h" -#include "webkit/plugins/ppapi/ppapi_plugin_instance_impl.h" -#include "webkit/plugins/ppapi/ppb_file_ref_impl.h" -#include "webkit/plugins/ppapi/quota_file_io.h" namespace content { diff --git a/content/renderer/pepper/pepper_file_io_host.h b/content/renderer/pepper/pepper_file_io_host.h index 62e4356..89e70f7 100644 --- a/content/renderer/pepper/pepper_file_io_host.h +++ b/content/renderer/pepper/pepper_file_io_host.h @@ -11,11 +11,11 @@ #include "base/basictypes.h" #include "base/memory/weak_ptr.h" #include "content/public/renderer/renderer_ppapi_host.h" +#include "content/renderer/pepper/plugin_delegate.h" #include "ppapi/host/host_message_context.h" #include "ppapi/host/resource_host.h" #include "ppapi/shared_impl/file_io_state_manager.h" #include "ppapi/thunk/ppb_file_ref_api.h" -#include "webkit/plugins/ppapi/plugin_delegate.h" using ppapi::host::ReplyMessageContext; using webkit::ppapi::PluginDelegate; diff --git a/content/renderer/pepper/pepper_file_system_host.cc b/content/renderer/pepper/pepper_file_system_host.cc index d710a7a..3e60dfe 100644 --- a/content/renderer/pepper/pepper_file_system_host.cc +++ b/content/renderer/pepper/pepper_file_system_host.cc @@ -10,6 +10,7 @@ #include "content/child/fileapi/file_system_dispatcher.h" #include "content/public/renderer/render_view.h" #include "content/public/renderer/renderer_ppapi_host.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" #include "ppapi/c/pp_errors.h" #include "ppapi/host/dispatch_host_message.h" #include "ppapi/host/ppapi_host.h" @@ -21,7 +22,6 @@ #include "third_party/WebKit/public/web/WebPluginContainer.h" #include "third_party/WebKit/public/web/WebView.h" #include "webkit/common/fileapi/file_system_util.h" -#include "webkit/plugins/ppapi/ppapi_plugin_instance_impl.h" namespace content { diff --git a/content/renderer/pepper/pepper_graphics_2d_host.cc b/content/renderer/pepper/pepper_graphics_2d_host.cc index 528daca..ca4d524 100644 --- a/content/renderer/pepper/pepper_graphics_2d_host.cc +++ b/content/renderer/pepper/pepper_graphics_2d_host.cc @@ -9,6 +9,11 @@ #include "base/logging.h" #include "base/message_loop/message_loop.h" #include "content/public/renderer/renderer_ppapi_host.h" +#include "content/renderer/pepper/common.h" +#include "content/renderer/pepper/gfx_conversion.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" +#include "content/renderer/pepper/ppb_image_data_impl.h" +#include "content/renderer/pepper/resource_helper.h" #include "ppapi/c/pp_bool.h" #include "ppapi/c/pp_errors.h" #include "ppapi/c/pp_rect.h" @@ -28,11 +33,6 @@ #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" #include "ui/gfx/size_conversions.h" #include "ui/gfx/skia_util.h" -#include "webkit/plugins/ppapi/common.h" -#include "webkit/plugins/ppapi/gfx_conversion.h" -#include "webkit/plugins/ppapi/ppapi_plugin_instance_impl.h" -#include "webkit/plugins/ppapi/ppb_image_data_impl.h" -#include "webkit/plugins/ppapi/resource_helper.h" #if defined(OS_MACOSX) #include "base/mac/mac_util.h" diff --git a/content/renderer/pepper/pepper_graphics_2d_host.h b/content/renderer/pepper/pepper_graphics_2d_host.h index 9ce1fda..a582f2e 100644 --- a/content/renderer/pepper/pepper_graphics_2d_host.h +++ b/content/renderer/pepper/pepper_graphics_2d_host.h @@ -11,11 +11,11 @@ #include "base/compiler_specific.h" #include "base/memory/weak_ptr.h" #include "content/common/content_export.h" +#include "content/renderer/pepper/plugin_delegate.h" #include "ppapi/c/ppb_graphics_2d.h" #include "ppapi/host/host_message_context.h" #include "ppapi/host/resource_host.h" #include "third_party/WebKit/public/platform/WebCanvas.h" -#include "webkit/plugins/ppapi/plugin_delegate.h" namespace gfx { class Point; diff --git a/content/renderer/pepper/pepper_in_process_resource_creation.cc b/content/renderer/pepper/pepper_in_process_resource_creation.cc index 7cd2c70..f9713c4 100644 --- a/content/renderer/pepper/pepper_in_process_resource_creation.cc +++ b/content/renderer/pepper/pepper_in_process_resource_creation.cc @@ -9,6 +9,7 @@ #include "base/memory/weak_ptr.h" #include "base/message_loop/message_loop.h" #include "content/renderer/pepper/pepper_in_process_router.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" #include "content/renderer/pepper/renderer_ppapi_host_impl.h" #include "content/renderer/render_view_impl.h" #include "ipc/ipc_message.h" @@ -30,7 +31,6 @@ #include "ppapi/shared_impl/ppapi_permissions.h" #include "ppapi/shared_impl/resource_tracker.h" #include "ppapi/shared_impl/var.h" -#include "webkit/plugins/ppapi/ppapi_plugin_instance_impl.h" // Note that the code in the creation functions in this file should generally // be the same as that in ppapi/proxy/resource_creation_proxy.cc. See diff --git a/content/renderer/pepper/pepper_in_process_resource_creation.h b/content/renderer/pepper/pepper_in_process_resource_creation.h index 2f36aa9..72a5943 100644 --- a/content/renderer/pepper/pepper_in_process_resource_creation.h +++ b/content/renderer/pepper/pepper_in_process_resource_creation.h @@ -7,8 +7,8 @@ #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" +#include "content/renderer/pepper/resource_creation_impl.h" #include "ppapi/proxy/connection.h" -#include "webkit/plugins/ppapi/resource_creation_impl.h" namespace content { @@ -20,7 +20,7 @@ class RendererPpapiHostImpl; // (See pepper_in_process_router.h for more information.) // // This is a bit confusing. The "old-style" resources live in -// webkit/plugins/ppapi and are created by the ResourceCreationImpl in that +// content/renderer/pepper and are created by the ResourceCreationImpl in that // directory. The "new-style" IPC-only resources are in ppapi/proxy and are // created by the RessourceCreationProxy in that directory. // diff --git a/content/renderer/pepper/pepper_platform_audio_input_impl.h b/content/renderer/pepper/pepper_platform_audio_input_impl.h index 975d921..c5b3fb0 100644 --- a/content/renderer/pepper/pepper_platform_audio_input_impl.h +++ b/content/renderer/pepper/pepper_platform_audio_input_impl.h @@ -12,9 +12,9 @@ #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" +#include "content/renderer/pepper/plugin_delegate.h" #include "media/audio/audio_input_ipc.h" #include "media/audio/audio_parameters.h" -#include "webkit/plugins/ppapi/plugin_delegate.h" class GURL; diff --git a/content/renderer/pepper/pepper_platform_audio_output_impl.h b/content/renderer/pepper/pepper_platform_audio_output_impl.h index 4382bf2..d13aa62 100644 --- a/content/renderer/pepper/pepper_platform_audio_output_impl.h +++ b/content/renderer/pepper/pepper_platform_audio_output_impl.h @@ -8,8 +8,8 @@ #include "base/basictypes.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "content/renderer/pepper/plugin_delegate.h" #include "media/audio/audio_output_ipc.h" -#include "webkit/plugins/ppapi/plugin_delegate.h" namespace media { class AudioParameters; diff --git a/content/renderer/pepper/pepper_platform_context_3d_impl.h b/content/renderer/pepper/pepper_platform_context_3d_impl.h index 88496c2..c975453 100644 --- a/content/renderer/pepper/pepper_platform_context_3d_impl.h +++ b/content/renderer/pepper/pepper_platform_context_3d_impl.h @@ -10,8 +10,8 @@ #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h" +#include "content/renderer/pepper/plugin_delegate.h" #include "gpu/command_buffer/common/mailbox.h" -#include "webkit/plugins/ppapi/plugin_delegate.h" #ifdef ENABLE_GPU diff --git a/content/renderer/pepper/pepper_platform_image_2d_impl.h b/content/renderer/pepper/pepper_platform_image_2d_impl.h index 099a428..5ad75e1 100644 --- a/content/renderer/pepper/pepper_platform_image_2d_impl.h +++ b/content/renderer/pepper/pepper_platform_image_2d_impl.h @@ -6,7 +6,7 @@ #define CONTENT_RENDERER_PEPPER_PEPPER_PLATFORM_IMAGE_2D_IMPL_H_ #include "base/basictypes.h" -#include "webkit/plugins/ppapi/plugin_delegate.h" +#include "content/renderer/pepper/plugin_delegate.h" namespace content { diff --git a/content/renderer/pepper/pepper_platform_video_capture_impl.h b/content/renderer/pepper/pepper_platform_video_capture_impl.h index 4e9c13b..58101e53 100644 --- a/content/renderer/pepper/pepper_platform_video_capture_impl.h +++ b/content/renderer/pepper/pepper_platform_video_capture_impl.h @@ -11,9 +11,9 @@ #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" +#include "content/renderer/pepper/plugin_delegate.h" #include "media/video/capture/video_capture.h" #include "media/video/capture/video_capture_types.h" -#include "webkit/plugins/ppapi/plugin_delegate.h" class GURL; diff --git a/content/renderer/pepper/pepper_plugin_delegate_impl.cc b/content/renderer/pepper/pepper_plugin_delegate_impl.cc index 69dee4e..7beb399 100644 --- a/content/renderer/pepper/pepper_plugin_delegate_impl.cc +++ b/content/renderer/pepper/pepper_plugin_delegate_impl.cc @@ -57,7 +57,13 @@ #include "content/renderer/pepper/pepper_platform_video_capture_impl.h" #include "content/renderer/pepper/pepper_proxy_channel_delegate_impl.h" #include "content/renderer/pepper/pepper_url_loader_host.h" +#include "content/renderer/pepper/plugin_module.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" +#include "content/renderer/pepper/ppapi_webplugin_impl.h" +#include "content/renderer/pepper/ppb_tcp_server_socket_private_impl.h" +#include "content/renderer/pepper/ppb_tcp_socket_private_impl.h" #include "content/renderer/pepper/renderer_ppapi_host_impl.h" +#include "content/renderer/pepper/resource_helper.h" #include "content/renderer/pepper/url_response_info_util.h" #include "content/renderer/render_thread_impl.h" #include "content/renderer/render_view_impl.h" @@ -95,12 +101,6 @@ #include "third_party/WebKit/public/web/WebView.h" #include "ui/gfx/size.h" #include "url/gurl.h" -#include "webkit/plugins/ppapi/plugin_module.h" -#include "webkit/plugins/ppapi/ppapi_plugin_instance_impl.h" -#include "webkit/plugins/ppapi/ppapi_webplugin_impl.h" -#include "webkit/plugins/ppapi/ppb_tcp_server_socket_private_impl.h" -#include "webkit/plugins/ppapi/ppb_tcp_socket_private_impl.h" -#include "webkit/plugins/ppapi/resource_helper.h" using WebKit::WebView; using WebKit::WebFrame; @@ -413,7 +413,6 @@ PepperPluginDelegateImpl::CreatePepperPluginModule( // module's destructor will remove itself. module = new webkit::ppapi::PluginModule( info->name, path, - PepperPluginRegistry::GetInstance(), permissions); PepperPluginRegistry::GetInstance()->AddLiveModule(path, module.get()); diff --git a/content/renderer/pepper/pepper_plugin_delegate_impl.h b/content/renderer/pepper/pepper_plugin_delegate_impl.h index 2e311d5..5a0f977 100644 --- a/content/renderer/pepper/pepper_plugin_delegate_impl.h +++ b/content/renderer/pepper/pepper_plugin_delegate_impl.h @@ -19,12 +19,12 @@ #include "content/public/renderer/render_view_observer.h" #include "content/renderer/mouse_lock_dispatcher.h" #include "content/renderer/pepper/pepper_browser_connection.h" +#include "content/renderer/pepper/plugin_delegate.h" #include "content/renderer/render_view_pepper_helper.h" #include "ppapi/c/pp_file_info.h" #include "ppapi/shared_impl/private/ppb_tcp_server_socket_shared.h" #include "ppapi/shared_impl/private/tcp_socket_private_impl.h" #include "ui/base/ime/text_input_type.h" -#include "webkit/plugins/ppapi/plugin_delegate.h" namespace base { class FilePath; diff --git a/content/renderer/pepper/pepper_url_loader_host.cc b/content/renderer/pepper/pepper_url_loader_host.cc index 6863606..3087ebc 100644 --- a/content/renderer/pepper/pepper_url_loader_host.cc +++ b/content/renderer/pepper/pepper_url_loader_host.cc @@ -4,7 +4,9 @@ #include "content/renderer/pepper/pepper_url_loader_host.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" #include "content/renderer/pepper/renderer_ppapi_host_impl.h" +#include "content/renderer/pepper/url_request_info_util.h" #include "content/renderer/pepper/url_response_info_util.h" #include "net/base/net_errors.h" #include "ppapi/c/pp_errors.h" @@ -24,8 +26,6 @@ #include "third_party/WebKit/public/web/WebPluginContainer.h" #include "third_party/WebKit/public/web/WebSecurityOrigin.h" #include "third_party/WebKit/public/web/WebURLLoaderOptions.h" -#include "webkit/plugins/ppapi/ppapi_plugin_instance_impl.h" -#include "webkit/plugins/ppapi/url_request_info_util.h" using WebKit::WebFrame; using WebKit::WebString; diff --git a/content/renderer/pepper/pepper_url_request_unittest.cc b/content/renderer/pepper/pepper_url_request_unittest.cc index a242cee..08cae5a 100644 --- a/content/renderer/pepper/pepper_url_request_unittest.cc +++ b/content/renderer/pepper/pepper_url_request_unittest.cc @@ -4,6 +4,7 @@ #include "base/compiler_specific.h" #include "content/public/test/render_view_test.h" +#include "content/renderer/pepper/url_request_info_util.h" #include "ppapi/proxy/connection.h" #include "ppapi/proxy/url_request_info_resource.h" #include "ppapi/shared_impl/test_globals.h" @@ -16,7 +17,6 @@ #include "third_party/WebKit/public/web/WebView.h" #include "webkit/common/user_agent/user_agent.h" #include "webkit/common/user_agent/user_agent_util.h" -#include "webkit/plugins/ppapi/url_request_info_util.h" // This test is a end-to-end test from the resource to the WebKit request // object. The actual resource implementation is so simple, it makes sense to @@ -51,9 +51,6 @@ class TestWebFrameClient : public WebFrameClient { using ppapi::proxy::URLRequestInfoResource; using ppapi::URLRequestInfoData; -// TODO(brettw) move to content namespace when url_request_info_util.h is moved -// to this directory. This file used to be in webkit/plugins/ppapi and had to -// be moved in advance of the rest of the files to make things compile. namespace webkit { namespace ppapi { diff --git a/content/renderer/pepper/pepper_video_capture_host.cc b/content/renderer/pepper/pepper_video_capture_host.cc index 6c6b933..cef1e88 100644 --- a/content/renderer/pepper/pepper_video_capture_host.cc +++ b/content/renderer/pepper/pepper_video_capture_host.cc @@ -4,6 +4,8 @@ #include "content/renderer/pepper/pepper_video_capture_host.h" +#include "content/renderer/pepper/host_globals.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" #include "content/renderer/pepper/renderer_ppapi_host_impl.h" #include "ppapi/host/dispatch_host_message.h" #include "ppapi/host/ppapi_host.h" @@ -15,8 +17,6 @@ #include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebElement.h" #include "third_party/WebKit/public/web/WebPluginContainer.h" -#include "webkit/plugins/ppapi/host_globals.h" -#include "webkit/plugins/ppapi/ppapi_plugin_instance_impl.h" using ppapi::HostResource; using ppapi::TrackedCallback; diff --git a/content/renderer/pepper/pepper_video_capture_host.h b/content/renderer/pepper/pepper_video_capture_host.h index 9d0dd4e..9fdc299 100644 --- a/content/renderer/pepper/pepper_video_capture_host.h +++ b/content/renderer/pepper/pepper_video_capture_host.h @@ -9,13 +9,13 @@ #include "base/memory/ref_counted.h" #include "content/public/renderer/renderer_ppapi_host.h" #include "content/renderer/pepper/pepper_device_enumeration_host_helper.h" +#include "content/renderer/pepper/plugin_delegate.h" +#include "content/renderer/pepper/ppb_buffer_impl.h" #include "media/video/capture/video_capture.h" #include "media/video/capture/video_capture_types.h" #include "ppapi/c/dev/ppp_video_capture_dev.h" #include "ppapi/host/host_message_context.h" #include "ppapi/host/resource_host.h" -#include "webkit/plugins/ppapi/plugin_delegate.h" -#include "webkit/plugins/ppapi/ppb_buffer_impl.h" namespace content { class RendererPpapiHostImpl; diff --git a/content/renderer/pepper/pepper_video_destination_host.cc b/content/renderer/pepper/pepper_video_destination_host.cc index 67234d5..2ab0657 100644 --- a/content/renderer/pepper/pepper_video_destination_host.cc +++ b/content/renderer/pepper/pepper_video_destination_host.cc @@ -6,6 +6,7 @@ #include "base/time/time.h" #include "content/public/renderer/renderer_ppapi_host.h" +#include "content/renderer/pepper/ppb_image_data_impl.h" #include "ppapi/c/pp_errors.h" #include "ppapi/host/dispatch_host_message.h" #include "ppapi/host/host_message_context.h" @@ -13,7 +14,6 @@ #include "ppapi/proxy/ppapi_messages.h" #include "ppapi/thunk/enter.h" #include "ppapi/thunk/ppb_image_data_api.h" -#include "webkit/plugins/ppapi/ppb_image_data_impl.h" using ppapi::host::HostMessageContext; using ppapi::host::ReplyMessageContext; diff --git a/content/renderer/pepper/pepper_video_source_host.cc b/content/renderer/pepper/pepper_video_source_host.cc index 8183113..165c5c0 100644 --- a/content/renderer/pepper/pepper_video_source_host.cc +++ b/content/renderer/pepper/pepper_video_source_host.cc @@ -7,6 +7,7 @@ #include "base/bind.h" #include "base/safe_numerics.h" #include "content/public/renderer/renderer_ppapi_host.h" +#include "content/renderer/pepper/ppb_image_data_impl.h" #include "content/renderer/render_thread_impl.h" #include "ppapi/c/pp_errors.h" #include "ppapi/host/dispatch_host_message.h" @@ -19,7 +20,6 @@ #include "third_party/libjingle/source/talk/media/base/videocommon.h" #include "third_party/libjingle/source/talk/media/base/videoframe.h" #include "third_party/skia/include/core/SkBitmap.h" -#include "webkit/plugins/ppapi/ppb_image_data_impl.h" using ppapi::host::HostMessageContext; using ppapi::host::ReplyMessageContext; diff --git a/content/renderer/pepper/plugin_delegate.h b/content/renderer/pepper/plugin_delegate.h new file mode 100644 index 0000000..e5f2118 --- /dev/null +++ b/content/renderer/pepper/plugin_delegate.h @@ -0,0 +1,692 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_PEPPER_PLUGIN_DELEGATE_H_ +#define CONTENT_RENDERER_PEPPER_PLUGIN_DELEGATE_H_ + +#include <string> +#include <vector> + +#include "base/callback.h" +#include "base/memory/ref_counted.h" +#include "base/memory/shared_memory.h" +#include "base/message_loop/message_loop_proxy.h" +#include "base/platform_file.h" +#include "base/process.h" +#include "base/sync_socket.h" +#include "base/time/time.h" +#include "content/common/content_export.h" +#include "ipc/ipc_platform_file.h" +#include "media/video/capture/video_capture.h" +#include "media/video/video_decode_accelerator.h" +#include "ppapi/c/dev/pp_video_dev.h" +#include "ppapi/c/dev/ppb_device_ref_dev.h" +#include "ppapi/c/pp_completion_callback.h" +#include "ppapi/c/pp_errors.h" +#include "ppapi/c/pp_file_info.h" +#include "ppapi/c/pp_instance.h" +#include "ppapi/c/pp_resource.h" +#include "ppapi/c/pp_stdint.h" +#include "ppapi/c/ppb_tcp_socket.h" +#include "ppapi/c/private/ppb_flash.h" +#include "ppapi/c/private/ppb_tcp_socket_private.h" +#include "ppapi/c/private/ppb_udp_socket_private.h" +#include "ppapi/shared_impl/dir_contents.h" +#include "ui/gfx/size.h" +#include "url/gurl.h" +#include "webkit/common/fileapi/file_system_types.h" +#include "webkit/common/quota/quota_types.h" + +class GURL; +class SkBitmap; +class SkCanvas; +class TransportDIB; +struct PP_NetAddress_Private; + +namespace IPC { +struct ChannelHandle; +} + +namespace WebKit { +class WebGraphicsContext3D; +} + +namespace base { +class MessageLoopProxy; +class Time; +} + +namespace content { +class RendererPpapiHost; +} + +namespace fileapi { +struct DirectoryEntry; +} + +namespace gfx { +class Point; +} + +namespace gpu { +class CommandBuffer; +struct Mailbox; +} + +namespace ppapi { +class PepperFilePath; +class PpapiPermissions; +class PPB_X509Certificate_Fields; +class SocketOptionData; +struct DeviceRefData; +struct HostPortPair; +struct Preferences; + +namespace thunk { +class ResourceCreationAPI; +} + +} // namespace ppapi + +namespace WebKit { +typedef SkCanvas WebCanvas; +class WebGamepads; +class WebPlugin; +struct WebCompositionUnderline; +struct WebCursorInfo; +struct WebURLError; +class WebURLLoaderClient; +class WebURLResponse; +} + +namespace webkit_glue { +class P2PTransport; +class NetworkListObserver; +} // namespace webkit_glue + +namespace webkit { + +namespace ppapi { + +class FileIO; +class FullscreenContainer; +class PluginInstanceImpl; +class PluginModule; +class PPB_Broker_Impl; +class PPB_Flash_Menu_Impl; +class PPB_ImageData_Impl; +class PPB_TCPSocket_Private_Impl; + +// Virtual interface that the browser implements to implement features for +// PPAPI plugins. +class PluginDelegate { + public: + // This class is implemented by the PluginDelegate implementation and is + // designed to manage the lifetime and communication with the proxy's + // HostDispatcher for out-of-process PPAPI plugins. + // + // The point of this is to avoid having a relationship from the PPAPI plugin + // implementation to the ppapi proxy code. Otherwise, things like the IPC + // system will be dependencies of the webkit directory, which we don't want. + // + // The PluginModule will scope the lifetime of this object to its own + // lifetime, so the implementation can use this to manage the HostDispatcher + // lifetime without introducing the dependency. + class OutOfProcessProxy { + public: + virtual ~OutOfProcessProxy() {} + + // Implements GetInterface for the proxied plugin. + virtual const void* GetProxiedInterface(const char* name) = 0; + + // Notification to the out-of-process layer that the given plugin instance + // has been created. This will happen before the normal PPB_Instance method + // calls so the out-of-process code can set up the tracking information for + // the new instance. + virtual void AddInstance(PP_Instance instance) = 0; + + // Like AddInstance but removes the given instance. This is called after + // regular instance shutdown so the out-of-process code can clean up its + // tracking information. + virtual void RemoveInstance(PP_Instance instance) = 0; + + virtual base::ProcessId GetPeerProcessId() = 0; + virtual int GetPluginChildId() = 0; + }; + + // Represents an image. This is to allow the browser layer to supply a correct + // image representation. In Chrome, this will be a TransportDIB. + class PlatformImage2D { + public: + virtual ~PlatformImage2D() {} + + // Caller will own the returned pointer, returns NULL on failure. + virtual SkCanvas* Map() = 0; + + // Returns the platform-specific shared memory handle of the data backing + // this image. This is used by PPAPI proxying to send the image to the + // out-of-process plugin. On success, the size in bytes will be placed into + // |*bytes_count|. Returns 0 on failure. + virtual intptr_t GetSharedMemoryHandle(uint32* byte_count) const = 0; + + virtual TransportDIB* GetTransportDIB() const = 0; + }; + + class CONTENT_EXPORT PlatformGraphics2D { + public: + virtual ~PlatformGraphics2D() {} + + virtual bool ReadImageData(PP_Resource image, const PP_Point* top_left) = 0; + + // Assciates this device with the given plugin instance. You can pass NULL + // to clear the existing device. Returns true on success. In this case, a + // repaint of the page will also be scheduled. Failure means that the device + // is already bound to a different instance, and nothing will happen. + virtual bool BindToInstance(PluginInstanceImpl* new_instance) = 0; + + // Paints the current backing store to the web page. + virtual void Paint(WebKit::WebCanvas* canvas, + const gfx::Rect& plugin_rect, + const gfx::Rect& paint_rect) = 0; + + // Notifications about the view's progress painting. See PluginInstance. + // These messages are used to send Flush callbacks to the plugin. + virtual void ViewWillInitiatePaint() = 0; + virtual void ViewInitiatedPaint() = 0; + virtual void ViewFlushedPaint() = 0; + + virtual bool IsAlwaysOpaque() const = 0; + virtual void SetScale(float scale) = 0; + virtual float GetScale() const = 0; + virtual PPB_ImageData_Impl* ImageData() = 0; + }; + + class PlatformContext3D { + public: + virtual ~PlatformContext3D() {} + + // Initialize the context. + virtual bool Init(const int32* attrib_list, + PlatformContext3D* share_context) = 0; + + // Retrieves the mailbox name for the front buffer backing the context. + virtual void GetBackingMailbox(::gpu::Mailbox* mailbox) = 0; + + // Returns true if the backing texture is always opaque. + virtual bool IsOpaque() = 0; + + // This call will return the address of the command buffer for this context + // that is constructed in Initialize() and is valid until this context is + // destroyed. + virtual ::gpu::CommandBuffer* GetCommandBuffer() = 0; + + // If the command buffer is routed in the GPU channel, return the route id. + // Otherwise return 0. + virtual int GetCommandBufferRouteId() = 0; + + // Set an optional callback that will be invoked when the context is lost + // (e.g. gpu process crash). Takes ownership of the callback. + virtual void SetContextLostCallback( + const base::Callback<void()>& callback) = 0; + + // Set an optional callback that will be invoked when the GPU process + // sends a console message. + typedef base::Callback<void(const std::string&, int)> + ConsoleMessageCallback; + virtual void SetOnConsoleMessageCallback( + const ConsoleMessageCallback& callback) = 0; + + // Run the callback once the channel has been flushed. + virtual bool Echo(const base::Callback<void()>& callback) = 0; + }; + + // The base class of clients used by |PlatformAudioOutput| and + // |PlatformAudioInput|. + class PlatformAudioClientBase { + protected: + virtual ~PlatformAudioClientBase() {} + + public: + // Called when the stream is created. + virtual void StreamCreated(base::SharedMemoryHandle shared_memory_handle, + size_t shared_memory_size, + base::SyncSocket::Handle socket) = 0; + }; + + class PlatformAudioOutputClient : public PlatformAudioClientBase { + protected: + virtual ~PlatformAudioOutputClient() {} + }; + + class PlatformAudioOutput { + public: + // Starts the playback. Returns false on error or if called before the + // stream is created or after the stream is closed. + virtual bool StartPlayback() = 0; + + // Stops the playback. Returns false on error or if called before the stream + // is created or after the stream is closed. + virtual bool StopPlayback() = 0; + + // Closes the stream. Make sure to call this before the object is + // destructed. + virtual void ShutDown() = 0; + + protected: + virtual ~PlatformAudioOutput() {} + }; + + class PlatformAudioInputClient : public PlatformAudioClientBase { + public: + virtual void StreamCreationFailed() = 0; + + protected: + virtual ~PlatformAudioInputClient() {} + }; + + class PlatformAudioInput { + public: + virtual void StartCapture() = 0; + virtual void StopCapture() = 0; + + // Closes the stream. Make sure to call this before the object is + // destructed. + virtual void ShutDown() = 0; + + protected: + virtual ~PlatformAudioInput() {} + }; + + // Interface for PlatformVideoDecoder is directly inherited from general media + // VideoDecodeAccelerator interface. + class PlatformVideoDecoder : public media::VideoDecodeAccelerator { + public: + virtual ~PlatformVideoDecoder() {} + }; + + class PlatformVideoCaptureEventHandler + : public media::VideoCapture::EventHandler { + public: + virtual ~PlatformVideoCaptureEventHandler() {} + + virtual void OnInitialized(media::VideoCapture* capture, + bool succeeded) = 0; + }; + + class PlatformVideoCapture : public media::VideoCapture, + public base::RefCounted<PlatformVideoCapture> { + public: + // Detaches the event handler and stops sending notifications to it. + virtual void DetachEventHandler() = 0; + + protected: + virtual ~PlatformVideoCapture() {} + + private: + friend class base::RefCounted<PlatformVideoCapture>; + }; + + // Provides access to the ppapi broker. + class Broker { + public: + // Decrements the references to the broker. + // When there are no more references, this renderer's dispatcher is + // destroyed, allowing the broker to shutdown if appropriate. + // Callers should not reference this object after calling Disconnect(). + virtual void Disconnect(webkit::ppapi::PPB_Broker_Impl* client) = 0; + + protected: + virtual ~Broker() {} + }; + + // Notification that the given plugin is focused or unfocused. + virtual void PluginFocusChanged(webkit::ppapi::PluginInstanceImpl* instance, + bool focused) = 0; + // Notification that the text input status of the given plugin is changed. + virtual void PluginTextInputTypeChanged( + webkit::ppapi::PluginInstanceImpl* instance) = 0; + // Notification that the caret position in the given plugin is changed. + virtual void PluginCaretPositionChanged( + webkit::ppapi::PluginInstanceImpl* instance) = 0; + // Notification that the plugin requested to cancel the current composition. + virtual void PluginRequestedCancelComposition( + webkit::ppapi::PluginInstanceImpl* instance) = 0; + // Notification that the text selection in the given plugin is changed. + virtual void PluginSelectionChanged( + webkit::ppapi::PluginInstanceImpl* instance) = 0; + // Requests simulating IME events for testing purpose. + virtual void SimulateImeSetComposition( + const base::string16& text, + const std::vector<WebKit::WebCompositionUnderline>& underlines, + int selection_start, + int selection_end) = 0; + virtual void SimulateImeConfirmComposition(const base::string16& text) = 0; + + // Notification that the given plugin has crashed. When a plugin crashes, all + // instances associated with that plugin will notify that they've crashed via + // this function. + virtual void PluginCrashed(PluginInstanceImpl* instance) = 0; + + // Indicates that the given instance has been created. + virtual void InstanceCreated(PluginInstanceImpl* instance) = 0; + + // Indicates that the given instance is being destroyed. This is called from + // the destructor, so it's important that the instance is not dereferenced + // from this call. + virtual void InstanceDeleted(PluginInstanceImpl* instance) = 0; + + // Creates the resource creation API for the given instance. + virtual scoped_ptr< ::ppapi::thunk::ResourceCreationAPI> + CreateResourceCreationAPI(PluginInstanceImpl* instance) = 0; + + // Returns a pointer (ownership not transferred) to the bitmap to paint the + // sad plugin screen with. Returns NULL on failure. + virtual SkBitmap* GetSadPluginBitmap() = 0; + + // Creates a replacement plug-in that is shown when the plug-in at |file_path| + // couldn't be loaded. + virtual WebKit::WebPlugin* CreatePluginReplacement( + const base::FilePath& file_path) = 0; + + // The caller will own the pointer returned from this. + virtual PlatformImage2D* CreateImage2D(int width, int height) = 0; + + // Returns the internal PlatformGraphics2D implementation. + virtual PlatformGraphics2D* GetGraphics2D(PluginInstanceImpl* instance, + PP_Resource graphics_2d) = 0; + + // The caller will own the pointer returned from this. + virtual PlatformContext3D* CreateContext3D() = 0; + + // If |device_id| is empty, the default video capture device will be used. The + // user can start using the returned object to capture video right away. + // Otherwise, the specified device will be used. The user needs to wait till + // |handler| gets an OnInitialized() notification to start using the returned + // object. + virtual PlatformVideoCapture* CreateVideoCapture( + const std::string& device_id, + const GURL& document_url, + PlatformVideoCaptureEventHandler* handler) = 0; + + // The caller will own the pointer returned from this. + virtual PlatformVideoDecoder* CreateVideoDecoder( + media::VideoDecodeAccelerator::Client* client, + int32 command_buffer_route_id) = 0; + + // Get audio hardware output sample rate. + virtual uint32_t GetAudioHardwareOutputSampleRate() = 0; + + // Get audio hardware output buffer size. + virtual uint32_t GetAudioHardwareOutputBufferSize() = 0; + + // The caller is responsible for calling Shutdown() on the returned pointer + // to clean up the corresponding resources allocated during this call. + virtual PlatformAudioOutput* CreateAudioOutput( + uint32_t sample_rate, + uint32_t sample_count, + PlatformAudioOutputClient* client) = 0; + + // If |device_id| is empty, the default audio input device will be used. + // The caller is responsible for calling Shutdown() on the returned pointer + // to clean up the corresponding resources allocated during this call. + virtual PlatformAudioInput* CreateAudioInput( + const std::string& device_id, + const GURL& document_url, + uint32_t sample_rate, + uint32_t sample_count, + PlatformAudioInputClient* client) = 0; + + // A pointer is returned immediately, but it is not ready to be used until + // BrokerConnected has been called. + // The caller is responsible for calling Disconnect() on the returned pointer + // to clean up the corresponding resources allocated during this call. + virtual Broker* ConnectToBroker(webkit::ppapi::PPB_Broker_Impl* client) = 0; + + // Notifies that the number of find results has changed. + virtual void NumberOfFindResultsChanged(int identifier, + int total, + bool final_result) = 0; + + // Notifies that the index of the currently selected item has been updated. + virtual void SelectedFindResultChanged(int identifier, int index) = 0; + + // Sends an async IPC to open a local file. + typedef base::Callback<void (base::PlatformFileError, base::PassPlatformFile)> + AsyncOpenFileCallback; + virtual bool AsyncOpenFile(const base::FilePath& path, + int flags, + const AsyncOpenFileCallback& callback) = 0; + + // These functions expose some of PepperFileSystemHost methods for + // PPB_FileRef_Impl (which is in webkit) to access. Once we migrate FileRef + // to the new design in content/, we won't need this delegation. + // TODO(victorhsieh): remove these delegation. + virtual bool IsFileSystemOpened(PP_Instance instance, + PP_Resource resource) const = 0; + virtual PP_FileSystemType GetFileSystemType(PP_Instance instance, + PP_Resource resource) const = 0; + virtual GURL GetFileSystemRootUrl(PP_Instance instance, + PP_Resource resource) const = 0; + + // Sends an async IPC to open a file through filesystem API. + // When a file is successfully opened, |callback| is invoked with + // PLATFORM_FILE_OK, the opened file handle, and a callback function for + // notifying that the file is closed. When the users of this function + // finished using the file, they must close the file handle and then must call + // the supplied callback function. + typedef base::Callback<void (base::PlatformFileError)> + NotifyCloseFileCallback; + typedef base::Callback< + void (base::PlatformFileError error, + base::PassPlatformFile file, + quota::QuotaLimitType quota_policy, + const NotifyCloseFileCallback& close_file_callback)> + AsyncOpenFileSystemURLCallback; + virtual void AsyncOpenFileSystemURL( + const GURL& path, + int flags, + const AsyncOpenFileSystemURLCallback& callback) = 0; + + // Callback typedefs for FileSystem related methods. + typedef base::Callback<void (base::PlatformFileError)> StatusCallback; + typedef base::Callback<void( + const std::vector<fileapi::DirectoryEntry>& entries, + bool has_more)> ReadDirectoryCallback; + typedef base::Callback<void( + const base::PlatformFileInfo& file_info)> MetadataCallback; + + virtual void MakeDirectory( + const GURL& path, + bool recursive, + const StatusCallback& callback) = 0; + virtual void Query(const GURL& path, + const MetadataCallback& success_callback, + const StatusCallback& error_callback) = 0; + virtual void ReadDirectoryEntries( + const GURL& path, + const ReadDirectoryCallback& success_callback, + const StatusCallback& error_callback) = 0; + virtual void Touch(const GURL& path, + const base::Time& last_access_time, + const base::Time& last_modified_time, + const StatusCallback& callback) = 0; + virtual void SetLength(const GURL& path, + int64_t length, + const StatusCallback& callback) = 0; + virtual void Delete(const GURL& path, + const StatusCallback& callback) = 0; + virtual void Rename(const GURL& file_path, + const GURL& new_file_path, + const StatusCallback& callback) = 0; + virtual void ReadDirectory( + const GURL& directory_path, + const ReadDirectoryCallback& success_callback, + const StatusCallback& error_callback) = 0; + + // For quota handlings for FileIO API. + typedef base::Callback<void (int64)> AvailableSpaceCallback; + virtual void QueryAvailableSpace(const GURL& origin, + quota::StorageType type, + const AvailableSpaceCallback& callback) = 0; + virtual void WillUpdateFile(const GURL& file_path) = 0; + virtual void DidUpdateFile(const GURL& file_path, int64_t delta) = 0; + + // Synchronously returns the platform file path for a filesystem URL. + virtual void SyncGetFileSystemPlatformPath(const GURL& url, + base::FilePath* platform_path) = 0; + + // Returns a MessageLoopProxy instance associated with the message loop + // of the file thread in this renderer. + virtual scoped_refptr<base::MessageLoopProxy> + GetFileThreadMessageLoopProxy() = 0; + + // For PPB_TCPSocket_Private. + virtual uint32 TCPSocketCreate() = 0; + virtual void TCPSocketConnect(PPB_TCPSocket_Private_Impl* socket, + uint32 socket_id, + const std::string& host, + uint16_t port) = 0; + virtual void TCPSocketConnectWithNetAddress( + PPB_TCPSocket_Private_Impl* socket, + uint32 socket_id, + const PP_NetAddress_Private& addr) = 0; + virtual void TCPSocketSSLHandshake( + uint32 socket_id, + const std::string& server_name, + uint16_t server_port, + const std::vector<std::vector<char> >& trusted_certs, + const std::vector<std::vector<char> >& untrusted_certs) = 0; + virtual void TCPSocketRead(uint32 socket_id, int32_t bytes_to_read) = 0; + virtual void TCPSocketWrite(uint32 socket_id, const std::string& buffer) = 0; + virtual void TCPSocketDisconnect(uint32 socket_id) = 0; + virtual void TCPSocketSetOption(uint32 socket_id, + PP_TCPSocket_Option name, + const ::ppapi::SocketOptionData& value) = 0; + virtual void RegisterTCPSocket(PPB_TCPSocket_Private_Impl* socket, + uint32 socket_id) = 0; + + // For PPB_TCPServerSocket_Private. + virtual void TCPServerSocketListen(PP_Resource socket_resource, + const PP_NetAddress_Private& addr, + int32_t backlog) = 0; + virtual void TCPServerSocketAccept(uint32 server_socket_id) = 0; + virtual void TCPServerSocketStopListening( + PP_Resource socket_resource, + uint32 socket_id) = 0; + + // Add/remove a network list observer. + virtual bool AddNetworkListObserver( + webkit_glue::NetworkListObserver* observer) = 0; + virtual void RemoveNetworkListObserver( + webkit_glue::NetworkListObserver* observer) = 0; + + // For PPB_X509Certificate_Private. + virtual bool X509CertificateParseDER( + const std::vector<char>& der, + ::ppapi::PPB_X509Certificate_Fields* fields) = 0; + + // Create a fullscreen container for a plugin instance. This effectively + // switches the plugin to fullscreen. + virtual FullscreenContainer* CreateFullscreenContainer( + PluginInstanceImpl* instance) = 0; + + // Gets the size of the screen. The fullscreen window will be created at that + // size. + virtual gfx::Size GetScreenSize() = 0; + + // Returns a string with the name of the default 8-bit char encoding. + virtual std::string GetDefaultEncoding() = 0; + + // Sets the minimum and maximum zoom factors. + virtual void ZoomLimitsChanged(double minimum_factor, + double maximum_factor) = 0; + + // Create an anonymous shared memory segment of size |size| bytes, and return + // a pointer to it, or NULL on error. Caller owns the returned pointer. + virtual base::SharedMemory* CreateAnonymousSharedMemory(size_t size) = 0; + + // Returns the current preferences. + virtual ::ppapi::Preferences GetPreferences() = 0; + + // Locks the mouse for |instance|. If false is returned, the lock is not + // possible. If true is returned then the lock is pending. Success or + // failure will be delivered asynchronously via + // PluginInstance::OnLockMouseACK(). + virtual bool LockMouse(PluginInstanceImpl* instance) = 0; + + // Unlocks the mouse if |instance| currently owns the mouse lock. Whenever an + // plugin instance has lost the mouse lock, it will be notified by + // PluginInstance::OnMouseLockLost(). Please note that UnlockMouse() is not + // the only cause of losing mouse lock. For example, a user may press the Esc + // key to quit the mouse lock mode, which also results in an OnMouseLockLost() + // call to the current mouse lock owner. + virtual void UnlockMouse(PluginInstanceImpl* instance) = 0; + + // Returns true iff |instance| currently owns the mouse lock. + virtual bool IsMouseLocked(PluginInstanceImpl* instance) = 0; + + // Notifies that |instance| has changed the cursor. + // This will update the cursor appearance if it is currently over the plugin + // instance. + virtual void DidChangeCursor(PluginInstanceImpl* instance, + const WebKit::WebCursorInfo& cursor) = 0; + + // Notifies that |instance| has received a mouse event. + virtual void DidReceiveMouseEvent(PluginInstanceImpl* instance) = 0; + + // Determines if the browser entered fullscreen mode. + virtual bool IsInFullscreenMode() = 0; + + // Retrieve current gamepad data. + virtual void SampleGamepads(WebKit::WebGamepads* data) = 0; + + // Returns true if the containing page is visible. + virtual bool IsPageVisible() const = 0; + + typedef base::Callback< + void (int /* request_id */, + bool /* succeeded */, + const std::vector< ::ppapi::DeviceRefData>& /* devices */)> + EnumerateDevicesCallback; + + // Enumerates devices of the specified type. The request ID passed into the + // callback will be the same as the return value. + virtual int EnumerateDevices(PP_DeviceType_Dev type, + const EnumerateDevicesCallback& callback) = 0; + // Stop enumerating devices of the specified |request_id|. The |request_id| + // is the return value of EnumerateDevicesCallback. + virtual void StopEnumerateDevices(int request_id) = 0; + + // Share a given handle with the target process. + virtual IPC::PlatformFileForTransit ShareHandleWithRemote( + base::PlatformFile handle, + base::ProcessId target_process_id, + bool should_close_source) const = 0; + + // Returns true if running in process. + virtual bool IsRunningInProcess(PP_Instance instance) const = 0; + + // Notifies the plugin of the document load. This should initiate the call to + // PPP_Instance.HandleDocumentLoad. + // + // The loader object should set itself on the PluginInstance as the document + // loader using set_document_loader. + virtual void HandleDocumentLoad(PluginInstanceImpl* instance, + const WebKit::WebURLResponse& response) = 0; + + // Sets up the renderer host and out-of-process proxy for an external plugin + // module. Returns the renderer host, or NULL if it couldn't be created. + virtual content::RendererPpapiHost* CreateExternalPluginModule( + scoped_refptr<PluginModule> module, + const base::FilePath& path, + ::ppapi::PpapiPermissions permissions, + const IPC::ChannelHandle& channel_handle, + base::ProcessId plugin_pid, + int plugin_child_id) = 0; +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_PLUGIN_DELEGATE_H_ diff --git a/content/renderer/pepper/plugin_module.cc b/content/renderer/pepper/plugin_module.cc new file mode 100644 index 0000000..820ba56 --- /dev/null +++ b/content/renderer/pepper/plugin_module.cc @@ -0,0 +1,634 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/plugin_module.h" + +#include <set> + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" +#include "base/message_loop/message_loop_proxy.h" +#include "base/time/time.h" +#include "content/common/pepper_plugin_registry.h" +#include "content/renderer/pepper/common.h" +#include "content/renderer/pepper/host_globals.h" +#include "content/renderer/pepper/ppapi_interface_factory.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" +#include "content/renderer/pepper/ppb_gpu_blacklist_private_impl.h" +#include "content/renderer/pepper/ppb_image_data_impl.h" +#include "content/renderer/pepper/ppb_proxy_impl.h" +#include "content/renderer/pepper/ppb_scrollbar_impl.h" +#include "content/renderer/pepper/ppb_uma_private_impl.h" +#include "content/renderer/pepper/ppb_var_deprecated_impl.h" +#include "content/renderer/pepper/ppb_video_decoder_impl.h" +#include "ppapi/c/dev/ppb_audio_input_dev.h" +#include "ppapi/c/dev/ppb_buffer_dev.h" +#include "ppapi/c/dev/ppb_char_set_dev.h" +#include "ppapi/c/dev/ppb_crypto_dev.h" +#include "ppapi/c/dev/ppb_cursor_control_dev.h" +#include "ppapi/c/dev/ppb_device_ref_dev.h" +#include "ppapi/c/dev/ppb_file_chooser_dev.h" +#include "ppapi/c/dev/ppb_find_dev.h" +#include "ppapi/c/dev/ppb_font_dev.h" +#include "ppapi/c/dev/ppb_gles_chromium_texture_mapping_dev.h" +#include "ppapi/c/dev/ppb_graphics_2d_dev.h" +#include "ppapi/c/dev/ppb_memory_dev.h" +#include "ppapi/c/dev/ppb_opengles2ext_dev.h" +#include "ppapi/c/dev/ppb_printing_dev.h" +#include "ppapi/c/dev/ppb_resource_array_dev.h" +#include "ppapi/c/dev/ppb_scrollbar_dev.h" +#include "ppapi/c/dev/ppb_testing_dev.h" +#include "ppapi/c/dev/ppb_text_input_dev.h" +#include "ppapi/c/dev/ppb_trace_event_dev.h" +#include "ppapi/c/dev/ppb_truetype_font_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_capture_dev.h" +#include "ppapi/c/dev/ppb_video_decoder_dev.h" +#include "ppapi/c/dev/ppb_view_dev.h" +#include "ppapi/c/dev/ppb_widget_dev.h" +#include "ppapi/c/dev/ppb_zoom_dev.h" +#include "ppapi/c/extensions/dev/ppb_ext_alarms_dev.h" +#include "ppapi/c/extensions/dev/ppb_ext_socket_dev.h" +#include "ppapi/c/pp_module.h" +#include "ppapi/c/pp_resource.h" +#include "ppapi/c/pp_var.h" +#include "ppapi/c/ppb_audio.h" +#include "ppapi/c/ppb_audio_config.h" +#include "ppapi/c/ppb_console.h" +#include "ppapi/c/ppb_core.h" +#include "ppapi/c/ppb_file_io.h" +#include "ppapi/c/ppb_file_ref.h" +#include "ppapi/c/ppb_file_system.h" +#include "ppapi/c/ppb_fullscreen.h" +#include "ppapi/c/ppb_graphics_2d.h" +#include "ppapi/c/ppb_graphics_3d.h" +#include "ppapi/c/ppb_host_resolver.h" +#include "ppapi/c/ppb_image_data.h" +#include "ppapi/c/ppb_instance.h" +#include "ppapi/c/ppb_messaging.h" +#include "ppapi/c/ppb_mouse_cursor.h" +#include "ppapi/c/ppb_mouse_lock.h" +#include "ppapi/c/ppb_net_address.h" +#include "ppapi/c/ppb_network_proxy.h" +#include "ppapi/c/ppb_opengles2.h" +#include "ppapi/c/ppb_tcp_socket.h" +#include "ppapi/c/ppb_udp_socket.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/ppb_var_array.h" +#include "ppapi/c/ppb_var_array_buffer.h" +#include "ppapi/c/ppb_var_dictionary.h" +#include "ppapi/c/ppb_view.h" +#include "ppapi/c/ppp.h" +#include "ppapi/c/ppp_instance.h" +#include "ppapi/c/private/ppb_ext_crx_file_system_private.h" +#include "ppapi/c/private/ppb_file_io_private.h" +#include "ppapi/c/private/ppb_file_ref_private.h" +#include "ppapi/c/private/ppb_flash.h" +#include "ppapi/c/private/ppb_flash_clipboard.h" +#include "ppapi/c/private/ppb_flash_device_id.h" +#include "ppapi/c/private/ppb_flash_drm.h" +#include "ppapi/c/private/ppb_flash_file.h" +#include "ppapi/c/private/ppb_flash_font_file.h" +#include "ppapi/c/private/ppb_flash_fullscreen.h" +#include "ppapi/c/private/ppb_flash_menu.h" +#include "ppapi/c/private/ppb_flash_message_loop.h" +#include "ppapi/c/private/ppb_flash_print.h" +#include "ppapi/c/private/ppb_gpu_blacklist_private.h" +#include "ppapi/c/private/ppb_host_resolver_private.h" +#include "ppapi/c/private/ppb_instance_private.h" +#include "ppapi/c/private/ppb_network_list_private.h" +#include "ppapi/c/private/ppb_network_monitor_private.h" +#include "ppapi/c/private/ppb_pdf.h" +#include "ppapi/c/private/ppb_proxy_private.h" +#include "ppapi/c/private/ppb_talk_private.h" +#include "ppapi/c/private/ppb_tcp_server_socket_private.h" +#include "ppapi/c/private/ppb_tcp_socket_private.h" +#include "ppapi/c/private/ppb_udp_socket_private.h" +#include "ppapi/c/private/ppb_uma_private.h" +#include "ppapi/c/private/ppb_video_destination_private.h" +#include "ppapi/c/private/ppb_video_source_private.h" +#include "ppapi/c/private/ppb_x509_certificate_private.h" +#include "ppapi/c/trusted/ppb_broker_trusted.h" +#include "ppapi/c/trusted/ppb_browser_font_trusted.h" +#include "ppapi/c/trusted/ppb_char_set_trusted.h" +#include "ppapi/c/trusted/ppb_file_chooser_trusted.h" +#include "ppapi/c/trusted/ppb_file_io_trusted.h" +#include "ppapi/c/trusted/ppb_url_loader_trusted.h" +#include "ppapi/shared_impl/callback_tracker.h" +#include "ppapi/shared_impl/ppapi_switches.h" +#include "ppapi/shared_impl/ppb_input_event_shared.h" +#include "ppapi/shared_impl/ppb_opengles2_shared.h" +#include "ppapi/shared_impl/ppb_var_shared.h" +#include "ppapi/shared_impl/time_conversion.h" +#include "ppapi/thunk/enter.h" +#include "ppapi/thunk/ppb_graphics_2d_api.h" +#include "ppapi/thunk/thunk.h" +#include "webkit/plugins/plugin_switches.h" + +using ppapi::InputEventData; +using ppapi::PpapiGlobals; +using ppapi::TimeTicksToPPTimeTicks; +using ppapi::TimeToPPTime; +using ppapi::thunk::EnterResource; +using ppapi::thunk::PPB_Graphics2D_API; +using ppapi::thunk::PPB_InputEvent_API; + +namespace webkit { +namespace ppapi { + +namespace { + +// Global tracking info for PPAPI plugins. This is lazily created before the +// first plugin is allocated, and leaked on shutdown. +// +// Note that we don't want a Singleton here since destroying this object will +// try to free some stuff that requires WebKit, and Singletons are destroyed +// after WebKit. +// TODO(raymes): I'm not sure if it is completely necessary to leak the +// HostGlobals. Figure out the shutdown sequence and find a way to do this +// more elegantly. +webkit::ppapi::HostGlobals* host_globals = NULL; + +// Maintains all currently loaded plugin libs for validating PP_Module +// identifiers. +typedef std::set<PluginModule*> PluginModuleSet; + +PluginModuleSet* GetLivePluginSet() { + CR_DEFINE_STATIC_LOCAL(PluginModuleSet, live_plugin_libs, ()); + return &live_plugin_libs; +} + +// PPB_Core -------------------------------------------------------------------- + +void AddRefResource(PP_Resource resource) { + PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(resource); +} + +void ReleaseResource(PP_Resource resource) { + PpapiGlobals::Get()->GetResourceTracker()->ReleaseResource(resource); +} + +PP_Time GetTime() { + return TimeToPPTime(base::Time::Now()); +} + +PP_TimeTicks GetTickTime() { + return TimeTicksToPPTimeTicks(base::TimeTicks::Now()); +} + +void CallOnMainThread(int delay_in_msec, + PP_CompletionCallback callback, + int32_t result) { + if (callback.func) { + PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostDelayedTask( + FROM_HERE, + base::Bind(callback.func, callback.user_data, result), + base::TimeDelta::FromMilliseconds(delay_in_msec)); + } +} + +PP_Bool IsMainThread() { + return BoolToPPBool(PpapiGlobals::Get()-> + GetMainThreadMessageLoop()->BelongsToCurrentThread()); +} + +const PPB_Core core_interface = { + &AddRefResource, + &ReleaseResource, + &GetTime, + &GetTickTime, + &CallOnMainThread, + &IsMainThread +}; + +// PPB_Testing ----------------------------------------------------------------- + +PP_Bool ReadImageData(PP_Resource device_context_2d, + PP_Resource image, + const PP_Point* top_left) { + EnterResource<PPB_Graphics2D_API> enter(device_context_2d, true); + if (enter.failed()) + return PP_FALSE; + return BoolToPPBool(enter.object()->ReadImageData(image, top_left)); +} + +void RunMessageLoop(PP_Instance instance) { + base::MessageLoop::ScopedNestableTaskAllower allow( + base::MessageLoop::current()); + base::MessageLoop::current()->Run(); +} + +void QuitMessageLoop(PP_Instance instance) { + base::MessageLoop::current()->QuitNow(); +} + +uint32_t GetLiveObjectsForInstance(PP_Instance instance_id) { + return HostGlobals::Get()->GetResourceTracker()->GetLiveObjectsForInstance( + instance_id); +} + +PP_Bool IsOutOfProcess() { + return PP_FALSE; +} + +void SimulateInputEvent(PP_Instance instance, PP_Resource input_event) { + PluginInstanceImpl* plugin_instance = host_globals->GetInstance(instance); + if (!plugin_instance) + return; + + EnterResource<PPB_InputEvent_API> enter(input_event, false); + if (enter.failed()) + return; + + const InputEventData& input_event_data = enter.object()->GetInputEventData(); + plugin_instance->SimulateInputEvent(input_event_data); +} + +PP_Var GetDocumentURL(PP_Instance instance, PP_URLComponents_Dev* components) { + PluginInstanceImpl* plugin_instance = host_globals->GetInstance(instance); + if (!plugin_instance) + return PP_MakeUndefined(); + return plugin_instance->GetDocumentURL(instance, components); +} + +uint32_t GetLiveVars(PP_Var live_vars[], uint32_t array_size) { + std::vector<PP_Var> vars = + PpapiGlobals::Get()->GetVarTracker()->GetLiveVars(); + for (size_t i = 0u; + i < std::min(static_cast<size_t>(array_size), vars.size()); + ++i) + live_vars[i] = vars[i]; + return vars.size(); +} + +void SetMinimumArrayBufferSizeForShmem(PP_Instance /*instance*/, + uint32_t /*threshold*/) { + // Does nothing. Not needed in-process. +} + +const PPB_Testing_Dev testing_interface = { + &ReadImageData, + &RunMessageLoop, + &QuitMessageLoop, + &GetLiveObjectsForInstance, + &IsOutOfProcess, + &SimulateInputEvent, + &GetDocumentURL, + &GetLiveVars, + &SetMinimumArrayBufferSizeForShmem +}; + +// GetInterface ---------------------------------------------------------------- + +const void* InternalGetInterface(const char* name) { + // Allow custom interface factories first stab at the GetInterface call. + const void* custom_interface = + PpapiInterfaceFactoryManager::GetInstance()->GetInterface(name); + if (custom_interface) + return custom_interface; + + // TODO(brettw) put these in a hash map for better performance. + #define UNPROXIED_IFACE(api_name, iface_str, iface_struct) \ + if (strcmp(name, iface_str) == 0) \ + return ::ppapi::thunk::Get##iface_struct##_Thunk(); + #define PROXIED_IFACE(api_name, iface_str, iface_struct) \ + UNPROXIED_IFACE(api_name, iface_str, iface_struct) + + #include "ppapi/thunk/interfaces_ppb_public_stable.h" + #include "ppapi/thunk/interfaces_ppb_public_dev.h" + #include "ppapi/thunk/interfaces_ppb_private.h" + #include "ppapi/thunk/interfaces_ppb_private_no_permissions.h" + #include "ppapi/thunk/interfaces_ppb_private_flash.h" + + #undef UNPROXIED_API + #undef PROXIED_IFACE + + #define LEGACY_IFACE(iface_str, function_name) \ + if (strcmp(name, iface_str) == 0) \ + return function_name; + + #include "ppapi/thunk/interfaces_legacy.h" + + #undef LEGACY_IFACE + + // 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 (CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnablePepperTesting)) { + if (strcmp(name, PPB_TESTING_DEV_INTERFACE) == 0 || + strcmp(name, PPB_TESTING_DEV_INTERFACE_0_9) == 0) { + return &testing_interface; + } + } + return NULL; +} + +const void* GetInterface(const char* name) { + // All interfaces should be used on the main thread. + CHECK(IsMainThread()); + + return InternalGetInterface(name); +} + +// 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, + content::PepperPluginInfo::EntryPoints* entry_points) { + entry_points->get_interface = + reinterpret_cast<content::PepperPluginInfo::GetInterfaceFunc>( + 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<content::PepperPluginInfo::PPP_InitializeModuleFunc>( + 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<content::PepperPluginInfo::PPP_ShutdownModuleFunc>( + base::GetFunctionPointerFromNativeLibrary(library, + "PPP_ShutdownModule")); + + return true; +} + +} // namespace + +// PluginModule ---------------------------------------------------------------- + +PluginModule::PluginModule(const std::string& name, + const base::FilePath& path, + const ::ppapi::PpapiPermissions& perms) + : callback_tracker_(new ::ppapi::CallbackTracker), + is_in_destructor_(false), + is_crashed_(false), + broker_(NULL), + library_(NULL), + name_(name), + path_(path), + permissions_(perms), + reserve_instance_id_(NULL) { + // Ensure the globals object is created. + if (!host_globals) + host_globals = new HostGlobals; + + memset(&entry_points_, 0, sizeof(entry_points_)); + pp_module_ = HostGlobals::Get()->AddModule(this); + GetLivePluginSet()->insert(this); +} + +PluginModule::~PluginModule() { + // In the past there have been crashes reentering the plugin module + // destructor. Catch if that happens again earlier. + CHECK(!is_in_destructor_); + is_in_destructor_ = true; + + // When the module is being deleted, there should be no more instances still + // holding a reference to us. + DCHECK(instances_.empty()); + + // Some resources and other stuff are hung off of the embedder state, which + // should be torn down before the routing stuff below. + embedder_state_.reset(); + + GetLivePluginSet()->erase(this); + + callback_tracker_->AbortAll(); + + if (entry_points_.shutdown_module) + entry_points_.shutdown_module(); + + if (library_) + base::UnloadNativeLibrary(library_); + + // Notifications that we've been deleted should be last. + HostGlobals::Get()->ModuleDeleted(pp_module_); + if (!is_crashed_) { + // When the plugin crashes, we immediately tell the lifetime delegate that + // we're gone, so we don't want to tell it again. + content::PepperPluginRegistry::GetInstance()->PluginModuleDead(this); + } + + // Don't add stuff here, the two notifications that the module object has + // been deleted should be last. This allows, for example, + // PPB_Proxy.IsInModuleDestructor to map PP_Module to this class during the + // previous parts of the destructor. +} + +void PluginModule::SetEmbedderState(scoped_ptr<EmbedderState> state) { + embedder_state_ = state.Pass(); +} + +PluginModule::EmbedderState* PluginModule::GetEmbedderState() { + return embedder_state_.get(); +} + +bool PluginModule::InitAsInternalPlugin( + const content::PepperPluginInfo::EntryPoints& entry_points) { + if (InitializeModule(entry_points)) { + entry_points_ = entry_points; + return true; + } + return false; +} + +bool PluginModule::InitAsLibrary(const base::FilePath& path) { + base::NativeLibrary library = base::LoadNativeLibrary(path, NULL); + if (!library) + return false; + + content::PepperPluginInfo::EntryPoints entry_points; + + if (!LoadEntryPointsFromLibrary(library, &entry_points) || + !InitializeModule(entry_points)) { + base::UnloadNativeLibrary(library); + return false; + } + entry_points_ = entry_points; + 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); +} + +scoped_refptr<PluginModule> + PluginModule::CreateModuleForExternalPluginInstance() { + // Create a new module, but don't set the lifetime delegate. This isn't a + // plugin in the usual sense, so it isn't tracked by the browser. + scoped_refptr<PluginModule> external_plugin_module( + new PluginModule(name_, + path_, + permissions_)); + return external_plugin_module; +} + +PP_ExternalPluginResult PluginModule::InitAsProxiedExternalPlugin( + PluginInstanceImpl* instance) { + DCHECK(out_of_process_proxy_.get()); + // InitAsProxied (for the trusted/out-of-process case) initializes only the + // module, and one or more instances are added later. In this case, the + // PluginInstance was already created as in-process, so we missed the proxy + // AddInstance step and must do it now. + out_of_process_proxy_->AddInstance(instance->pp_instance()); + // For external plugins, we need to tell the instance to reset itself as + // proxied. This will clear cached interface pointers and send DidCreate (etc) + // to the plugin side of the proxy. + return instance->ResetAsProxied(this); +} + +bool PluginModule::IsProxied() const { + return !!out_of_process_proxy_; +} + +base::ProcessId PluginModule::GetPeerProcessId() { + if (out_of_process_proxy_) + return out_of_process_proxy_->GetPeerProcessId(); + return base::kNullProcessId; +} + +int PluginModule::GetPluginChildId() { + if (out_of_process_proxy_) + return out_of_process_proxy_->GetPluginChildId(); + return 0; +} + +// static +const PPB_Core* PluginModule::GetCore() { + return &core_interface; +} + +// static +content::PepperPluginInfo::GetInterfaceFunc + PluginModule::GetLocalGetInterfaceFunc() { + return &GetInterface; +} + +// static +bool PluginModule::SupportsInterface(const char* name) { + return !!InternalGetInterface(name); +} + +PluginInstanceImpl* PluginModule::CreateInstance( + PluginDelegate* delegate, + content::RenderView* render_view, + WebKit::WebPluginContainer* container, + const GURL& plugin_url) { + PluginInstanceImpl* instance = PluginInstanceImpl::Create( + delegate, render_view, this, container, plugin_url); + if (!instance) { + LOG(WARNING) << "Plugin doesn't support instance interface, failing."; + return NULL; + } + if (out_of_process_proxy_) + out_of_process_proxy_->AddInstance(instance->pp_instance()); + return instance; +} + +PluginInstanceImpl* 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_) + 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(PluginInstanceImpl* instance) { + instances_.insert(instance); +} + +void PluginModule::InstanceDeleted(PluginInstanceImpl* instance) { + if (out_of_process_proxy_) + out_of_process_proxy_->RemoveInstance(instance->pp_instance()); + instances_.erase(instance); +} + +scoped_refptr< ::ppapi::CallbackTracker> 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(); + + content::PepperPluginRegistry::GetInstance()->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::Broker* broker) { + DCHECK(!broker_ || !broker); + broker_ = broker; +} + +PluginDelegate::Broker* PluginModule::GetBroker() { + return broker_; +} + +// static +void PluginModule::ResetHostGlobalsForTest() { + delete host_globals; + host_globals = NULL; +} + +bool PluginModule::InitializeModule( + const content::PepperPluginInfo::EntryPoints& entry_points) { + DCHECK(!out_of_process_proxy_.get()) << "Don't call for proxied modules."; + DCHECK(entry_points.initialize_module != NULL); + 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 diff --git a/content/renderer/pepper/plugin_module.h b/content/renderer/pepper/plugin_module.h new file mode 100644 index 0000000..c4f3527 --- /dev/null +++ b/content/renderer/pepper/plugin_module.h @@ -0,0 +1,267 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_PEPPER_PLUGIN_MODULE_H_ +#define CONTENT_RENDERER_PEPPER_PLUGIN_MODULE_H_ + +#include <map> +#include <set> +#include <string> + +#include "base/basictypes.h" +#include "base/files/file_path.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/native_library.h" +#include "base/process.h" +#include "content/common/content_export.h" +#include "content/public/common/pepper_plugin_info.h" +#include "content/renderer/pepper/plugin_delegate.h" +#include "ppapi/c/pp_bool.h" +#include "ppapi/c/pp_instance.h" +#include "ppapi/c/ppb_core.h" +#include "ppapi/c/private/ppb_instance_private.h" +#include "ppapi/shared_impl/ppapi_permissions.h" + +typedef void* NPIdentifier; + +namespace base { +class FilePath; +} + +namespace content { +class RenderView; +} + +namespace ppapi { +class CallbackTracker; +class WebKitForwarding; +} // namespace ppapi + +namespace WebKit { +class WebPluginContainer; +} // namespace WebKit + +namespace webkit { +namespace ppapi { + +class PluginDelegate; +class PluginInstanceImpl; + +// Represents one plugin library loaded into one renderer. This library may +// have multiple instances. +// +// Note: to get from a PP_Instance to a PluginInstance*, use the +// ResourceTracker. +class CONTENT_EXPORT PluginModule : + public base::RefCounted<PluginModule>, + public base::SupportsWeakPtr<PluginModule> { + public: + // Allows the embedder to associate a class with this module. This is opaque + // from the PluginModule's perspective (see Set/GetEmbedderState below) but + // the module is in charge of deleting the class. + class EmbedderState { + public: + virtual ~EmbedderState() {} + }; + + typedef std::set<PluginInstanceImpl*> PluginInstanceSet; + + // You must call one of the Init functions after the constructor to create a + // module of the type you desire. + // + // The module lifetime delegate is a non-owning pointer that must outlive + // all plugin modules. In practice it will be a global singleton that + // tracks which modules are alive. + PluginModule(const std::string& name, + const base::FilePath& path, + const ::ppapi::PpapiPermissions& perms); + + // Sets the given class as being associated with this module. It will be + // deleted when the module is destroyed. You can only set it once, subsequent + // sets will assert. + // + // See EmbedderState above for more. + void SetEmbedderState(scoped_ptr<EmbedderState> state); + EmbedderState* GetEmbedderState(); + + // Initializes this module as an internal plugin with the given entrypoints. + // This is used for "plugins" compiled into Chrome. Returns true on success. + // False means that the plugin can not be used. + bool InitAsInternalPlugin( + const content::PepperPluginInfo::EntryPoints& entry_points); + + // Initializes this module using the given library path as the plugin. + // Returns true on success. False means that the plugin can not be used. + bool InitAsLibrary(const base::FilePath& path); + + // Initializes this module for the given out of process proxy. This takes + // ownership of the given pointer, even in the failure case. + void InitAsProxied(PluginDelegate::OutOfProcessProxy* out_of_process_proxy); + + // Creates a new module for an external plugin instance that will be using the + // IPC proxy. We can't use the existing module, or new instances of the plugin + // can't be created. + scoped_refptr<PluginModule> CreateModuleForExternalPluginInstance(); + + // Initializes the external plugin module for the out of process proxy. + // InitAsProxied must be called before calling InitAsProxiedExternalPlugin. + // Returns a result code indicating whether the proxy started successfully or + // there was an error. + PP_ExternalPluginResult InitAsProxiedExternalPlugin( + PluginInstanceImpl* instance); + + bool IsProxied() const; + + // Returns the peer process ID if the plugin is running out of process; + // returns |base::kNullProcessId| otherwise. + base::ProcessId GetPeerProcessId(); + + // Returns the plugin child process ID if the plugin is running out of + // process. Returns 0 otherwise. This is the ID that the browser process uses + // to idetify the child process for the plugin. This isn't directly useful + // from our process (the renderer) except in messages to the browser to + // disambiguate plugins. + int GetPluginChildId(); + + static const PPB_Core* GetCore(); + + // Returns a pointer to the local GetInterface function for retrieving + // PPB interfaces. + static content::PepperPluginInfo::GetInterfaceFunc GetLocalGetInterfaceFunc(); + + // Returns whether an interface is supported. This method can be called from + // the browser process and used for interface matching before plugin + // registration. + // NOTE: those custom interfaces provided by PpapiInterfaceFactoryManager + // will not be considered when called on the browser process. + static bool SupportsInterface(const char* name); + + // Returns the module handle. This may be used before Init() is called (the + // proxy needs this information to set itself up properly). + PP_Module pp_module() const { return pp_module_; } + + const std::string& name() const { return name_; } + const base::FilePath& path() const { return path_; } + const ::ppapi::PpapiPermissions& permissions() const { return permissions_; } + + PluginInstanceImpl* CreateInstance(PluginDelegate* delegate, + content::RenderView* render_view, + WebKit::WebPluginContainer* container, + const GURL& plugin_url); + + // Returns "some" plugin instance associated with this module. This is not + // guaranteed to be any one in particular. This is normally used to execute + // callbacks up to the browser layer that are not inherently per-instance, + // but the delegate lives only on the plugin instance so we need one of them. + PluginInstanceImpl* GetSomeInstance() const; + + const PluginInstanceSet& GetAllInstances() const { return instances_; } + + // Calls the plugin's GetInterface and returns the given interface pointer, + // which could be NULL. + const void* GetPluginInterface(const char* name) const; + + // This module is associated with a set of instances. The PluginInstance + // object declares its association with this module in its destructor and + // releases us in its destructor. + void InstanceCreated(PluginInstanceImpl* instance); + void InstanceDeleted(PluginInstanceImpl* instance); + + scoped_refptr< ::ppapi::CallbackTracker> GetCallbackTracker(); + + // Called when running out of process and the plugin crashed. This will + // release relevant resources and update all affected instances. + void PluginCrashed(); + + bool is_in_destructor() const { return is_in_destructor_; } + bool is_crashed() const { return is_crashed_; } + + // Reserves the given instance is unique within the plugin, checking for + // collisions. See PPB_Proxy_Private for more information. + // + // The setter will set the callback which is set up when the proxy + // initializes. The Reserve function will call the previously set callback if + // it exists to validate the ID. If the callback has not been set (such as + // for in-process plugins), the Reserve function will assume that the ID is + // usable and will return true. + void SetReserveInstanceIDCallback( + PP_Bool (*reserve)(PP_Module, PP_Instance)); + bool ReserveInstanceID(PP_Instance instance); + + // These should only be called from the main thread. + void SetBroker(PluginDelegate::Broker* broker); + PluginDelegate::Broker* GetBroker(); + + // In production we purposely leak the HostGlobals object but in unittest + // code, this can interfere with subsequent tests. This deletes the + // existing HostGlobals. A new one will be constructed when a PluginModule is + // instantiated. + static void ResetHostGlobalsForTest(); + + private: + friend class base::RefCounted<PluginModule>; + ~PluginModule(); + // Calls the InitializeModule entrypoint. The entrypoint must have been + // set and the plugin must not be out of process (we don't maintain + // entrypoints in that case). + bool InitializeModule( + const content::PepperPluginInfo::EntryPoints& entry_points); + + // See EmbedderState above. + scoped_ptr<EmbedderState> embedder_state_; + + // Tracker for completion callbacks, used mainly to ensure that all callbacks + // are properly aborted on module shutdown. + scoped_refptr< ::ppapi::CallbackTracker> callback_tracker_; + + PP_Module pp_module_; + + // True when we're running in the destructor. This allows us to write some + // assertions. + bool is_in_destructor_; + + // True if the plugin is running out-of-process and has crashed. + bool is_crashed_; + + // Manages the out of process proxy interface. The presence of this + // pointer indicates that the plugin is running out of process and that the + // entry_points_ aren't valid. + scoped_ptr<PluginDelegate::OutOfProcessProxy> out_of_process_proxy_; + + // Non-owning pointer to the broker for this plugin module, if one exists. + // It is populated and cleared in the main thread. + PluginDelegate::Broker* broker_; + + // Holds a reference to the base::NativeLibrary handle if this PluginModule + // instance wraps functions loaded from a library. Can be NULL. If + // |library_| is non-NULL, PluginModule will attempt to unload the library + // during destruction. + base::NativeLibrary library_; + + // Contains pointers to the entry points of the actual plugin implementation. + // These will be NULL for out-of-process plugins, which is indicated by the + // presence of the out_of_process_proxy_ value. + content::PepperPluginInfo::EntryPoints entry_points_; + + // The name and file location of the module. + const std::string name_; + const base::FilePath path_; + + ::ppapi::PpapiPermissions permissions_; + + // Non-owning pointers to all instances associated with this module. When + // there are no more instances, this object should be deleted. + PluginInstanceSet instances_; + + PP_Bool (*reserve_instance_id_)(PP_Module, PP_Instance); + + DISALLOW_COPY_AND_ASSIGN(PluginModule); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_PLUGIN_MODULE_H_ diff --git a/content/renderer/pepper/plugin_object.cc b/content/renderer/pepper/plugin_object.cc new file mode 100644 index 0000000..9955d0cf8 --- /dev/null +++ b/content/renderer/pepper/plugin_object.cc @@ -0,0 +1,360 @@ +// 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 "content/renderer/pepper/plugin_object.h" + +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "content/renderer/pepper/npapi_glue.h" +#include "content/renderer/pepper/plugin_module.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" +#include "ppapi/c/dev/ppb_var_deprecated.h" +#include "ppapi/c/dev/ppp_class_deprecated.h" +#include "ppapi/c/pp_resource.h" +#include "ppapi/c/pp_var.h" +#include "ppapi/shared_impl/ppapi_globals.h" +#include "ppapi/shared_impl/resource_tracker.h" +#include "ppapi/shared_impl/var.h" +#include "ppapi/shared_impl/var_tracker.h" +#include "third_party/WebKit/public/web/WebBindings.h" +#include "third_party/npapi/bindings/npapi.h" +#include "third_party/npapi/bindings/npruntime.h" + +using ppapi::PpapiGlobals; +using ppapi::StringVar; +using ppapi::Var; +using WebKit::WebBindings; + +namespace webkit { +namespace ppapi { + +namespace { + +const char kInvalidValueException[] = "Error: Invalid value"; + +// NPObject implementation in terms of PPP_Class_Deprecated -------------------- + +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()->GetNPObject(), 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()->GetNPObject(), result); + PPVarArrayFromNPVariantArray args(accessor.object()->instance(), + argc, argv); + + // For the OOP plugin case we need to grab a reference on the plugin module + // object to ensure that it is not destroyed courtsey an incoming + // ExecuteScript call which destroys the plugin module and in turn the + // dispatcher. + scoped_refptr<webkit::ppapi::PluginModule> ref( + accessor.object()->instance()->module()); + + 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->instance(), argc, argv); + PPResultAndExceptionToNPResult result_converter(obj->GetNPObject(), result); + + // For the OOP plugin case we need to grab a reference on the plugin module + // object to ensure that it is not destroyed courtsey an incoming + // ExecuteScript call which destroys the plugin module and in turn the + // dispatcher. + scoped_refptr<webkit::ppapi::PluginModule> ref( + obj->instance()->module()); + + result_converter.SetResult(obj->ppp_class()->Call( + obj->ppp_class_data(), PP_MakeUndefined(), 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()->GetNPObject(), 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()->GetNPObject(), 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()->GetNPObject(), NULL); + PP_Var value_var = NPVariantToPPVar(accessor.object()->instance(), value); + accessor.object()->ppp_class()->SetProperty( + accessor.object()->ppp_class_data(), accessor.identifier(), value_var, + result_converter.exception()); + PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(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()->GetNPObject(), 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->GetNPObject(), 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] = PPVarToNPIdentifier(properties[i]))) { + // Throw an exception for the failed convertion. + *result_converter.exception() = + StringVar::StringToPPVar(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. + ::ppapi::VarTracker* var_tracker = PpapiGlobals::Get()->GetVarTracker(); + for (uint32_t i = 0; i < property_count; ++i) + var_tracker->ReleaseVar(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->instance(), argc, argv); + PPResultAndExceptionToNPResult result_converter(obj->GetNPObject(), 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(PluginInstanceImpl* instance, + NPObjectWrapper* object_wrapper, + const PPP_Class_Deprecated* ppp_class, + void* ppp_class_data) + : instance_(instance), + 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; + instance_->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; + instance_->RemovePluginObject(this); +} + +PP_Var PluginObject::Create(PluginInstanceImpl* instance, + const PPP_Class_Deprecated* 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(instance->instanceNPP(), + 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(instance, 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. + PP_Var obj_var(NPObjectToPPVar(instance, wrapper)); + + // Note that the ObjectVar constructor incremented the reference count, and so + // did WebBindings::createObject above. Now that the PP_Var has taken + // ownership, we need to release to balance out the createObject reference + // count bump. + WebBindings::releaseObject(wrapper); + return obj_var; +} + +NPObject* PluginObject::GetNPObject() const { + return object_wrapper_; +} + +// static +bool PluginObject::IsInstanceOf(NPObject* np_object, + const PPP_Class_Deprecated* 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, 0, sizeof(NPObjectWrapper)); + return wrapper; +} + +} // namespace ppapi +} // namespace webkit + diff --git a/content/renderer/pepper/plugin_object.h b/content/renderer/pepper/plugin_object.h new file mode 100644 index 0000000..c222e50 --- /dev/null +++ b/content/renderer/pepper/plugin_object.h @@ -0,0 +1,95 @@ +// 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 CONTENT_RENDERER_PEPPER_PLUGIN_OBJECT_H_ +#define CONTENT_RENDERER_PEPPER_PLUGIN_OBJECT_H_ + +#include <string> + +#include "base/basictypes.h" + +struct PP_Var; +struct PPP_Class_Deprecated; +typedef struct NPObject NPObject; +typedef struct _NPVariant NPVariant; + +namespace webkit { +namespace ppapi { + +class PluginInstanceImpl; + +// A PluginObject is a JS-accessible object implemented by the plugin. +// +// In contrast, a var of type PP_VARTYPE_OBJECT is a reference to a JS object, +// which might be implemented by the plugin (here) or by the JS engine. +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(PluginInstanceImpl* instance, + const PPP_Class_Deprecated* ppp_class, + void* ppp_class_data); + + PluginInstanceImpl* instance() const { return instance_; } + + const PPP_Class_Deprecated* 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_Deprecated* 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(PluginInstanceImpl* instance, + NPObjectWrapper* object_wrapper, + const PPP_Class_Deprecated* ppp_class, + void* ppp_class_data); + + PluginInstanceImpl* instance_; + + // 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_Deprecated* ppp_class_; + void* ppp_class_data_; + + DISALLOW_COPY_AND_ASSIGN(PluginObject); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_PLUGIN_OBJECT_H_ diff --git a/content/renderer/pepper/ppapi_interface_factory.cc b/content/renderer/pepper/ppapi_interface_factory.cc new file mode 100644 index 0000000..acb6858 --- /dev/null +++ b/content/renderer/pepper/ppapi_interface_factory.cc @@ -0,0 +1,62 @@ +// 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 "content/renderer/pepper/ppapi_interface_factory.h" + +#include <algorithm> + +#include "base/logging.h" + +namespace webkit { +namespace ppapi { + +base::LazyInstance<PpapiInterfaceFactoryManager> + g_ppapi_interface_factory_manager = LAZY_INSTANCE_INITIALIZER; + +PpapiInterfaceFactoryManager::PpapiInterfaceFactoryManager() { +} + +PpapiInterfaceFactoryManager::~PpapiInterfaceFactoryManager() { +} + +void PpapiInterfaceFactoryManager::RegisterFactory(InterfaceFactory* factory) { + DCHECK(std::find(interface_factory_list_.begin(), + interface_factory_list_.end(), factory) == + interface_factory_list_.end()); + interface_factory_list_.push_back(factory); +} + +void PpapiInterfaceFactoryManager::UnregisterFactory( + InterfaceFactory* factory) { + FactoryList::iterator index = + std::find(interface_factory_list_.begin(), interface_factory_list_.end(), + factory); + if (index != interface_factory_list_.end()) + interface_factory_list_.erase(index); +} + +const void* PpapiInterfaceFactoryManager::GetInterface( + const std::string& interface_name) { + FactoryList::iterator index; + + const void* ppapi_interface = NULL; + + for (index = interface_factory_list_.begin(); + index != interface_factory_list_.end(); + ++index) { + ppapi_interface = (*index)(interface_name); + if (ppapi_interface) + break; + } + return ppapi_interface; +} + +// static +PpapiInterfaceFactoryManager* PpapiInterfaceFactoryManager::GetInstance() { + return &g_ppapi_interface_factory_manager.Get(); +} + +} // namespace ppapi +} // namespace webkit + diff --git a/content/renderer/pepper/ppapi_interface_factory.h b/content/renderer/pepper/ppapi_interface_factory.h new file mode 100644 index 0000000..6654f61 --- /dev/null +++ b/content/renderer/pepper/ppapi_interface_factory.h @@ -0,0 +1,56 @@ +// 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. + +#ifndef CONTENT_RENDERER_PEPPER_PLUGIN_INTERFACE_FACTORY_H_ +#define CONTENT_RENDERER_PEPPER_PLUGIN_INTERFACE_FACTORY_H_ + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/lazy_instance.h" +#include "content/common/content_export.h" + +namespace webkit { +namespace ppapi { + +// This class provides functionality to manage custom PPAPI interface +// factories. +class PpapiInterfaceFactoryManager { + public: + typedef const void* (InterfaceFactory)(const std::string& interface_name); + + // Registers a custom PPAPI interface factory. + CONTENT_EXPORT void RegisterFactory(InterfaceFactory* factory); + + // Unregisters the custom PPAPI interface factory passed in. + CONTENT_EXPORT void UnregisterFactory(InterfaceFactory* factory); + + // Returns a pointer to the interface identified by the name passed in. + // Returns NULL if no factory handles this interface. + const void* GetInterface(const std::string& interface_name); + + // Returns a pointer to the global instance of the + // PpapiInterfaceFactoryManager class. + CONTENT_EXPORT static PpapiInterfaceFactoryManager* GetInstance(); + + private: + friend struct base::DefaultLazyInstanceTraits<PpapiInterfaceFactoryManager>; + + PpapiInterfaceFactoryManager(); + ~PpapiInterfaceFactoryManager(); + + typedef std::vector<InterfaceFactory*> FactoryList; + + // List of registered factories. + FactoryList interface_factory_list_; + + DISALLOW_COPY_AND_ASSIGN(PpapiInterfaceFactoryManager); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_PLUGIN_INTERFACE_FACTORY_H_ + diff --git a/content/renderer/pepper/ppapi_plugin_instance_impl.cc b/content/renderer/pepper/ppapi_plugin_instance_impl.cc new file mode 100644 index 0000000..229c31e --- /dev/null +++ b/content/renderer/pepper/ppapi_plugin_instance_impl.cc @@ -0,0 +1,2726 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/ppapi_plugin_instance_impl.h" + +#include "base/bind.h" +#include "base/callback_helpers.h" +#include "base/debug/trace_event.h" +#include "base/logging.h" +#include "base/memory/linked_ptr.h" +#include "base/message_loop/message_loop.h" +#include "base/stl_util.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_offset_string_conversions.h" +#include "base/strings/utf_string_conversions.h" +#include "base/time/time.h" +#include "cc/layers/texture_layer.h" +#include "content/renderer/pepper/common.h" +#include "content/renderer/pepper/content_decryptor_delegate.h" +#include "content/renderer/pepper/event_conversion.h" +#include "content/renderer/pepper/fullscreen_container.h" +#include "content/renderer/pepper/gfx_conversion.h" +#include "content/renderer/pepper/host_globals.h" +#include "content/renderer/pepper/message_channel.h" +#include "content/renderer/pepper/npapi_glue.h" +#include "content/renderer/pepper/plugin_module.h" +#include "content/renderer/pepper/plugin_object.h" +#include "content/renderer/pepper/ppb_buffer_impl.h" +#include "content/renderer/pepper/ppb_file_ref_impl.h" +#include "content/renderer/pepper/ppb_graphics_3d_impl.h" +#include "content/renderer/pepper/ppb_image_data_impl.h" +#include "content/renderer/pepper/ppp_pdf.h" +#include "content/renderer/pepper/url_request_info_util.h" +#include "ppapi/c/dev/ppb_find_dev.h" +#include "ppapi/c/dev/ppb_zoom_dev.h" +#include "ppapi/c/dev/ppp_find_dev.h" +#include "ppapi/c/dev/ppp_selection_dev.h" +#include "ppapi/c/dev/ppp_text_input_dev.h" +#include "ppapi/c/dev/ppp_zoom_dev.h" +#include "ppapi/c/pp_rect.h" +#include "ppapi/c/ppb_audio_config.h" +#include "ppapi/c/ppb_core.h" +#include "ppapi/c/ppb_gamepad.h" +#include "ppapi/c/ppp_input_event.h" +#include "ppapi/c/ppp_instance.h" +#include "ppapi/c/ppp_messaging.h" +#include "ppapi/c/ppp_mouse_lock.h" +#include "ppapi/c/private/ppp_instance_private.h" +#include "ppapi/shared_impl/ppapi_permissions.h" +#include "ppapi/shared_impl/ppapi_preferences.h" +#include "ppapi/shared_impl/ppb_gamepad_shared.h" +#include "ppapi/shared_impl/ppb_input_event_shared.h" +#include "ppapi/shared_impl/ppb_url_util_shared.h" +#include "ppapi/shared_impl/ppb_view_shared.h" +#include "ppapi/shared_impl/ppp_instance_combined.h" +#include "ppapi/shared_impl/resource.h" +#include "ppapi/shared_impl/scoped_pp_resource.h" +#include "ppapi/shared_impl/time_conversion.h" +#include "ppapi/shared_impl/url_request_info_data.h" +#include "ppapi/shared_impl/var.h" +#include "ppapi/thunk/enter.h" +#include "ppapi/thunk/ppb_buffer_api.h" +#include "printing/metafile.h" +#include "printing/metafile_skia_wrapper.h" +#include "printing/units.h" +#include "skia/ext/platform_canvas.h" +#include "skia/ext/platform_device.h" +#include "third_party/WebKit/public/platform/WebGamepads.h" +#include "third_party/WebKit/public/platform/WebString.h" +#include "third_party/WebKit/public/platform/WebURL.h" +#include "third_party/WebKit/public/platform/WebURLError.h" +#include "third_party/WebKit/public/platform/WebURLRequest.h" +#include "third_party/WebKit/public/web/WebBindings.h" +#include "third_party/WebKit/public/web/WebCompositionUnderline.h" +#include "third_party/WebKit/public/web/WebCursorInfo.h" +#include "third_party/WebKit/public/web/WebDocument.h" +#include "third_party/WebKit/public/web/WebElement.h" +#include "third_party/WebKit/public/web/WebFrame.h" +#include "third_party/WebKit/public/web/WebInputEvent.h" +#include "third_party/WebKit/public/web/WebPluginContainer.h" +#include "third_party/WebKit/public/web/WebPrintParams.h" +#include "third_party/WebKit/public/web/WebPrintScalingOption.h" +#include "third_party/WebKit/public/web/WebScopedUserGesture.h" +#include "third_party/WebKit/public/web/WebSecurityOrigin.h" +#include "third_party/WebKit/public/web/WebUserGestureIndicator.h" +#include "third_party/WebKit/public/web/WebView.h" +#include "third_party/skia/include/core/SkCanvas.h" +#include "third_party/skia/include/core/SkRect.h" +#include "ui/base/range/range.h" +#include "ui/gfx/image/image_skia.h" +#include "ui/gfx/image/image_skia_rep.h" +#include "ui/gfx/rect_conversions.h" +#include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" +#include "v8/include/v8.h" +#include "webkit/plugins/plugin_constants.h" +#include "webkit/plugins/sad_plugin.h" +#include "webkit/renderer/compositor_bindings/web_layer_impl.h" + +#if defined(OS_MACOSX) +#include "printing/metafile_impl.h" +#endif // defined(OS_MACOSX) + +#if defined(OS_WIN) +#include "base/metrics/histogram.h" +#include "base/win/windows_version.h" +#include "skia/ext/platform_canvas.h" +#include "ui/gfx/codec/jpeg_codec.h" +#include "ui/gfx/gdi_util.h" +#endif + +using base::StringPrintf; +using ppapi::InputEventData; +using ppapi::PpapiGlobals; +using ppapi::PPB_InputEvent_Shared; +using ppapi::PPB_View_Shared; +using ppapi::PPP_Instance_Combined; +using ppapi::Resource; +using ppapi::ScopedPPResource; +using ppapi::StringVar; +using ppapi::TrackedCallback; +using ppapi::thunk::EnterResourceNoLock; +using ppapi::thunk::PPB_Buffer_API; +using ppapi::thunk::PPB_Gamepad_API; +using ppapi::thunk::PPB_Graphics2D_API; +using ppapi::thunk::PPB_Graphics3D_API; +using ppapi::thunk::PPB_ImageData_API; +using ppapi::Var; +using ppapi::ArrayBufferVar; +using ppapi::ViewData; +using WebKit::WebBindings; +using WebKit::WebCanvas; +using WebKit::WebCursorInfo; +using WebKit::WebDocument; +using WebKit::WebElement; +using WebKit::WebFrame; +using WebKit::WebInputEvent; +using WebKit::WebPlugin; +using WebKit::WebPluginContainer; +using WebKit::WebPrintParams; +using WebKit::WebPrintScalingOption; +using WebKit::WebScopedUserGesture; +using WebKit::WebString; +using WebKit::WebURLError; +using WebKit::WebURLLoader; +using WebKit::WebURLLoaderClient; +using WebKit::WebURLRequest; +using WebKit::WebURLResponse; +using WebKit::WebUserGestureIndicator; +using WebKit::WebUserGestureToken; +using WebKit::WebView; + +namespace webkit { +namespace ppapi { + +#if defined(OS_WIN) +// Exported by pdf.dll +typedef bool (*RenderPDFPageToDCProc)( + const unsigned char* pdf_buffer, int buffer_size, int page_number, HDC dc, + int dpi_x, int dpi_y, int bounds_origin_x, int bounds_origin_y, + int bounds_width, int bounds_height, bool fit_to_bounds, + bool stretch_to_bounds, bool keep_aspect_ratio, bool center_in_bounds, + bool autorotate); + +void DrawEmptyRectangle(HDC dc) { + // TODO(sanjeevr): This is a temporary hack. If we output a JPEG + // to the EMF, the EnumEnhMetaFile call fails in the browser + // process. The failure also happens if we output nothing here. + // We need to investigate the reason for this failure and fix it. + // In the meantime this temporary hack of drawing an empty + // rectangle in the DC gets us by. + Rectangle(dc, 0, 0, 0, 0); +} +#endif // defined(OS_WIN) + +namespace { + +// Check PP_TextInput_Type and ui::TextInputType are kept in sync. +COMPILE_ASSERT(int(ui::TEXT_INPUT_TYPE_NONE) == \ + int(PP_TEXTINPUT_TYPE_NONE), mismatching_enums); +COMPILE_ASSERT(int(ui::TEXT_INPUT_TYPE_TEXT) == \ + int(PP_TEXTINPUT_TYPE_TEXT), mismatching_enums); +COMPILE_ASSERT(int(ui::TEXT_INPUT_TYPE_PASSWORD) == \ + int(PP_TEXTINPUT_TYPE_PASSWORD), mismatching_enums); +COMPILE_ASSERT(int(ui::TEXT_INPUT_TYPE_SEARCH) == \ + int(PP_TEXTINPUT_TYPE_SEARCH), mismatching_enums); +COMPILE_ASSERT(int(ui::TEXT_INPUT_TYPE_EMAIL) == \ + int(PP_TEXTINPUT_TYPE_EMAIL), mismatching_enums); +COMPILE_ASSERT(int(ui::TEXT_INPUT_TYPE_NUMBER) == \ + int(PP_TEXTINPUT_TYPE_NUMBER), mismatching_enums); +COMPILE_ASSERT(int(ui::TEXT_INPUT_TYPE_TELEPHONE) == \ + int(PP_TEXTINPUT_TYPE_TELEPHONE), mismatching_enums); +COMPILE_ASSERT(int(ui::TEXT_INPUT_TYPE_URL) == \ + int(PP_TEXTINPUT_TYPE_URL), mismatching_enums); + +// The default text input type is to regard the plugin always accept text input. +// This is for allowing users to use input methods even on completely-IME- +// unaware plugins (e.g., PPAPI Flash or PDF plugin for M16). +// Plugins need to explicitly opt out the text input mode if they know +// that they don't accept texts. +const ui::TextInputType kPluginDefaultTextInputType = ui::TEXT_INPUT_TYPE_TEXT; + +#define COMPILE_ASSERT_MATCHING_ENUM(webkit_name, np_name) \ + COMPILE_ASSERT(static_cast<int>(WebCursorInfo::webkit_name) \ + == static_cast<int>(np_name), \ + mismatching_enums) + +#define COMPILE_ASSERT_PRINT_SCALING_MATCHING_ENUM(webkit_name, pp_name) \ + COMPILE_ASSERT(static_cast<int>(webkit_name) \ + == static_cast<int>(pp_name), \ + mismatching_enums) + +// <embed>/<object> attributes. +const char kWidth[] = "width"; +const char kHeight[] = "height"; +const char kBorder[] = "border"; // According to w3c, deprecated. +const char kStyle[] = "style"; + +COMPILE_ASSERT_MATCHING_ENUM(TypePointer, PP_MOUSECURSOR_TYPE_POINTER); +COMPILE_ASSERT_MATCHING_ENUM(TypeCross, PP_MOUSECURSOR_TYPE_CROSS); +COMPILE_ASSERT_MATCHING_ENUM(TypeHand, PP_MOUSECURSOR_TYPE_HAND); +COMPILE_ASSERT_MATCHING_ENUM(TypeIBeam, PP_MOUSECURSOR_TYPE_IBEAM); +COMPILE_ASSERT_MATCHING_ENUM(TypeWait, PP_MOUSECURSOR_TYPE_WAIT); +COMPILE_ASSERT_MATCHING_ENUM(TypeHelp, PP_MOUSECURSOR_TYPE_HELP); +COMPILE_ASSERT_MATCHING_ENUM(TypeEastResize, PP_MOUSECURSOR_TYPE_EASTRESIZE); +COMPILE_ASSERT_MATCHING_ENUM(TypeNorthResize, PP_MOUSECURSOR_TYPE_NORTHRESIZE); +COMPILE_ASSERT_MATCHING_ENUM(TypeNorthEastResize, + PP_MOUSECURSOR_TYPE_NORTHEASTRESIZE); +COMPILE_ASSERT_MATCHING_ENUM(TypeNorthWestResize, + PP_MOUSECURSOR_TYPE_NORTHWESTRESIZE); +COMPILE_ASSERT_MATCHING_ENUM(TypeSouthResize, PP_MOUSECURSOR_TYPE_SOUTHRESIZE); +COMPILE_ASSERT_MATCHING_ENUM(TypeSouthEastResize, + PP_MOUSECURSOR_TYPE_SOUTHEASTRESIZE); +COMPILE_ASSERT_MATCHING_ENUM(TypeSouthWestResize, + PP_MOUSECURSOR_TYPE_SOUTHWESTRESIZE); +COMPILE_ASSERT_MATCHING_ENUM(TypeWestResize, PP_MOUSECURSOR_TYPE_WESTRESIZE); +COMPILE_ASSERT_MATCHING_ENUM(TypeNorthSouthResize, + PP_MOUSECURSOR_TYPE_NORTHSOUTHRESIZE); +COMPILE_ASSERT_MATCHING_ENUM(TypeEastWestResize, + PP_MOUSECURSOR_TYPE_EASTWESTRESIZE); +COMPILE_ASSERT_MATCHING_ENUM(TypeNorthEastSouthWestResize, + PP_MOUSECURSOR_TYPE_NORTHEASTSOUTHWESTRESIZE); +COMPILE_ASSERT_MATCHING_ENUM(TypeNorthWestSouthEastResize, + PP_MOUSECURSOR_TYPE_NORTHWESTSOUTHEASTRESIZE); +COMPILE_ASSERT_MATCHING_ENUM(TypeColumnResize, + PP_MOUSECURSOR_TYPE_COLUMNRESIZE); +COMPILE_ASSERT_MATCHING_ENUM(TypeRowResize, PP_MOUSECURSOR_TYPE_ROWRESIZE); +COMPILE_ASSERT_MATCHING_ENUM(TypeMiddlePanning, + PP_MOUSECURSOR_TYPE_MIDDLEPANNING); +COMPILE_ASSERT_MATCHING_ENUM(TypeEastPanning, PP_MOUSECURSOR_TYPE_EASTPANNING); +COMPILE_ASSERT_MATCHING_ENUM(TypeNorthPanning, + PP_MOUSECURSOR_TYPE_NORTHPANNING); +COMPILE_ASSERT_MATCHING_ENUM(TypeNorthEastPanning, + PP_MOUSECURSOR_TYPE_NORTHEASTPANNING); +COMPILE_ASSERT_MATCHING_ENUM(TypeNorthWestPanning, + PP_MOUSECURSOR_TYPE_NORTHWESTPANNING); +COMPILE_ASSERT_MATCHING_ENUM(TypeSouthPanning, + PP_MOUSECURSOR_TYPE_SOUTHPANNING); +COMPILE_ASSERT_MATCHING_ENUM(TypeSouthEastPanning, + PP_MOUSECURSOR_TYPE_SOUTHEASTPANNING); +COMPILE_ASSERT_MATCHING_ENUM(TypeSouthWestPanning, + PP_MOUSECURSOR_TYPE_SOUTHWESTPANNING); +COMPILE_ASSERT_MATCHING_ENUM(TypeWestPanning, PP_MOUSECURSOR_TYPE_WESTPANNING); +COMPILE_ASSERT_MATCHING_ENUM(TypeMove, PP_MOUSECURSOR_TYPE_MOVE); +COMPILE_ASSERT_MATCHING_ENUM(TypeVerticalText, + PP_MOUSECURSOR_TYPE_VERTICALTEXT); +COMPILE_ASSERT_MATCHING_ENUM(TypeCell, PP_MOUSECURSOR_TYPE_CELL); +COMPILE_ASSERT_MATCHING_ENUM(TypeContextMenu, PP_MOUSECURSOR_TYPE_CONTEXTMENU); +COMPILE_ASSERT_MATCHING_ENUM(TypeAlias, PP_MOUSECURSOR_TYPE_ALIAS); +COMPILE_ASSERT_MATCHING_ENUM(TypeProgress, PP_MOUSECURSOR_TYPE_PROGRESS); +COMPILE_ASSERT_MATCHING_ENUM(TypeNoDrop, PP_MOUSECURSOR_TYPE_NODROP); +COMPILE_ASSERT_MATCHING_ENUM(TypeCopy, PP_MOUSECURSOR_TYPE_COPY); +COMPILE_ASSERT_MATCHING_ENUM(TypeNone, PP_MOUSECURSOR_TYPE_NONE); +COMPILE_ASSERT_MATCHING_ENUM(TypeNotAllowed, PP_MOUSECURSOR_TYPE_NOTALLOWED); +COMPILE_ASSERT_MATCHING_ENUM(TypeZoomIn, PP_MOUSECURSOR_TYPE_ZOOMIN); +COMPILE_ASSERT_MATCHING_ENUM(TypeZoomOut, PP_MOUSECURSOR_TYPE_ZOOMOUT); +COMPILE_ASSERT_MATCHING_ENUM(TypeGrab, PP_MOUSECURSOR_TYPE_GRAB); +COMPILE_ASSERT_MATCHING_ENUM(TypeGrabbing, PP_MOUSECURSOR_TYPE_GRABBING); +// Do not assert WebCursorInfo::TypeCustom == PP_CURSORTYPE_CUSTOM; +// PP_CURSORTYPE_CUSTOM is pinned to allow new cursor types. + +COMPILE_ASSERT_PRINT_SCALING_MATCHING_ENUM(WebKit::WebPrintScalingOptionNone, + PP_PRINTSCALINGOPTION_NONE); +COMPILE_ASSERT_PRINT_SCALING_MATCHING_ENUM( + WebKit::WebPrintScalingOptionFitToPrintableArea, + PP_PRINTSCALINGOPTION_FIT_TO_PRINTABLE_AREA); +COMPILE_ASSERT_PRINT_SCALING_MATCHING_ENUM( + WebKit::WebPrintScalingOptionSourceSize, PP_PRINTSCALINGOPTION_SOURCE_SIZE); + +// Sets |*security_origin| to be the WebKit security origin associated with the +// document containing the given plugin instance. On success, returns true. If +// the instance is invalid, returns false and |*security_origin| will be +// unchanged. +bool SecurityOriginForInstance(PP_Instance instance_id, + WebKit::WebSecurityOrigin* security_origin) { + PluginInstanceImpl* instance = HostGlobals::Get()->GetInstance(instance_id); + if (!instance) + return false; + + WebElement plugin_element = instance->container()->element(); + *security_origin = plugin_element.document().securityOrigin(); + return true; +} + +// Convert the given vector to an array of C-strings. The strings in the +// returned vector are only guaranteed valid so long as the vector of strings +// is not modified. +scoped_ptr<const char*[]> StringVectorToArgArray( + const std::vector<std::string>& vector) { + scoped_ptr<const char*[]> array(new const char*[vector.size()]); + for (size_t i = 0; i < vector.size(); ++i) + array[i] = vector[i].c_str(); + return array.Pass(); +} + +} // namespace + +// static +PluginInstanceImpl* PluginInstanceImpl::Create(PluginDelegate* delegate, + content::RenderView* render_view, + PluginModule* module, + WebPluginContainer* container, + const GURL& plugin_url) { + base::Callback<const void*(const char*)> get_plugin_interface_func = + base::Bind(&PluginModule::GetPluginInterface, module); + PPP_Instance_Combined* ppp_instance_combined = + PPP_Instance_Combined::Create(get_plugin_interface_func); + if (!ppp_instance_combined) + return NULL; + return new PluginInstanceImpl(delegate, render_view, module, + ppp_instance_combined, container, plugin_url); +} + +PluginInstanceImpl::NaClDocumentLoader::NaClDocumentLoader() + : finished_loading_(false) { +} + +PluginInstanceImpl::NaClDocumentLoader::~NaClDocumentLoader(){ +} + +void PluginInstanceImpl::NaClDocumentLoader::ReplayReceivedData( + WebURLLoaderClient* document_loader) { + for (std::list<std::string>::iterator it = data_.begin(); + it != data_.end(); ++it) { + document_loader->didReceiveData(NULL, it->c_str(), it->length(), + 0 /* encoded_data_length */); + } + if (finished_loading_) { + document_loader->didFinishLoading(NULL, + 0 /* finish_time */); + } + if (error_.get()) { + document_loader->didFail(NULL, *error_); + } +} + +void PluginInstanceImpl::NaClDocumentLoader::didReceiveData( + WebURLLoader* loader, + const char* data, + int data_length, + int encoded_data_length) { + data_.push_back(std::string(data, data_length)); +} + +void PluginInstanceImpl::NaClDocumentLoader::didFinishLoading( + WebURLLoader* loader, + double finish_time) { + DCHECK(!finished_loading_); + finished_loading_ = true; +} + +void PluginInstanceImpl::NaClDocumentLoader::didFail( + WebURLLoader* loader, + const WebURLError& error) { + DCHECK(!error_.get()); + error_.reset(new WebURLError(error)); +} + +PluginInstanceImpl::GamepadImpl::GamepadImpl(PluginDelegate* delegate) + : Resource(::ppapi::Resource::Untracked()), + delegate_(delegate) { +} + +PluginInstanceImpl::GamepadImpl::~GamepadImpl() { +} + +PPB_Gamepad_API* PluginInstanceImpl::GamepadImpl::AsPPB_Gamepad_API() { + return this; +} + +void PluginInstanceImpl::GamepadImpl::Sample(PP_Instance instance, + PP_GamepadsSampleData* data) { + WebKit::WebGamepads webkit_data; + delegate_->SampleGamepads(&webkit_data); + ConvertWebKitGamepadData( + *reinterpret_cast<const ::ppapi::WebKitGamepads*>(&webkit_data), data); +} + +PluginInstanceImpl::PluginInstanceImpl( + PluginDelegate* delegate, + content::RenderView* render_view, + PluginModule* module, + ::ppapi::PPP_Instance_Combined* instance_interface, + WebPluginContainer* container, + const GURL& plugin_url) + : delegate_(delegate), + render_view_(render_view), + module_(module), + instance_interface_(instance_interface), + pp_instance_(0), + container_(container), + layer_bound_to_fullscreen_(false), + plugin_url_(plugin_url), + full_frame_(false), + sent_initial_did_change_view_(false), + view_change_weak_ptr_factory_(this), + bound_graphics_2d_platform_(NULL), + has_webkit_focus_(false), + has_content_area_focus_(false), + find_identifier_(-1), + plugin_find_interface_(NULL), + plugin_input_event_interface_(NULL), + plugin_messaging_interface_(NULL), + plugin_mouse_lock_interface_(NULL), + plugin_pdf_interface_(NULL), + plugin_private_interface_(NULL), + plugin_selection_interface_(NULL), + plugin_textinput_interface_(NULL), + plugin_zoom_interface_(NULL), + checked_for_plugin_input_event_interface_(false), + checked_for_plugin_messaging_interface_(false), + checked_for_plugin_pdf_interface_(false), + gamepad_impl_(new GamepadImpl(delegate)), + plugin_print_interface_(NULL), + plugin_graphics_3d_interface_(NULL), + always_on_top_(false), + fullscreen_container_(NULL), + flash_fullscreen_(false), + desired_fullscreen_state_(false), + sad_plugin_(NULL), + input_event_mask_(0), + filtered_input_event_mask_(0), + text_input_type_(kPluginDefaultTextInputType), + text_input_caret_(0, 0, 0, 0), + text_input_caret_bounds_(0, 0, 0, 0), + text_input_caret_set_(false), + selection_caret_(0), + selection_anchor_(0), + pending_user_gesture_(0.0), + document_loader_(NULL), + nacl_document_load_(false), + npp_(new NPP_t), + isolate_(v8::Isolate::GetCurrent()) { + pp_instance_ = HostGlobals::Get()->AddInstance(this); + + memset(¤t_print_settings_, 0, sizeof(current_print_settings_)); + DCHECK(delegate); + module_->InstanceCreated(this); + delegate_->InstanceCreated(this); + + view_data_.is_page_visible = delegate->IsPageVisible(); + resource_creation_ = delegate_->CreateResourceCreationAPI(this); + + // TODO(bbudge) remove this when the trusted NaCl plugin has been removed. + // We must defer certain plugin events for NaCl instances since we switch + // from the in-process to the out-of-process proxy after instantiating them. + if (module->name() == "Native Client") + nacl_document_load_ = true; +} + +PluginInstanceImpl::~PluginInstanceImpl() { + DCHECK(!fullscreen_container_); + + // Force-unbind any Graphics. In the case of Graphics2D, if the plugin + // leaks the graphics 2D, it may actually get cleaned up after our + // destruction, so we need its pointers to be up-to-date. + BindGraphics(pp_instance(), 0); + + // 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 = plugin_object_copy.begin(); + i != plugin_object_copy.end(); ++i) + delete *i; + + if (TrackedCallback::IsPending(lock_mouse_callback_)) + lock_mouse_callback_->Abort(); + + delegate_->InstanceDeleted(this); + module_->InstanceDeleted(this); + // If we switched from the NaCl plugin module, notify it too. + if (original_module_.get()) + original_module_->InstanceDeleted(this); + + // This should be last since some of the above "instance deleted" calls will + // want to look up in the global map to get info off of our object. + HostGlobals::Get()->InstanceDeleted(pp_instance_); +} + +// NOTE: Any of these methods that calls into the plugin needs to take into +// account that the plugin may use Var to remove the <embed> from the DOM, which +// will make the WebPluginImpl drop its reference, usually the last one. If a +// method needs to access a member of the instance after the call has returned, +// then it needs to keep its own reference on the stack. + +void PluginInstanceImpl::Delete() { + // Keep a reference on the stack. See NOTE above. + scoped_refptr<PluginInstanceImpl> ref(this); + // Force the MessageChannel to release its "passthrough object" which should + // release our last reference to the "InstanceObject" and will probably + // destroy it. We want to do this prior to calling DidDestroy in case the + // destructor of the instance object tries to use the instance. + message_channel_->SetPassthroughObject(NULL); + // If this is a NaCl plugin instance, shut down the NaCl plugin by calling + // its DidDestroy. Don't call DidDestroy on the untrusted plugin instance, + // since there is little that it can do at this point. + if (original_instance_interface_) + original_instance_interface_->DidDestroy(pp_instance()); + else + instance_interface_->DidDestroy(pp_instance()); + // Ensure we don't attempt to call functions on the destroyed instance. + original_instance_interface_.reset(); + instance_interface_.reset(); + + if (fullscreen_container_) { + fullscreen_container_->Destroy(); + fullscreen_container_ = NULL; + } + bound_graphics_3d_ = NULL; + UpdateLayer(); + container_ = NULL; +} + +void PluginInstanceImpl::Paint(WebCanvas* canvas, + const gfx::Rect& plugin_rect, + const gfx::Rect& paint_rect) { + TRACE_EVENT0("ppapi", "PluginInstance::Paint"); + if (module()->is_crashed()) { + // Crashed plugin painting. + if (!sad_plugin_) // Lazily initialize bitmap. + sad_plugin_ = delegate_->GetSadPluginBitmap(); + if (sad_plugin_) + webkit::PaintSadPlugin(canvas, plugin_rect, *sad_plugin_); + return; + } + + PluginDelegate::PlatformGraphics2D* bound_graphics_2d = GetBoundGraphics2D(); + if (bound_graphics_2d) + bound_graphics_2d->Paint(canvas, plugin_rect, paint_rect); +} + +void PluginInstanceImpl::InvalidateRect(const gfx::Rect& rect) { + if (fullscreen_container_) { + if (rect.IsEmpty()) + fullscreen_container_->Invalidate(); + else + fullscreen_container_->InvalidateRect(rect); + } else { + if (!container_ || + view_data_.rect.size.width == 0 || view_data_.rect.size.height == 0) + return; // Nothing to do. + if (rect.IsEmpty()) + container_->invalidate(); + else + container_->invalidateRect(rect); + } +} + +void PluginInstanceImpl::ScrollRect(int dx, int dy, const gfx::Rect& rect) { + if (fullscreen_container_) { + fullscreen_container_->ScrollRect(dx, dy, rect); + } else { + if (full_frame_ && !IsViewAccelerated()) { + container_->scrollRect(dx, dy, rect); + } else { + // Can't do optimized scrolling since there could be other elements on top + // of us or the view renders via the accelerated compositor which is + // incompatible with the move and backfill scrolling model. + InvalidateRect(rect); + } + } +} + +void PluginInstanceImpl::CommitBackingTexture() { + if (texture_layer_.get()) + texture_layer_->SetNeedsDisplay(); +} + +void PluginInstanceImpl::InstanceCrashed() { + // Force free all resources and vars. + HostGlobals::Get()->InstanceCrashed(pp_instance()); + + // Free any associated graphics. + SetFullscreen(false); + FlashSetFullscreen(false, false); + // Unbind current 2D or 3D graphics context. + BindGraphics(pp_instance(), 0); + InvalidateRect(gfx::Rect()); + + delegate()->PluginCrashed(this); +} + +static void SetGPUHistogram(const ::ppapi::Preferences& prefs, + const std::vector<std::string>& arg_names, + const std::vector<std::string>& arg_values) { + // Calculate a histogram to let us determine how likely people are to try to + // run Stage3D content on machines that have it blacklisted. +#if defined(OS_WIN) + bool needs_gpu = false; + bool is_xp = base::win::GetVersion() <= base::win::VERSION_XP; + + for (size_t i = 0; i < arg_names.size(); i++) { + if (arg_names[i] == "wmode") { + // In theory content other than Flash could have a "wmode" argument, + // but that's pretty unlikely. + if (arg_values[i] == "direct" || arg_values[i] == "gpu") + needs_gpu = true; + break; + } + } + // 0 : No 3D content and GPU is blacklisted + // 1 : No 3D content and GPU is not blacklisted + // 2 : 3D content but GPU is blacklisted + // 3 : 3D content and GPU is not blacklisted + // 4 : No 3D content and GPU is blacklisted on XP + // 5 : No 3D content and GPU is not blacklisted on XP + // 6 : 3D content but GPU is blacklisted on XP + // 7 : 3D content and GPU is not blacklisted on XP + UMA_HISTOGRAM_ENUMERATION("Flash.UsesGPU", + is_xp * 4 + needs_gpu * 2 + prefs.is_webgl_supported, 8); +#endif +} + +bool PluginInstanceImpl::Initialize(const std::vector<std::string>& arg_names, + const std::vector<std::string>& arg_values, + bool full_frame) { + message_channel_.reset(new MessageChannel(this)); + + full_frame_ = full_frame; + + UpdateTouchEventRequest(); + container_->setWantsWheelEvents(IsAcceptingWheelEvents()); + + SetGPUHistogram(delegate_->GetPreferences(), arg_names, arg_values); + + argn_ = arg_names; + argv_ = arg_values; + scoped_ptr<const char*[]> argn_array(StringVectorToArgArray(argn_)); + scoped_ptr<const char*[]> argv_array(StringVectorToArgArray(argv_)); + bool success = PP_ToBool(instance_interface_->DidCreate(pp_instance(), + argn_.size(), + argn_array.get(), + argv_array.get())); + if (success) + message_channel_->StopQueueingJavaScriptMessages(); + return success; +} + +bool PluginInstanceImpl::HandleDocumentLoad( + const WebKit::WebURLResponse& response) { + DCHECK(!document_loader_); + if (!nacl_document_load_) { + if (module()->is_crashed()) { + // Don't create a resource for a crashed plugin. + container()->element().document().frame()->stopLoading(); + return false; + } + delegate()->HandleDocumentLoad(this, response); + // If the load was not abandoned, document_loader_ will now be set. It's + // possible that the load was canceled by now and document_loader_ was + // already nulled out. + } else { + // The NaCl proxy isn't available, so save the response and record document + // load notifications for later replay. + nacl_document_response_ = response; + nacl_document_loader_.reset(new NaClDocumentLoader()); + document_loader_ = nacl_document_loader_.get(); + } + return true; +} + +bool PluginInstanceImpl::SendCompositionEventToPlugin( + PP_InputEvent_Type type, const base::string16& text) { + std::vector<WebKit::WebCompositionUnderline> empty; + return SendCompositionEventWithUnderlineInformationToPlugin( + type, text, empty, static_cast<int>(text.size()), + static_cast<int>(text.size())); +} + +bool PluginInstanceImpl::SendCompositionEventWithUnderlineInformationToPlugin( + PP_InputEvent_Type type, + const base::string16& text, + const std::vector<WebKit::WebCompositionUnderline>& underlines, + int selection_start, + int selection_end) { + // Keep a reference on the stack. See NOTE above. + scoped_refptr<PluginInstanceImpl> ref(this); + + if (!LoadInputEventInterface()) + return false; + + PP_InputEvent_Class event_class = PP_INPUTEVENT_CLASS_IME; + if (!(filtered_input_event_mask_ & event_class) && + !(input_event_mask_ & event_class)) + return false; + + ::ppapi::InputEventData event; + event.event_type = type; + event.event_time_stamp = ::ppapi::TimeTicksToPPTimeTicks( + base::TimeTicks::Now()); + + // Convert UTF16 text to UTF8 with offset conversion. + std::vector<size_t> utf16_offsets; + utf16_offsets.push_back(selection_start); + utf16_offsets.push_back(selection_end); + for (size_t i = 0; i < underlines.size(); ++i) { + utf16_offsets.push_back(underlines[i].startOffset); + utf16_offsets.push_back(underlines[i].endOffset); + } + std::vector<size_t> utf8_offsets(utf16_offsets); + event.character_text = base::UTF16ToUTF8AndAdjustOffsets(text, &utf8_offsets); + + // Set the converted selection range. + event.composition_selection_start = (utf8_offsets[0] == std::string::npos ? + event.character_text.size() : utf8_offsets[0]); + event.composition_selection_end = (utf8_offsets[1] == std::string::npos ? + event.character_text.size() : utf8_offsets[1]); + + // Set the converted segmentation points. + // Be sure to add 0 and size(), and remove duplication or errors. + std::set<size_t> offset_set(utf8_offsets.begin()+2, utf8_offsets.end()); + offset_set.insert(0); + offset_set.insert(event.character_text.size()); + offset_set.erase(std::string::npos); + event.composition_segment_offsets.assign(offset_set.begin(), + offset_set.end()); + + // Set the composition target. + for (size_t i = 0; i < underlines.size(); ++i) { + if (underlines[i].thick) { + std::vector<uint32_t>::iterator it = + std::find(event.composition_segment_offsets.begin(), + event.composition_segment_offsets.end(), + utf8_offsets[2*i+2]); + if (it != event.composition_segment_offsets.end()) { + event.composition_target_segment = + it - event.composition_segment_offsets.begin(); + break; + } + } + } + + // Send the event. + bool handled = false; + if (filtered_input_event_mask_ & event_class) + event.is_filtered = true; + else + handled = true; // Unfiltered events are assumed to be handled. + scoped_refptr<PPB_InputEvent_Shared> event_resource( + new PPB_InputEvent_Shared(::ppapi::OBJECT_IS_IMPL, pp_instance(), event)); + handled |= PP_ToBool(plugin_input_event_interface_->HandleInputEvent( + pp_instance(), event_resource->pp_resource())); + return handled; +} + +void PluginInstanceImpl::RequestInputEventsHelper(uint32_t event_classes) { + if (event_classes & PP_INPUTEVENT_CLASS_TOUCH) + UpdateTouchEventRequest(); + if (event_classes & PP_INPUTEVENT_CLASS_WHEEL) + container_->setWantsWheelEvents(IsAcceptingWheelEvents()); +} + +bool PluginInstanceImpl::HandleCompositionStart(const base::string16& text) { + return SendCompositionEventToPlugin(PP_INPUTEVENT_TYPE_IME_COMPOSITION_START, + text); +} + +bool PluginInstanceImpl::HandleCompositionUpdate( + const base::string16& text, + const std::vector<WebKit::WebCompositionUnderline>& underlines, + int selection_start, + int selection_end) { + return SendCompositionEventWithUnderlineInformationToPlugin( + PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE, + text, underlines, selection_start, selection_end); +} + +bool PluginInstanceImpl::HandleCompositionEnd(const base::string16& text) { + return SendCompositionEventToPlugin(PP_INPUTEVENT_TYPE_IME_COMPOSITION_END, + text); +} + +bool PluginInstanceImpl::HandleTextInput(const base::string16& text) { + return SendCompositionEventToPlugin(PP_INPUTEVENT_TYPE_IME_TEXT, + text); +} + +void PluginInstanceImpl::GetSurroundingText(base::string16* text, + ui::Range* range) const { + std::vector<size_t> offsets; + offsets.push_back(selection_anchor_); + offsets.push_back(selection_caret_); + *text = base::UTF8ToUTF16AndAdjustOffsets(surrounding_text_, &offsets); + range->set_start(offsets[0] == base::string16::npos ? text->size() + : offsets[0]); + range->set_end(offsets[1] == base::string16::npos ? text->size() + : offsets[1]); +} + +bool PluginInstanceImpl::IsPluginAcceptingCompositionEvents() const { + return (filtered_input_event_mask_ & PP_INPUTEVENT_CLASS_IME) || + (input_event_mask_ & PP_INPUTEVENT_CLASS_IME); +} + +gfx::Rect PluginInstanceImpl::GetCaretBounds() const { + if (!text_input_caret_set_) { + // If it is never set by the plugin, use the bottom left corner. + return gfx::Rect(view_data_.rect.point.x, + view_data_.rect.point.y + view_data_.rect.size.height, + 0, 0); + } + + // TODO(kinaba) Take CSS transformation into accont. + // TODO(kinaba) Take bounding_box into account. On some platforms, an + // "exclude rectangle" where candidate window must avoid the region can be + // passed to IME. Currently, we pass only the caret rectangle because + // it is the only information supported uniformly in Chromium. + gfx::Rect caret(text_input_caret_); + caret.Offset(view_data_.rect.point.x, view_data_.rect.point.y); + return caret; +} + +bool PluginInstanceImpl::HandleInputEvent(const WebKit::WebInputEvent& event, + WebCursorInfo* cursor_info) { + TRACE_EVENT0("ppapi", "PluginInstanceImpl::HandleInputEvent"); + + if (WebInputEvent::isMouseEventType(event.type)) + delegate()->DidReceiveMouseEvent(this); + + // Don't dispatch input events to crashed plugins. + if (module()->is_crashed()) + return false; + + // Keep a reference on the stack. See NOTE above. + scoped_refptr<PluginInstanceImpl> ref(this); + + bool rv = false; + if (LoadInputEventInterface()) { + PP_InputEvent_Class event_class = ClassifyInputEvent(event.type); + if (!event_class) + return false; + + if ((filtered_input_event_mask_ & event_class) || + (input_event_mask_ & event_class)) { + // Actually send the event. + std::vector< ::ppapi::InputEventData > events; + CreateInputEventData(event, &events); + + // Allow the user gesture to be pending after the plugin handles the + // event. This allows out-of-process plugins to respond to the user + // gesture after processing has finished here. + if (WebUserGestureIndicator::isProcessingUserGesture()) { + pending_user_gesture_ = + ::ppapi::EventTimeToPPTimeTicks(event.timeStampSeconds); + pending_user_gesture_token_ = + WebUserGestureIndicator::currentUserGestureToken(); + pending_user_gesture_token_.setOutOfProcess(); + } + + // Each input event may generate more than one PP_InputEvent. + for (size_t i = 0; i < events.size(); i++) { + if (filtered_input_event_mask_ & event_class) + events[i].is_filtered = true; + else + rv = true; // Unfiltered events are assumed to be handled. + scoped_refptr<PPB_InputEvent_Shared> event_resource( + new PPB_InputEvent_Shared(::ppapi::OBJECT_IS_IMPL, + pp_instance(), events[i])); + + rv |= PP_ToBool(plugin_input_event_interface_->HandleInputEvent( + pp_instance(), event_resource->pp_resource())); + } + } + } + + if (cursor_) + *cursor_info = *cursor_; + return rv; +} + +void PluginInstanceImpl::HandleMessage(PP_Var message) { + TRACE_EVENT0("ppapi", "PluginInstanceImpl::HandleMessage"); + // Keep a reference on the stack. See NOTE above. + scoped_refptr<PluginInstanceImpl> ref(this); + if (!LoadMessagingInterface()) + return; + plugin_messaging_interface_->HandleMessage(pp_instance(), message); +} + +PP_Var PluginInstanceImpl::GetInstanceObject() { + // Keep a reference on the stack. See NOTE above. + scoped_refptr<PluginInstanceImpl> ref(this); + + // If the plugin supports the private instance interface, try to retrieve its + // instance object. + if (LoadPrivateInterface()) + return plugin_private_interface_->GetInstanceObject(pp_instance()); + return PP_MakeUndefined(); +} + +void PluginInstanceImpl::ViewChanged( + const gfx::Rect& position, + const gfx::Rect& clip, + const std::vector<gfx::Rect>& cut_outs_rects) { + // WebKit can give weird (x,y) positions for empty clip rects (since the + // position technically doesn't matter). But we want to make these + // consistent since this is given to the plugin, so force everything to 0 + // in the "everything is clipped" case. + gfx::Rect new_clip; + if (!clip.IsEmpty()) + new_clip = clip; + + cut_outs_rects_ = cut_outs_rects; + + view_data_.rect = PP_FromGfxRect(position); + view_data_.clip_rect = PP_FromGfxRect(clip); + view_data_.device_scale = container_->deviceScaleFactor(); + view_data_.css_scale = container_->pageZoomFactor() * + container_->pageScaleFactor(); + + if (desired_fullscreen_state_ || view_data_.is_fullscreen) { + WebElement element = container_->element(); + WebDocument document = element.document(); + bool is_fullscreen_element = (element == document.fullScreenElement()); + if (!view_data_.is_fullscreen && desired_fullscreen_state_ && + delegate()->IsInFullscreenMode() && is_fullscreen_element) { + // Entered fullscreen. Only possible via SetFullscreen(). + view_data_.is_fullscreen = true; + } else if (view_data_.is_fullscreen && !is_fullscreen_element) { + // Exited fullscreen. Possible via SetFullscreen() or F11/link, + // so desired_fullscreen_state might be out-of-date. + desired_fullscreen_state_ = false; + view_data_.is_fullscreen = false; + + // This operation will cause the plugin to re-layout which will send more + // DidChangeView updates. Schedule an asynchronous update and suppress + // notifications until that completes to avoid sending intermediate sizes + // to the plugins. + ScheduleAsyncDidChangeView(); + + // Reset the size attributes that we hacked to fill in the screen and + // retrigger ViewChanged. Make sure we don't forward duplicates of + // this view to the plugin. + ResetSizeAttributesAfterFullscreen(); + return; + } + } + + UpdateFlashFullscreenState(fullscreen_container_ != NULL); + + SendDidChangeView(); +} + +void PluginInstanceImpl::SetWebKitFocus(bool has_focus) { + if (has_webkit_focus_ == has_focus) + return; + + bool old_plugin_focus = PluginHasFocus(); + has_webkit_focus_ = has_focus; + if (PluginHasFocus() != old_plugin_focus) + SendFocusChangeNotification(); +} + +void PluginInstanceImpl::SetContentAreaFocus(bool has_focus) { + if (has_content_area_focus_ == has_focus) + return; + + bool old_plugin_focus = PluginHasFocus(); + has_content_area_focus_ = has_focus; + if (PluginHasFocus() != old_plugin_focus) + SendFocusChangeNotification(); +} + +void PluginInstanceImpl::PageVisibilityChanged(bool is_visible) { + if (is_visible == view_data_.is_page_visible) + return; // Nothing to do. + view_data_.is_page_visible = is_visible; + + // If the initial DidChangeView notification hasn't been sent to the plugin, + // let it pass the visibility state for us, instead of sending a notification + // immediately. It is possible that PluginInstanceImpl::ViewChanged() hasn't + // been called for the first time. In that case, most of the fields in + // |view_data_| haven't been properly initialized. + if (sent_initial_did_change_view_) + SendDidChangeView(); +} + +void PluginInstanceImpl::ViewWillInitiatePaint() { + if (GetBoundGraphics2D()) + GetBoundGraphics2D()->ViewWillInitiatePaint(); + else if (bound_graphics_3d_.get()) + bound_graphics_3d_->ViewWillInitiatePaint(); +} + +void PluginInstanceImpl::ViewInitiatedPaint() { + if (GetBoundGraphics2D()) + GetBoundGraphics2D()->ViewInitiatedPaint(); + else if (bound_graphics_3d_.get()) + bound_graphics_3d_->ViewInitiatedPaint(); +} + +void PluginInstanceImpl::ViewFlushedPaint() { + // Keep a reference on the stack. See NOTE above. + scoped_refptr<PluginInstanceImpl> ref(this); + if (GetBoundGraphics2D()) + GetBoundGraphics2D()->ViewFlushedPaint(); + else if (bound_graphics_3d_.get()) + bound_graphics_3d_->ViewFlushedPaint(); +} + +bool PluginInstanceImpl::GetBitmapForOptimizedPluginPaint( + const gfx::Rect& paint_bounds, + TransportDIB** dib, + gfx::Rect* location, + gfx::Rect* clip, + float* scale_factor) { + if (!always_on_top_) + return false; + if (!GetBoundGraphics2D() || !GetBoundGraphics2D()->IsAlwaysOpaque()) + return false; + + // We specifically want to compare against the area covered by the backing + // store when seeing if we cover the given paint bounds, since the backing + // store could be smaller than the declared plugin area. + PPB_ImageData_Impl* image_data = GetBoundGraphics2D()->ImageData(); + // ImageDatas created by NaCl don't have a PlatformImage, so can't be + // optimized this way. + if (!image_data->PlatformImage()) + return false; + + gfx::Point plugin_origin = PP_ToGfxPoint(view_data_.rect.point); + gfx::Vector2d plugin_offset = plugin_origin.OffsetFromOrigin(); + // Convert |paint_bounds| to be relative to the left-top corner of the plugin. + gfx::Rect relative_paint_bounds(paint_bounds); + relative_paint_bounds.Offset(-plugin_offset); + + gfx::Rect pixel_plugin_backing_store_rect( + 0, 0, image_data->width(), image_data->height()); + float scale = GetBoundGraphics2D()->GetScale(); + gfx::Rect plugin_backing_store_rect = gfx::ToEnclosedRect( + gfx::ScaleRect(pixel_plugin_backing_store_rect, scale)); + + gfx::Rect clip_page = PP_ToGfxRect(view_data_.clip_rect); + gfx::Rect plugin_paint_rect = + gfx::IntersectRects(plugin_backing_store_rect, clip_page); + if (!plugin_paint_rect.Contains(relative_paint_bounds)) + return false; + + // Don't do optimized painting if the area to paint intersects with the + // cut-out rects, otherwise we will paint over them. + for (std::vector<gfx::Rect>::const_iterator iter = cut_outs_rects_.begin(); + iter != cut_outs_rects_.end(); ++iter) { + if (relative_paint_bounds.Intersects(*iter)) + return false; + } + + *dib = image_data->PlatformImage()->GetTransportDIB(); + plugin_backing_store_rect.Offset(plugin_offset); + *location = plugin_backing_store_rect; + clip_page.Offset(plugin_offset); + *clip = clip_page; + // The plugin scale factor is inverted, e.g. for a device scale factor of 2x + // the plugin scale factor is 0.5. + *scale_factor = 1.0 / scale; + return true; +} + +base::string16 PluginInstanceImpl::GetSelectedText(bool html) { + // Keep a reference on the stack. See NOTE above. + scoped_refptr<PluginInstanceImpl> ref(this); + if (!LoadSelectionInterface()) + return base::string16(); + + PP_Var rv = plugin_selection_interface_->GetSelectedText(pp_instance(), + PP_FromBool(html)); + StringVar* string = StringVar::FromPPVar(rv); + base::string16 selection; + if (string) + selection = UTF8ToUTF16(string->value()); + // Release the ref the plugin transfered to us. + HostGlobals::Get()->GetVarTracker()->ReleaseVar(rv); + return selection; +} + +base::string16 PluginInstanceImpl::GetLinkAtPosition(const gfx::Point& point) { + // Keep a reference on the stack. See NOTE above. + scoped_refptr<PluginInstanceImpl> ref(this); + if (!LoadPdfInterface()) + return base::string16(); + + PP_Point p; + p.x = point.x(); + p.y = point.y(); + PP_Var rv = plugin_pdf_interface_->GetLinkAtPosition(pp_instance(), p); + StringVar* string = StringVar::FromPPVar(rv); + base::string16 link; + if (string) + link = UTF8ToUTF16(string->value()); + // Release the ref the plugin transfered to us. + PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(rv); + return link; +} + +void PluginInstanceImpl::RequestSurroundingText( + size_t desired_number_of_characters) { + // Keep a reference on the stack. See NOTE above. + scoped_refptr<PluginInstanceImpl> ref(this); + if (!LoadTextInputInterface()) + return; + plugin_textinput_interface_->RequestSurroundingText( + pp_instance(), desired_number_of_characters); +} + +void PluginInstanceImpl::Zoom(double factor, bool text_only) { + // Keep a reference on the stack. See NOTE above. + scoped_refptr<PluginInstanceImpl> ref(this); + if (!LoadZoomInterface()) + return; + plugin_zoom_interface_->Zoom(pp_instance(), factor, PP_FromBool(text_only)); +} + +bool PluginInstanceImpl::StartFind(const base::string16& search_text, + bool case_sensitive, + int identifier) { + // Keep a reference on the stack. See NOTE above. + scoped_refptr<PluginInstanceImpl> ref(this); + if (!LoadFindInterface()) + return false; + find_identifier_ = identifier; + return PP_ToBool( + plugin_find_interface_->StartFind( + pp_instance(), + UTF16ToUTF8(search_text.c_str()).c_str(), + PP_FromBool(case_sensitive))); +} + +void PluginInstanceImpl::SelectFindResult(bool forward) { + // Keep a reference on the stack. See NOTE above. + scoped_refptr<PluginInstanceImpl> ref(this); + if (LoadFindInterface()) + plugin_find_interface_->SelectFindResult(pp_instance(), + PP_FromBool(forward)); +} + +void PluginInstanceImpl::StopFind() { + // Keep a reference on the stack. See NOTE above. + scoped_refptr<PluginInstanceImpl> ref(this); + if (!LoadFindInterface()) + return; + find_identifier_ = -1; + plugin_find_interface_->StopFind(pp_instance()); +} + +bool PluginInstanceImpl::LoadFindInterface() { + if (!plugin_find_interface_) { + plugin_find_interface_ = + static_cast<const PPP_Find_Dev*>(module_->GetPluginInterface( + PPP_FIND_DEV_INTERFACE)); + } + + return !!plugin_find_interface_; +} + +bool PluginInstanceImpl::LoadInputEventInterface() { + if (!checked_for_plugin_input_event_interface_) { + checked_for_plugin_input_event_interface_ = true; + plugin_input_event_interface_ = + static_cast<const PPP_InputEvent*>(module_->GetPluginInterface( + PPP_INPUT_EVENT_INTERFACE)); + } + return !!plugin_input_event_interface_; +} + +bool PluginInstanceImpl::LoadMessagingInterface() { + if (!checked_for_plugin_messaging_interface_) { + checked_for_plugin_messaging_interface_ = true; + plugin_messaging_interface_ = + static_cast<const PPP_Messaging*>(module_->GetPluginInterface( + PPP_MESSAGING_INTERFACE)); + } + return !!plugin_messaging_interface_; +} + +bool PluginInstanceImpl::LoadMouseLockInterface() { + if (!plugin_mouse_lock_interface_) { + plugin_mouse_lock_interface_ = + static_cast<const PPP_MouseLock*>(module_->GetPluginInterface( + PPP_MOUSELOCK_INTERFACE)); + } + + return !!plugin_mouse_lock_interface_; +} + +bool PluginInstanceImpl::LoadPdfInterface() { + if (!checked_for_plugin_pdf_interface_) { + checked_for_plugin_pdf_interface_ = true; + plugin_pdf_interface_ = + static_cast<const PPP_Pdf_1*>(module_->GetPluginInterface( + PPP_PDF_INTERFACE_1)); + } + + return !!plugin_pdf_interface_; +} + +bool PluginInstanceImpl::LoadPrintInterface() { + // Only check for the interface if the plugin has dev permission. + if (!module_->permissions().HasPermission(::ppapi::PERMISSION_DEV)) + return false; + if (!plugin_print_interface_) { + plugin_print_interface_ = static_cast<const PPP_Printing_Dev*>( + module_->GetPluginInterface(PPP_PRINTING_DEV_INTERFACE)); + } + return !!plugin_print_interface_; +} + +bool PluginInstanceImpl::LoadPrivateInterface() { + // Only check for the interface if the plugin has private permission. + if (!module_->permissions().HasPermission(::ppapi::PERMISSION_PRIVATE)) + return false; + if (!plugin_private_interface_) { + plugin_private_interface_ = static_cast<const PPP_Instance_Private*>( + module_->GetPluginInterface(PPP_INSTANCE_PRIVATE_INTERFACE)); + } + + return !!plugin_private_interface_; +} + +bool PluginInstanceImpl::LoadSelectionInterface() { + if (!plugin_selection_interface_) { + plugin_selection_interface_ = + static_cast<const PPP_Selection_Dev*>(module_->GetPluginInterface( + PPP_SELECTION_DEV_INTERFACE)); + } + return !!plugin_selection_interface_; +} + +bool PluginInstanceImpl::LoadTextInputInterface() { + if (!plugin_textinput_interface_) { + plugin_textinput_interface_ = + static_cast<const PPP_TextInput_Dev*>(module_->GetPluginInterface( + PPP_TEXTINPUT_DEV_INTERFACE)); + } + + return !!plugin_textinput_interface_; +} + +bool PluginInstanceImpl::LoadZoomInterface() { + if (!plugin_zoom_interface_) { + plugin_zoom_interface_ = + static_cast<const PPP_Zoom_Dev*>(module_->GetPluginInterface( + PPP_ZOOM_DEV_INTERFACE)); + } + + return !!plugin_zoom_interface_; +} + +bool PluginInstanceImpl::PluginHasFocus() const { + return flash_fullscreen_ || (has_webkit_focus_ && has_content_area_focus_); +} + +void PluginInstanceImpl::SendFocusChangeNotification() { + // This call can happen during PluginInstanceImpl destruction, because WebKit + // informs the plugin it's losing focus. See crbug.com/236574 + if (!delegate_ || !instance_interface_) + return; + bool has_focus = PluginHasFocus(); + delegate()->PluginFocusChanged(this, has_focus); + instance_interface_->DidChangeFocus(pp_instance(), PP_FromBool(has_focus)); +} + +void PluginInstanceImpl::UpdateTouchEventRequest() { + bool raw_touch = (filtered_input_event_mask_ & PP_INPUTEVENT_CLASS_TOUCH) || + (input_event_mask_ & PP_INPUTEVENT_CLASS_TOUCH); + container_->requestTouchEventType(raw_touch ? + WebKit::WebPluginContainer::TouchEventRequestTypeRaw : + WebKit::WebPluginContainer::TouchEventRequestTypeSynthesizedMouse); +} + +bool PluginInstanceImpl::IsAcceptingWheelEvents() const { + return (filtered_input_event_mask_ & PP_INPUTEVENT_CLASS_WHEEL) || + (input_event_mask_ & PP_INPUTEVENT_CLASS_WHEEL); +} + +void PluginInstanceImpl::ScheduleAsyncDidChangeView() { + if (view_change_weak_ptr_factory_.HasWeakPtrs()) + return; // Already scheduled. + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&PluginInstanceImpl::SendAsyncDidChangeView, + view_change_weak_ptr_factory_.GetWeakPtr())); +} + +void PluginInstanceImpl::SendAsyncDidChangeView() { + // The bound callback that owns the weak pointer is still valid until after + // this function returns. SendDidChangeView checks HasWeakPtrs, so we need to + // invalidate them here. + // NOTE: If we ever want to have more than one pending callback, it should + // use a different factory, or we should have a different strategy here. + view_change_weak_ptr_factory_.InvalidateWeakPtrs(); + SendDidChangeView(); +} + +void PluginInstanceImpl::SendDidChangeView() { + // Don't send DidChangeView to crashed plugins. + if (module()->is_crashed()) + return; + + if (view_change_weak_ptr_factory_.HasWeakPtrs() || + (sent_initial_did_change_view_ && + last_sent_view_data_.Equals(view_data_))) + return; // Nothing to update. + + const PP_Size& size = view_data_.rect.size; + // Avoid sending a notification with a huge rectangle. + if (size.width < 0 || size.width > kMaxPluginSideLength || + size.height < 0 || size.height > kMaxPluginSideLength || + // We know this won't overflow due to above checks. + static_cast<uint32>(size.width) * static_cast<uint32>(size.height) > + kMaxPluginSize) { + return; + } + + sent_initial_did_change_view_ = true; + last_sent_view_data_ = view_data_; + ScopedPPResource resource( + ScopedPPResource::PassRef(), + (new PPB_View_Shared(::ppapi::OBJECT_IS_IMPL, + pp_instance(), view_data_))->GetReference()); + + instance_interface_->DidChangeView(pp_instance(), resource, + &view_data_.rect, + &view_data_.clip_rect); +} + +void PluginInstanceImpl::ReportGeometry() { + // If this call was delayed, we may have transitioned back to fullscreen in + // the mean time, so only report the geometry if we are actually in normal + // mode. + if (container_ && !fullscreen_container_ && !flash_fullscreen_) + container_->reportGeometry(); +} + +bool PluginInstanceImpl::GetPreferredPrintOutputFormat( + PP_PrintOutputFormat_Dev* format) { + // Keep a reference on the stack. See NOTE above. + scoped_refptr<PluginInstanceImpl> ref(this); + if (!LoadPrintInterface()) + return false; + uint32_t supported_formats = + plugin_print_interface_->QuerySupportedFormats(pp_instance()); + if (supported_formats & PP_PRINTOUTPUTFORMAT_PDF) { + *format = PP_PRINTOUTPUTFORMAT_PDF; + return true; + } + return false; +} + +bool PluginInstanceImpl::SupportsPrintInterface() { + PP_PrintOutputFormat_Dev format; + return GetPreferredPrintOutputFormat(&format); +} + +bool PluginInstanceImpl::IsPrintScalingDisabled() { + DCHECK(plugin_print_interface_); + if (!plugin_print_interface_) + return false; + return plugin_print_interface_->IsScalingDisabled(pp_instance()) == PP_TRUE; +} + +int PluginInstanceImpl::PrintBegin(const WebPrintParams& print_params) { + // Keep a reference on the stack. See NOTE above. + scoped_refptr<PluginInstanceImpl> ref(this); + PP_PrintOutputFormat_Dev format; + if (!GetPreferredPrintOutputFormat(&format)) { + // PrintBegin should not have been called since SupportsPrintInterface + // would have returned false; + NOTREACHED(); + return 0; + } + int num_pages = 0; + PP_PrintSettings_Dev print_settings; + print_settings.printable_area = PP_FromGfxRect(print_params.printableArea); + print_settings.content_area = PP_FromGfxRect(print_params.printContentArea); + print_settings.paper_size = PP_FromGfxSize(print_params.paperSize); + print_settings.dpi = print_params.printerDPI; + print_settings.orientation = PP_PRINTORIENTATION_NORMAL; + print_settings.grayscale = PP_FALSE; + print_settings.print_scaling_option = static_cast<PP_PrintScalingOption_Dev>( + print_params.printScalingOption); + print_settings.format = format; + num_pages = plugin_print_interface_->Begin(pp_instance(), + &print_settings); + if (!num_pages) + return 0; + current_print_settings_ = print_settings; + canvas_.clear(); + ranges_.clear(); + return num_pages; +} + +bool PluginInstanceImpl::PrintPage(int page_number, WebKit::WebCanvas* canvas) { +#if defined(ENABLE_PRINTING) + DCHECK(plugin_print_interface_); + PP_PrintPageNumberRange_Dev page_range; + page_range.first_page_number = page_range.last_page_number = page_number; + // The canvas only has a metafile on it for print preview. + bool save_for_later = + (printing::MetafileSkiaWrapper::GetMetafileFromCanvas(*canvas) != NULL); +#if defined(OS_MACOSX) || defined(OS_WIN) + save_for_later = save_for_later && skia::IsPreviewMetafile(*canvas); +#endif + if (save_for_later) { + ranges_.push_back(page_range); + canvas_ = skia::SharePtr(canvas); + return true; + } else { + return PrintPageHelper(&page_range, 1, canvas); + } +#else // defined(ENABLED_PRINTING) + return false; +#endif +} + +bool PluginInstanceImpl::PrintPageHelper( + PP_PrintPageNumberRange_Dev* page_ranges, + int num_ranges, + WebKit::WebCanvas* canvas) { + // Keep a reference on the stack. See NOTE above. + scoped_refptr<PluginInstanceImpl> ref(this); + DCHECK(plugin_print_interface_); + if (!plugin_print_interface_) + return false; + PP_Resource print_output = plugin_print_interface_->PrintPages( + pp_instance(), page_ranges, num_ranges); + if (!print_output) + return false; + + bool ret = false; + + if (current_print_settings_.format == PP_PRINTOUTPUTFORMAT_PDF) + ret = PrintPDFOutput(print_output, canvas); + + // Now we need to release the print output resource. + PluginModule::GetCore()->ReleaseResource(print_output); + + return ret; +} + +void PluginInstanceImpl::PrintEnd() { + // Keep a reference on the stack. See NOTE above. + scoped_refptr<PluginInstanceImpl> ref(this); + if (!ranges_.empty()) + PrintPageHelper(&(ranges_.front()), ranges_.size(), canvas_.get()); + canvas_.clear(); + ranges_.clear(); + + DCHECK(plugin_print_interface_); + if (plugin_print_interface_) + plugin_print_interface_->End(pp_instance()); + + memset(¤t_print_settings_, 0, sizeof(current_print_settings_)); +#if defined(OS_MACOSX) + last_printed_page_ = NULL; +#endif // defined(OS_MACOSX) +} + +bool PluginInstanceImpl::CanRotateView() { + if (!LoadPdfInterface()) + return false; + + return true; +} + +void PluginInstanceImpl::SetBoundGraphics2DForTest( + PluginDelegate::PlatformGraphics2D* graphics) { + BindGraphics(pp_instance(), 0); // Unbind any old stuff. + if (graphics) { + bound_graphics_2d_platform_ = graphics; + bound_graphics_2d_platform_->BindToInstance(this); + } +} + +void PluginInstanceImpl::RotateView(WebPlugin::RotationType type) { + if (!LoadPdfInterface()) + return; + PP_PrivatePageTransformType transform_type = + type == WebPlugin::RotationType90Clockwise ? + PP_PRIVATEPAGETRANSFORMTYPE_ROTATE_90_CW : + PP_PRIVATEPAGETRANSFORMTYPE_ROTATE_90_CCW; + plugin_pdf_interface_->Transform(pp_instance(), transform_type); + // NOTE: plugin instance may have been deleted. +} + +bool PluginInstanceImpl::FlashIsFullscreenOrPending() { + return fullscreen_container_ != NULL; +} + +bool PluginInstanceImpl::IsFullscreenOrPending() { + return desired_fullscreen_state_; +} + +bool PluginInstanceImpl::SetFullscreen(bool fullscreen) { + // Keep a reference on the stack. See NOTE above. + scoped_refptr<PluginInstanceImpl> ref(this); + + // Check whether we are trying to switch to the state we're already going + // to (i.e. if we're already switching to fullscreen but the fullscreen + // container isn't ready yet, don't do anything more). + if (fullscreen == IsFullscreenOrPending()) + return false; + + // Check whether we are trying to switch while the state is in transition. + // The 2nd request gets dropped while messing up the internal state, so + // disallow this. + if (view_data_.is_fullscreen != desired_fullscreen_state_) + return false; + + if (fullscreen && !IsProcessingUserGesture()) + return false; + + VLOG(1) << "Setting fullscreen to " << (fullscreen ? "on" : "off"); + desired_fullscreen_state_ = fullscreen; + + if (fullscreen) { + // Create the user gesture in case we're processing one that's pending. + WebScopedUserGesture user_gesture(CurrentUserGestureToken()); + // WebKit does not resize the plugin to fill the screen in fullscreen mode, + // so we will tweak plugin's attributes to support the expected behavior. + KeepSizeAttributesBeforeFullscreen(); + SetSizeAttributesForFullscreen(); + container_->element().requestFullScreen(); + } else { + container_->element().document().cancelFullScreen(); + } + return true; +} + +void PluginInstanceImpl::UpdateFlashFullscreenState(bool flash_fullscreen) { + bool is_mouselock_pending = TrackedCallback::IsPending(lock_mouse_callback_); + + if (flash_fullscreen == flash_fullscreen_) { + // Manually clear callback when fullscreen fails with mouselock pending. + if (!flash_fullscreen && is_mouselock_pending) + lock_mouse_callback_->Run(PP_ERROR_FAILED); + return; + } + + PPB_Graphics3D_Impl* graphics_3d = bound_graphics_3d_.get(); + if (graphics_3d) + UpdateLayer(); + + bool old_plugin_focus = PluginHasFocus(); + flash_fullscreen_ = flash_fullscreen; + if (is_mouselock_pending && !delegate()->IsMouseLocked(this)) { + if (!IsProcessingUserGesture() && + !module_->permissions().HasPermission( + ::ppapi::PERMISSION_BYPASS_USER_GESTURE)) { + lock_mouse_callback_->Run(PP_ERROR_NO_USER_GESTURE); + } else { + // Open a user gesture here so the Webkit user gesture checks will succeed + // for out-of-process plugins. + WebScopedUserGesture user_gesture(CurrentUserGestureToken()); + if (!delegate()->LockMouse(this)) + lock_mouse_callback_->Run(PP_ERROR_FAILED); + } + } + + if (PluginHasFocus() != old_plugin_focus) + SendFocusChangeNotification(); +} + +bool PluginInstanceImpl::IsViewAccelerated() { + if (!container_) + return false; + + WebDocument document = container_->element().document(); + WebFrame* frame = document.frame(); + if (!frame) + return false; + WebView* view = frame->view(); + if (!view) + return false; + + return view->isAcceleratedCompositingActive(); +} + +PluginDelegate::PlatformContext3D* PluginInstanceImpl::CreateContext3D() { + return delegate_->CreateContext3D(); +} + +bool PluginInstanceImpl::PrintPDFOutput(PP_Resource print_output, + WebKit::WebCanvas* canvas) { +#if defined(ENABLE_PRINTING) + ::ppapi::thunk::EnterResourceNoLock<PPB_Buffer_API> enter(print_output, true); + if (enter.failed()) + return false; + + BufferAutoMapper mapper(enter.object()); + if (!mapper.data() || !mapper.size()) { + NOTREACHED(); + return false; + } +#if defined(OS_WIN) + // For Windows, we need the PDF DLL to render the output PDF to a DC. + HMODULE pdf_module = GetModuleHandle(L"pdf.dll"); + if (!pdf_module) + return false; + RenderPDFPageToDCProc render_proc = + reinterpret_cast<RenderPDFPageToDCProc>( + GetProcAddress(pdf_module, "RenderPDFPageToDC")); + if (!render_proc) + return false; +#endif // defined(OS_WIN) + + bool ret = false; +#if defined(OS_LINUX) || defined(OS_MACOSX) + // On Linux we just set the final bits in the native metafile + // (NativeMetafile and PreviewMetafile must have compatible formats, + // i.e. both PDF for this to work). + printing::Metafile* metafile = + printing::MetafileSkiaWrapper::GetMetafileFromCanvas(*canvas); + DCHECK(metafile != NULL); + if (metafile) + ret = metafile->InitFromData(mapper.data(), mapper.size()); +#elif defined(OS_WIN) + printing::Metafile* metafile = + printing::MetafileSkiaWrapper::GetMetafileFromCanvas(*canvas); + if (metafile) { + // We only have a metafile when doing print preview, so we just want to + // pass the PDF off to preview. + ret = metafile->InitFromData(mapper.data(), mapper.size()); + } else { + // On Windows, we now need to render the PDF to the DC that backs the + // supplied canvas. + HDC dc = skia::BeginPlatformPaint(canvas); + DrawEmptyRectangle(dc); + gfx::Size size_in_pixels; + size_in_pixels.set_width(printing::ConvertUnit( + current_print_settings_.printable_area.size.width, + static_cast<int>(printing::kPointsPerInch), + current_print_settings_.dpi)); + size_in_pixels.set_height(printing::ConvertUnit( + current_print_settings_.printable_area.size.height, + static_cast<int>(printing::kPointsPerInch), + current_print_settings_.dpi)); + // We need to scale down DC to fit an entire page into DC available area. + // First, we'll try to use default scaling based on the 72dpi that is + // used in webkit for printing. + // If default scaling is not enough to fit the entire PDF without + // Current metafile is based on screen DC and have current screen size. + // Writing outside of those boundaries will result in the cut-off output. + // On metafiles (this is the case here), scaling down will still record + // original coordinates and we'll be able to print in full resolution. + // Before playback we'll need to counter the scaling up that will happen + // in the browser (printed_document_win.cc). + double dynamic_scale = gfx::CalculatePageScale(dc, size_in_pixels.width(), + size_in_pixels.height()); + double page_scale = static_cast<double>(printing::kPointsPerInch) / + static_cast<double>(current_print_settings_.dpi); + + if (dynamic_scale < page_scale) { + page_scale = dynamic_scale; + printing::MetafileSkiaWrapper::SetCustomScaleOnCanvas(*canvas, + page_scale); + } + + gfx::ScaleDC(dc, page_scale); + + ret = render_proc(static_cast<unsigned char*>(mapper.data()), mapper.size(), + 0, dc, current_print_settings_.dpi, + current_print_settings_.dpi, 0, 0, size_in_pixels.width(), + size_in_pixels.height(), true, false, true, true, true); + skia::EndPlatformPaint(canvas); + } +#endif // defined(OS_WIN) + + return ret; +#else // defined(ENABLE_PRINTING) + return false; +#endif +} + +PluginDelegate::PlatformGraphics2D* + PluginInstanceImpl::GetBoundGraphics2D() const { + return bound_graphics_2d_platform_; +} + +static void IgnoreCallback(unsigned, bool) {} + +void PluginInstanceImpl::UpdateLayer() { + if (!container_) + return; + + gpu::Mailbox mailbox; + if (bound_graphics_3d_.get()) { + PluginDelegate::PlatformContext3D* context = + bound_graphics_3d_->platform_context(); + context->GetBackingMailbox(&mailbox); + } + bool want_layer = !mailbox.IsZero(); + + if (want_layer == !!texture_layer_.get() && + layer_bound_to_fullscreen_ == !!fullscreen_container_) + return; + + if (texture_layer_.get()) { + if (!layer_bound_to_fullscreen_) + container_->setWebLayer(NULL); + else if (fullscreen_container_) + fullscreen_container_->SetLayer(NULL); + web_layer_.reset(); + texture_layer_ = NULL; + } + if (want_layer) { + DCHECK(bound_graphics_3d_.get()); + texture_layer_ = cc::TextureLayer::CreateForMailbox(NULL); + web_layer_.reset(new webkit::WebLayerImpl(texture_layer_)); + if (fullscreen_container_) { + fullscreen_container_->SetLayer(web_layer_.get()); + // Ignore transparency in fullscreen, since that's what Flash always + // wants to do, and that lets it not recreate a context if + // wmode=transparent was specified. + texture_layer_->SetContentsOpaque(true); + } else { + container_->setWebLayer(web_layer_.get()); + texture_layer_->SetContentsOpaque(bound_graphics_3d_->IsOpaque()); + } + texture_layer_->SetTextureMailbox( + cc::TextureMailbox(mailbox, base::Bind(&IgnoreCallback), 0)); + } + layer_bound_to_fullscreen_ = !!fullscreen_container_; +} + +void PluginInstanceImpl::AddPluginObject(PluginObject* plugin_object) { + DCHECK(live_plugin_objects_.find(plugin_object) == + live_plugin_objects_.end()); + live_plugin_objects_.insert(plugin_object); +} + +void PluginInstanceImpl::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); +} + +bool PluginInstanceImpl::IsProcessingUserGesture() { + PP_TimeTicks now = + ::ppapi::TimeTicksToPPTimeTicks(base::TimeTicks::Now()); + // Give a lot of slack so tests won't be flaky. + const PP_TimeTicks kUserGestureDurationInSeconds = 10.0; + return pending_user_gesture_token_.hasGestures() && + (now - pending_user_gesture_ < kUserGestureDurationInSeconds); +} + +WebUserGestureToken PluginInstanceImpl::CurrentUserGestureToken() { + if (!IsProcessingUserGesture()) + pending_user_gesture_token_ = WebUserGestureToken(); + return pending_user_gesture_token_; +} + +void PluginInstanceImpl::OnLockMouseACK(bool succeeded) { + if (TrackedCallback::IsPending(lock_mouse_callback_)) + lock_mouse_callback_->Run(succeeded ? PP_OK : PP_ERROR_FAILED); +} + +void PluginInstanceImpl::OnMouseLockLost() { + if (LoadMouseLockInterface()) + plugin_mouse_lock_interface_->MouseLockLost(pp_instance()); +} + +void PluginInstanceImpl::HandleMouseLockedInputEvent( + const WebKit::WebMouseEvent& event) { + // |cursor_info| is ignored since it is hidden when the mouse is locked. + WebKit::WebCursorInfo cursor_info; + HandleInputEvent(event, &cursor_info); +} + +void PluginInstanceImpl::SimulateInputEvent(const InputEventData& input_event) { + WebView* web_view = container()->element().document().frame()->view(); + if (!web_view) { + NOTREACHED(); + return; + } + + bool handled = SimulateIMEEvent(input_event); + if (handled) + return; + + std::vector<linked_ptr<WebInputEvent> > events = + CreateSimulatedWebInputEvents( + input_event, + view_data_.rect.point.x + view_data_.rect.size.width / 2, + view_data_.rect.point.y + view_data_.rect.size.height / 2); + for (std::vector<linked_ptr<WebInputEvent> >::iterator it = events.begin(); + it != events.end(); ++it) { + web_view->handleInputEvent(*it->get()); + } +} + +bool PluginInstanceImpl::SimulateIMEEvent(const InputEventData& input_event) { + switch (input_event.event_type) { + case PP_INPUTEVENT_TYPE_IME_COMPOSITION_START: + case PP_INPUTEVENT_TYPE_IME_COMPOSITION_UPDATE: + SimulateImeSetCompositionEvent(input_event); + break; + case PP_INPUTEVENT_TYPE_IME_COMPOSITION_END: + DCHECK(input_event.character_text.empty()); + SimulateImeSetCompositionEvent(input_event); + break; + case PP_INPUTEVENT_TYPE_IME_TEXT: + delegate()->SimulateImeConfirmComposition( + UTF8ToUTF16(input_event.character_text)); + break; + default: + return false; + } + return true; +} + +void PluginInstanceImpl::SimulateImeSetCompositionEvent( + const InputEventData& input_event) { + std::vector<size_t> offsets; + offsets.push_back(input_event.composition_selection_start); + offsets.push_back(input_event.composition_selection_end); + offsets.insert(offsets.end(), + input_event.composition_segment_offsets.begin(), + input_event.composition_segment_offsets.end()); + + base::string16 utf16_text = + base::UTF8ToUTF16AndAdjustOffsets(input_event.character_text, &offsets); + + std::vector<WebKit::WebCompositionUnderline> underlines; + for (size_t i = 2; i + 1 < offsets.size(); ++i) { + WebKit::WebCompositionUnderline underline; + underline.startOffset = offsets[i]; + underline.endOffset = offsets[i + 1]; + if (input_event.composition_target_segment == static_cast<int32_t>(i - 2)) + underline.thick = true; + underlines.push_back(underline); + } + + delegate()->SimulateImeSetComposition( + utf16_text, underlines, offsets[0], offsets[1]); +} + +ContentDecryptorDelegate* PluginInstanceImpl::GetContentDecryptorDelegate() { + if (content_decryptor_delegate_) + return content_decryptor_delegate_.get(); + + const PPP_ContentDecryptor_Private* plugin_decryption_interface = + static_cast<const PPP_ContentDecryptor_Private*>( + module_->GetPluginInterface( + PPP_CONTENTDECRYPTOR_PRIVATE_INTERFACE)); + if (!plugin_decryption_interface) + return NULL; + + content_decryptor_delegate_.reset( + new ContentDecryptorDelegate(pp_instance_, plugin_decryption_interface)); + return content_decryptor_delegate_.get(); +} + +PP_Bool PluginInstanceImpl::BindGraphics(PP_Instance instance, + PP_Resource device) { + TRACE_EVENT0("ppapi", "PluginInstanceImpl::BindGraphics"); + // The Graphics3D instance can't be destroyed until we call + // UpdateLayer(). + scoped_refptr< ::ppapi::Resource> old_graphics = bound_graphics_3d_.get(); + if (bound_graphics_3d_.get()) { + bound_graphics_3d_->BindToInstance(false); + bound_graphics_3d_ = NULL; + } + if (bound_graphics_2d_platform_) { + GetBoundGraphics2D()->BindToInstance(NULL); + bound_graphics_2d_platform_ = NULL; + } + + // Special-case clearing the current device. + if (!device) { + UpdateLayer(); + InvalidateRect(gfx::Rect()); + return PP_TRUE; + } + + // Refuse to bind if in transition to fullscreen with PPB_FlashFullscreen or + // to/from fullscreen with PPB_Fullscreen. + if ((fullscreen_container_ && !flash_fullscreen_) || + desired_fullscreen_state_ != view_data_.is_fullscreen) + return PP_FALSE; + + PluginDelegate::PlatformGraphics2D* graphics_2d = + delegate_->GetGraphics2D(this, device); + EnterResourceNoLock<PPB_Graphics3D_API> enter_3d(device, false); + PPB_Graphics3D_Impl* graphics_3d = enter_3d.succeeded() ? + static_cast<PPB_Graphics3D_Impl*>(enter_3d.object()) : NULL; + + if (graphics_2d) { + if (graphics_2d->BindToInstance(this)) { + bound_graphics_2d_platform_ = graphics_2d; + UpdateLayer(); + return PP_TRUE; + } + } else if (graphics_3d) { + // Make sure graphics can only be bound to the instance it is + // associated with. + if (graphics_3d->pp_instance() == pp_instance() && + graphics_3d->BindToInstance(true)) { + bound_graphics_3d_ = graphics_3d; + UpdateLayer(); + return PP_TRUE; + } + } + + // The instance cannot be bound or the device is not a valid resource type. + return PP_FALSE; +} + +PP_Bool PluginInstanceImpl::IsFullFrame(PP_Instance instance) { + return PP_FromBool(full_frame()); +} + +const ViewData* PluginInstanceImpl::GetViewData(PP_Instance instance) { + return &view_data_; +} + +PP_Bool PluginInstanceImpl::FlashIsFullscreen(PP_Instance instance) { + return PP_FromBool(flash_fullscreen_); +} + +PP_Var PluginInstanceImpl::GetWindowObject(PP_Instance instance) { + if (!container_) + return PP_MakeUndefined(); + + WebFrame* frame = container_->element().document().frame(); + if (!frame) + return PP_MakeUndefined(); + + return NPObjectToPPVar(this, frame->windowObject()); +} + +PP_Var PluginInstanceImpl::GetOwnerElementObject(PP_Instance instance) { + if (!container_) + return PP_MakeUndefined(); + return NPObjectToPPVar(this, container_->scriptableObjectForElement()); +} + +PP_Var PluginInstanceImpl::ExecuteScript(PP_Instance instance, + PP_Var script, + PP_Var* exception) { + // Executing the script may remove the plugin from the DOM, so we need to keep + // a reference to ourselves so that we can still process the result after the + // WebBindings::evaluate() below. + scoped_refptr<PluginInstanceImpl> ref(this); + TryCatch try_catch(exception); + if (try_catch.has_exception()) + return PP_MakeUndefined(); + + // Convert the script into an inconvenient NPString object. + StringVar* script_string = StringVar::FromPPVar(script); + if (!script_string) { + try_catch.SetException("Script param to ExecuteScript must be a string."); + return PP_MakeUndefined(); + } + NPString np_script; + np_script.UTF8Characters = script_string->value().c_str(); + np_script.UTF8Length = script_string->value().length(); + + // Get the current frame to pass to the evaluate function. + WebFrame* frame = container_->element().document().frame(); + if (!frame) { + try_catch.SetException("No frame to execute script in."); + return PP_MakeUndefined(); + } + + NPVariant result; + bool ok = false; + if (IsProcessingUserGesture()) { + WebKit::WebScopedUserGesture user_gesture(CurrentUserGestureToken()); + ok = WebBindings::evaluate(NULL, frame->windowObject(), &np_script, + &result); + } else { + ok = WebBindings::evaluate(NULL, frame->windowObject(), &np_script, + &result); + } + if (!ok) { + // TryCatch doesn't catch the exceptions properly. Since this is only for + // a trusted API, just set to a general exception message. + try_catch.SetException("Exception caught"); + WebBindings::releaseVariantValue(&result); + return PP_MakeUndefined(); + } + + PP_Var ret = NPVariantToPPVar(this, &result); + WebBindings::releaseVariantValue(&result); + return ret; +} + +uint32_t PluginInstanceImpl::GetAudioHardwareOutputSampleRate( + PP_Instance instance) { + return delegate()->GetAudioHardwareOutputSampleRate(); +} + +uint32_t PluginInstanceImpl::GetAudioHardwareOutputBufferSize( + PP_Instance instance) { + return delegate()->GetAudioHardwareOutputBufferSize(); +} + +PP_Var PluginInstanceImpl::GetDefaultCharSet(PP_Instance instance) { + std::string encoding = delegate()->GetDefaultEncoding(); + return StringVar::StringToPPVar(encoding); +} + +// These PPB_ContentDecryptor_Private calls are responses to +// PPP_ContentDecryptor_Private calls made on |content_decryptor_delegate_|. +// Therefore, |content_decryptor_delegate_| must have been initialized when +// the following methods are called. +void PluginInstanceImpl::NeedKey(PP_Instance instance, + PP_Var key_system_var, + PP_Var session_id_var, + PP_Var init_data_var) { + content_decryptor_delegate_->NeedKey( + key_system_var, session_id_var, init_data_var); +} + +void PluginInstanceImpl::KeyAdded(PP_Instance instance, + PP_Var key_system_var, + PP_Var session_id_var) { + content_decryptor_delegate_->KeyAdded(key_system_var, session_id_var); +} + +void PluginInstanceImpl::KeyMessage(PP_Instance instance, + PP_Var key_system_var, + PP_Var session_id_var, + PP_Var message_var, + PP_Var default_url_var) { + content_decryptor_delegate_->KeyMessage( + key_system_var, session_id_var, message_var, default_url_var); +} + +void PluginInstanceImpl::KeyError(PP_Instance instance, + PP_Var key_system_var, + PP_Var session_id_var, + int32_t media_error, + int32_t system_code) { + content_decryptor_delegate_->KeyError( + key_system_var, session_id_var, media_error, system_code); +} + +void PluginInstanceImpl::DeliverBlock(PP_Instance instance, + PP_Resource decrypted_block, + const PP_DecryptedBlockInfo* block_info) { + content_decryptor_delegate_->DeliverBlock(decrypted_block, block_info); +} + +void PluginInstanceImpl::DecoderInitializeDone( + PP_Instance instance, + PP_DecryptorStreamType decoder_type, + uint32_t request_id, + PP_Bool success) { + content_decryptor_delegate_->DecoderInitializeDone( + decoder_type, request_id, success); +} + +void PluginInstanceImpl::DecoderDeinitializeDone( + PP_Instance instance, + PP_DecryptorStreamType decoder_type, + uint32_t request_id) { + content_decryptor_delegate_->DecoderDeinitializeDone(decoder_type, + request_id); +} + +void PluginInstanceImpl::DecoderResetDone(PP_Instance instance, + PP_DecryptorStreamType decoder_type, + uint32_t request_id) { + content_decryptor_delegate_->DecoderResetDone(decoder_type, request_id); +} + + +void PluginInstanceImpl::DeliverFrame(PP_Instance instance, + PP_Resource decrypted_frame, + const PP_DecryptedFrameInfo* frame_info) { + content_decryptor_delegate_->DeliverFrame(decrypted_frame, frame_info); +} + +void PluginInstanceImpl::DeliverSamples( + PP_Instance instance, + PP_Resource audio_frames, + const PP_DecryptedBlockInfo* block_info) { + content_decryptor_delegate_->DeliverSamples(audio_frames, block_info); +} + +void PluginInstanceImpl::NumberOfFindResultsChanged(PP_Instance instance, + int32_t total, + PP_Bool final_result) { + DCHECK_NE(find_identifier_, -1); + delegate_->NumberOfFindResultsChanged(find_identifier_, total, + PP_ToBool(final_result)); +} + +void PluginInstanceImpl::SelectedFindResultChanged(PP_Instance instance, + int32_t index) { + DCHECK_NE(find_identifier_, -1); + delegate_->SelectedFindResultChanged(find_identifier_, index); +} + +PP_Bool PluginInstanceImpl::IsFullscreen(PP_Instance instance) { + return PP_FromBool(view_data_.is_fullscreen); +} + +PP_Bool PluginInstanceImpl::SetFullscreen(PP_Instance instance, + PP_Bool fullscreen) { + return PP_FromBool(SetFullscreen(PP_ToBool(fullscreen))); +} + +PP_Bool PluginInstanceImpl::GetScreenSize(PP_Instance instance, PP_Size* size) { + gfx::Size screen_size = delegate()->GetScreenSize(); + *size = PP_MakeSize(screen_size.width(), screen_size.height()); + return PP_TRUE; +} + +::ppapi::Resource* PluginInstanceImpl::GetSingletonResource( + PP_Instance instance, + ::ppapi::SingletonResourceID id) { + // Flash APIs and some others aren't implemented in-process. + switch (id) { + case ::ppapi::BROKER_SINGLETON_ID: + case ::ppapi::BROWSER_FONT_SINGLETON_ID: + case ::ppapi::CRX_FILESYSTEM_SINGLETON_ID: + case ::ppapi::EXTENSIONS_COMMON_SINGLETON_ID: + case ::ppapi::FLASH_CLIPBOARD_SINGLETON_ID: + case ::ppapi::FLASH_FILE_SINGLETON_ID: + case ::ppapi::FLASH_FULLSCREEN_SINGLETON_ID: + case ::ppapi::FLASH_SINGLETON_ID: + case ::ppapi::NETWORK_PROXY_SINGLETON_ID: + case ::ppapi::PDF_SINGLETON_ID: + case ::ppapi::TRUETYPE_FONT_SINGLETON_ID: + NOTIMPLEMENTED(); + return NULL; + case ::ppapi::GAMEPAD_SINGLETON_ID: + return gamepad_impl_.get(); + } + + NOTREACHED(); + return NULL; +} + +int32_t PluginInstanceImpl::RequestInputEvents(PP_Instance instance, + uint32_t event_classes) { + input_event_mask_ |= event_classes; + filtered_input_event_mask_ &= ~(event_classes); + RequestInputEventsHelper(event_classes); + return ValidateRequestInputEvents(false, event_classes); +} + +int32_t PluginInstanceImpl::RequestFilteringInputEvents( + PP_Instance instance, + uint32_t event_classes) { + filtered_input_event_mask_ |= event_classes; + input_event_mask_ &= ~(event_classes); + RequestInputEventsHelper(event_classes); + return ValidateRequestInputEvents(true, event_classes); +} + +void PluginInstanceImpl::ClearInputEventRequest(PP_Instance instance, + uint32_t event_classes) { + input_event_mask_ &= ~(event_classes); + filtered_input_event_mask_ &= ~(event_classes); + RequestInputEventsHelper(event_classes); +} + +void PluginInstanceImpl::ZoomChanged(PP_Instance instance, double factor) { + // We only want to tell the page to change its zoom if the whole page is the + // plugin. If we're in an iframe, then don't do anything. + if (!IsFullPagePlugin()) + return; + container()->zoomLevelChanged(WebView::zoomFactorToZoomLevel(factor)); +} + +void PluginInstanceImpl::ZoomLimitsChanged(PP_Instance instance, + double minimum_factor, + double maximium_factor) { + if (minimum_factor > maximium_factor) { + NOTREACHED(); + return; + } + delegate()->ZoomLimitsChanged(minimum_factor, maximium_factor); +} + +void PluginInstanceImpl::PostMessage(PP_Instance instance, PP_Var message) { + message_channel_->PostMessageToJavaScript(message); +} + +PP_Bool PluginInstanceImpl::SetCursor(PP_Instance instance, + PP_MouseCursor_Type type, + PP_Resource image, + const PP_Point* hot_spot) { + if (!ValidateSetCursorParams(type, image, hot_spot)) + return PP_FALSE; + + if (type != PP_MOUSECURSOR_TYPE_CUSTOM) { + DoSetCursor(new WebCursorInfo(static_cast<WebCursorInfo::Type>(type))); + return PP_TRUE; + } + + EnterResourceNoLock<PPB_ImageData_API> enter(image, true); + if (enter.failed()) + return PP_FALSE; + PPB_ImageData_Impl* image_data = + static_cast<PPB_ImageData_Impl*>(enter.object()); + + ImageDataAutoMapper auto_mapper(image_data); + if (!auto_mapper.is_valid()) + return PP_FALSE; + + scoped_ptr<WebCursorInfo> custom_cursor( + new WebCursorInfo(WebCursorInfo::TypeCustom)); + custom_cursor->hotSpot.x = hot_spot->x; + custom_cursor->hotSpot.y = hot_spot->y; + + const SkBitmap* bitmap = image_data->GetMappedBitmap(); + // Make a deep copy, so that the cursor remains valid even after the original + // image data gets freed. + if (!bitmap->copyTo(&custom_cursor->customImage.getSkBitmap(), + bitmap->config())) { + return PP_FALSE; + } + + DoSetCursor(custom_cursor.release()); + return PP_TRUE; +} + +int32_t PluginInstanceImpl::LockMouse(PP_Instance instance, + scoped_refptr<TrackedCallback> callback) { + if (TrackedCallback::IsPending(lock_mouse_callback_)) + return PP_ERROR_INPROGRESS; + + if (delegate()->IsMouseLocked(this)) + return PP_OK; + + if (!CanAccessMainFrame()) + return PP_ERROR_NOACCESS; + + if (!IsProcessingUserGesture()) + return PP_ERROR_NO_USER_GESTURE; + + // Attempt mouselock only if Flash isn't waiting on fullscreen, otherwise + // we wait and call LockMouse() in UpdateFlashFullscreenState(). + if (!FlashIsFullscreenOrPending() || flash_fullscreen()) { + // Open a user gesture here so the Webkit user gesture checks will succeed + // for out-of-process plugins. + WebScopedUserGesture user_gesture(CurrentUserGestureToken()); + if (!delegate()->LockMouse(this)) + return PP_ERROR_FAILED; + } + + // Either mouselock succeeded or a Flash fullscreen is pending. + lock_mouse_callback_ = callback; + return PP_OK_COMPLETIONPENDING; +} + +void PluginInstanceImpl::UnlockMouse(PP_Instance instance) { + delegate()->UnlockMouse(this); +} + +void PluginInstanceImpl::SetTextInputType(PP_Instance instance, + PP_TextInput_Type type) { + int itype = type; + if (itype < 0 || itype > ui::TEXT_INPUT_TYPE_URL) + itype = ui::TEXT_INPUT_TYPE_NONE; + text_input_type_ = static_cast<ui::TextInputType>(itype); + delegate()->PluginTextInputTypeChanged(this); +} + +void PluginInstanceImpl::UpdateCaretPosition(PP_Instance instance, + const PP_Rect& caret, + const PP_Rect& bounding_box) { + text_input_caret_ = PP_ToGfxRect(caret); + text_input_caret_bounds_ = PP_ToGfxRect(bounding_box); + text_input_caret_set_ = true; + delegate()->PluginCaretPositionChanged(this); +} + +void PluginInstanceImpl::CancelCompositionText(PP_Instance instance) { + delegate()->PluginRequestedCancelComposition(this); +} + +void PluginInstanceImpl::SelectionChanged(PP_Instance instance) { + // TODO(kinaba): currently the browser always calls RequestSurroundingText. + // It can be optimized so that it won't call it back until the information + // is really needed. + + // Avoid calling in nested context or else this will reenter the plugin. This + // uses a weak pointer rather than exploiting the fact that this class is + // refcounted because we don't actually want this operation to affect the + // lifetime of the instance. + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&PluginInstanceImpl::RequestSurroundingText, + AsWeakPtr(), + static_cast<size_t>(kExtraCharsForTextInput))); +} + +void PluginInstanceImpl::UpdateSurroundingText(PP_Instance instance, + const char* text, + uint32_t caret, + uint32_t anchor) { + surrounding_text_ = text; + selection_caret_ = caret; + selection_anchor_ = anchor; + delegate()->PluginSelectionChanged(this); +} + +PP_Var PluginInstanceImpl::ResolveRelativeToDocument( + PP_Instance instance, + PP_Var relative, + PP_URLComponents_Dev* components) { + StringVar* relative_string = StringVar::FromPPVar(relative); + if (!relative_string) + return PP_MakeNull(); + + WebElement plugin_element = container()->element(); + GURL document_url = plugin_element.document().baseURL(); + return ::ppapi::PPB_URLUtil_Shared::GenerateURLReturn( + document_url.Resolve(relative_string->value()), + components); +} + +PP_Bool PluginInstanceImpl::DocumentCanRequest(PP_Instance instance, + PP_Var url) { + StringVar* url_string = StringVar::FromPPVar(url); + if (!url_string) + return PP_FALSE; + + WebKit::WebSecurityOrigin security_origin; + if (!SecurityOriginForInstance(instance, &security_origin)) + return PP_FALSE; + + GURL gurl(url_string->value()); + if (!gurl.is_valid()) + return PP_FALSE; + + return BoolToPPBool(security_origin.canRequest(gurl)); +} + +PP_Bool PluginInstanceImpl::DocumentCanAccessDocument(PP_Instance instance, + PP_Instance target) { + WebKit::WebSecurityOrigin our_origin; + if (!SecurityOriginForInstance(instance, &our_origin)) + return PP_FALSE; + + WebKit::WebSecurityOrigin target_origin; + if (!SecurityOriginForInstance(instance, &target_origin)) + return PP_FALSE; + + return BoolToPPBool(our_origin.canAccess(target_origin)); +} + +PP_Var PluginInstanceImpl::GetDocumentURL(PP_Instance instance, + PP_URLComponents_Dev* components) { + WebKit::WebDocument document = container()->element().document(); + return ::ppapi::PPB_URLUtil_Shared::GenerateURLReturn(document.url(), + components); +} + +PP_Var PluginInstanceImpl::GetPluginInstanceURL( + PP_Instance instance, + PP_URLComponents_Dev* components) { + return ::ppapi::PPB_URLUtil_Shared::GenerateURLReturn(plugin_url_, + components); +} + +PP_ExternalPluginResult PluginInstanceImpl::ResetAsProxied( + scoped_refptr<PluginModule> module) { + // Save the original module and switch over to the new one now that this + // plugin is using the IPC-based proxy. + original_module_ = module_; + module_ = module; + + // Don't send any messages to the plugin until DidCreate() has finished. + message_channel_->QueueJavaScriptMessages(); + + // For NaCl instances, remember the NaCl plugin instance interface, so we + // can shut it down by calling its DidDestroy in our Delete() method. + original_instance_interface_.reset(instance_interface_.release()); + + base::Callback<const void*(const char*)> get_plugin_interface_func = + base::Bind(&PluginModule::GetPluginInterface, module_.get()); + PPP_Instance_Combined* ppp_instance_combined = + PPP_Instance_Combined::Create(get_plugin_interface_func); + if (!ppp_instance_combined) { + // The proxy must support at least one usable PPP_Instance interface. + // While this could be a failure to implement the interface in the NaCl + // module, it is more likely that the NaCl process has crashed. Either + // way, report that module initialization failed. + return PP_EXTERNAL_PLUGIN_ERROR_MODULE; + } + + instance_interface_.reset(ppp_instance_combined); + // Clear all PPP interfaces we may have cached. + plugin_find_interface_ = NULL; + plugin_input_event_interface_ = NULL; + checked_for_plugin_input_event_interface_ = false; + plugin_messaging_interface_ = NULL; + checked_for_plugin_messaging_interface_ = false; + plugin_mouse_lock_interface_ = NULL; + plugin_pdf_interface_ = NULL; + checked_for_plugin_pdf_interface_ = false; + plugin_private_interface_ = NULL; + plugin_selection_interface_ = NULL; + plugin_textinput_interface_ = NULL; + plugin_zoom_interface_ = NULL; + + // Re-send the DidCreate event via the proxy. + scoped_ptr<const char*[]> argn_array(StringVectorToArgArray(argn_)); + scoped_ptr<const char*[]> argv_array(StringVectorToArgArray(argv_)); + if (!instance_interface_->DidCreate(pp_instance(), argn_.size(), + argn_array.get(), argv_array.get())) + return PP_EXTERNAL_PLUGIN_ERROR_INSTANCE; + message_channel_->StopQueueingJavaScriptMessages(); + + // Clear sent_initial_did_change_view_ and cancel any pending DidChangeView + // event. This way, SendDidChangeView will send the "current" view + // immediately (before other events like HandleDocumentLoad). + sent_initial_did_change_view_ = false; + view_change_weak_ptr_factory_.InvalidateWeakPtrs(); + SendDidChangeView(); + + DCHECK(nacl_document_load_); + nacl_document_load_ = false; + if (!nacl_document_response_.isNull()) { + document_loader_ = NULL; + // Pass the response to the new proxy. + HandleDocumentLoad(nacl_document_response_); + nacl_document_response_ = WebKit::WebURLResponse(); + // Replay any document load events we've received to the real loader. + nacl_document_loader_->ReplayReceivedData(document_loader_); + nacl_document_loader_.reset(NULL); + } + + return PP_EXTERNAL_PLUGIN_OK; +} + +bool PluginInstanceImpl::IsValidInstanceOf(PluginModule* module) { + DCHECK(module); + return module == module_.get() || + module == original_module_.get(); +} + +NPP PluginInstanceImpl::instanceNPP() { + return npp_.get(); +} + +v8::Isolate* PluginInstanceImpl::GetIsolate() const { + return isolate_; +} + +PluginInstance* PluginInstance::Get(PP_Instance instance_id) { + return HostGlobals::Get()->GetInstance(instance_id); +} + +content::RenderView* PluginInstanceImpl::GetRenderView() { + return render_view_; +} + +WebKit::WebPluginContainer* PluginInstanceImpl::GetContainer() { + return container_; +} + +::ppapi::VarTracker* PluginInstanceImpl::GetVarTracker() { + return HostGlobals::Get()->GetVarTracker(); +} + +const GURL& PluginInstanceImpl::GetPluginURL() { + return plugin_url_; +} + +base::FilePath PluginInstanceImpl::GetModulePath() { + return module_->path(); +} + +PP_Resource PluginInstanceImpl::CreateExternalFileReference( + const base::FilePath& external_file_path) { + webkit::ppapi::PPB_FileRef_Impl* ref = + webkit::ppapi::PPB_FileRef_Impl::CreateExternal( + pp_instance(), external_file_path, ""); + return ref->GetReference(); +} + +PP_Resource PluginInstanceImpl::CreateImage(gfx::ImageSkia* source_image, + float scale) { + ui::ScaleFactor scale_factor = ui::GetScaleFactorFromScale(scale); + gfx::ImageSkiaRep image_skia_rep = source_image->GetRepresentation( + scale_factor); + + if (image_skia_rep.is_null() || image_skia_rep.scale_factor() != scale_factor) + return 0; + + scoped_refptr<webkit::ppapi::PPB_ImageData_Impl> image_data( + new webkit::ppapi::PPB_ImageData_Impl( + pp_instance(), + webkit::ppapi::PPB_ImageData_Impl::PLATFORM)); + if (!image_data->Init( + webkit::ppapi::PPB_ImageData_Impl::GetNativeImageDataFormat(), + image_skia_rep.pixel_width(), + image_skia_rep.pixel_height(), + false)) { + return 0; + } + + webkit::ppapi::ImageDataAutoMapper mapper(image_data.get()); + if (!mapper.is_valid()) + return 0; + + skia::PlatformCanvas* canvas = image_data->GetPlatformCanvas(); + // Note: Do not SkBitmap::copyTo the canvas bitmap directly because it will + // ignore the allocated pixels in shared memory and re-allocate a new buffer. + canvas->writePixels(image_skia_rep.sk_bitmap(), 0, 0); + + return image_data->GetReference(); +} + +PP_ExternalPluginResult PluginInstanceImpl::SwitchToOutOfProcessProxy( + const base::FilePath& file_path, + ::ppapi::PpapiPermissions permissions, + const IPC::ChannelHandle& channel_handle, + base::ProcessId plugin_pid, + int plugin_child_id) { + // Create a new module for each instance of the external plugin that is using + // the IPC based out-of-process proxy. We can't use the existing module, + // because it is configured for the in-process plugin, and we must keep it + // that way to allow the page to create other instances. + scoped_refptr<webkit::ppapi::PluginModule> external_plugin_module( + module_->CreateModuleForExternalPluginInstance()); + + content::RendererPpapiHost* renderer_ppapi_host = + delegate_->CreateExternalPluginModule( + external_plugin_module, + file_path, + permissions, + channel_handle, + plugin_pid, + plugin_child_id); + if (!renderer_ppapi_host) { + DLOG(ERROR) << "CreateExternalPluginModule() failed"; + return PP_EXTERNAL_PLUGIN_ERROR_MODULE; + } + + // Finally, switch the instance to the proxy. + return external_plugin_module->InitAsProxiedExternalPlugin(this); +} + +void PluginInstanceImpl::SetAlwaysOnTop(bool on_top) { + always_on_top_ = on_top; +} + +void PluginInstanceImpl::DoSetCursor(WebCursorInfo* cursor) { + cursor_.reset(cursor); + if (fullscreen_container_) { + fullscreen_container_->DidChangeCursor(*cursor); + } else { + delegate()->DidChangeCursor(this, *cursor); + } +} + +bool PluginInstanceImpl::IsFullPagePlugin() { + WebFrame* frame = container()->element().document().frame(); + return frame->view()->mainFrame()->document().isPluginDocument(); +} + +void PluginInstanceImpl::FlashSetFullscreen(bool fullscreen, + bool delay_report) { + TRACE_EVENT0("ppapi", "PluginInstanceImpl::FlashSetFullscreen"); + // Keep a reference on the stack. See NOTE above. + scoped_refptr<PluginInstanceImpl> ref(this); + + // We check whether we are trying to switch to the state we're already going + // to (i.e. if we're already switching to fullscreen but the fullscreen + // container isn't ready yet, don't do anything more). + if (fullscreen == FlashIsFullscreenOrPending()) + return; + + // Unbind current 2D or 3D graphics context. + VLOG(1) << "Setting fullscreen to " << (fullscreen ? "on" : "off"); + if (fullscreen) { + DCHECK(!fullscreen_container_); + fullscreen_container_ = delegate_->CreateFullscreenContainer(this); + UpdateLayer(); + } else { + DCHECK(fullscreen_container_); + fullscreen_container_->Destroy(); + fullscreen_container_ = NULL; + UpdateFlashFullscreenState(false); + if (!delay_report) { + ReportGeometry(); + } else { + base::MessageLoop::current()->PostTask( + FROM_HERE, base::Bind(&PluginInstanceImpl::ReportGeometry, this)); + } + } +} + +bool PluginInstanceImpl::IsRectTopmost(const gfx::Rect& rect) { + if (flash_fullscreen_) + return true; + + return container_->isRectTopmost(rect); +} + +int32_t PluginInstanceImpl::Navigate(const ::ppapi::URLRequestInfoData& request, + const char* target, + bool from_user_action) { + if (!container_) + return PP_ERROR_FAILED; + + WebDocument document = container_->element().document(); + WebFrame* frame = document.frame(); + if (!frame) + return PP_ERROR_FAILED; + + ::ppapi::URLRequestInfoData completed_request = request; + + WebURLRequest web_request; + if (!CreateWebURLRequest(&completed_request, frame, &web_request)) + return PP_ERROR_FAILED; + web_request.setFirstPartyForCookies(document.firstPartyForCookies()); + web_request.setHasUserGesture(from_user_action); + + GURL gurl(web_request.url()); + if (gurl.SchemeIs("javascript")) { + // In imitation of the NPAPI implementation, only |target_frame == frame| is + // allowed for security reasons. + WebFrame* target_frame = + frame->view()->findFrameByName(WebString::fromUTF8(target), frame); + if (target_frame != frame) + return PP_ERROR_NOACCESS; + + // TODO(viettrungluu): NPAPI sends the result back to the plugin -- do we + // need that? + WebString result = container_->executeScriptURL(gurl, from_user_action); + return result.isNull() ? PP_ERROR_FAILED : PP_OK; + } + + // Only GETs and POSTs are supported. + if (web_request.httpMethod() != "GET" && + web_request.httpMethod() != "POST") + return PP_ERROR_BADARGUMENT; + + WebString target_str = WebString::fromUTF8(target); + container_->loadFrameRequest(web_request, target_str, false, NULL); + return PP_OK; +} + +bool PluginInstanceImpl::CanAccessMainFrame() const { + if (!container_) + return false; + WebKit::WebDocument containing_document = container_->element().document(); + + if (!containing_document.frame() || + !containing_document.frame()->view() || + !containing_document.frame()->view()->mainFrame()) { + return false; + } + WebKit::WebDocument main_document = + containing_document.frame()->view()->mainFrame()->document(); + + return containing_document.securityOrigin().canAccess( + main_document.securityOrigin()); +} + +void PluginInstanceImpl::KeepSizeAttributesBeforeFullscreen() { + WebElement element = container_->element(); + width_before_fullscreen_ = element.getAttribute(WebString::fromUTF8(kWidth)); + height_before_fullscreen_ = + element.getAttribute(WebString::fromUTF8(kHeight)); + border_before_fullscreen_ = + element.getAttribute(WebString::fromUTF8(kBorder)); + style_before_fullscreen_ = element.getAttribute(WebString::fromUTF8(kStyle)); +} + +void PluginInstanceImpl::SetSizeAttributesForFullscreen() { + screen_size_for_fullscreen_ = delegate()->GetScreenSize(); + std::string width = StringPrintf("%d", screen_size_for_fullscreen_.width()); + std::string height = StringPrintf("%d", screen_size_for_fullscreen_.height()); + + WebElement element = container_->element(); + element.setAttribute(WebString::fromUTF8(kWidth), WebString::fromUTF8(width)); + element.setAttribute(WebString::fromUTF8(kHeight), + WebString::fromUTF8(height)); + element.setAttribute(WebString::fromUTF8(kBorder), WebString::fromUTF8("0")); + + // There should be no style settings that matter in fullscreen mode, + // so just replace them instead of appending. + // NOTE: "position: fixed" and "display: block" reset the plugin and + // using %% settings might not work without them (e.g. if the plugin is a + // child of a container element). + std::string style; + style += StringPrintf("width: %s !important; ", width.c_str()); + style += StringPrintf("height: %s !important; ", height.c_str()); + style += "margin: 0 !important; padding: 0 !important; border: 0 !important"; + container_->element().setAttribute(kStyle, WebString::fromUTF8(style)); +} + +void PluginInstanceImpl::ResetSizeAttributesAfterFullscreen() { + screen_size_for_fullscreen_ = gfx::Size(); + WebElement element = container_->element(); + element.setAttribute(WebString::fromUTF8(kWidth), width_before_fullscreen_); + element.setAttribute(WebString::fromUTF8(kHeight), height_before_fullscreen_); + element.setAttribute(WebString::fromUTF8(kBorder), border_before_fullscreen_); + element.setAttribute(WebString::fromUTF8(kStyle), style_before_fullscreen_); +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/ppapi_plugin_instance_impl.h b/content/renderer/pepper/ppapi_plugin_instance_impl.h new file mode 100644 index 0000000..c5d22c4 --- /dev/null +++ b/content/renderer/pepper/ppapi_plugin_instance_impl.h @@ -0,0 +1,864 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_PEPPER_PPAPI_PLUGIN_INSTANCE_IMPL_H_ +#define CONTENT_RENDERER_PEPPER_PPAPI_PLUGIN_INSTANCE_IMPL_H_ + +#include <set> +#include <string> +#include <vector> + +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/strings/string16.h" +#include "cc/layers/texture_layer_client.h" +#include "content/common/content_export.h" +#include "content/public/renderer/ppapi_plugin_instance.h" +#include "content/renderer/pepper/plugin_delegate.h" +#include "content/renderer/pepper/ppp_pdf.h" +#include "ppapi/c/dev/pp_cursor_type_dev.h" +#include "ppapi/c/dev/ppp_find_dev.h" +#include "ppapi/c/dev/ppp_printing_dev.h" +#include "ppapi/c/dev/ppp_selection_dev.h" +#include "ppapi/c/dev/ppp_text_input_dev.h" +#include "ppapi/c/dev/ppp_zoom_dev.h" +#include "ppapi/c/pp_completion_callback.h" +#include "ppapi/c/pp_instance.h" +#include "ppapi/c/pp_time.h" +#include "ppapi/c/pp_var.h" +#include "ppapi/c/ppb_audio_config.h" +#include "ppapi/c/ppb_gamepad.h" +#include "ppapi/c/ppb_input_event.h" +#include "ppapi/c/ppp_graphics_3d.h" +#include "ppapi/c/ppp_input_event.h" +#include "ppapi/c/ppp_messaging.h" +#include "ppapi/c/ppp_mouse_lock.h" +#include "ppapi/c/private/ppb_content_decryptor_private.h" +#include "ppapi/c/private/ppp_instance_private.h" +#include "ppapi/shared_impl/ppb_instance_shared.h" +#include "ppapi/shared_impl/ppb_view_shared.h" +#include "ppapi/shared_impl/singleton_resource_id.h" +#include "ppapi/shared_impl/tracked_callback.h" +#include "ppapi/thunk/ppb_gamepad_api.h" +#include "ppapi/thunk/resource_creation_api.h" +#include "skia/ext/refptr.h" +#include "third_party/WebKit/public/platform/WebCanvas.h" +#include "third_party/WebKit/public/platform/WebString.h" +#include "third_party/WebKit/public/platform/WebURLLoaderClient.h" +#include "third_party/WebKit/public/platform/WebURLResponse.h" +#include "third_party/WebKit/public/web/WebPlugin.h" +#include "third_party/WebKit/public/web/WebUserGestureToken.h" +#include "ui/base/ime/text_input_type.h" +#include "ui/gfx/rect.h" +#include "url/gurl.h" + +struct PP_Point; +struct _NPP; + +class SkBitmap; +class TransportDIB; + +namespace WebKit { +class WebInputEvent; +class WebLayer; +class WebMouseEvent; +class WebPluginContainer; +class WebURLLoader; +class WebURLResponse; +struct WebCompositionUnderline; +struct WebCursorInfo; +struct WebURLError; +struct WebPrintParams; +} + +namespace cc { +class TextureLayer; +} + +namespace ppapi { +class Resource; +struct InputEventData; +struct PPP_Instance_Combined; +} + +namespace ui { +class Range; +} + +namespace v8 { +class Isolate; +} + +namespace webkit { +namespace ppapi { + +class ContentDecryptorDelegate; +class FullscreenContainer; +class MessageChannel; +class PluginDelegate; +class PluginModule; +class PluginObject; +class PPB_Graphics3D_Impl; +class PPB_ImageData_Impl; +class PPB_URLLoader_Impl; + +// Represents one time a plugin appears on one web page. +// +// Note: to get from a PP_Instance to a PluginInstance*, use the +// ResourceTracker. +class CONTENT_EXPORT PluginInstanceImpl + : public base::RefCounted<PluginInstanceImpl>, + public base::SupportsWeakPtr<PluginInstanceImpl>, + public NON_EXPORTED_BASE(PluginInstance), + public ::ppapi::PPB_Instance_Shared { + public: + // Create and return a PluginInstanceImpl object which supports the most + // recent version of PPP_Instance possible by querying the given + // get_plugin_interface function. If the plugin does not support any valid + // PPP_Instance interface, returns NULL. + static PluginInstanceImpl* Create( + PluginDelegate* delegate, + content::RenderView* render_view, + PluginModule* module, + WebKit::WebPluginContainer* container, + const GURL& plugin_url); + PluginDelegate* delegate() const { return delegate_; } + PluginModule* module() const { return module_.get(); } + MessageChannel& message_channel() { return *message_channel_; } + + WebKit::WebPluginContainer* container() const { return container_; } + + // Returns the PP_Instance uniquely identifying this instance. Guaranteed + // nonzero. + PP_Instance pp_instance() const { return pp_instance_; } + + ::ppapi::PPP_Instance_Combined* instance_interface() const { + return instance_interface_.get(); + } + + ::ppapi::thunk::ResourceCreationAPI& resource_creation() { + return *resource_creation_.get(); + } + + // Does some pre-destructor cleanup on the instance. This is necessary + // because some cleanup depends on the plugin instance still existing (like + // calling the plugin's DidDestroy function). This function is called from + // the WebPlugin implementation when WebKit is about to remove the plugin. + void Delete(); + + // Paints the current backing store to the web page. + void Paint(WebKit::WebCanvas* canvas, + const gfx::Rect& plugin_rect, + const gfx::Rect& paint_rect); + + // Schedules a paint of the page for the given region. The coordinates are + // relative to the top-left of the plugin. This does nothing if the plugin + // has not yet been positioned. You can supply an empty gfx::Rect() to + // invalidate the entire plugin. + void InvalidateRect(const gfx::Rect& rect); + + // Schedules a scroll of the plugin. This uses optimized scrolling only for + // full-frame plugins, as otherwise there could be other elements on top. The + // slow path can also be triggered if there is an overlapping frame. + void ScrollRect(int dx, int dy, const gfx::Rect& rect); + + // Commit the backing texture to the screen once the side effects some + // rendering up to an offscreen SwapBuffers are visible. + void CommitBackingTexture(); + + // Called when the out-of-process plugin implementing this instance crashed. + void InstanceCrashed(); + + // PPB_Instance and PPB_Instance_Private implementation. + bool full_frame() const { return full_frame_; } + const ::ppapi::ViewData& view_data() const { return view_data_; } + + // PPP_Instance and PPP_Instance_Private. + bool Initialize(const std::vector<std::string>& arg_names, + const std::vector<std::string>& arg_values, + bool full_frame); + bool HandleDocumentLoad(const WebKit::WebURLResponse& response); + bool HandleInputEvent(const WebKit::WebInputEvent& event, + WebKit::WebCursorInfo* cursor_info); + PP_Var GetInstanceObject(); + void ViewChanged(const gfx::Rect& position, const gfx::Rect& clip, + const std::vector<gfx::Rect>& cut_outs_rects); + + // Handlers for composition events. + bool HandleCompositionStart(const base::string16& text); + bool HandleCompositionUpdate( + const base::string16& text, + const std::vector<WebKit::WebCompositionUnderline>& underlines, + int selection_start, + int selection_end); + bool HandleCompositionEnd(const base::string16& text); + bool HandleTextInput(const base::string16& text); + + // Gets the current text input status. + ui::TextInputType text_input_type() const { return text_input_type_; } + gfx::Rect GetCaretBounds() const; + bool IsPluginAcceptingCompositionEvents() const; + void GetSurroundingText(base::string16* text, ui::Range* range) const; + + // Notifications about focus changes, see has_webkit_focus_ below. + void SetWebKitFocus(bool has_focus); + void SetContentAreaFocus(bool has_focus); + + // Notification about page visibility. The default is "visible". + void PageVisibilityChanged(bool is_visible); + + // Notifications that the view is about to paint, has started painting, and + // has flushed the painted content to the screen. These messages are used to + // send Flush callbacks to the plugin for DeviceContext2D/3D. + void ViewWillInitiatePaint(); + void ViewInitiatedPaint(); + void ViewFlushedPaint(); + + // If this plugin can be painted merely by copying the backing store to the + // screen, and the plugin bounds encloses the given paint bounds, returns + // true. In this case, the location, clipping, and ID of the backing store + // will be filled into the given output parameters. + bool GetBitmapForOptimizedPluginPaint( + const gfx::Rect& paint_bounds, + TransportDIB** dib, + gfx::Rect* dib_bounds, + gfx::Rect* clip, + float* scale_factor); + + // Tracks all live PluginObjects. + void AddPluginObject(PluginObject* plugin_object); + void RemovePluginObject(PluginObject* plugin_object); + + base::string16 GetSelectedText(bool html); + base::string16 GetLinkAtPosition(const gfx::Point& point); + void RequestSurroundingText(size_t desired_number_of_characters); + void Zoom(double factor, bool text_only); + bool StartFind(const base::string16& search_text, + bool case_sensitive, + int identifier); + void SelectFindResult(bool forward); + void StopFind(); + + bool SupportsPrintInterface(); + bool IsPrintScalingDisabled(); + int PrintBegin(const WebKit::WebPrintParams& print_params); + bool PrintPage(int page_number, WebKit::WebCanvas* canvas); + void PrintEnd(); + + bool CanRotateView(); + void RotateView(WebKit::WebPlugin::RotationType type); + + // Sets the bound_graphics_2d_platform_ for testing purposes. This is instead + // of calling BindGraphics and allows any PlatformGraphics implementation to + // be used, not just a resource one. + void SetBoundGraphics2DForTest(PluginDelegate::PlatformGraphics2D* graphics); + + // There are 2 implementations of the fullscreen interface + // PPB_FlashFullscreen is used by Pepper Flash. + // PPB_Fullscreen is intended for other applications including NaCl. + // The two interface are mutually exclusive. + + // Implementation of PPB_FlashFullscreen. + + // Because going to fullscreen is asynchronous (but going out is not), there + // are 3 states: + // - normal : fullscreen_container_ == NULL + // flash_fullscreen_ == false + // - fullscreen pending: fullscreen_container_ != NULL + // flash_fullscreen_ == false + // - fullscreen : fullscreen_container_ != NULL + // flash_fullscreen_ == true + // + // In normal state, events come from webkit and painting goes back to it. + // In fullscreen state, events come from the fullscreen container, and + // painting goes back to it. + // In pending state, events from webkit are ignored, and as soon as we + // receive events from the fullscreen container, we go to the fullscreen + // state. + bool FlashIsFullscreenOrPending(); + + // Updates |flash_fullscreen_| and sends focus change notification if + // necessary. + void UpdateFlashFullscreenState(bool flash_fullscreen); + + FullscreenContainer* fullscreen_container() const { + return fullscreen_container_; + } + + // Implementation of PPB_Fullscreen. + + // Because going to/from fullscreen is asynchronous, there are 4 states: + // - normal : desired_fullscreen_state_ == false + // view_data_.is_fullscreen == false + // - fullscreen pending: desired_fullscreen_state_ == true + // view_data_.is_fullscreen == false + // - fullscreen : desired_fullscreen_state_ == true + // view_data_.is_fullscreen == true + // - normal pending : desired_fullscreen_state_ = false + // view_data_.is_fullscreen = true + bool IsFullscreenOrPending(); + + bool flash_fullscreen() const { return flash_fullscreen_; } + + // Switches between fullscreen and normal mode. The transition is + // asynchronous. WebKit will trigger corresponding VewChanged calls. + // Returns true on success, false on failure (e.g. trying to enter fullscreen + // when not processing a user gesture or trying to set fullscreen when + // already in fullscreen mode). + bool SetFullscreen(bool fullscreen); + + // Implementation of PPP_Messaging. + void HandleMessage(PP_Var message); + + PluginDelegate::PlatformContext3D* CreateContext3D(); + + // Returns true if the plugin is processing a user gesture. + bool IsProcessingUserGesture(); + + // Returns the user gesture token to use for creating a WebScopedUserGesture, + // if IsProcessingUserGesture returned true. + WebKit::WebUserGestureToken CurrentUserGestureToken(); + + // A mouse lock request was pending and this reports success or failure. + void OnLockMouseACK(bool succeeded); + // A mouse lock was in place, but has been lost. + void OnMouseLockLost(); + // A mouse lock is enabled and mouse events are being delivered. + void HandleMouseLockedInputEvent(const WebKit::WebMouseEvent& event); + + // Simulates an input event to the plugin by passing it down to WebKit, + // which sends it back up to the plugin as if it came from the user. + void SimulateInputEvent(const ::ppapi::InputEventData& input_event); + + // Simulates an IME event at the level of RenderView which sends it back up to + // the plugin as if it came from the user. + bool SimulateIMEEvent(const ::ppapi::InputEventData& input_event); + void SimulateImeSetCompositionEvent( + const ::ppapi::InputEventData& input_event); + + // The document loader is valid when the plugin is "full-frame" and in this + // case is non-NULL as long as the corresponding loader resource is alive. + // This pointer is non-owning, so the loader must use set_document_loader to + // clear itself when it is destroyed. + WebKit::WebURLLoaderClient* document_loader() const { + return document_loader_; + } + void set_document_loader(WebKit::WebURLLoaderClient* loader) { + document_loader_ = loader; + } + + ContentDecryptorDelegate* GetContentDecryptorDelegate(); + + // webkit::ppapi::PluginInstance implementation + virtual content::RenderView* GetRenderView() OVERRIDE; + virtual WebKit::WebPluginContainer* GetContainer() OVERRIDE; + virtual ::ppapi::VarTracker* GetVarTracker() OVERRIDE; + virtual const GURL& GetPluginURL() OVERRIDE; + virtual base::FilePath GetModulePath() OVERRIDE; + virtual PP_Resource CreateExternalFileReference( + const base::FilePath& external_file_path) OVERRIDE; + virtual PP_Resource CreateImage(gfx::ImageSkia* source_image, + float scale) OVERRIDE; + virtual PP_ExternalPluginResult SwitchToOutOfProcessProxy( + const base::FilePath& file_path, + ::ppapi::PpapiPermissions permissions, + const IPC::ChannelHandle& channel_handle, + base::ProcessId plugin_pid, + int plugin_child_id) OVERRIDE; + virtual void SetAlwaysOnTop(bool on_top) OVERRIDE; + virtual bool IsFullPagePlugin() OVERRIDE; + virtual void FlashSetFullscreen(bool fullscreen, bool delay_report) OVERRIDE; + virtual bool IsRectTopmost(const gfx::Rect& rect) OVERRIDE; + virtual int32_t Navigate(const ::ppapi::URLRequestInfoData& request, + const char* target, + bool from_user_action) OVERRIDE; + + // PPB_Instance_API implementation. + virtual PP_Bool BindGraphics(PP_Instance instance, + PP_Resource device) OVERRIDE; + virtual PP_Bool IsFullFrame(PP_Instance instance) OVERRIDE; + virtual const ::ppapi::ViewData* GetViewData(PP_Instance instance) OVERRIDE; + virtual PP_Bool FlashIsFullscreen(PP_Instance instance) OVERRIDE; + virtual PP_Var GetWindowObject(PP_Instance instance) OVERRIDE; + virtual PP_Var GetOwnerElementObject(PP_Instance instance) OVERRIDE; + virtual PP_Var ExecuteScript(PP_Instance instance, + PP_Var script, + PP_Var* exception) OVERRIDE; + virtual uint32_t GetAudioHardwareOutputSampleRate(PP_Instance instance) + OVERRIDE; + virtual uint32_t GetAudioHardwareOutputBufferSize(PP_Instance instance) + OVERRIDE; + virtual PP_Var GetDefaultCharSet(PP_Instance instance) OVERRIDE; + virtual void NumberOfFindResultsChanged(PP_Instance instance, + int32_t total, + PP_Bool final_result) OVERRIDE; + virtual void SelectedFindResultChanged(PP_Instance instance, + int32_t index) OVERRIDE; + virtual PP_Bool IsFullscreen(PP_Instance instance) OVERRIDE; + virtual PP_Bool SetFullscreen(PP_Instance instance, + PP_Bool fullscreen) OVERRIDE; + virtual PP_Bool GetScreenSize(PP_Instance instance, PP_Size* size) + OVERRIDE; + virtual ::ppapi::Resource* GetSingletonResource(PP_Instance instance, + ::ppapi::SingletonResourceID id) OVERRIDE; + virtual int32_t RequestInputEvents(PP_Instance instance, + uint32_t event_classes) OVERRIDE; + virtual int32_t RequestFilteringInputEvents(PP_Instance instance, + uint32_t event_classes) OVERRIDE; + virtual void ClearInputEventRequest(PP_Instance instance, + uint32_t event_classes) OVERRIDE; + virtual void ZoomChanged(PP_Instance instance, double factor) OVERRIDE; + virtual void ZoomLimitsChanged(PP_Instance instance, + double minimum_factor, + double maximium_factor) OVERRIDE; + virtual void PostMessage(PP_Instance instance, PP_Var message) OVERRIDE; + virtual PP_Bool SetCursor(PP_Instance instance, + PP_MouseCursor_Type type, + PP_Resource image, + const PP_Point* hot_spot) OVERRIDE; + virtual int32_t LockMouse( + PP_Instance instance, + scoped_refptr< ::ppapi::TrackedCallback> callback) OVERRIDE; + virtual void UnlockMouse(PP_Instance instance) OVERRIDE; + virtual void SetTextInputType(PP_Instance instance, + PP_TextInput_Type type) OVERRIDE; + virtual void UpdateCaretPosition(PP_Instance instance, + const PP_Rect& caret, + const PP_Rect& bounding_box) OVERRIDE; + virtual void CancelCompositionText(PP_Instance instance) OVERRIDE; + virtual void SelectionChanged(PP_Instance instance) OVERRIDE; + virtual void UpdateSurroundingText(PP_Instance instance, + const char* text, + uint32_t caret, + uint32_t anchor) OVERRIDE; + virtual PP_Var ResolveRelativeToDocument( + PP_Instance instance, + PP_Var relative, + PP_URLComponents_Dev* components) OVERRIDE; + virtual PP_Bool DocumentCanRequest(PP_Instance instance, PP_Var url) OVERRIDE; + virtual PP_Bool DocumentCanAccessDocument(PP_Instance instance, + PP_Instance target) OVERRIDE; + virtual PP_Var GetDocumentURL(PP_Instance instance, + PP_URLComponents_Dev* components) OVERRIDE; + virtual PP_Var GetPluginInstanceURL( + PP_Instance instance, + PP_URLComponents_Dev* components) OVERRIDE; + + // PPB_ContentDecryptor_Private implementation. + virtual void NeedKey(PP_Instance instance, + PP_Var key_system, + PP_Var session_id, + PP_Var init_data) OVERRIDE; + virtual void KeyAdded(PP_Instance instance, + PP_Var key_system, + PP_Var session_id) OVERRIDE; + virtual void KeyMessage(PP_Instance instance, + PP_Var key_system, + PP_Var session_id, + PP_Var message, + PP_Var default_url) OVERRIDE; + virtual void KeyError(PP_Instance instance, + PP_Var key_system, + PP_Var session_id, + int32_t media_error, + int32_t system_code) OVERRIDE; + virtual void DeliverBlock(PP_Instance instance, + PP_Resource decrypted_block, + const PP_DecryptedBlockInfo* block_info) OVERRIDE; + virtual void DecoderInitializeDone(PP_Instance instance, + PP_DecryptorStreamType decoder_type, + uint32_t request_id, + PP_Bool success) OVERRIDE; + virtual void DecoderDeinitializeDone(PP_Instance instance, + PP_DecryptorStreamType decoder_type, + uint32_t request_id) OVERRIDE; + virtual void DecoderResetDone(PP_Instance instance, + PP_DecryptorStreamType decoder_type, + uint32_t request_id) OVERRIDE; + virtual void DeliverFrame(PP_Instance instance, + PP_Resource decrypted_frame, + const PP_DecryptedFrameInfo* frame_info) OVERRIDE; + virtual void DeliverSamples(PP_Instance instance, + PP_Resource audio_frames, + const PP_DecryptedBlockInfo* block_info) OVERRIDE; + + // Reset this instance as proxied. Assigns the instance a new module, resets + // cached interfaces to point to the out-of-process proxy and re-sends + // DidCreate, DidChangeView, and HandleDocumentLoad (if necessary). + // This should be used only when switching an in-process instance to an + // external out-of-process instance. + PP_ExternalPluginResult ResetAsProxied(scoped_refptr<PluginModule> module); + + // Checks whether this is a valid instance of the given module. After calling + // ResetAsProxied above, a NaCl plugin instance's module changes, so external + // hosts won't recognize it as a valid instance of the original module. This + // method fixes that be checking that either module_ or original_module_ match + // the given module. + bool IsValidInstanceOf(PluginModule* module); + + // Returns the plugin NPP identifier that this plugin will use to identify + // itself when making NPObject scripting calls to WebBindings. + struct _NPP* instanceNPP(); + + // Returns the v8::Isolate that was current when this Instance was created. + // This is not inlined so as to avoid an unnecessary header include of v8.h. + v8::Isolate* GetIsolate() const; + + private: + friend class base::RefCounted<PluginInstanceImpl>; + friend class PpapiUnittest; + + // Delete should be called by the WebPlugin before this destructor. + virtual ~PluginInstanceImpl(); + + // Class to record document load notifications and play them back once the + // real document loader becomes available. Used only by NaCl instances. + class NaClDocumentLoader : public WebKit::WebURLLoaderClient { + public: + NaClDocumentLoader(); + virtual ~NaClDocumentLoader(); + + void ReplayReceivedData(WebURLLoaderClient* document_loader); + + // WebKit::WebURLLoaderClient implementation. + virtual void didReceiveData(WebKit::WebURLLoader* loader, + const char* data, + int data_length, + int encoded_data_length); + virtual void didFinishLoading(WebKit::WebURLLoader* loader, + double finish_time); + virtual void didFail(WebKit::WebURLLoader* loader, + const WebKit::WebURLError& error); + + private: + std::list<std::string> data_; + bool finished_loading_; + scoped_ptr<WebKit::WebURLError> error_; + }; + + // Implements PPB_Gamepad_API. This is just to avoid having an excessive + // number of interfaces implemented by PluginInstanceImpl. + class GamepadImpl : public ::ppapi::thunk::PPB_Gamepad_API, + public ::ppapi::Resource { + public: + explicit GamepadImpl(PluginDelegate* delegate); + // Resource implementation. + virtual ::ppapi::thunk::PPB_Gamepad_API* AsPPB_Gamepad_API() OVERRIDE; + virtual void Sample(PP_Instance instance, + PP_GamepadsSampleData* data) OVERRIDE; + private: + virtual ~GamepadImpl(); + PluginDelegate* delegate_; + }; + + // See the static Create functions above for creating PluginInstanceImpl + // objects. This constructor is private so that we can hide the + // PPP_Instance_Combined details while still having 1 constructor to maintain + // for member initialization. + PluginInstanceImpl(PluginDelegate* delegate, + content::RenderView* render_view, + PluginModule* module, + ::ppapi::PPP_Instance_Combined* instance_interface, + WebKit::WebPluginContainer* container, + const GURL& plugin_url); + + bool LoadFindInterface(); + bool LoadInputEventInterface(); + bool LoadMessagingInterface(); + bool LoadMouseLockInterface(); + bool LoadPdfInterface(); + bool LoadPrintInterface(); + bool LoadPrivateInterface(); + bool LoadSelectionInterface(); + bool LoadTextInputInterface(); + bool LoadZoomInterface(); + + // Determines if we think the plugin has focus, both content area and webkit + // (see has_webkit_focus_ below). + bool PluginHasFocus() const; + void SendFocusChangeNotification(); + + void UpdateTouchEventRequest(); + + // Returns true if the plugin has registered to accept wheel events. + bool IsAcceptingWheelEvents() const; + + void ScheduleAsyncDidChangeView(); + void SendAsyncDidChangeView(); + void SendDidChangeView(); + + // Reports the current plugin geometry to the plugin by calling + // DidChangeView. + void ReportGeometry(); + + // Queries the plugin for supported print formats and sets |format| to the + // best format to use. Returns false if the plugin does not support any + // print format that we can handle (we can handle only PDF). + bool GetPreferredPrintOutputFormat(PP_PrintOutputFormat_Dev* format); + bool PrintPDFOutput(PP_Resource print_output, WebKit::WebCanvas* canvas); + + // Get the bound graphics context as a concrete 2D graphics context or returns + // null if the context is not 2D. + PluginDelegate::PlatformGraphics2D* GetBoundGraphics2D() const; + + // Updates the layer for compositing. This creates a layer and attaches to the + // container if: + // - we have a bound Graphics3D + // - the Graphics3D has a texture + // - we are not in Flash full-screen mode (or transitioning to it) + // Otherwise it destroys the layer. + // It does either operation lazily. + void UpdateLayer(); + + // Internal helper function for PrintPage(). + bool PrintPageHelper(PP_PrintPageNumberRange_Dev* page_ranges, + int num_ranges, + WebKit::WebCanvas* canvas); + + void DoSetCursor(WebKit::WebCursorInfo* cursor); + + // Internal helper functions for HandleCompositionXXX(). + bool SendCompositionEventToPlugin( + PP_InputEvent_Type type, + const base::string16& text); + bool SendCompositionEventWithUnderlineInformationToPlugin( + PP_InputEvent_Type type, + const base::string16& text, + const std::vector<WebKit::WebCompositionUnderline>& underlines, + int selection_start, + int selection_end); + + // Internal helper function for XXXInputEvents(). + void RequestInputEventsHelper(uint32_t event_classes); + + // Checks if the security origin of the document containing this instance can + // assess the security origin of the main frame document. + bool CanAccessMainFrame() const; + + // Returns true if the WebView the plugin is in renders via the accelerated + // compositing path. + bool IsViewAccelerated(); + + // Track, set and reset size attributes to control the size of the plugin + // in and out of fullscreen mode. + void KeepSizeAttributesBeforeFullscreen(); + void SetSizeAttributesForFullscreen(); + void ResetSizeAttributesAfterFullscreen(); + + PluginDelegate* delegate_; + content::RenderView* render_view_; + scoped_refptr<PluginModule> module_; + scoped_ptr< ::ppapi::PPP_Instance_Combined> instance_interface_; + // If this is the NaCl plugin, we create a new module when we switch to the + // IPC-based PPAPI proxy. Store the original module and instance interface + // so we can shut down properly. + scoped_refptr<PluginModule> original_module_; + scoped_ptr< ::ppapi::PPP_Instance_Combined> original_instance_interface_; + + PP_Instance pp_instance_; + + // NULL until we have been initialized. + WebKit::WebPluginContainer* container_; + scoped_refptr<cc::TextureLayer> texture_layer_; + scoped_ptr<WebKit::WebLayer> web_layer_; + bool layer_bound_to_fullscreen_; + + // Plugin URL. + GURL plugin_url_; + + // Indicates whether this is a full frame instance, which means it represents + // an entire document rather than an embed tag. + bool full_frame_; + + // Stores the current state of the plugin view. + ::ppapi::ViewData view_data_; + // The last state sent to the plugin. It is only valid after + // |sent_initial_did_change_view_| is set to true. + ::ppapi::ViewData last_sent_view_data_; + + // Indicates if we've ever sent a didChangeView to the plugin. This ensures we + // always send an initial notification, even if the position and clip are the + // same as the default values. + bool sent_initial_did_change_view_; + + // We use a weak ptr factory for scheduling DidChangeView events so that we + // can tell whether updates are pending and consolidate them. When there's + // already a weak ptr pending (HasWeakPtrs is true), code should update the + // view_data_ but not send updates. This also allows us to cancel scheduled + // view change events. + base::WeakPtrFactory<PluginInstanceImpl> view_change_weak_ptr_factory_; + + // The current device context for painting in 2D and 3D. + scoped_refptr<PPB_Graphics3D_Impl> bound_graphics_3d_; + PluginDelegate::PlatformGraphics2D* bound_graphics_2d_platform_; + + // We track two types of focus, one from WebKit, which is the focus among + // all elements of the page, one one from the browser, which is whether the + // tab/window has focus. We tell the plugin it has focus only when both of + // these values are set to true. + bool has_webkit_focus_; + bool has_content_area_focus_; + + // The id of the current find operation, or -1 if none is in process. + int find_identifier_; + + // Helper object that creates resources. + scoped_ptr< ::ppapi::thunk::ResourceCreationAPI> resource_creation_; + + // The plugin-provided interfaces. + // When adding PPP interfaces, make sure to reset them in ResetAsProxied. + const PPP_Find_Dev* plugin_find_interface_; + const PPP_InputEvent* plugin_input_event_interface_; + const PPP_Messaging* plugin_messaging_interface_; + const PPP_MouseLock* plugin_mouse_lock_interface_; + const PPP_Pdf* plugin_pdf_interface_; + const PPP_Instance_Private* plugin_private_interface_; + const PPP_Selection_Dev* plugin_selection_interface_; + const PPP_TextInput_Dev* plugin_textinput_interface_; + const PPP_Zoom_Dev* plugin_zoom_interface_; + + // Flags indicating whether we have asked this plugin instance for the + // corresponding interfaces, so that we can ask only once. + // When adding flags, make sure to reset them in ResetAsProxied. + bool checked_for_plugin_input_event_interface_; + bool checked_for_plugin_messaging_interface_; + bool checked_for_plugin_pdf_interface_; + + // This is only valid between a successful PrintBegin call and a PrintEnd + // call. + PP_PrintSettings_Dev current_print_settings_; +#if defined(OS_MACOSX) + // On the Mac, when we draw the bitmap to the PDFContext, it seems necessary + // to keep the pixels valid until CGContextEndPage is called. We use this + // variable to hold on to the pixels. + scoped_refptr<PPB_ImageData_Impl> last_printed_page_; +#endif // defined(OS_MACOSX) + // Always when printing to PDF on Linux and when printing for preview on Mac + // and Win, the entire document goes into one metafile. However, when users + // print only a subset of all the pages, it is impossible to know if a call + // to PrintPage() is the last call. Thus in PrintPage(), just store the page + // number in |ranges_|. The hack is in PrintEnd(), where a valid |canvas_| + // is preserved in PrintWebViewHelper::PrintPages. This makes it possible + // to generate the entire PDF given the variables below: + // + // The most recently used WebCanvas, guaranteed to be valid. + skia::RefPtr<WebKit::WebCanvas> canvas_; + // An array of page ranges. + std::vector<PP_PrintPageNumberRange_Dev> ranges_; + + scoped_refptr< ::ppapi::Resource> gamepad_impl_; + + // The plugin print interface. + const PPP_Printing_Dev* plugin_print_interface_; + + // The plugin 3D interface. + const PPP_Graphics3D* plugin_graphics_3d_interface_; + + // Contains the cursor if it's set by the plugin. + scoped_ptr<WebKit::WebCursorInfo> cursor_; + + // Set to true if this plugin thinks it will always be on top. This allows us + // to use a more optimized painting path in some cases. + bool always_on_top_; + // Even if |always_on_top_| is true, the plugin is not fully visible if there + // are some cut-out areas (occupied by iframes higher in the stacking order). + // This information is used in the optimized painting path. + std::vector<gfx::Rect> cut_outs_rects_; + + // Implementation of PPB_FlashFullscreen. + + // Plugin container for fullscreen mode. NULL if not in fullscreen mode. Note: + // there is a transition state where fullscreen_container_ is non-NULL but + // flash_fullscreen_ is false (see above). + FullscreenContainer* fullscreen_container_; + + // True if we are in "flash" fullscreen mode. False if we are in normal mode + // or in transition to fullscreen. Normal fullscreen mode is indicated in + // the ViewData. + bool flash_fullscreen_; + + // Implementation of PPB_Fullscreen. + + // Since entering fullscreen mode is an asynchronous operation, we set this + // variable to the desired state at the time we issue the fullscreen change + // request. The plugin will receive a DidChangeView event when it goes + // fullscreen. + bool desired_fullscreen_state_; + + // WebKit does not resize the plugin when going into fullscreen mode, so we do + // this here by modifying the various plugin attributes and then restoring + // them on exit. + WebKit::WebString width_before_fullscreen_; + WebKit::WebString height_before_fullscreen_; + WebKit::WebString border_before_fullscreen_; + WebKit::WebString style_before_fullscreen_; + gfx::Size screen_size_for_fullscreen_; + + // The MessageChannel used to implement bidirectional postMessage for the + // instance. + scoped_ptr<MessageChannel> message_channel_; + + // Bitmap for crashed plugin. Lazily initialized, non-owning pointer. + SkBitmap* sad_plugin_; + + typedef std::set<PluginObject*> PluginObjectSet; + PluginObjectSet live_plugin_objects_; + + // Classes of events that the plugin has registered for, both for filtering + // and not. The bits are PP_INPUTEVENT_CLASS_*. + uint32_t input_event_mask_; + uint32_t filtered_input_event_mask_; + + // Text composition status. + ui::TextInputType text_input_type_; + gfx::Rect text_input_caret_; + gfx::Rect text_input_caret_bounds_; + bool text_input_caret_set_; + + // Text selection status. + std::string surrounding_text_; + size_t selection_caret_; + size_t selection_anchor_; + + scoped_refptr< ::ppapi::TrackedCallback> lock_mouse_callback_; + + // Track pending user gestures so out-of-process plugins can respond to + // a user gesture after it has been processed. + PP_TimeTicks pending_user_gesture_; + WebKit::WebUserGestureToken pending_user_gesture_token_; + + // We store the arguments so we can re-send them if we are reset to talk to + // NaCl via the IPC NaCl proxy. + std::vector<std::string> argn_; + std::vector<std::string> argv_; + + // Non-owning pointer to the document loader, if any. + WebKit::WebURLLoaderClient* document_loader_; + // State for deferring document loads. Used only by NaCl instances. + WebKit::WebURLResponse nacl_document_response_; + scoped_ptr<NaClDocumentLoader> nacl_document_loader_; + bool nacl_document_load_; + + // The ContentDecryptorDelegate forwards PPP_ContentDecryptor_Private + // calls and handles PPB_ContentDecryptor_Private calls. + scoped_ptr<ContentDecryptorDelegate> content_decryptor_delegate_; + + // Dummy NPP value used when calling in to WebBindings, to allow the bindings + // to correctly track NPObjects belonging to this plugin instance. + scoped_ptr<struct _NPP> npp_; + + // We store the isolate at construction so that we can be sure to use the + // Isolate in which this Instance was created when interacting with v8. + v8::Isolate* isolate_; + + friend class PpapiPluginInstanceTest; + DISALLOW_COPY_AND_ASSIGN(PluginInstanceImpl); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_PPAPI_PLUGIN_INSTANCE_IMPL_H_ diff --git a/content/renderer/pepper/ppapi_plugin_instance_unittest.cc b/content/renderer/pepper/ppapi_plugin_instance_unittest.cc new file mode 100644 index 0000000..ceaac04 --- /dev/null +++ b/content/renderer/pepper/ppapi_plugin_instance_unittest.cc @@ -0,0 +1,127 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/ppapi_plugin_instance_impl.h" + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "content/renderer/pepper/plugin_delegate.h" +#include "content/renderer/pepper/ppapi_unittest.h" +#include "content/renderer/pepper/ppb_image_data_impl.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/rect.h" +#include "ui/gfx/safe_integer_conversions.h" + +namespace webkit { +namespace ppapi { + +namespace { + +// A mock graphics object to bind. We only have to implement enough so that +// GetBitmapForOptimizedPluginPaint runs. +// +// If this is eventually all moved to content, we can simplify this. The +// point here is that we need to just have a bound graphics 2d resource host +// with the right properties. +class MockPlatformGraphics2D : public PluginDelegate::PlatformGraphics2D { + public: + // The image data pointer must outlive this class. + MockPlatformGraphics2D(PPB_ImageData_Impl* image_data, float scale) + : image_data_(image_data), + scale_(scale), + bound_instance_(NULL) { + } + virtual ~MockPlatformGraphics2D() { + if (bound_instance_) + bound_instance_->SetBoundGraphics2DForTest(NULL); + } + + virtual bool ReadImageData(PP_Resource image, + const PP_Point* top_left) OVERRIDE { + return false; + } + virtual bool BindToInstance(PluginInstanceImpl* new_instance) OVERRIDE { + bound_instance_ = new_instance; + return true; + } + virtual void Paint(WebKit::WebCanvas* canvas, + const gfx::Rect& plugin_rect, + const gfx::Rect& paint_rect) OVERRIDE {} + virtual void ViewWillInitiatePaint() OVERRIDE {} + virtual void ViewInitiatedPaint() OVERRIDE {} + virtual void ViewFlushedPaint() OVERRIDE {} + + virtual bool IsAlwaysOpaque() const OVERRIDE { return true; } + virtual void SetScale(float scale) OVERRIDE {} + virtual float GetScale() const OVERRIDE { return scale_; } + virtual PPB_ImageData_Impl* ImageData() OVERRIDE { + return image_data_; + } + + private: + PPB_ImageData_Impl* image_data_; + float scale_; + PluginInstanceImpl* bound_instance_; + + DISALLOW_COPY_AND_ASSIGN(MockPlatformGraphics2D); +}; + +} // namespace + +// This class is forward-declared as a friend so can't be in the anonymous +// namespace. +class PpapiPluginInstanceTest : public PpapiUnittest { + public: + bool GetBitmapForOptimizedPluginPaint(const gfx::Rect& paint_bounds, + TransportDIB** dib, + gfx::Rect* location, + gfx::Rect* clip, + float* scale_factor) { + return !!instance()->GetBitmapForOptimizedPluginPaint( + paint_bounds, dib, location, clip, scale_factor); + } +}; + + +// Test that GetBitmapForOptimizedPluginPaint doesn't return a bitmap rect +// that's bigger than the actual backing store bitmap. +TEST_F(PpapiPluginInstanceTest, GetBitmap2xScale) { + PP_Size size; + size.width = 3; + size.height = 3; + + scoped_refptr<PPB_ImageData_Impl> image_data = new PPB_ImageData_Impl( + instance()->pp_instance(), PPB_ImageData_Impl::PLATFORM); + image_data->Init(PPB_ImageData_Impl::GetNativeImageDataFormat(), + size.width, size.height, true); + ASSERT_TRUE(image_data->Map() != NULL); + + float scale = 2.0; + MockPlatformGraphics2D mock_graphics_2d(image_data.get(), 1.0 / scale); + instance()->SetBoundGraphics2DForTest(&mock_graphics_2d); + + instance()->SetAlwaysOnTop(true); + SetViewSize(size.width, size.height); + + gfx::Rect bounds(0, 0, 1, 1); + gfx::Rect location; + gfx::Rect clip; + float bitmap_scale = 0; + TransportDIB* dib = NULL; + EXPECT_TRUE(GetBitmapForOptimizedPluginPaint( + bounds, &dib, &location, &clip, &bitmap_scale)); + + EXPECT_EQ(0, location.x()); + EXPECT_EQ(0, location.y()); + EXPECT_EQ(gfx::ToFlooredInt(size.width / scale), location.width()); + EXPECT_EQ(gfx::ToFlooredInt(size.height / scale), location.height()); + EXPECT_EQ(0, clip.x()); + EXPECT_EQ(0, clip.y()); + EXPECT_EQ(size.width, clip.width()); + EXPECT_EQ(size.height, clip.height()); + EXPECT_EQ(scale, bitmap_scale); +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/ppapi_unittest.cc b/content/renderer/pepper/ppapi_unittest.cc new file mode 100644 index 0000000..e6edf94 --- /dev/null +++ b/content/renderer/pepper/ppapi_unittest.cc @@ -0,0 +1,169 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/ppapi_unittest.h" + +#include "base/message_loop/message_loop.h" +#include "content/renderer/pepper/gfx_conversion.h" +#include "content/renderer/pepper/host_globals.h" +#include "content/renderer/pepper/mock_plugin_delegate.h" +#include "content/renderer/pepper/plugin_module.h" +#include "content/renderer/pepper/ppapi_interface_factory.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" +#include "ppapi/c/pp_var.h" +#include "ppapi/c/ppp_instance.h" +#include "ppapi/shared_impl/ppapi_globals.h" +#include "ppapi/shared_impl/ppapi_permissions.h" + +namespace webkit { +namespace ppapi { + +namespace { + +PpapiUnittest* current_unittest = NULL; + +const void* MockGetInterface(const char* interface_name) { + return current_unittest->GetMockInterface(interface_name); +} + +int MockInitializeModule(PP_Module, PPB_GetInterface) { + return PP_OK; +} + +// PPP_Instance implementation ------------------------------------------------ + +PP_Bool Instance_DidCreate(PP_Instance pp_instance, + uint32_t argc, + const char* argn[], + const char* argv[]) { + return PP_TRUE; +} + +void Instance_DidDestroy(PP_Instance instance) { +} + +void Instance_DidChangeView(PP_Instance pp_instance, PP_Resource view) { +} + +void Instance_DidChangeFocus(PP_Instance pp_instance, PP_Bool has_focus) { +} + +PP_Bool Instance_HandleDocumentLoad(PP_Instance pp_instance, + PP_Resource pp_url_loader) { + return PP_FALSE; +} + +static PPP_Instance mock_instance_interface = { + &Instance_DidCreate, + &Instance_DidDestroy, + &Instance_DidChangeView, + &Instance_DidChangeFocus, + &Instance_HandleDocumentLoad +}; + +} // namespace + +// PpapiUnittest -------------------------------------------------------------- + +PpapiUnittest::PpapiUnittest() { + DCHECK(!current_unittest); + current_unittest = this; +} + +PpapiUnittest::~PpapiUnittest() { + DCHECK(current_unittest == this); + current_unittest = NULL; +} + +void PpapiUnittest::SetUp() { + message_loop_.reset(new base::MessageLoop()); + delegate_.reset(NewPluginDelegate()); + + // Initialize the mock module. + module_ = new PluginModule("Mock plugin", base::FilePath(), + ::ppapi::PpapiPermissions()); + ::ppapi::PpapiGlobals::Get()->ResetMainThreadMessageLoopForTesting(); + content::PepperPluginInfo::EntryPoints entry_points; + entry_points.get_interface = &MockGetInterface; + entry_points.initialize_module = &MockInitializeModule; + ASSERT_TRUE(module_->InitAsInternalPlugin(entry_points)); + + // Initialize the mock instance. + instance_ = PluginInstanceImpl::Create( + delegate_.get(), NULL, module(), NULL, GURL()); +} + +void PpapiUnittest::TearDown() { + instance_ = NULL; + module_ = NULL; + message_loop_.reset(); + PluginModule::ResetHostGlobalsForTest(); +} + +MockPluginDelegate* PpapiUnittest::NewPluginDelegate() { + return new MockPluginDelegate; +} + +const void* PpapiUnittest::GetMockInterface(const char* interface_name) const { + if (strcmp(interface_name, PPP_INSTANCE_INTERFACE_1_0) == 0) + return &mock_instance_interface; + return NULL; +} + +void PpapiUnittest::ShutdownModule() { + DCHECK(instance_->HasOneRef()); + instance_ = NULL; + DCHECK(module_->HasOneRef()); + module_ = NULL; +} + +void PpapiUnittest::SetViewSize(int width, int height) const { + instance_->view_data_.rect = PP_FromGfxRect(gfx::Rect(0, 0, width, height)); + instance_->view_data_.clip_rect = instance_->view_data_.rect; +} + +// Tests whether custom PPAPI interface factories are called when PPAPI +// interfaces are requested. +class PpapiCustomInterfaceFactoryTest : public PpapiUnittest { + public: + PpapiCustomInterfaceFactoryTest() {} + virtual ~PpapiCustomInterfaceFactoryTest() {} + + bool result() { + return result_; + } + + void reset_result() { + result_ = false; + } + + static const void* InterfaceFactory(const std::string& interface_name) { + result_ = true; + return NULL; + } + + private: + static bool result_; +}; + +bool PpapiCustomInterfaceFactoryTest::result_ = false; + +// This test validates whether custom PPAPI interface factories are invoked in +// response to PluginModule::GetPluginInterface calls. +TEST_F(PpapiCustomInterfaceFactoryTest, BasicFactoryTest) { + PpapiInterfaceFactoryManager::GetInstance()->RegisterFactory( + PpapiCustomInterfaceFactoryTest::InterfaceFactory); + (*PluginModule::GetLocalGetInterfaceFunc())("DummyInterface"); + EXPECT_TRUE(result()); + + reset_result(); + PpapiInterfaceFactoryManager::GetInstance()->UnregisterFactory( + PpapiCustomInterfaceFactoryTest::InterfaceFactory); + + (*PluginModule::GetLocalGetInterfaceFunc())("DummyInterface"); + EXPECT_FALSE(result()); +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/ppapi_unittest.h b/content/renderer/pepper/ppapi_unittest.h new file mode 100644 index 0000000..e7fd8f2 --- /dev/null +++ b/content/renderer/pepper/ppapi_unittest.h @@ -0,0 +1,65 @@ +// 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. + +#ifndef CONTENT_RENDERER_PEPPER_PPAPI_UNITTEST_H_ +#define CONTENT_RENDERER_PEPPER_PPAPI_UNITTEST_H_ + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "content/renderer/pepper/plugin_delegate.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +class MessageLoop; +} + +namespace webkit { +namespace ppapi { + +class MockPluginDelegate; +class PluginInstanceImpl; +class PluginModule; + +class PpapiUnittest : public testing::Test { + public: + PpapiUnittest(); + virtual ~PpapiUnittest(); + + virtual void SetUp(); + virtual void TearDown(); + + MockPluginDelegate* delegate() { return delegate_.get(); } + PluginModule* module() const { return module_.get(); } + PluginInstanceImpl* instance() const { return instance_.get(); } + + // Provides access to the interfaces implemented by the test. The default one + // implements PPP_INSTANCE. + virtual const void* GetMockInterface(const char* interface_name) const; + + // Deletes the instance and module to simulate module shutdown. + void ShutdownModule(); + + // Sets the view size of the plugin instance. + void SetViewSize(int width, int height) const; + + protected: + virtual MockPluginDelegate* NewPluginDelegate(); + + private: + scoped_ptr<MockPluginDelegate> delegate_; + + // Note: module must be declared first since we want it to get destroyed last. + scoped_refptr<PluginModule> module_; + scoped_refptr<PluginInstanceImpl> instance_; + + scoped_ptr<base::MessageLoop> message_loop_; + + DISALLOW_COPY_AND_ASSIGN(PpapiUnittest); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_PPAPI_UNITTEST_H_ diff --git a/content/renderer/pepper/ppapi_webplugin_impl.cc b/content/renderer/pepper/ppapi_webplugin_impl.cc new file mode 100644 index 0000000..a3bca36 --- /dev/null +++ b/content/renderer/pepper/ppapi_webplugin_impl.cc @@ -0,0 +1,308 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/ppapi_webplugin_impl.h" + +#include <cmath> + +#include "base/debug/crash_logging.h" +#include "base/message_loop/message_loop.h" +#include "content/renderer/pepper/message_channel.h" +#include "content/renderer/pepper/npobject_var.h" +#include "content/renderer/pepper/plugin_module.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" +#include "ppapi/shared_impl/ppapi_globals.h" +#include "ppapi/shared_impl/var_tracker.h" +#include "third_party/WebKit/public/platform/WebPoint.h" +#include "third_party/WebKit/public/platform/WebRect.h" +#include "third_party/WebKit/public/platform/WebSize.h" +#include "third_party/WebKit/public/platform/WebURLLoaderClient.h" +#include "third_party/WebKit/public/web/WebBindings.h" +#include "third_party/WebKit/public/web/WebDocument.h" +#include "third_party/WebKit/public/web/WebElement.h" +#include "third_party/WebKit/public/web/WebFrame.h" +#include "third_party/WebKit/public/web/WebPluginContainer.h" +#include "third_party/WebKit/public/web/WebPluginParams.h" +#include "third_party/WebKit/public/web/WebPrintParams.h" +#include "third_party/WebKit/public/web/WebPrintScalingOption.h" +#include "third_party/WebKit/public/web/WebView.h" +#include "url/gurl.h" + +using ppapi::NPObjectVar; +using WebKit::WebCanvas; +using WebKit::WebPlugin; +using WebKit::WebPluginContainer; +using WebKit::WebPluginParams; +using WebKit::WebPoint; +using WebKit::WebPrintParams; +using WebKit::WebRect; +using WebKit::WebSize; +using WebKit::WebString; +using WebKit::WebURL; +using WebKit::WebVector; +using WebKit::WebView; + +namespace webkit { +namespace ppapi { + +struct WebPluginImpl::InitData { + scoped_refptr<PluginModule> module; + base::WeakPtr<PluginDelegate> delegate; + base::WeakPtr<content::RenderView> render_view; + std::vector<std::string> arg_names; + std::vector<std::string> arg_values; + GURL url; +}; + +WebPluginImpl::WebPluginImpl( + PluginModule* plugin_module, + const WebPluginParams& params, + const base::WeakPtr<PluginDelegate>& plugin_delegate, + const base::WeakPtr<content::RenderView>& render_view) + : init_data_(new InitData()), + full_frame_(params.loadManually), + instance_object_(PP_MakeUndefined()), + container_(NULL) { + DCHECK(plugin_module); + init_data_->module = plugin_module; + init_data_->delegate = plugin_delegate; + init_data_->render_view = render_view; + for (size_t i = 0; i < params.attributeNames.size(); ++i) { + init_data_->arg_names.push_back(params.attributeNames[i].utf8()); + init_data_->arg_values.push_back(params.attributeValues[i].utf8()); + } + init_data_->url = params.url; + + // Set subresource URL for crash reporting. + base::debug::SetCrashKeyValue("subresource_url", init_data_->url.spec()); +} + +WebPluginImpl::~WebPluginImpl() { +} + +WebKit::WebPluginContainer* WebPluginImpl::container() const { + return container_; +} + +bool WebPluginImpl::initialize(WebPluginContainer* container) { + // The plugin delegate may have gone away. + if (!init_data_->delegate.get()) + return false; + + instance_ = init_data_->module->CreateInstance( + init_data_->delegate.get(), init_data_->render_view.get(), container, + init_data_->url); + if (!instance_.get()) + return false; + + // Enable script objects for this plugin. + container->allowScriptObjects(); + + bool success = instance_->Initialize(init_data_->arg_names, + init_data_->arg_values, + full_frame_); + if (!success) { + instance_->Delete(); + instance_ = NULL; + + WebKit::WebPlugin* replacement_plugin = + init_data_->delegate->CreatePluginReplacement( + init_data_->module->path()); + if (!replacement_plugin || !replacement_plugin->initialize(container)) + return false; + + container->setPlugin(replacement_plugin); + return true; + } + + init_data_.reset(); + container_ = container; + return true; +} + +void WebPluginImpl::destroy() { + // Tell |container_| to clear references to this plugin's script objects. + if (container_) + container_->clearScriptObjects(); + + if (instance_.get()) { + ::ppapi::PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(instance_object_); + instance_object_ = PP_MakeUndefined(); + instance_->Delete(); + instance_ = NULL; + } + + base::MessageLoop::current()->DeleteSoon(FROM_HERE, this); +} + +NPObject* WebPluginImpl::scriptableObject() { + // Call through the plugin to get its instance object. The plugin should pass + // us a reference which we release in destroy(). + if (instance_object_.type == PP_VARTYPE_UNDEFINED) + instance_object_ = instance_->GetInstanceObject(); + // GetInstanceObject talked to the plugin which may have removed the instance + // from the DOM, in which case instance_ would be NULL now. + if (!instance_.get()) + return NULL; + + scoped_refptr<NPObjectVar> object(NPObjectVar::FromPPVar(instance_object_)); + // If there's an InstanceObject, tell the Instance's MessageChannel to pass + // any non-postMessage calls to it. + if (object.get()) { + instance_->message_channel().SetPassthroughObject(object->np_object()); + } + NPObject* message_channel_np_object(instance_->message_channel().np_object()); + // The object is expected to be retained before it is returned. + WebKit::WebBindings::retainObject(message_channel_np_object); + return message_channel_np_object; +} + +NPP WebPluginImpl::pluginNPP() { + return instance_->instanceNPP(); +} + +bool WebPluginImpl::getFormValue(WebString& value) { + return false; +} + +void WebPluginImpl::paint(WebCanvas* canvas, const WebRect& rect) { + if (!instance_->FlashIsFullscreenOrPending()) + instance_->Paint(canvas, plugin_rect_, rect); +} + +void WebPluginImpl::updateGeometry( + const WebRect& window_rect, + const WebRect& clip_rect, + const WebVector<WebRect>& cut_outs_rects, + bool is_visible) { + plugin_rect_ = window_rect; + if (!instance_->FlashIsFullscreenOrPending()) { + std::vector<gfx::Rect> cut_outs; + for (size_t i = 0; i < cut_outs_rects.size(); ++i) + cut_outs.push_back(cut_outs_rects[i]); + instance_->ViewChanged(plugin_rect_, clip_rect, cut_outs); + } +} + +void WebPluginImpl::updateFocus(bool focused) { + instance_->SetWebKitFocus(focused); +} + +void WebPluginImpl::updateVisibility(bool visible) { +} + +bool WebPluginImpl::acceptsInputEvents() { + return true; +} + +bool WebPluginImpl::handleInputEvent(const WebKit::WebInputEvent& event, + WebKit::WebCursorInfo& cursor_info) { + if (instance_->FlashIsFullscreenOrPending()) + return false; + return instance_->HandleInputEvent(event, &cursor_info); +} + +void WebPluginImpl::didReceiveResponse( + const WebKit::WebURLResponse& response) { + DCHECK(!instance_->document_loader()); + instance_->HandleDocumentLoad(response); +} + +void WebPluginImpl::didReceiveData(const char* data, int data_length) { + WebKit::WebURLLoaderClient* document_loader = instance_->document_loader(); + if (document_loader) + document_loader->didReceiveData(NULL, data, data_length, 0); +} + +void WebPluginImpl::didFinishLoading() { + WebKit::WebURLLoaderClient* document_loader = instance_->document_loader(); + if (document_loader) + document_loader->didFinishLoading(NULL, 0.0); +} + +void WebPluginImpl::didFailLoading(const WebKit::WebURLError& error) { + WebKit::WebURLLoaderClient* document_loader = instance_->document_loader(); + if (document_loader) + document_loader->didFail(NULL, error); +} + +void WebPluginImpl::didFinishLoadingFrameRequest(const WebKit::WebURL& url, + void* notify_data) { +} + +void WebPluginImpl::didFailLoadingFrameRequest( + const WebKit::WebURL& url, + void* notify_data, + const WebKit::WebURLError& error) { +} + +bool WebPluginImpl::hasSelection() const { + return !selectionAsText().isEmpty(); +} + +WebString WebPluginImpl::selectionAsText() const { + return instance_->GetSelectedText(false); +} + +WebString WebPluginImpl::selectionAsMarkup() const { + return instance_->GetSelectedText(true); +} + +WebURL WebPluginImpl::linkAtPosition(const WebPoint& position) const { + return GURL(instance_->GetLinkAtPosition(position)); +} + +void WebPluginImpl::setZoomLevel(double level, bool text_only) { + instance_->Zoom(WebView::zoomLevelToZoomFactor(level), text_only); +} + +bool WebPluginImpl::startFind(const WebKit::WebString& search_text, + bool case_sensitive, + int identifier) { + return instance_->StartFind(search_text, case_sensitive, identifier); +} + +void WebPluginImpl::selectFindResult(bool forward) { + instance_->SelectFindResult(forward); +} + +void WebPluginImpl::stopFind() { + instance_->StopFind(); +} + +bool WebPluginImpl::supportsPaginatedPrint() { + return instance_->SupportsPrintInterface(); +} + +bool WebPluginImpl::isPrintScalingDisabled() { + return instance_->IsPrintScalingDisabled(); +} + +int WebPluginImpl::printBegin(const WebPrintParams& print_params) { + return instance_->PrintBegin(print_params); +} + +bool WebPluginImpl::printPage(int page_number, + WebKit::WebCanvas* canvas) { + return instance_->PrintPage(page_number, canvas); +} + +void WebPluginImpl::printEnd() { + return instance_->PrintEnd(); +} + +bool WebPluginImpl::canRotateView() { + return instance_->CanRotateView(); +} + +void WebPluginImpl::rotateView(RotationType type) { + instance_->RotateView(type); +} + +bool WebPluginImpl::isPlaceholder() { + return false; +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/ppapi_webplugin_impl.h b/content/renderer/pepper/ppapi_webplugin_impl.h new file mode 100644 index 0000000..ed26b75 --- /dev/null +++ b/content/renderer/pepper/ppapi_webplugin_impl.h @@ -0,0 +1,115 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_PEPPER_PPAPI_WEBPLUGIN_IMPL_H_ +#define CONTENT_RENDERER_PEPPER_PPAPI_WEBPLUGIN_IMPL_H_ + +#include <string> +#include <vector> + +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/sequenced_task_runner_helpers.h" +#include "ppapi/c/pp_var.h" +#include "third_party/WebKit/public/web/WebPlugin.h" +#include "ui/gfx/rect.h" + +struct _NPP; + +namespace content { +class RenderView; +} + +namespace WebKit { +struct WebPluginParams; +struct WebPrintParams; +} + +namespace webkit { +namespace ppapi { + +class PluginDelegate; +class PluginInstanceImpl; +class PluginModule; +class PPB_URLLoader_Impl; + +class WebPluginImpl : public WebKit::WebPlugin { + public: + WebPluginImpl(PluginModule* module, + const WebKit::WebPluginParams& params, + const base::WeakPtr<PluginDelegate>& plugin_delegate, + const base::WeakPtr<content::RenderView>& render_view); + + PluginInstanceImpl* instance() { return instance_.get(); } + + // WebKit::WebPlugin implementation. + virtual WebKit::WebPluginContainer* container() const; + virtual bool initialize(WebKit::WebPluginContainer* container); + virtual void destroy(); + virtual NPObject* scriptableObject(); + virtual struct _NPP* pluginNPP(); + virtual bool getFormValue(WebKit::WebString& value); + virtual void paint(WebKit::WebCanvas* canvas, const WebKit::WebRect& rect); + virtual void updateGeometry( + const WebKit::WebRect& frame_rect, + const WebKit::WebRect& clip_rect, + const WebKit::WebVector<WebKit::WebRect>& cut_outs_rects, + bool is_visible); + virtual void updateFocus(bool focused); + virtual void updateVisibility(bool visible); + virtual bool acceptsInputEvents(); + virtual bool handleInputEvent(const WebKit::WebInputEvent& event, + WebKit::WebCursorInfo& cursor_info); + virtual void didReceiveResponse(const WebKit::WebURLResponse& response); + virtual void didReceiveData(const char* data, int data_length); + virtual void didFinishLoading(); + virtual void didFailLoading(const WebKit::WebURLError&); + virtual void didFinishLoadingFrameRequest(const WebKit::WebURL& url, + void* notify_data); + virtual void didFailLoadingFrameRequest(const WebKit::WebURL& url, + void* notify_data, + const WebKit::WebURLError& error); + virtual bool hasSelection() const; + virtual WebKit::WebString selectionAsText() const; + virtual WebKit::WebString selectionAsMarkup() const; + virtual WebKit::WebURL linkAtPosition(const WebKit::WebPoint& position) const; + virtual void setZoomLevel(double level, bool text_only); + virtual bool startFind(const WebKit::WebString& search_text, + bool case_sensitive, + int identifier); + virtual void selectFindResult(bool forward); + virtual void stopFind(); + virtual bool supportsPaginatedPrint() OVERRIDE; + virtual bool isPrintScalingDisabled() OVERRIDE; + + virtual int printBegin(const WebKit::WebPrintParams& print_params) OVERRIDE; + virtual bool printPage(int page_number, WebKit::WebCanvas* canvas) OVERRIDE; + virtual void printEnd() OVERRIDE; + + virtual bool canRotateView() OVERRIDE; + virtual void rotateView(RotationType type) OVERRIDE; + virtual bool isPlaceholder() OVERRIDE; + + private: + friend class base::DeleteHelper<WebPluginImpl>; + + virtual ~WebPluginImpl(); + struct InitData; + + scoped_ptr<InitData> init_data_; // Cleared upon successful initialization. + // True if the instance represents the entire document in a frame instead of + // being an embedded resource. + bool full_frame_; + scoped_refptr<PluginInstanceImpl> instance_; + gfx::Rect plugin_rect_; + PP_Var instance_object_; + WebKit::WebPluginContainer* container_; + + DISALLOW_COPY_AND_ASSIGN(WebPluginImpl); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_PPAPI_WEBPLUGIN_IMPL_H_ diff --git a/content/renderer/pepper/ppb_audio_impl.cc b/content/renderer/pepper/ppb_audio_impl.cc new file mode 100644 index 0000000..5263826 --- /dev/null +++ b/content/renderer/pepper/ppb_audio_impl.cc @@ -0,0 +1,161 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/ppb_audio_impl.h" + +#include "base/logging.h" +#include "content/renderer/pepper/common.h" +#include "content/renderer/pepper/resource_helper.h" +#include "media/audio/audio_output_controller.h" +#include "ppapi/c/pp_completion_callback.h" +#include "ppapi/c/ppb_audio.h" +#include "ppapi/c/ppb_audio_config.h" +#include "ppapi/shared_impl/resource_tracker.h" +#include "ppapi/thunk/enter.h" +#include "ppapi/thunk/ppb_audio_config_api.h" +#include "ppapi/thunk/thunk.h" + +using ppapi::PpapiGlobals; +using ppapi::thunk::EnterResourceNoLock; +using ppapi::thunk::PPB_Audio_API; +using ppapi::thunk::PPB_AudioConfig_API; +using ppapi::TrackedCallback; + +namespace webkit { +namespace ppapi { + +// PPB_Audio_Impl -------------------------------------------------------------- + +PPB_Audio_Impl::PPB_Audio_Impl(PP_Instance instance) + : Resource(::ppapi::OBJECT_IS_IMPL, instance), + audio_(NULL), + sample_frame_count_(0) { +} + +PPB_Audio_Impl::~PPB_Audio_Impl() { + // Calling ShutDown() makes sure StreamCreated cannot be called anymore and + // releases the audio data associated with the pointer. Note however, that + // until ShutDown returns, StreamCreated may still be called. This will be + // OK since we'll just immediately clean up the data it stored later in this + // destructor. + if (audio_) { + audio_->ShutDown(); + audio_ = NULL; + } +} + +// static +PP_Resource PPB_Audio_Impl::Create(PP_Instance instance, + PP_Resource config, + PPB_Audio_Callback audio_callback, + void* user_data) { + scoped_refptr<PPB_Audio_Impl> audio(new PPB_Audio_Impl(instance)); + if (!audio->Init(config, audio_callback, user_data)) + return 0; + return audio->GetReference(); +} + +PPB_Audio_API* PPB_Audio_Impl::AsPPB_Audio_API() { + return this; +} + +bool PPB_Audio_Impl::Init(PP_Resource config, + PPB_Audio_Callback callback, void* user_data) { + // Validate the config and keep a reference to it. + EnterResourceNoLock<PPB_AudioConfig_API> enter(config, true); + if (enter.failed()) + return false; + config_ = config; + + if (!callback) + return false; + SetCallback(callback, user_data); + + PluginDelegate* plugin_delegate = ResourceHelper::GetPluginDelegate(this); + if (!plugin_delegate) + return false; + + // When the stream is created, we'll get called back on StreamCreated(). + CHECK(!audio_); + audio_ = plugin_delegate->CreateAudioOutput( + enter.object()->GetSampleRate(), enter.object()->GetSampleFrameCount(), + this); + sample_frame_count_ = enter.object()->GetSampleFrameCount(); + return audio_ != NULL; +} + +PP_Resource PPB_Audio_Impl::GetCurrentConfig() { + // AddRef on behalf of caller, while keeping a ref for ourselves. + PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(config_); + return config_; +} + +PP_Bool PPB_Audio_Impl::StartPlayback() { + if (!audio_) + return PP_FALSE; + if (playing()) + return PP_TRUE; + SetStartPlaybackState(); + return BoolToPPBool(audio_->StartPlayback()); +} + +PP_Bool PPB_Audio_Impl::StopPlayback() { + if (!audio_) + return PP_FALSE; + if (!playing()) + return PP_TRUE; + if (!audio_->StopPlayback()) + return PP_FALSE; + SetStopPlaybackState(); + return PP_TRUE; +} + +int32_t PPB_Audio_Impl::Open( + PP_Resource config, + scoped_refptr<TrackedCallback> create_callback) { + // Validate the config and keep a reference to it. + EnterResourceNoLock<PPB_AudioConfig_API> enter(config, true); + if (enter.failed()) + return PP_ERROR_FAILED; + config_ = config; + + PluginDelegate* plugin_delegate = ResourceHelper::GetPluginDelegate(this); + if (!plugin_delegate) + return PP_ERROR_FAILED; + + // When the stream is created, we'll get called back on StreamCreated(). + DCHECK(!audio_); + audio_ = plugin_delegate->CreateAudioOutput( + enter.object()->GetSampleRate(), enter.object()->GetSampleFrameCount(), + this); + if (!audio_) + return PP_ERROR_FAILED; + + // At this point, we are guaranteeing ownership of the completion + // callback. Audio promises to fire the completion callback + // once and only once. + SetCreateCallback(create_callback); + + return PP_OK_COMPLETIONPENDING; +} + +int32_t PPB_Audio_Impl::GetSyncSocket(int* sync_socket) { + return GetSyncSocketImpl(sync_socket); +} + +int32_t PPB_Audio_Impl::GetSharedMemory(int* shm_handle, + uint32_t* shm_size) { + return GetSharedMemoryImpl(shm_handle, shm_size); +} + +void PPB_Audio_Impl::OnSetStreamInfo( + base::SharedMemoryHandle shared_memory_handle, + size_t shared_memory_size, + base::SyncSocket::Handle socket_handle) { + SetStreamInfo(pp_instance(), shared_memory_handle, shared_memory_size, + socket_handle, sample_frame_count_); +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/ppb_audio_impl.h b/content/renderer/pepper/ppb_audio_impl.h new file mode 100644 index 0000000..49146cb --- /dev/null +++ b/content/renderer/pepper/ppb_audio_impl.h @@ -0,0 +1,86 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_PEPPER_PPB_AUDIO_IMPL_H_ +#define CONTENT_RENDERER_PEPPER_PPB_AUDIO_IMPL_H_ + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "base/memory/shared_memory.h" +#include "base/sync_socket.h" +#include "content/renderer/pepper/audio_helper.h" +#include "content/renderer/pepper/plugin_delegate.h" +#include "ppapi/c/pp_completion_callback.h" +#include "ppapi/c/ppb_audio.h" +#include "ppapi/c/ppb_audio_config.h" +#include "ppapi/shared_impl/ppb_audio_config_shared.h" +#include "ppapi/shared_impl/ppb_audio_shared.h" +#include "ppapi/shared_impl/resource.h" +#include "ppapi/shared_impl/scoped_pp_resource.h" + +namespace webkit { +namespace ppapi { + +// Some of the backend functionality of this class is implemented by the +// PPB_Audio_Shared so it can be shared with the proxy. +class PPB_Audio_Impl : public ::ppapi::Resource, + public ::ppapi::PPB_Audio_Shared, + public AudioHelper { + public: + // Trusted initialization. You must call Init after this. + // + // Untrusted initialization should just call the static Create() function + // to properly create & initialize this class. + explicit PPB_Audio_Impl(PP_Instance instance); + + // Creation function for untrusted plugins. This handles all initialization + // and will return 0 on failure. + static PP_Resource Create(PP_Instance instance, + PP_Resource config_id, + PPB_Audio_Callback audio_callback, + void* user_data); + + // Initialization function for trusted init. + bool Init(PP_Resource config_id, + PPB_Audio_Callback user_callback, + void* user_data); + + // Resource overrides. + virtual ::ppapi::thunk::PPB_Audio_API* AsPPB_Audio_API() OVERRIDE; + + // PPB_Audio_API implementation. + virtual PP_Resource GetCurrentConfig() OVERRIDE; + virtual PP_Bool StartPlayback() OVERRIDE; + virtual PP_Bool StopPlayback() OVERRIDE; + virtual int32_t Open( + PP_Resource config_id, + scoped_refptr< ::ppapi::TrackedCallback> create_callback) OVERRIDE; + virtual int32_t GetSyncSocket(int* sync_socket) OVERRIDE; + virtual int32_t GetSharedMemory(int* shm_handle, uint32_t* shm_size) OVERRIDE; + + private: + virtual ~PPB_Audio_Impl(); + + // AudioHelper implementation. + virtual void OnSetStreamInfo(base::SharedMemoryHandle shared_memory_handle, + size_t shared_memory_size_, + base::SyncSocket::Handle socket) OVERRIDE; + + // AudioConfig used for creating this Audio object. We own a ref. + ::ppapi::ScopedPPResource config_; + + // PluginDelegate audio object that we delegate audio IPC through. We don't + // own this pointer but are responsible for calling Shutdown on it. + PluginDelegate::PlatformAudioOutput* audio_; + + // Track frame count for passing on to PPB_Audio_Shared::SetStreamInfo(). + int sample_frame_count_; + + DISALLOW_COPY_AND_ASSIGN(PPB_Audio_Impl); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_PPB_AUDIO_IMPL_H_ diff --git a/content/renderer/pepper/ppb_broker_impl.cc b/content/renderer/pepper/ppb_broker_impl.cc new file mode 100644 index 0000000..7401b28 --- /dev/null +++ b/content/renderer/pepper/ppb_broker_impl.cc @@ -0,0 +1,103 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/ppb_broker_impl.h" + +#include "base/logging.h" +#include "content/renderer/pepper/common.h" +#include "content/renderer/pepper/plugin_module.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" +#include "content/renderer/pepper/resource_helper.h" +#include "ppapi/shared_impl/platform_file.h" +#include "third_party/WebKit/public/web/WebDocument.h" +#include "third_party/WebKit/public/web/WebElement.h" +#include "third_party/WebKit/public/web/WebPluginContainer.h" + +using ppapi::PlatformFileToInt; +using ppapi::thunk::PPB_Broker_API; +using ppapi::TrackedCallback; + +namespace webkit { +namespace ppapi { + +// PPB_Broker_Impl ------------------------------------------------------ + +PPB_Broker_Impl::PPB_Broker_Impl(PP_Instance instance) + : Resource(::ppapi::OBJECT_IS_IMPL, instance), + broker_(NULL), + connect_callback_(), + pipe_handle_(PlatformFileToInt(base::kInvalidPlatformFileValue)) { +} + +PPB_Broker_Impl::~PPB_Broker_Impl() { + if (broker_) { + broker_->Disconnect(this); + broker_ = NULL; + } + + // The plugin owns the handle. + pipe_handle_ = PlatformFileToInt(base::kInvalidPlatformFileValue); +} + +PPB_Broker_API* PPB_Broker_Impl::AsPPB_Broker_API() { + return this; +} + +int32_t PPB_Broker_Impl::Connect( + scoped_refptr<TrackedCallback> connect_callback) { + // TODO(ddorwin): Return PP_ERROR_FAILED if plugin is in-process. + + if (broker_) { + // May only be called once. + return PP_ERROR_FAILED; + } + + PluginInstanceImpl* plugin_instance = ResourceHelper::GetPluginInstance(this); + if (!plugin_instance) + return PP_ERROR_FAILED; + + // The callback must be populated now in case we are connected to the broker + // and BrokerConnected is called before ConnectToBroker returns. + // Because it must be created now, it must be aborted and cleared if + // ConnectToBroker fails. + connect_callback_ = connect_callback; + + broker_ = plugin_instance->delegate()->ConnectToBroker(this); + if (!broker_) { + connect_callback_->Abort(); + return PP_ERROR_FAILED; + } + + return PP_OK_COMPLETIONPENDING; +} + +int32_t PPB_Broker_Impl::GetHandle(int32_t* handle) { + if (pipe_handle_ == PlatformFileToInt(base::kInvalidPlatformFileValue)) + return PP_ERROR_FAILED; // Handle not set yet. + *handle = pipe_handle_; + return PP_OK; +} + +GURL PPB_Broker_Impl::GetDocumentUrl() { + PluginInstanceImpl* plugin_instance = ResourceHelper::GetPluginInstance(this); + return plugin_instance->container()->element().document().url(); +} + +// Transfers ownership of the handle to the plugin. +void PPB_Broker_Impl::BrokerConnected(int32_t handle, int32_t result) { + DCHECK(pipe_handle_ == + PlatformFileToInt(base::kInvalidPlatformFileValue)); + DCHECK(result == PP_OK || + handle == PlatformFileToInt(base::kInvalidPlatformFileValue)); + + pipe_handle_ = handle; + + // Synchronous calls are not supported. + DCHECK(TrackedCallback::IsPending(connect_callback_)); + + connect_callback_->Run(result); +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/ppb_broker_impl.h b/content/renderer/pepper/ppb_broker_impl.h new file mode 100644 index 0000000..0c7a76f --- /dev/null +++ b/content/renderer/pepper/ppb_broker_impl.h @@ -0,0 +1,60 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_PEPPER_PPB_BROKER_IMPL_H_ +#define CONTENT_RENDERER_PEPPER_PPB_BROKER_IMPL_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/weak_ptr.h" +#include "content/renderer/pepper/plugin_delegate.h" +#include "ppapi/c/pp_completion_callback.h" +#include "ppapi/c/trusted/ppb_broker_trusted.h" +#include "ppapi/shared_impl/resource.h" +#include "ppapi/shared_impl/tracked_callback.h" +#include "ppapi/thunk/ppb_broker_api.h" + +namespace webkit { +namespace ppapi { + +class PPB_Broker_Impl : public ::ppapi::Resource, + public ::ppapi::thunk::PPB_Broker_API, + public base::SupportsWeakPtr<PPB_Broker_Impl> { + public: + explicit PPB_Broker_Impl(PP_Instance instance); + + // Resource override. + virtual ::ppapi::thunk::PPB_Broker_API* AsPPB_Broker_API() OVERRIDE; + + // PPB_BrokerTrusted implementation. + virtual int32_t Connect( + scoped_refptr< ::ppapi::TrackedCallback> connect_callback) OVERRIDE; + virtual int32_t GetHandle(int32_t* handle) OVERRIDE; + + // Returns the URL of the document this plug-in runs in. This is necessary to + // decide whether to grant access to the PPAPI broker. + GURL GetDocumentUrl(); + + void BrokerConnected(int32_t handle, int32_t result); + + private: + virtual ~PPB_Broker_Impl(); + // PluginDelegate ppapi broker object. + // We don't own this pointer but are responsible for calling Disconnect on it. + PluginDelegate::Broker* broker_; + + // Callback invoked from BrokerConnected. + scoped_refptr< ::ppapi::TrackedCallback> connect_callback_; + + // Pipe handle for the plugin instance to use to communicate with the broker. + // Never owned by this object. + int32_t pipe_handle_; + + DISALLOW_COPY_AND_ASSIGN(PPB_Broker_Impl); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_PPB_BROKER_IMPL_H_ diff --git a/content/renderer/pepper/ppb_buffer_impl.cc b/content/renderer/pepper/ppb_buffer_impl.cc new file mode 100644 index 0000000..24599e7 --- /dev/null +++ b/content/renderer/pepper/ppb_buffer_impl.cc @@ -0,0 +1,113 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/ppb_buffer_impl.h" + +#include <algorithm> + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "content/renderer/pepper/common.h" +#include "content/renderer/pepper/plugin_delegate.h" +#include "content/renderer/pepper/resource_helper.h" +#include "ppapi/c/dev/ppb_buffer_dev.h" +#include "ppapi/c/pp_instance.h" +#include "ppapi/c/pp_resource.h" + +using ::ppapi::thunk::PPB_Buffer_API; + +namespace webkit { +namespace ppapi { + +PPB_Buffer_Impl::PPB_Buffer_Impl(PP_Instance instance) + : Resource(::ppapi::OBJECT_IS_IMPL, instance), + size_(0), + map_count_(0) { +} + +PPB_Buffer_Impl::~PPB_Buffer_Impl() { +} + +// static +PP_Resource PPB_Buffer_Impl::Create(PP_Instance instance, uint32_t size) { + scoped_refptr<PPB_Buffer_Impl> new_resource(CreateResource(instance, size)); + if (new_resource.get()) + return new_resource->GetReference(); + return 0; +} + +// static +scoped_refptr<PPB_Buffer_Impl> PPB_Buffer_Impl::CreateResource( + PP_Instance instance, + uint32_t size) { + scoped_refptr<PPB_Buffer_Impl> buffer(new PPB_Buffer_Impl(instance)); + if (!buffer->Init(size)) + return scoped_refptr<PPB_Buffer_Impl>(); + return buffer; +} + +PPB_Buffer_Impl* PPB_Buffer_Impl::AsPPB_Buffer_Impl() { + return this; +} + +PPB_Buffer_API* PPB_Buffer_Impl::AsPPB_Buffer_API() { + return this; +} + +bool PPB_Buffer_Impl::Init(uint32_t size) { + PluginDelegate* plugin_delegate = ResourceHelper::GetPluginDelegate(this); + if (size == 0 || !plugin_delegate) + return false; + size_ = size; + shared_memory_.reset(plugin_delegate->CreateAnonymousSharedMemory(size)); + return shared_memory_.get() != NULL; +} + +PP_Bool PPB_Buffer_Impl::Describe(uint32_t* size_in_bytes) { + *size_in_bytes = size_; + return PP_TRUE; +} + +PP_Bool PPB_Buffer_Impl::IsMapped() { + return PP_FromBool(!!shared_memory_->memory()); +} + +void* PPB_Buffer_Impl::Map() { + DCHECK(size_); + DCHECK(shared_memory_.get()); + if (map_count_++ == 0) + shared_memory_->Map(size_); + return shared_memory_->memory(); +} + +void PPB_Buffer_Impl::Unmap() { + if (--map_count_ == 0) + shared_memory_->Unmap(); +} + +int32_t PPB_Buffer_Impl::GetSharedMemory(int* shm_handle) { +#if defined(OS_POSIX) + *shm_handle = shared_memory_->handle().fd; +#elif defined(OS_WIN) + *shm_handle = reinterpret_cast<int>( + shared_memory_->handle()); +#else +#error "Platform not supported." +#endif + return PP_OK; +} + +BufferAutoMapper::BufferAutoMapper(PPB_Buffer_API* api) : api_(api) { + needs_unmap_ = !PP_ToBool(api->IsMapped()); + data_ = api->Map(); + api->Describe(&size_); +} + +BufferAutoMapper::~BufferAutoMapper() { + if (needs_unmap_) + api_->Unmap(); +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/ppb_buffer_impl.h b/content/renderer/pepper/ppb_buffer_impl.h new file mode 100644 index 0000000..5da2c72 --- /dev/null +++ b/content/renderer/pepper/ppb_buffer_impl.h @@ -0,0 +1,80 @@ +// 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. + +#ifndef CONTENT_RENDERER_PEPPER_PPB_BUFFER_IMPL_H_ +#define CONTENT_RENDERER_PEPPER_PPB_BUFFER_IMPL_H_ + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/shared_memory.h" +#include "ppapi/shared_impl/resource.h" +#include "ppapi/thunk/ppb_buffer_api.h" + +namespace webkit { +namespace ppapi { + +class PPB_Buffer_Impl : public ::ppapi::Resource, + public ::ppapi::thunk::PPB_Buffer_API { + public: + static PP_Resource Create(PP_Instance instance, uint32_t size); + static scoped_refptr<PPB_Buffer_Impl> CreateResource(PP_Instance instance, + uint32_t size); + + virtual PPB_Buffer_Impl* AsPPB_Buffer_Impl(); + + base::SharedMemory* shared_memory() const { return shared_memory_.get(); } + uint32_t size() const { return size_; } + + // Resource overrides. + virtual ::ppapi::thunk::PPB_Buffer_API* AsPPB_Buffer_API() OVERRIDE; + + // PPB_Buffer_API implementation. + virtual PP_Bool Describe(uint32_t* size_in_bytes) OVERRIDE; + virtual PP_Bool IsMapped() OVERRIDE; + virtual void* Map() OVERRIDE; + virtual void Unmap() OVERRIDE; + + // Trusted. + virtual int32_t GetSharedMemory(int* handle) OVERRIDE; + + private: + virtual ~PPB_Buffer_Impl(); + + explicit PPB_Buffer_Impl(PP_Instance instance); + bool Init(uint32_t size); + + scoped_ptr<base::SharedMemory> shared_memory_; + uint32_t size_; + int map_count_; + + DISALLOW_COPY_AND_ASSIGN(PPB_Buffer_Impl); +}; + +// Ensures that the given buffer is mapped, and returns it to its previous +// mapped state in the destructor. +class BufferAutoMapper { + public: + explicit BufferAutoMapper(::ppapi::thunk::PPB_Buffer_API* api); + ~BufferAutoMapper(); + + // Will be NULL on failure to map. + void* data() { return data_; } + uint32_t size() { return size_; } + + private: + ::ppapi::thunk::PPB_Buffer_API* api_; + + bool needs_unmap_; + + void* data_; + uint32_t size_; + + DISALLOW_COPY_AND_ASSIGN(BufferAutoMapper); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_PPB_BUFFER_IMPL_H_ diff --git a/content/renderer/pepper/ppb_file_ref_impl.cc b/content/renderer/pepper/ppb_file_ref_impl.cc new file mode 100644 index 0000000..9a97bed --- /dev/null +++ b/content/renderer/pepper/ppb_file_ref_impl.cc @@ -0,0 +1,511 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/ppb_file_ref_impl.h" + +#include "base/files/file_util_proxy.h" +#include "base/platform_file.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "content/renderer/pepper/common.h" +#include "content/renderer/pepper/plugin_delegate.h" +#include "content/renderer/pepper/plugin_module.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" +#include "content/renderer/pepper/resource_helper.h" +#include "net/base/escape.h" +#include "ppapi/c/pp_errors.h" +#include "ppapi/shared_impl/file_type_conversion.h" +#include "ppapi/shared_impl/time_conversion.h" +#include "ppapi/shared_impl/var.h" +#include "ppapi/thunk/enter.h" +#include "ppapi/thunk/ppb_file_system_api.h" +#include "url/gurl.h" +#include "webkit/common/fileapi/directory_entry.h" +#include "webkit/common/fileapi/file_system_util.h" + +using ppapi::HostResource; +using ppapi::PPB_FileRef_CreateInfo; +using ppapi::PPTimeToTime; +using ppapi::StringVar; +using ppapi::TrackedCallback; +using ppapi::thunk::EnterResourceNoLock; +using ppapi::thunk::PPB_FileRef_API; +using ppapi::thunk::PPB_FileSystem_API; + +namespace webkit { +namespace ppapi { + +namespace { + +bool IsValidLocalPath(const std::string& path) { + // The path must start with '/' + if (path.empty() || path[0] != '/') + return false; + + // The path must contain valid UTF-8 characters. + if (!IsStringUTF8(path)) + return false; + +#if defined(OS_WIN) + base::FilePath::StringType path_win(path.begin(), path.end()); + base::FilePath file_path(path_win); +#else + base::FilePath file_path(path); +#endif + if (file_path.ReferencesParent()) + return false; + + return true; +} + +void TrimTrailingSlash(std::string* path) { + // If this path ends with a slash, then normalize it away unless path is the + // root path. + if (path->size() > 1 && path->at(path->size() - 1) == '/') + path->erase(path->size() - 1, 1); +} + +std::string GetNameForExternalFilePath(const base::FilePath& in_path) { + const base::FilePath::StringType& path = in_path.value(); + size_t pos = path.rfind(base::FilePath::kSeparators[0]); + CHECK(pos != base::FilePath::StringType::npos); +#if defined(OS_WIN) + return WideToUTF8(path.substr(pos + 1)); +#elif defined(OS_POSIX) + return path.substr(pos + 1); +#else +#error "Unsupported platform." +#endif +} + +std::string GetNameForVirtualFilePath(const std::string& path) { + if (path.size() == 1 && path[0] == '/') + return path; + + // There should always be a leading slash at least! + size_t pos = path.rfind('/'); + CHECK(pos != std::string::npos); + return path.substr(pos + 1); +} + +void IgnoreCloseCallback(base::PlatformFileError error_code) { +} + +void PlatformFileInfoToPPFileInfo( + const base::PlatformFileInfo& file_info, + PP_FileSystemType file_system_type, + PP_FileInfo* info) { + DCHECK(info); + ::ppapi::PlatformFileInfoToPepperFileInfo(file_info, file_system_type, info); +} + +void GetFileInfoCallback( + scoped_refptr<base::TaskRunner> task_runner, + base::PlatformFile file, + linked_ptr<PP_FileInfo> info, + scoped_refptr<TrackedCallback> callback, + base::PlatformFileError error_code, + const base::PlatformFileInfo& file_info) { + base::FileUtilProxy::Close( + task_runner.get(), file, base::Bind(&IgnoreCloseCallback)); + + if (!TrackedCallback::IsPending(callback)) + return; + + int32_t pp_error = ::ppapi::PlatformFileErrorToPepperError(error_code); + if (pp_error != PP_OK) { + callback->Run(pp_error); + return; + } + + PlatformFileInfoToPPFileInfo( + file_info, PP_FILESYSTEMTYPE_EXTERNAL, info.get()); + + callback->Run(PP_OK); +} + +void QueryCallback(scoped_refptr<base::TaskRunner> task_runner, + linked_ptr<PP_FileInfo> info, + scoped_refptr<TrackedCallback> callback, + base::PlatformFileError error_code, + base::PassPlatformFile passed_file) { + if (!TrackedCallback::IsPending(callback)) + return; + + int32_t pp_error = ::ppapi::PlatformFileErrorToPepperError(error_code); + if (pp_error != PP_OK) { + callback->Run(pp_error); + return; + } + base::PlatformFile file = passed_file.ReleaseValue(); + + if (!base::FileUtilProxy::GetFileInfoFromPlatformFile( + task_runner.get(), + file, + base::Bind( + &GetFileInfoCallback, task_runner, file, info, callback))) { + base::FileUtilProxy::Close( + task_runner.get(), file, base::Bind(&IgnoreCloseCallback)); + callback->Run(PP_ERROR_FAILED); + } +} + +void DidReadMetadata( + scoped_refptr< ::ppapi::TrackedCallback> callback, + linked_ptr<PP_FileInfo> info, + PP_FileSystemType file_system_type, + const base::PlatformFileInfo& file_info) { + if (!TrackedCallback::IsPending(callback)) + return; + + PlatformFileInfoToPPFileInfo(file_info, file_system_type, info.get()); + callback->Run(PP_OK); +} + +void DidReadDirectory( + scoped_refptr< ::ppapi::TrackedCallback> callback, + PPB_FileRef_Impl* dir_ref, + linked_ptr<std::vector< ::ppapi::PPB_FileRef_CreateInfo> > dir_files, + linked_ptr<std::vector<PP_FileType> > dir_file_types, + const std::vector<fileapi::DirectoryEntry>& entries, + bool has_more) { + if (!TrackedCallback::IsPending(callback)) + return; + + // The current filesystem backend always returns false. + DCHECK(!has_more); + + DCHECK(dir_ref); + DCHECK(dir_files.get()); + DCHECK(dir_file_types.get()); + + std::string dir_path = dir_ref->GetCreateInfo().path; + if (dir_path.empty() || dir_path[dir_path.size() - 1] != '/') + dir_path += '/'; + + for (size_t i = 0; i < entries.size(); ++i) { + const fileapi::DirectoryEntry& entry = entries[i]; + scoped_refptr<PPB_FileRef_Impl> file_ref(PPB_FileRef_Impl::CreateInternal( + dir_ref->pp_instance(), + dir_ref->file_system_resource(), + dir_path + fileapi::FilePathToString(base::FilePath(entry.name)))); + dir_files->push_back(file_ref->GetCreateInfo()); + dir_file_types->push_back( + entry.is_directory ? PP_FILETYPE_DIRECTORY : PP_FILETYPE_REGULAR); + // Add a ref count on behalf of the plugin side. + file_ref->GetReference(); + } + CHECK_EQ(dir_files->size(), dir_file_types->size()); + + callback->Run(PP_OK); +} + +void DidFinishFileOperation( + scoped_refptr< ::ppapi::TrackedCallback> callback, + base::PlatformFileError error_code) { + if (callback->completed()) + return; + callback->Run(::ppapi::PlatformFileErrorToPepperError(error_code)); +} + +} // namespace + +PPB_FileRef_Impl::PPB_FileRef_Impl(const PPB_FileRef_CreateInfo& info, + PP_Resource file_system) + : PPB_FileRef_Shared(::ppapi::OBJECT_IS_IMPL, info), + file_system_(file_system), + external_file_system_path_() { +} + +PPB_FileRef_Impl::PPB_FileRef_Impl(const PPB_FileRef_CreateInfo& info, + const base::FilePath& external_file_path) + : PPB_FileRef_Shared(::ppapi::OBJECT_IS_IMPL, info), + file_system_(), + external_file_system_path_(external_file_path) { +} + +PPB_FileRef_Impl::~PPB_FileRef_Impl() { +} + +// static +PPB_FileRef_Impl* PPB_FileRef_Impl::CreateInternal(PP_Instance instance, + PP_Resource pp_file_system, + const std::string& path) { + PluginInstanceImpl* plugin_instance = + ResourceHelper::PPInstanceToPluginInstance(instance); + if (!plugin_instance || !plugin_instance->delegate()) + return 0; + + PP_FileSystemType type = + plugin_instance->delegate()->GetFileSystemType(instance, pp_file_system); + if (type != PP_FILESYSTEMTYPE_LOCALPERSISTENT && + type != PP_FILESYSTEMTYPE_LOCALTEMPORARY && + type != PP_FILESYSTEMTYPE_EXTERNAL && + type != PP_FILESYSTEMTYPE_ISOLATED) + return 0; + + PPB_FileRef_CreateInfo info; + info.resource = HostResource::MakeInstanceOnly(instance); + info.file_system_plugin_resource = pp_file_system; + info.file_system_type = type; + + // Validate the path. + info.path = path; + if (!IsValidLocalPath(info.path)) + return 0; + TrimTrailingSlash(&info.path); + + info.name = GetNameForVirtualFilePath(info.path); + + PPB_FileRef_Impl* file_ref = new PPB_FileRef_Impl(info, pp_file_system); + if (plugin_instance->delegate()->IsRunningInProcess(instance)) + file_ref->AddFileSystemRefCount(); + return file_ref; +} + +// static +PPB_FileRef_Impl* PPB_FileRef_Impl::CreateExternal( + PP_Instance instance, + const base::FilePath& external_file_path, + const std::string& display_name) { + PPB_FileRef_CreateInfo info; + info.resource = HostResource::MakeInstanceOnly(instance); + info.file_system_plugin_resource = 0; + info.file_system_type = PP_FILESYSTEMTYPE_EXTERNAL; + if (display_name.empty()) + info.name = GetNameForExternalFilePath(external_file_path); + else + info.name = display_name; + + return new PPB_FileRef_Impl(info, external_file_path); +} + +PP_Resource PPB_FileRef_Impl::GetParent() { + if (GetFileSystemType() == PP_FILESYSTEMTYPE_EXTERNAL) + return 0; + + const std::string& virtual_path = GetCreateInfo().path; + + // There should always be a leading slash at least! + size_t pos = virtual_path.rfind('/'); + CHECK(pos != std::string::npos); + + // If the path is "/foo", then we want to include the slash. + if (pos == 0) + pos++; + std::string parent_path = virtual_path.substr(0, pos); + + scoped_refptr<PPB_FileRef_Impl> parent_ref( + CreateInternal(pp_instance(), file_system_, parent_path)); + if (!parent_ref.get()) + return 0; + return parent_ref->GetReference(); +} + +int32_t PPB_FileRef_Impl::MakeDirectory( + PP_Bool make_ancestors, + scoped_refptr<TrackedCallback> callback) { + if (!IsValidNonExternalFileSystem()) + return PP_ERROR_NOACCESS; + + PluginInstanceImpl* plugin_instance = ResourceHelper::GetPluginInstance(this); + if (!plugin_instance) + return PP_ERROR_FAILED; + plugin_instance->delegate()->MakeDirectory( + GetFileSystemURL(), PP_ToBool(make_ancestors), + base::Bind(&DidFinishFileOperation, callback)); + return PP_OK_COMPLETIONPENDING; +} + +int32_t PPB_FileRef_Impl::Touch(PP_Time last_access_time, + PP_Time last_modified_time, + scoped_refptr<TrackedCallback> callback) { + if (!IsValidNonExternalFileSystem()) + return PP_ERROR_NOACCESS; + + PluginInstanceImpl* plugin_instance = ResourceHelper::GetPluginInstance(this); + if (!plugin_instance) + return PP_ERROR_FAILED; + plugin_instance->delegate()->Touch( + GetFileSystemURL(), + PPTimeToTime(last_access_time), + PPTimeToTime(last_modified_time), + base::Bind(&DidFinishFileOperation, callback)); + return PP_OK_COMPLETIONPENDING; +} + +int32_t PPB_FileRef_Impl::Delete(scoped_refptr<TrackedCallback> callback) { + if (!IsValidNonExternalFileSystem()) + return PP_ERROR_NOACCESS; + + PluginInstanceImpl* plugin_instance = ResourceHelper::GetPluginInstance(this); + if (!plugin_instance) + return PP_ERROR_FAILED; + plugin_instance->delegate()->Delete( + GetFileSystemURL(), + base::Bind(&DidFinishFileOperation, callback)); + return PP_OK_COMPLETIONPENDING; +} + +int32_t PPB_FileRef_Impl::Rename(PP_Resource new_pp_file_ref, + scoped_refptr<TrackedCallback> callback) { + EnterResourceNoLock<PPB_FileRef_API> enter(new_pp_file_ref, true); + if (enter.failed()) + return PP_ERROR_BADRESOURCE; + PPB_FileRef_Impl* new_file_ref = + static_cast<PPB_FileRef_Impl*>(enter.object()); + + if (!IsValidNonExternalFileSystem() || + file_system_ != new_file_ref->file_system_) + return PP_ERROR_NOACCESS; + + // TODO(viettrungluu): Also cancel when the new file ref is destroyed? + // http://crbug.com/67624 + PluginInstanceImpl* plugin_instance = ResourceHelper::GetPluginInstance(this); + if (!plugin_instance) + return PP_ERROR_FAILED; + plugin_instance->delegate()->Rename( + GetFileSystemURL(), new_file_ref->GetFileSystemURL(), + base::Bind(&DidFinishFileOperation, callback)); + return PP_OK_COMPLETIONPENDING; +} + +PP_Var PPB_FileRef_Impl::GetAbsolutePath() { + if (GetFileSystemType() != PP_FILESYSTEMTYPE_EXTERNAL) + return GetPath(); + if (!external_path_var_.get()) { + external_path_var_ = + new StringVar(external_file_system_path_.AsUTF8Unsafe()); + } + return external_path_var_->GetPPVar(); +} + +base::FilePath PPB_FileRef_Impl::GetSystemPath() const { + if (GetFileSystemType() != PP_FILESYSTEMTYPE_EXTERNAL) { + NOTREACHED(); + return base::FilePath(); + } + return external_file_system_path_; +} + +GURL PPB_FileRef_Impl::GetFileSystemURL() const { + if (GetFileSystemType() != PP_FILESYSTEMTYPE_LOCALPERSISTENT && + GetFileSystemType() != PP_FILESYSTEMTYPE_LOCALTEMPORARY && + GetFileSystemType() != PP_FILESYSTEMTYPE_EXTERNAL && + GetFileSystemType() != PP_FILESYSTEMTYPE_ISOLATED) { + NOTREACHED(); + return GURL(); + } + + const std::string& virtual_path = GetCreateInfo().path; + CHECK(!virtual_path.empty()); // Should always be at least "/". + + // Since |virtual_path_| starts with a '/', it looks like an absolute path. + // We need to trim off the '/' before calling Resolve, as FileSystem URLs + // start with a storage type identifier that looks like a path segment. + + PluginInstanceImpl* plugin_instance = ResourceHelper::GetPluginInstance(this); + PluginDelegate* delegate = + plugin_instance ? plugin_instance->delegate() : NULL; + if (!delegate) + return GURL(); + return GURL(delegate->GetFileSystemRootUrl(pp_instance(), file_system_)) + .Resolve(net::EscapePath(virtual_path.substr(1))); +} + +bool PPB_FileRef_Impl::IsValidNonExternalFileSystem() const { + PluginInstanceImpl* plugin_instance = ResourceHelper::GetPluginInstance(this); + PluginDelegate* delegate = + plugin_instance ? plugin_instance->delegate() : NULL; + return delegate && + delegate->IsFileSystemOpened(pp_instance(), file_system_) && + delegate->GetFileSystemType(pp_instance(), file_system_) != + PP_FILESYSTEMTYPE_EXTERNAL; +} + +bool PPB_FileRef_Impl::HasValidFileSystem() const { + PluginInstanceImpl* plugin_instance = ResourceHelper::GetPluginInstance(this); + PluginDelegate* delegate = + plugin_instance ? plugin_instance->delegate() : NULL; + return delegate && delegate->IsFileSystemOpened(pp_instance(), file_system_); +} + +int32_t PPB_FileRef_Impl::Query(PP_FileInfo* info, + scoped_refptr<TrackedCallback> callback) { + NOTREACHED(); + return PP_ERROR_FAILED; +} + +int32_t PPB_FileRef_Impl::QueryInHost( + linked_ptr<PP_FileInfo> info, + scoped_refptr<TrackedCallback> callback) { + scoped_refptr<PluginInstanceImpl> plugin_instance = + ResourceHelper::GetPluginInstance(this); + if (!plugin_instance.get()) + return PP_ERROR_FAILED; + + if (!file_system_) { + // External file system + // We have to do something totally different for external file systems. + + // TODO(teravest): Use the SequencedWorkerPool instead. + scoped_refptr<base::TaskRunner> task_runner = + plugin_instance->delegate()->GetFileThreadMessageLoopProxy(); + if (!plugin_instance->delegate()->AsyncOpenFile( + GetSystemPath(), + base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ, + base::Bind(&QueryCallback, task_runner, info, callback))) + return PP_ERROR_FAILED; + } else { + // Non-external file system + if (!HasValidFileSystem()) + return PP_ERROR_NOACCESS; + + PluginInstanceImpl* plugin_instance = + ResourceHelper::GetPluginInstance(this); + PluginDelegate* delegate = + plugin_instance ? plugin_instance->delegate() : NULL; + if (!delegate) + return PP_ERROR_FAILED; + + PP_FileSystemType file_system_type = + delegate->GetFileSystemType(pp_instance(), file_system_); + plugin_instance->delegate()->Query( + GetFileSystemURL(), + base::Bind(&DidReadMetadata, callback, info, file_system_type), + base::Bind(&DidFinishFileOperation, callback)); + } + return PP_OK_COMPLETIONPENDING; +} + +int32_t PPB_FileRef_Impl::ReadDirectoryEntries( + const PP_ArrayOutput& output, + scoped_refptr<TrackedCallback> callback) { + NOTREACHED(); + return PP_ERROR_FAILED; +} + +int32_t PPB_FileRef_Impl::ReadDirectoryEntriesInHost( + linked_ptr<std::vector< ::ppapi::PPB_FileRef_CreateInfo> > files, + linked_ptr<std::vector<PP_FileType> > file_types, + scoped_refptr<TrackedCallback> callback) { + if (!IsValidNonExternalFileSystem()) + return PP_ERROR_NOACCESS; + + PluginInstanceImpl* plugin_instance = ResourceHelper::GetPluginInstance(this); + if (!plugin_instance) + return PP_ERROR_FAILED; + + // TODO(yzshen): Passing base::Unretained(this) to the callback could + // be dangerous. + plugin_instance->delegate()->ReadDirectoryEntries( + GetFileSystemURL(), + base::Bind(&DidReadDirectory, + callback, base::Unretained(this), files, file_types), + base::Bind(&DidFinishFileOperation, callback)); + return PP_OK_COMPLETIONPENDING; +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/ppb_file_ref_impl.h b/content/renderer/pepper/ppb_file_ref_impl.h new file mode 100644 index 0000000..30612cd --- /dev/null +++ b/content/renderer/pepper/ppb_file_ref_impl.h @@ -0,0 +1,126 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_PEPPER_PPB_FILE_REF_IMPL_H_ +#define CONTENT_RENDERER_PEPPER_PPB_FILE_REF_IMPL_H_ + +#include <string> +#include <vector> + +#include "base/files/file_path.h" +#include "base/memory/linked_ptr.h" +#include "ppapi/c/pp_file_info.h" +#include "ppapi/c/ppb_file_ref.h" +#include "ppapi/shared_impl/ppb_file_ref_shared.h" +#include "ppapi/shared_impl/scoped_pp_resource.h" +#include "ppapi/shared_impl/var.h" +#include "url/gurl.h" + +namespace webkit { +namespace ppapi { + +using ::ppapi::StringVar; + +class PPB_FileSystem_Impl; + +class PPB_FileRef_Impl : public ::ppapi::PPB_FileRef_Shared { + public: + PPB_FileRef_Impl(const ::ppapi::PPB_FileRef_CreateInfo& info, + PP_Resource file_system); + PPB_FileRef_Impl(const ::ppapi::PPB_FileRef_CreateInfo& info, + const base::FilePath& external_file_path); + + // The returned object will have a refcount of 0 (just like "new"). + static PPB_FileRef_Impl* CreateInternal(PP_Instance instance, + PP_Resource pp_file_system, + const std::string& path); + + // The returned object will have a refcount of 0 (just like "new"). + static PPB_FileRef_Impl* CreateExternal( + PP_Instance instance, + const base::FilePath& external_file_path, + const std::string& display_name); + + // PPB_FileRef_API implementation (not provided by PPB_FileRef_Shared). + virtual PP_Resource GetParent() OVERRIDE; + virtual int32_t MakeDirectory( + PP_Bool make_ancestors, + scoped_refptr< ::ppapi::TrackedCallback> callback) OVERRIDE; + virtual int32_t Touch( + PP_Time last_access_time, + PP_Time last_modified_time, + scoped_refptr< ::ppapi::TrackedCallback> callback) OVERRIDE; + virtual int32_t Delete( + scoped_refptr< ::ppapi::TrackedCallback> callback) OVERRIDE; + virtual int32_t Rename( + PP_Resource new_file_ref, + scoped_refptr< ::ppapi::TrackedCallback> callback) OVERRIDE; + virtual int32_t Query( + PP_FileInfo* info, + scoped_refptr< ::ppapi::TrackedCallback> callback) OVERRIDE; + virtual int32_t ReadDirectoryEntries( + const PP_ArrayOutput& output, + scoped_refptr< ::ppapi::TrackedCallback> callback) OVERRIDE; + virtual int32_t QueryInHost( + linked_ptr<PP_FileInfo> info, + scoped_refptr< ::ppapi::TrackedCallback> callback) OVERRIDE; + virtual int32_t ReadDirectoryEntriesInHost( + linked_ptr<std::vector< ::ppapi::PPB_FileRef_CreateInfo> > files, + linked_ptr<std::vector<PP_FileType> > file_types, + scoped_refptr< ::ppapi::TrackedCallback> callback) OVERRIDE; + virtual PP_Var GetAbsolutePath() OVERRIDE; + + PP_Resource file_system_resource() const { return file_system_; } + + // Returns the system path corresponding to this file. Valid only for + // external filesystems. + base::FilePath GetSystemPath() const; + + // Returns the FileSystem API URL corresponding to this file. + GURL GetFileSystemURL() const; + + // Checks if file ref has file system instance and if the instance is opened. + bool HasValidFileSystem() const; + + void AddFileSystemRefCount() { + file_system_ref_ = file_system_; + } + + private: + virtual ~PPB_FileRef_Impl(); + + // Many mutation functions are allow only to non-external filesystems, This + // function returns true if the filesystem is opened and isn't external as an + // access check for these functions. + bool IsValidNonExternalFileSystem() const; + + // 0 for external filesystems. This is a plugin side resource that we don't + // hold a reference here, so file_system_ could be destroyed earlier than + // this object. Right now we checked the existance in plugin delegate before + // use. But it's better to hold a reference once we migrate FileRef to the + // new design. + PP_Resource file_system_; + + // Holds a reference of FileSystem when running in process. See + // PPB_FileRef_Proxy for corresponding code for out-of-process mode. Note + // that this ScopedPPResource is only expected to be used when running in + // process (since PPB_FileRef_Proxy takes care of out-of-process case). + // Also note that this workaround will be no longer needed after FileRef + // refactoring. + ::ppapi::ScopedPPResource file_system_ref_; + + // Used only for external filesystems. + base::FilePath external_file_system_path_; + + // Lazily initialized var created from the external path. This is so we can + // return the identical string object every time it is requested. + scoped_refptr<StringVar> external_path_var_; + + DISALLOW_COPY_AND_ASSIGN(PPB_FileRef_Impl); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_PPB_FILE_REF_IMPL_H_ diff --git a/content/renderer/pepper/ppb_flash_message_loop_impl.cc b/content/renderer/pepper/ppb_flash_message_loop_impl.cc new file mode 100644 index 0000000..105c5a0 --- /dev/null +++ b/content/renderer/pepper/ppb_flash_message_loop_impl.cc @@ -0,0 +1,116 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/ppb_flash_message_loop_impl.h" + +#include "base/callback.h" +#include "base/message_loop/message_loop.h" +#include "ppapi/c/pp_errors.h" + +using ppapi::thunk::PPB_Flash_MessageLoop_API; + +namespace webkit { +namespace ppapi { + +class PPB_Flash_MessageLoop_Impl::State + : public base::RefCounted<PPB_Flash_MessageLoop_Impl::State> { + public: + State() : result_(PP_OK), run_called_(false), quit_called_(false) { + } + + int32_t result() const { return result_; } + void set_result(int32_t result) { result_ = result; } + + bool run_called() const { return run_called_; } + void set_run_called() { run_called_ = true; } + + bool quit_called() const { return quit_called_; } + void set_quit_called() { quit_called_ = true; } + + const RunFromHostProxyCallback& run_callback() const { return run_callback_; } + void set_run_callback(const RunFromHostProxyCallback& run_callback) { + run_callback_ = run_callback; + } + + private: + friend class base::RefCounted<State>; + virtual ~State() {} + + int32_t result_; + bool run_called_; + bool quit_called_; + RunFromHostProxyCallback run_callback_; +}; + +PPB_Flash_MessageLoop_Impl::PPB_Flash_MessageLoop_Impl(PP_Instance instance) + : Resource(::ppapi::OBJECT_IS_IMPL, instance), + state_(new State()) { +} + +PPB_Flash_MessageLoop_Impl::~PPB_Flash_MessageLoop_Impl() { + // It is a no-op if either Run() hasn't been called or Quit() has been called + // to balance the call to Run(). + InternalQuit(PP_ERROR_ABORTED); +} + +// static +PP_Resource PPB_Flash_MessageLoop_Impl::Create(PP_Instance instance) { + return (new PPB_Flash_MessageLoop_Impl(instance))->GetReference(); +} + +PPB_Flash_MessageLoop_API* + PPB_Flash_MessageLoop_Impl::AsPPB_Flash_MessageLoop_API() { + return this; +} + +int32_t PPB_Flash_MessageLoop_Impl::Run() { + return InternalRun(RunFromHostProxyCallback()); +} + +void PPB_Flash_MessageLoop_Impl::RunFromHostProxy( + const RunFromHostProxyCallback& callback) { + InternalRun(callback); +} + +void PPB_Flash_MessageLoop_Impl::Quit() { + InternalQuit(PP_OK); +} + +int32_t PPB_Flash_MessageLoop_Impl::InternalRun( + const RunFromHostProxyCallback& callback) { + if (state_->run_called()) { + if (!callback.is_null()) + callback.Run(PP_ERROR_FAILED); + return PP_ERROR_FAILED; + } + state_->set_run_called(); + state_->set_run_callback(callback); + + // It is possible that the PPB_Flash_MessageLoop_Impl object has been + // destroyed when the nested message loop exits. + scoped_refptr<State> state_protector(state_); + { + base::MessageLoop::ScopedNestableTaskAllower allow( + base::MessageLoop::current()); + base::MessageLoop::current()->Run(); + } + // Don't access data members of the class below. + + return state_protector->result(); +} + +void PPB_Flash_MessageLoop_Impl::InternalQuit(int32_t result) { + if (!state_->run_called() || state_->quit_called()) + return; + state_->set_quit_called(); + state_->set_result(result); + + base::MessageLoop::current()->QuitNow(); + + if (!state_->run_callback().is_null()) + state_->run_callback().Run(result); +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/ppb_flash_message_loop_impl.h b/content/renderer/pepper/ppb_flash_message_loop_impl.h new file mode 100644 index 0000000..ef6903c --- /dev/null +++ b/content/renderer/pepper/ppb_flash_message_loop_impl.h @@ -0,0 +1,54 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_PEPPER_PPB_FLASH_MESSAGE_LOOP_IMPL_H_ +#define CONTENT_RENDERER_PEPPER_PPB_FLASH_MESSAGE_LOOP_IMPL_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "ppapi/shared_impl/resource.h" +#include "ppapi/thunk/ppb_flash_message_loop_api.h" + +namespace webkit { +namespace ppapi { + +class PPB_Flash_MessageLoop_Impl + : public ::ppapi::Resource, + public ::ppapi::thunk::PPB_Flash_MessageLoop_API { + public: + static PP_Resource Create(PP_Instance instance); + + // Resource. + virtual ::ppapi::thunk::PPB_Flash_MessageLoop_API* + AsPPB_Flash_MessageLoop_API() OVERRIDE; + + // PPB_Flash_MessageLoop_API implementation. + virtual int32_t Run() OVERRIDE; + virtual void Quit() OVERRIDE; + virtual void RunFromHostProxy( + const RunFromHostProxyCallback& callback) OVERRIDE; + + private: + class State; + + explicit PPB_Flash_MessageLoop_Impl(PP_Instance instance); + virtual ~PPB_Flash_MessageLoop_Impl(); + + // If |callback| is valid, it will be called when the message loop is signaled + // to quit, and the result passed into it will be the same value as what this + // method returns. + // Please note that |callback| happens before this method returns. + int32_t InternalRun(const RunFromHostProxyCallback& callback); + void InternalQuit(int32_t result); + + scoped_refptr<State> state_; + + DISALLOW_COPY_AND_ASSIGN(PPB_Flash_MessageLoop_Impl); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_PPB_FLASH_MESSAGE_LOOP_IMPL_H_ diff --git a/content/renderer/pepper/ppb_gpu_blacklist_private_impl.cc b/content/renderer/pepper/ppb_gpu_blacklist_private_impl.cc new file mode 100644 index 0000000..81c4292 --- /dev/null +++ b/content/renderer/pepper/ppb_gpu_blacklist_private_impl.cc @@ -0,0 +1,40 @@ +// 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 "content/renderer/pepper/ppb_gpu_blacklist_private_impl.h" + +#include "base/command_line.h" +#include "base/logging.h" +#include "webkit/plugins/plugin_switches.h" + +// todo(nfullagar): Remove this private interface when the SRPC proxy is +// permanently disabled. + +namespace webkit { +namespace ppapi { + +namespace { + +PP_Bool IsGpuBlacklisted() { + CommandLine* command_line = CommandLine::ForCurrentProcess(); + if (command_line) + return PP_FromBool( + command_line->HasSwitch(switches::kDisablePepper3d)); + return PP_TRUE; +} + +} // namespace + +const PPB_GpuBlacklist_Private ppb_gpu_blacklist = { + &IsGpuBlacklisted, +}; + +// static +const PPB_GpuBlacklist_Private* PPB_GpuBlacklist_Private_Impl::GetInterface() { + return &ppb_gpu_blacklist; +} + +} // namespace ppapi +} // namespace webkit + diff --git a/content/renderer/pepper/ppb_gpu_blacklist_private_impl.h b/content/renderer/pepper/ppb_gpu_blacklist_private_impl.h new file mode 100644 index 0000000..189afe31 --- /dev/null +++ b/content/renderer/pepper/ppb_gpu_blacklist_private_impl.h @@ -0,0 +1,22 @@ +// 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. + +#ifndef CONTENT_RENDERER_PEPPER_PPB_GPU_BLACKLIST_PRIVATE_IMPL_H_ +#define CONTENT_RENDERER_PEPPER_PPB_GPU_BLACKLIST_PRIVATE_IMPL_H_ + +#include "ppapi/c/private/ppb_gpu_blacklist_private.h" + +namespace webkit { +namespace ppapi { + +class PPB_GpuBlacklist_Private_Impl { + public: + static const PPB_GpuBlacklist_Private* GetInterface(); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_PPB_GPU_BLACKLIST_PRIVATE_IMPL_H_ + diff --git a/content/renderer/pepper/ppb_graphics_3d_impl.cc b/content/renderer/pepper/ppb_graphics_3d_impl.cc new file mode 100644 index 0000000..26a7789 --- /dev/null +++ b/content/renderer/pepper/ppb_graphics_3d_impl.cc @@ -0,0 +1,328 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/ppb_graphics_3d_impl.h" + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/message_loop/message_loop.h" +#include "base/strings/utf_string_conversions.h" +#include "content/renderer/pepper/plugin_module.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" +#include "content/renderer/pepper/resource_helper.h" +#include "gpu/command_buffer/client/gles2_implementation.h" +#include "ppapi/c/ppp_graphics_3d.h" +#include "ppapi/thunk/enter.h" +#include "third_party/WebKit/public/platform/WebString.h" +#include "third_party/WebKit/public/web/WebConsoleMessage.h" +#include "third_party/WebKit/public/web/WebDocument.h" +#include "third_party/WebKit/public/web/WebElement.h" +#include "third_party/WebKit/public/web/WebFrame.h" +#include "third_party/WebKit/public/web/WebPluginContainer.h" +#include "webkit/plugins/plugin_switches.h" + +using ppapi::thunk::EnterResourceNoLock; +using ppapi::thunk::PPB_Graphics3D_API; +using WebKit::WebConsoleMessage; +using WebKit::WebFrame; +using WebKit::WebPluginContainer; +using WebKit::WebString; + +namespace webkit { +namespace ppapi { + +namespace { +const int32 kCommandBufferSize = 1024 * 1024; +const int32 kTransferBufferSize = 1024 * 1024; + +PP_Bool ShmToHandle(base::SharedMemory* shm, + size_t size, + int* shm_handle, + uint32_t* shm_size) { + if (!shm || !shm_handle || !shm_size) + return PP_FALSE; +#if defined(OS_POSIX) + *shm_handle = shm->handle().fd; +#elif defined(OS_WIN) + *shm_handle = reinterpret_cast<int>(shm->handle()); +#else + #error "Platform not supported." +#endif + *shm_size = size; + return PP_TRUE; +} + +} // namespace. + +PPB_Graphics3D_Impl::PPB_Graphics3D_Impl(PP_Instance instance) + : PPB_Graphics3D_Shared(instance), + bound_to_instance_(false), + commit_pending_(false), + weak_ptr_factory_(this) { +} + +PPB_Graphics3D_Impl::~PPB_Graphics3D_Impl() { + DestroyGLES2Impl(); +} + +// static +PP_Bool PPB_Graphics3D_Impl::IsGpuBlacklisted() { + CommandLine* command_line = CommandLine::ForCurrentProcess(); + if (command_line) + return PP_FromBool( + command_line->HasSwitch(switches::kDisablePepper3d)); + return PP_TRUE; +} + +// static +PP_Resource PPB_Graphics3D_Impl::Create(PP_Instance instance, + PP_Resource share_context, + const int32_t* attrib_list) { + PPB_Graphics3D_API* share_api = NULL; + if (IsGpuBlacklisted()) + return 0; + if (share_context) { + EnterResourceNoLock<PPB_Graphics3D_API> enter(share_context, true); + if (enter.failed()) + return 0; + share_api = enter.object(); + } + scoped_refptr<PPB_Graphics3D_Impl> graphics_3d( + new PPB_Graphics3D_Impl(instance)); + if (!graphics_3d->Init(share_api, attrib_list)) + return 0; + return graphics_3d->GetReference(); +} + +// static +PP_Resource PPB_Graphics3D_Impl::CreateRaw(PP_Instance instance, + PP_Resource share_context, + const int32_t* attrib_list) { + PPB_Graphics3D_API* share_api = NULL; + if (IsGpuBlacklisted()) + return 0; + if (share_context) { + EnterResourceNoLock<PPB_Graphics3D_API> enter(share_context, true); + if (enter.failed()) + return 0; + share_api = enter.object(); + } + scoped_refptr<PPB_Graphics3D_Impl> graphics_3d( + new PPB_Graphics3D_Impl(instance)); + if (!graphics_3d->InitRaw(share_api, attrib_list)) + return 0; + return graphics_3d->GetReference(); +} + +PP_Bool PPB_Graphics3D_Impl::SetGetBuffer(int32_t transfer_buffer_id) { + GetCommandBuffer()->SetGetBuffer(transfer_buffer_id); + return PP_TRUE; +} + +gpu::CommandBuffer::State PPB_Graphics3D_Impl::GetState() { + return GetCommandBuffer()->GetState(); +} + +int32_t PPB_Graphics3D_Impl::CreateTransferBuffer(uint32_t size) { + int32_t id = -1; + GetCommandBuffer()->CreateTransferBuffer(size, &id); + return id; +} + +PP_Bool PPB_Graphics3D_Impl::DestroyTransferBuffer(int32_t id) { + GetCommandBuffer()->DestroyTransferBuffer(id); + return PP_TRUE; +} + +PP_Bool PPB_Graphics3D_Impl::GetTransferBuffer(int32_t id, + int* shm_handle, + uint32_t* shm_size) { + gpu::Buffer buffer = GetCommandBuffer()->GetTransferBuffer(id); + return ShmToHandle(buffer.shared_memory, buffer.size, shm_handle, shm_size); +} + +PP_Bool PPB_Graphics3D_Impl::Flush(int32_t put_offset) { + GetCommandBuffer()->Flush(put_offset); + return PP_TRUE; +} + +gpu::CommandBuffer::State PPB_Graphics3D_Impl::FlushSync(int32_t put_offset) { + gpu::CommandBuffer::State state = GetCommandBuffer()->GetState(); + return GetCommandBuffer()->FlushSync(put_offset, state.get_offset); +} + +gpu::CommandBuffer::State PPB_Graphics3D_Impl::FlushSyncFast( + int32_t put_offset, + int32_t last_known_get) { + return GetCommandBuffer()->FlushSync(put_offset, last_known_get); +} + +uint32_t PPB_Graphics3D_Impl::InsertSyncPoint() { + return GetCommandBuffer()->InsertSyncPoint(); +} + +bool PPB_Graphics3D_Impl::BindToInstance(bool bind) { + bound_to_instance_ = bind; + return true; +} + +bool PPB_Graphics3D_Impl::IsOpaque() { + return platform_context_->IsOpaque(); +} + +void PPB_Graphics3D_Impl::ViewWillInitiatePaint() { +} + +void PPB_Graphics3D_Impl::ViewInitiatedPaint() { + commit_pending_ = false; + + if (HasPendingSwap()) + SwapBuffersACK(PP_OK); +} + +void PPB_Graphics3D_Impl::ViewFlushedPaint() { +} + +gpu::CommandBuffer* PPB_Graphics3D_Impl::GetCommandBuffer() { + return platform_context_->GetCommandBuffer(); +} + +int32 PPB_Graphics3D_Impl::DoSwapBuffers() { + // We do not have a GLES2 implementation when using an OOP proxy. + // The plugin-side proxy is responsible for adding the SwapBuffers command + // to the command buffer in that case. + if (gles2_impl()) + gles2_impl()->SwapBuffers(); + + if (bound_to_instance_) { + // If we are bound to the instance, we need to ask the compositor + // to commit our backing texture so that the graphics appears on the page. + // When the backing texture will be committed we get notified via + // ViewFlushedPaint(). + // + // Don't need to check for NULL from GetPluginInstance since when we're + // bound, we know our instance is valid. + ResourceHelper::GetPluginInstance(this)->CommitBackingTexture(); + commit_pending_ = true; + } else { + // Wait for the command to complete on the GPU to allow for throttling. + platform_context_->Echo(base::Bind(&PPB_Graphics3D_Impl::OnSwapBuffers, + weak_ptr_factory_.GetWeakPtr())); + } + + + return PP_OK_COMPLETIONPENDING; +} + +bool PPB_Graphics3D_Impl::Init(PPB_Graphics3D_API* share_context, + const int32_t* attrib_list) { + if (!InitRaw(share_context, attrib_list)) + return false; + + gpu::CommandBuffer* command_buffer = GetCommandBuffer(); + if (!command_buffer->Initialize()) + return false; + + gpu::gles2::GLES2Implementation* share_gles2 = NULL; + if (share_context) { + share_gles2 = + static_cast<PPB_Graphics3D_Shared*>(share_context)->gles2_impl(); + } + + return CreateGLES2Impl(kCommandBufferSize, kTransferBufferSize, + share_gles2); +} + +bool PPB_Graphics3D_Impl::InitRaw(PPB_Graphics3D_API* share_context, + const int32_t* attrib_list) { + PluginInstanceImpl* plugin_instance = ResourceHelper::GetPluginInstance(this); + if (!plugin_instance) + return false; + + PluginDelegate::PlatformContext3D* share_platform_context = NULL; + if (share_context) { + PPB_Graphics3D_Impl* share_graphics = + static_cast<PPB_Graphics3D_Impl*>(share_context); + share_platform_context = share_graphics->platform_context(); + } + + platform_context_.reset(plugin_instance->CreateContext3D()); + if (!platform_context_) + return false; + + if (!platform_context_->Init(attrib_list, share_platform_context)) + return false; + + platform_context_->SetContextLostCallback( + base::Bind(&PPB_Graphics3D_Impl::OnContextLost, + weak_ptr_factory_.GetWeakPtr())); + + platform_context_->SetOnConsoleMessageCallback( + base::Bind(&PPB_Graphics3D_Impl::OnConsoleMessage, + weak_ptr_factory_.GetWeakPtr())); + return true; +} + +void PPB_Graphics3D_Impl::OnConsoleMessage(const std::string& message, + int id) { + if (!bound_to_instance_) + return; + WebPluginContainer* container = + ResourceHelper::GetPluginInstance(this)->container(); + if (!container) + return; + WebFrame* frame = container->element().document().frame(); + if (!frame) + return; + WebConsoleMessage console_message = WebConsoleMessage( + WebConsoleMessage::LevelError, WebString(UTF8ToUTF16(message))); + frame->addMessageToConsole(console_message); +} + +void PPB_Graphics3D_Impl::OnSwapBuffers() { + if (HasPendingSwap()) { + // If we're off-screen, no need to trigger and wait for compositing. + // Just send the swap-buffers ACK to the plugin immediately. + commit_pending_ = false; + SwapBuffersACK(PP_OK); + } +} + +void PPB_Graphics3D_Impl::OnContextLost() { + // Don't need to check for NULL from GetPluginInstance since when we're + // bound, we know our instance is valid. + if (bound_to_instance_) + ResourceHelper::GetPluginInstance(this)->BindGraphics(pp_instance(), 0); + + // Send context lost to plugin. This may have been caused by a PPAPI call, so + // avoid re-entering. + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&PPB_Graphics3D_Impl::SendContextLost, + weak_ptr_factory_.GetWeakPtr())); +} + +void PPB_Graphics3D_Impl::SendContextLost() { + // By the time we run this, the instance may have been deleted, or in the + // process of being deleted. Even in the latter case, we don't want to send a + // callback after DidDestroy. + PluginInstanceImpl* instance = ResourceHelper::GetPluginInstance(this); + if (!instance || !instance->container()) + return; + + // This PPB_Graphics3D_Impl could be deleted during the call to + // GetPluginInterface (which sends a sync message in some cases). We still + // send the Graphics3DContextLost to the plugin; the instance may care about + // that event even though this context has been destroyed. + PP_Instance this_pp_instance = pp_instance(); + const PPP_Graphics3D* ppp_graphics_3d = + static_cast<const PPP_Graphics3D*>( + instance->module()->GetPluginInterface( + PPP_GRAPHICS_3D_INTERFACE)); + if (ppp_graphics_3d) + ppp_graphics_3d->Graphics3DContextLost(this_pp_instance); +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/ppb_graphics_3d_impl.h b/content/renderer/pepper/ppb_graphics_3d_impl.h new file mode 100644 index 0000000..e84a766 --- /dev/null +++ b/content/renderer/pepper/ppb_graphics_3d_impl.h @@ -0,0 +1,94 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_PEPPER_PPB_GRAPHICS_3D_IMPL_H_ +#define CONTENT_RENDERER_PEPPER_PPB_GRAPHICS_3D_IMPL_H_ + +#include "base/memory/weak_ptr.h" +#include "content/renderer/pepper/plugin_delegate.h" +#include "ppapi/shared_impl/ppb_graphics_3d_shared.h" +#include "ppapi/shared_impl/resource.h" + +namespace webkit { +namespace ppapi { + +class PPB_Graphics3D_Impl : public ::ppapi::PPB_Graphics3D_Shared { + public: + static PP_Resource Create(PP_Instance instance, + PP_Resource share_context, + const int32_t* attrib_list); + static PP_Resource CreateRaw(PP_Instance instance, + PP_Resource share_context, + const int32_t* attrib_list); + + // PPB_Graphics3D_API trusted implementation. + virtual PP_Bool SetGetBuffer(int32_t transfer_buffer_id) OVERRIDE; + virtual gpu::CommandBuffer::State GetState() OVERRIDE; + virtual int32_t CreateTransferBuffer(uint32_t size) OVERRIDE; + virtual PP_Bool DestroyTransferBuffer(int32_t id) OVERRIDE; + virtual PP_Bool GetTransferBuffer(int32_t id, + int* shm_handle, + uint32_t* shm_size) OVERRIDE; + virtual PP_Bool Flush(int32_t put_offset) OVERRIDE; + virtual gpu::CommandBuffer::State FlushSync(int32_t put_offset) OVERRIDE; + virtual gpu::CommandBuffer::State FlushSyncFast( + int32_t put_offset, + int32_t last_known_get) OVERRIDE; + virtual uint32_t InsertSyncPoint() OVERRIDE; + + // Binds/unbinds the graphics of this context with the associated instance. + // Returns true if binding/unbinding is successful. + bool BindToInstance(bool bind); + + // Returns true if the backing texture is always opaque. + bool IsOpaque(); + + // Notifications about the view's progress painting. See PluginInstance. + // These messages are used to send Flush callbacks to the plugin. + void ViewWillInitiatePaint(); + void ViewInitiatedPaint(); + void ViewFlushedPaint(); + + PluginDelegate::PlatformContext3D* platform_context() { + return platform_context_.get(); + } + + protected: + virtual ~PPB_Graphics3D_Impl(); + // ppapi::PPB_Graphics3D_Shared overrides. + virtual gpu::CommandBuffer* GetCommandBuffer() OVERRIDE; + virtual int32 DoSwapBuffers() OVERRIDE; + + private: + explicit PPB_Graphics3D_Impl(PP_Instance instance); + + static PP_Bool IsGpuBlacklisted(); + + bool Init(PPB_Graphics3D_API* share_context, + const int32_t* attrib_list); + bool InitRaw(PPB_Graphics3D_API* share_context, + const int32_t* attrib_list); + + // Notifications received from the GPU process. + void OnSwapBuffers(); + void OnContextLost(); + void OnConsoleMessage(const std::string& msg, int id); + // Notifications sent to plugin. + void SendContextLost(); + + // True if context is bound to instance. + bool bound_to_instance_; + // True when waiting for compositor to commit our backing texture. + bool commit_pending_; + // PluginDelegate's 3D Context. Responsible for providing the command buffer. + scoped_ptr<PluginDelegate::PlatformContext3D> platform_context_; + base::WeakPtrFactory<PPB_Graphics3D_Impl> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(PPB_Graphics3D_Impl); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_PPB_GRAPHICS_3D_IMPL_H_ diff --git a/content/renderer/pepper/ppb_image_data_impl.cc b/content/renderer/pepper/ppb_image_data_impl.cc new file mode 100644 index 0000000..e17b348 --- /dev/null +++ b/content/renderer/pepper/ppb_image_data_impl.cc @@ -0,0 +1,278 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/ppb_image_data_impl.h" + +#include <algorithm> +#include <limits> + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "content/renderer/pepper/common.h" +#include "content/renderer/pepper/resource_helper.h" +#include "skia/ext/platform_canvas.h" +#include "ppapi/c/pp_instance.h" +#include "ppapi/c/pp_resource.h" +#include "ppapi/c/ppb_image_data.h" +#include "ppapi/thunk/thunk.h" +#include "third_party/skia/include/core/SkColorPriv.h" + +using ::ppapi::thunk::PPB_ImageData_API; + +namespace webkit { +namespace ppapi { + +PPB_ImageData_Impl::PPB_ImageData_Impl(PP_Instance instance, + PPB_ImageData_Shared::ImageDataType type) + : Resource(::ppapi::OBJECT_IS_IMPL, instance), + format_(PP_IMAGEDATAFORMAT_BGRA_PREMUL), + width_(0), + height_(0) { + switch (type) { + case PPB_ImageData_Shared::PLATFORM: + backend_.reset(new ImageDataPlatformBackend); + return; + case PPB_ImageData_Shared::SIMPLE: + backend_.reset(new ImageDataSimpleBackend); + return; + // No default: so that we get a compiler warning if any types are added. + } + NOTREACHED(); +} + +PPB_ImageData_Impl::~PPB_ImageData_Impl() { +} + +bool PPB_ImageData_Impl::Init(PP_ImageDataFormat format, + int width, int height, + bool init_to_zero) { + // TODO(brettw) this should be called only on the main thread! + if (!IsImageDataFormatSupported(format)) + return false; // Only support this one format for now. + if (width <= 0 || height <= 0) + return false; + if (static_cast<int64>(width) * static_cast<int64>(height) >= + std::numeric_limits<int32>::max() / 4) + return false; // Prevent overflow of signed 32-bit ints. + + format_ = format; + width_ = width; + height_ = height; + return backend_->Init(this, format, width, height, init_to_zero); +} + +// static +PP_Resource PPB_ImageData_Impl::Create(PP_Instance instance, + PPB_ImageData_Shared::ImageDataType type, + PP_ImageDataFormat format, + const PP_Size& size, + PP_Bool init_to_zero) { + scoped_refptr<PPB_ImageData_Impl> + data(new PPB_ImageData_Impl(instance, type)); + if (!data->Init(format, size.width, size.height, !!init_to_zero)) + return 0; + return data->GetReference(); +} + +PPB_ImageData_API* PPB_ImageData_Impl::AsPPB_ImageData_API() { + return this; +} + +bool PPB_ImageData_Impl::IsMapped() const { + return backend_->IsMapped(); +} + +PluginDelegate::PlatformImage2D* PPB_ImageData_Impl::PlatformImage() const { + return backend_->PlatformImage(); +} + +PP_Bool PPB_ImageData_Impl::Describe(PP_ImageDataDesc* desc) { + desc->format = format_; + desc->size.width = width_; + desc->size.height = height_; + desc->stride = width_ * 4; + return PP_TRUE; +} + +void* PPB_ImageData_Impl::Map() { + return backend_->Map(); +} + +void PPB_ImageData_Impl::Unmap() { + backend_->Unmap(); +} + +int32_t PPB_ImageData_Impl::GetSharedMemory(int* handle, uint32_t* byte_count) { + return backend_->GetSharedMemory(handle, byte_count); +} + +skia::PlatformCanvas* PPB_ImageData_Impl::GetPlatformCanvas() { + return backend_->GetPlatformCanvas(); +} + +SkCanvas* PPB_ImageData_Impl::GetCanvas() { + return backend_->GetCanvas(); +} + +void PPB_ImageData_Impl::SetIsCandidateForReuse() { + // Nothing to do since we don't support image data re-use in-process. +} + +const SkBitmap* PPB_ImageData_Impl::GetMappedBitmap() const { + return backend_->GetMappedBitmap(); +} + +// ImageDataPlatformBackend ---------------------------------------------------- + +ImageDataPlatformBackend::ImageDataPlatformBackend() { +} + +ImageDataPlatformBackend::~ImageDataPlatformBackend() { +} + +bool ImageDataPlatformBackend::Init(PPB_ImageData_Impl* impl, + PP_ImageDataFormat format, + int width, int height, + bool init_to_zero) { + PluginDelegate* plugin_delegate = ResourceHelper::GetPluginDelegate(impl); + if (!plugin_delegate) + return false; + + // TODO(brettw) use init_to_zero when we implement caching. + platform_image_.reset(plugin_delegate->CreateImage2D(width, height)); + return !!platform_image_.get(); +} + +bool ImageDataPlatformBackend::IsMapped() const { + return !!mapped_canvas_.get(); +} + +PluginDelegate::PlatformImage2D* +ImageDataPlatformBackend::PlatformImage() const { + return platform_image_.get(); +} + +void* ImageDataPlatformBackend::Map() { + if (!mapped_canvas_) { + mapped_canvas_.reset(platform_image_->Map()); + if (!mapped_canvas_) + return NULL; + } + const SkBitmap& bitmap = + skia::GetTopDevice(*mapped_canvas_)->accessBitmap(true); + + // Our platform bitmaps are set to opaque by default, which we don't want. + const_cast<SkBitmap&>(bitmap).setIsOpaque(false); + + bitmap.lockPixels(); + return bitmap.getAddr32(0, 0); +} + +void ImageDataPlatformBackend::Unmap() { + // This is currently unimplemented, which is OK. The data will just always + // be around once it's mapped. Chrome's TransportDIB isn't currently + // unmappable without freeing it, but this may be something we want to support + // in the future to save some memory. +} + +int32_t ImageDataPlatformBackend::GetSharedMemory(int* handle, + uint32_t* byte_count) { + *handle = platform_image_->GetSharedMemoryHandle(byte_count); + return PP_OK; +} + +skia::PlatformCanvas* ImageDataPlatformBackend::GetPlatformCanvas() { + return mapped_canvas_.get(); +} + +SkCanvas* ImageDataPlatformBackend::GetCanvas() { + return mapped_canvas_.get(); +} + +const SkBitmap* ImageDataPlatformBackend::GetMappedBitmap() const { + if (!mapped_canvas_) + return NULL; + return &skia::GetTopDevice(*mapped_canvas_)->accessBitmap(false); +} + +// ImageDataSimpleBackend ------------------------------------------------------ + +ImageDataSimpleBackend::ImageDataSimpleBackend() + : map_count_(0) { +} + +ImageDataSimpleBackend::~ImageDataSimpleBackend() { +} + +bool ImageDataSimpleBackend::Init(PPB_ImageData_Impl* impl, + PP_ImageDataFormat format, + int width, int height, + bool init_to_zero) { + skia_bitmap_.setConfig(SkBitmap::kARGB_8888_Config, + impl->width(), impl->height()); + PluginDelegate* plugin_delegate = ResourceHelper::GetPluginDelegate(impl); + if (!plugin_delegate) + return false; + shared_memory_.reset( + plugin_delegate->CreateAnonymousSharedMemory(skia_bitmap_.getSize())); + return !!shared_memory_.get(); +} + +bool ImageDataSimpleBackend::IsMapped() const { + return map_count_ > 0; +} + +PluginDelegate::PlatformImage2D* ImageDataSimpleBackend::PlatformImage() const { + return NULL; +} + +void* ImageDataSimpleBackend::Map() { + DCHECK(shared_memory_.get()); + if (map_count_++ == 0) { + shared_memory_->Map(skia_bitmap_.getSize()); + skia_bitmap_.setPixels(shared_memory_->memory()); + // Our platform bitmaps are set to opaque by default, which we don't want. + skia_bitmap_.setIsOpaque(false); + skia_canvas_.reset(new SkCanvas(skia_bitmap_)); + return skia_bitmap_.getAddr32(0, 0); + } + return shared_memory_->memory(); +} + +void ImageDataSimpleBackend::Unmap() { + if (--map_count_ == 0) + shared_memory_->Unmap(); +} + +int32_t ImageDataSimpleBackend::GetSharedMemory(int* handle, + uint32_t* byte_count) { + *byte_count = skia_bitmap_.getSize(); +#if defined(OS_POSIX) + *handle = shared_memory_->handle().fd; +#elif defined(OS_WIN) + *handle = reinterpret_cast<int>(shared_memory_->handle()); +#else +#error "Platform not supported." +#endif + return PP_OK; +} + +skia::PlatformCanvas* ImageDataSimpleBackend::GetPlatformCanvas() { + return NULL; +} + +SkCanvas* ImageDataSimpleBackend::GetCanvas() { + if (!IsMapped()) + return NULL; + return skia_canvas_.get(); +} + +const SkBitmap* ImageDataSimpleBackend::GetMappedBitmap() const { + if (!IsMapped()) + return NULL; + return &skia_bitmap_; +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/ppb_image_data_impl.h b/content/renderer/pepper/ppb_image_data_impl.h new file mode 100644 index 0000000..58cba82 --- /dev/null +++ b/content/renderer/pepper/ppb_image_data_impl.h @@ -0,0 +1,197 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_PEPPER_PPB_IMAGE_DATA_IMPL_H_ +#define CONTENT_RENDERER_PEPPER_PPB_IMAGE_DATA_IMPL_H_ + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "content/common/content_export.h" +#include "content/renderer/pepper/plugin_delegate.h" +#include "ppapi/c/ppb_image_data.h" +#include "ppapi/shared_impl/ppb_image_data_shared.h" +#include "ppapi/shared_impl/resource.h" +#include "ppapi/thunk/ppb_image_data_api.h" +#include "third_party/skia/include/core/SkCanvas.h" + +class SkBitmap; +class SkCanvas; + +namespace webkit { +namespace ppapi { + +class CONTENT_EXPORT PPB_ImageData_Impl + : public ::ppapi::Resource, + public ::ppapi::PPB_ImageData_Shared, + public NON_EXPORTED_BASE(::ppapi::thunk::PPB_ImageData_API) { + public: + // We delegate most of our implementation to a back-end class that either uses + // a PlatformCanvas (for most trusted stuff) or bare shared memory (for use by + // NaCl, or trusted plugins when the PlatformCanvas isn't needed). This makes + // it cheap & easy to implement Swap. + class Backend { + public: + virtual ~Backend() {}; + virtual bool Init(PPB_ImageData_Impl* impl, PP_ImageDataFormat format, + int width, int height, bool init_to_zero) = 0; + virtual bool IsMapped() const = 0; + virtual PluginDelegate::PlatformImage2D* PlatformImage() const = 0; + virtual void* Map() = 0; + virtual void Unmap() = 0; + virtual int32_t GetSharedMemory(int* handle, uint32_t* byte_count) = 0; + virtual SkCanvas* GetPlatformCanvas() = 0; + virtual SkCanvas* GetCanvas() = 0; + virtual const SkBitmap* GetMappedBitmap() const = 0; + }; + + // If you call this constructor, you must also call Init before use. Normally + // you should use the static Create function, but this constructor is needed + // for some internal uses of ImageData (like Graphics2D). + PPB_ImageData_Impl(PP_Instance instance, + PPB_ImageData_Shared::ImageDataType type); + + bool Init(PP_ImageDataFormat format, + int width, int height, + bool init_to_zero); + + static PP_Resource Create(PP_Instance pp_instance, + PPB_ImageData_Shared::ImageDataType type, + PP_ImageDataFormat format, + const PP_Size& size, + PP_Bool init_to_zero); + + int width() const { return width_; } + int height() const { return height_; } + + // Returns the image format. + PP_ImageDataFormat format() const { return format_; } + + // Returns true if this image is mapped. False means that the image is either + // invalid or not mapped. See ImageDataAutoMapper below. + bool IsMapped() const; + PluginDelegate::PlatformImage2D* PlatformImage() const; + + // Resource override. + virtual ::ppapi::thunk::PPB_ImageData_API* AsPPB_ImageData_API() OVERRIDE; + + // PPB_ImageData_API implementation. + virtual PP_Bool Describe(PP_ImageDataDesc* desc) OVERRIDE; + virtual void* Map() OVERRIDE; + virtual void Unmap() OVERRIDE; + virtual int32_t GetSharedMemory(int* handle, uint32_t* byte_count) OVERRIDE; + virtual SkCanvas* GetPlatformCanvas() OVERRIDE; + virtual SkCanvas* GetCanvas() OVERRIDE; + virtual void SetIsCandidateForReuse() OVERRIDE; + + const SkBitmap* GetMappedBitmap() const; + + private: + virtual ~PPB_ImageData_Impl(); + + PP_ImageDataFormat format_; + int width_; + int height_; + scoped_ptr<Backend> backend_; + + DISALLOW_COPY_AND_ASSIGN(PPB_ImageData_Impl); +}; + +class ImageDataPlatformBackend : public PPB_ImageData_Impl::Backend { + public: + ImageDataPlatformBackend(); + virtual ~ImageDataPlatformBackend(); + + // PPB_ImageData_Impl::Backend implementation. + virtual bool Init(PPB_ImageData_Impl* impl, PP_ImageDataFormat format, + int width, int height, bool init_to_zero) OVERRIDE; + virtual bool IsMapped() const OVERRIDE; + virtual PluginDelegate::PlatformImage2D* PlatformImage() const OVERRIDE; + virtual void* Map() OVERRIDE; + virtual void Unmap() OVERRIDE; + virtual int32_t GetSharedMemory(int* handle, uint32_t* byte_count) OVERRIDE; + virtual SkCanvas* GetPlatformCanvas() OVERRIDE; + virtual SkCanvas* GetCanvas() OVERRIDE; + virtual const SkBitmap* GetMappedBitmap() const OVERRIDE; + + private: + // This will be NULL before initialization, and if this PPB_ImageData_Impl is + // swapped with another. + scoped_ptr<PluginDelegate::PlatformImage2D> platform_image_; + + // When the device is mapped, this is the image. Null when umapped. + scoped_ptr<SkCanvas> mapped_canvas_; + + DISALLOW_COPY_AND_ASSIGN(ImageDataPlatformBackend); +}; + +class ImageDataSimpleBackend : public PPB_ImageData_Impl::Backend { + public: + ImageDataSimpleBackend(); + virtual ~ImageDataSimpleBackend(); + + // PPB_ImageData_Impl::Backend implementation. + virtual bool Init(PPB_ImageData_Impl* impl, PP_ImageDataFormat format, + int width, int height, bool init_to_zero) OVERRIDE; + virtual bool IsMapped() const OVERRIDE; + virtual PluginDelegate::PlatformImage2D* PlatformImage() const OVERRIDE; + virtual void* Map() OVERRIDE; + virtual void Unmap() OVERRIDE; + virtual int32_t GetSharedMemory(int* handle, uint32_t* byte_count) OVERRIDE; + virtual SkCanvas* GetPlatformCanvas() OVERRIDE; + virtual SkCanvas* GetCanvas() OVERRIDE; + virtual const SkBitmap* GetMappedBitmap() const OVERRIDE; + + private: + scoped_ptr<base::SharedMemory> shared_memory_; + // skia_bitmap_ is backed by shared_memory_. + SkBitmap skia_bitmap_; + scoped_ptr<SkCanvas> skia_canvas_; + uint32 map_count_; + + DISALLOW_COPY_AND_ASSIGN(ImageDataSimpleBackend); +}; + +// Manages mapping an image resource if necessary. Use this to ensure the +// image is mapped. The destructor will put the image back into the previous +// state. You must check is_valid() to make sure the image was successfully +// mapped before using it. +// +// Example: +// ImageDataAutoMapper mapper(image_data); +// if (!mapper.is_valid()) +// return utter_failure; +// image_data->mapped_canvas()->blah(); // Guaranteed valid. +class ImageDataAutoMapper { + public: + explicit ImageDataAutoMapper(PPB_ImageData_Impl* image_data) + : image_data_(image_data) { + if (image_data_->IsMapped()) { + is_valid_ = true; + needs_unmap_ = false; + } else { + is_valid_ = needs_unmap_ = !!image_data_->Map(); + } + } + + ~ImageDataAutoMapper() { + if (needs_unmap_) + image_data_->Unmap(); + } + + // Check this to see if the image was successfully mapped. If this is false, + // the image could not be mapped and is unusable. + bool is_valid() const { return is_valid_; } + + private: + PPB_ImageData_Impl* image_data_; + bool is_valid_; + bool needs_unmap_; + + DISALLOW_COPY_AND_ASSIGN(ImageDataAutoMapper); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_PPB_IMAGE_DATA_IMPL_H_ diff --git a/content/renderer/pepper/ppb_network_monitor_private_impl.cc b/content/renderer/pepper/ppb_network_monitor_private_impl.cc new file mode 100644 index 0000000..75a8935 --- /dev/null +++ b/content/renderer/pepper/ppb_network_monitor_private_impl.cc @@ -0,0 +1,89 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/ppb_network_monitor_private_impl.h" + +#include "base/bind.h" +#include "content/renderer/pepper/resource_helper.h" +#include "ppapi/shared_impl/ppb_network_list_private_shared.h" +#include "ppapi/shared_impl/private/net_address_private_impl.h" +#include "net/base/ip_endpoint.h" +#include "net/base/net_util.h" + +namespace webkit { +namespace ppapi { + +PPB_NetworkMonitor_Private_Impl::PPB_NetworkMonitor_Private_Impl( + PP_Instance instance, + PPB_NetworkMonitor_Callback callback, + void* user_data) + : Resource(::ppapi::OBJECT_IS_IMPL, instance), + callback_(callback), + user_data_(user_data), + started_(false) { +} + +PPB_NetworkMonitor_Private_Impl::~PPB_NetworkMonitor_Private_Impl() { + PluginDelegate* plugin_delegate = ResourceHelper::GetPluginDelegate(this); + if (plugin_delegate && started_) { + plugin_delegate->RemoveNetworkListObserver(this); + } +} + +// static +PP_Resource PPB_NetworkMonitor_Private_Impl::Create( + PP_Instance instance, + PPB_NetworkMonitor_Callback callback, + void* user_data) { + scoped_refptr<PPB_NetworkMonitor_Private_Impl> result( + new PPB_NetworkMonitor_Private_Impl(instance, callback, user_data)); + if (!result->Start()) + return 0; + return result->GetReference(); +} + +::ppapi::thunk::PPB_NetworkMonitor_Private_API* +PPB_NetworkMonitor_Private_Impl::AsPPB_NetworkMonitor_Private_API() { + return this; +} + +bool PPB_NetworkMonitor_Private_Impl::Start() { + PluginDelegate* plugin_delegate = ResourceHelper::GetPluginDelegate(this); + if (!plugin_delegate) + return false; + started_ = plugin_delegate->AddNetworkListObserver(this); + return started_; +} + +void PPB_NetworkMonitor_Private_Impl::OnNetworkListChanged( + const net::NetworkInterfaceList& list) { + ::ppapi::NetworkList list_copy(list.size()); + for (size_t i = 0; i < list.size(); ++i) { + ::ppapi::NetworkInfo& network = list_copy.at(i); + network.name = list[i].name; + + network.addresses.resize( + 1, ::ppapi::NetAddressPrivateImpl::kInvalidNetAddress); + bool result = ::ppapi::NetAddressPrivateImpl::IPEndPointToNetAddress( + list[i].address, 0, &(network.addresses[0])); + DCHECK(result); + + // TODO(sergeyu): Currently net::NetworkInterfaceList provides + // only name and one IP address. Add all other fields and copy + // them here. + network.type = PP_NETWORKLIST_UNKNOWN; + network.state = PP_NETWORKLIST_UP; + network.display_name = list[i].name; + network.mtu = 0; + } + scoped_refptr< ::ppapi::NetworkListStorage> list_storage( + new ::ppapi::NetworkListStorage(list_copy)); + PP_Resource list_resource = + ::ppapi::PPB_NetworkList_Private_Shared::Create( + ::ppapi::OBJECT_IS_IMPL, pp_instance(), list_storage); + callback_(user_data_, list_resource); +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/ppb_network_monitor_private_impl.h b/content/renderer/pepper/ppb_network_monitor_private_impl.h new file mode 100644 index 0000000..c603eb1 --- /dev/null +++ b/content/renderer/pepper/ppb_network_monitor_private_impl.h @@ -0,0 +1,53 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_PEPPER_PPB_NETWORK_MONITOR_PRIVATE_IMPL_H_ +#define CONTENT_RENDERER_PEPPER_PPB_NETWORK_MONITOR_PRIVATE_IMPL_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "content/renderer/pepper/plugin_delegate.h" +#include "ppapi/c/private/ppb_network_monitor_private.h" +#include "ppapi/shared_impl/resource.h" +#include "ppapi/thunk/ppb_network_monitor_private_api.h" +#include "webkit/glue/network_list_observer.h" + +namespace webkit { +namespace ppapi { + +class PPB_NetworkMonitor_Private_Impl + : public ::ppapi::Resource, + public ::ppapi::thunk::PPB_NetworkMonitor_Private_API, + public webkit_glue::NetworkListObserver { + public: + static PP_Resource Create(PP_Instance instance, + PPB_NetworkMonitor_Callback callback, + void* user_data); + + virtual ::ppapi::thunk::PPB_NetworkMonitor_Private_API* + AsPPB_NetworkMonitor_Private_API() OVERRIDE; + + // NetworkListObserver interface. + virtual void OnNetworkListChanged( + const net::NetworkInterfaceList& list) OVERRIDE; + + private: + PPB_NetworkMonitor_Private_Impl(PP_Instance instance, + PPB_NetworkMonitor_Callback callback, + void* user_data); + virtual ~PPB_NetworkMonitor_Private_Impl(); + + bool Start(); + + PPB_NetworkMonitor_Callback callback_; + void* user_data_; + bool started_; + + DISALLOW_COPY_AND_ASSIGN(PPB_NetworkMonitor_Private_Impl); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_PPB_NETWORK_MONITOR_PRIVATE_IMPL_H_ diff --git a/content/renderer/pepper/ppb_proxy_impl.cc b/content/renderer/pepper/ppb_proxy_impl.cc new file mode 100644 index 0000000..caf63f7 --- /dev/null +++ b/content/renderer/pepper/ppb_proxy_impl.cc @@ -0,0 +1,79 @@ +// 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 "content/renderer/pepper/ppb_proxy_impl.h" + +#include "ppapi/c/private/ppb_proxy_private.h" +#include "ppapi/thunk/enter.h" +#include "ppapi/thunk/ppb_image_data_api.h" +#include "content/renderer/pepper/host_globals.h" +#include "content/renderer/pepper/plugin_module.h" + +using ppapi::PpapiGlobals; +using ppapi::thunk::EnterResource; +using ppapi::thunk::PPB_URLLoader_API; + +namespace webkit { +namespace ppapi { + +namespace { + +void PluginCrashed(PP_Module module) { + PluginModule* plugin_module = HostGlobals::Get()->GetModule(module); + if (plugin_module) + plugin_module->PluginCrashed(); +} + +PP_Instance GetInstanceForResource(PP_Resource resource) { + ::ppapi::Resource* obj = + PpapiGlobals::Get()->GetResourceTracker()->GetResource(resource); + if (!obj) + return 0; + return obj->pp_instance(); +} + +void SetReserveInstanceIDCallback(PP_Module module, + PP_Bool (*reserve)(PP_Module, PP_Instance)) { + PluginModule* plugin_module = HostGlobals::Get()->GetModule(module); + if (plugin_module) + plugin_module->SetReserveInstanceIDCallback(reserve); +} + +void AddRefModule(PP_Module module) { + PluginModule* plugin_module = HostGlobals::Get()->GetModule(module); + if (plugin_module) + plugin_module->AddRef(); +} + +void ReleaseModule(PP_Module module) { + PluginModule* plugin_module = HostGlobals::Get()->GetModule(module); + if (plugin_module) + plugin_module->Release(); +} + +PP_Bool IsInModuleDestructor(PP_Module module) { + PluginModule* plugin_module = HostGlobals::Get()->GetModule(module); + if (plugin_module) + return PP_FromBool(plugin_module->is_in_destructor()); + return PP_FALSE; +} + +const PPB_Proxy_Private ppb_proxy = { + &PluginCrashed, + &GetInstanceForResource, + &SetReserveInstanceIDCallback, + &AddRefModule, + &ReleaseModule, + &IsInModuleDestructor +}; + +} // namespace + +// static +const PPB_Proxy_Private* PPB_Proxy_Impl::GetInterface() { + return &ppb_proxy; +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/ppb_proxy_impl.h b/content/renderer/pepper/ppb_proxy_impl.h new file mode 100644 index 0000000..04e12fc --- /dev/null +++ b/content/renderer/pepper/ppb_proxy_impl.h @@ -0,0 +1,22 @@ +// 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. + +#ifndef CONTENT_RENDERER_PEPPER_PPB_PROXY_IMPL_H_ +#define CONTENT_RENDERER_PEPPER_PPB_PROXY_IMPL_H_ + +struct PPB_Proxy_Private; + +namespace webkit { +namespace ppapi { + +class PPB_Proxy_Impl { + public: + static const PPB_Proxy_Private* GetInterface(); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_PPB_PROXY_IMPL_H_ + diff --git a/content/renderer/pepper/ppb_scrollbar_impl.cc b/content/renderer/pepper/ppb_scrollbar_impl.cc new file mode 100644 index 0000000..a326cad --- /dev/null +++ b/content/renderer/pepper/ppb_scrollbar_impl.cc @@ -0,0 +1,251 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/ppb_scrollbar_impl.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "base/message_loop/message_loop.h" +#include "content/renderer/pepper/common.h" +#include "content/renderer/pepper/event_conversion.h" +#include "content/renderer/pepper/plugin_module.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" +#include "content/renderer/pepper/ppb_image_data_impl.h" +#include "content/renderer/pepper/resource_helper.h" +#include "ppapi/c/dev/ppp_scrollbar_dev.h" +#include "ppapi/thunk/thunk.h" +#include "skia/ext/platform_canvas.h" +#include "third_party/WebKit/public/platform/WebCanvas.h" +#include "third_party/WebKit/public/platform/WebRect.h" +#include "third_party/WebKit/public/platform/WebVector.h" +#include "third_party/WebKit/public/web/WebInputEvent.h" +#include "third_party/WebKit/public/web/WebPluginScrollbar.h" +#include "webkit/glue/webkit_glue.h" + +#if defined(OS_WIN) +#include "base/win/windows_version.h" +#endif + +using ppapi::thunk::PPB_Scrollbar_API; +using WebKit::WebInputEvent; +using WebKit::WebRect; +using WebKit::WebScrollbar; +using WebKit::WebPluginScrollbar; + +namespace webkit { +namespace ppapi { + +// static +PP_Resource PPB_Scrollbar_Impl::Create(PP_Instance instance, + bool vertical) { + scoped_refptr<PPB_Scrollbar_Impl> scrollbar( + new PPB_Scrollbar_Impl(instance)); + scrollbar->Init(vertical); + return scrollbar->GetReference(); +} + +PPB_Scrollbar_Impl::PPB_Scrollbar_Impl(PP_Instance instance) + : PPB_Widget_Impl(instance), + weak_ptr_factory_(this) { +} + +PPB_Scrollbar_Impl::~PPB_Scrollbar_Impl() { +} + +void PPB_Scrollbar_Impl::Init(bool vertical) { + PluginInstanceImpl* plugin_instance = ResourceHelper::GetPluginInstance(this); + if (!plugin_instance) + return; + scrollbar_.reset(WebPluginScrollbar::createForPlugin( + vertical ? WebScrollbar::Vertical : WebScrollbar::Horizontal, + ResourceHelper::GetPluginInstance(this)->container(), + static_cast<WebKit::WebPluginScrollbarClient*>(this))); +} + +PPB_Scrollbar_API* PPB_Scrollbar_Impl::AsPPB_Scrollbar_API() { + return this; +} + +void PPB_Scrollbar_Impl::InstanceWasDeleted() { + scrollbar_.reset(); +} + +uint32_t PPB_Scrollbar_Impl::GetThickness() { + return WebPluginScrollbar::defaultThickness(); +} + +bool PPB_Scrollbar_Impl::IsOverlay() { + return scrollbar_->isOverlay(); +} + +uint32_t PPB_Scrollbar_Impl::GetValue() { + return scrollbar_->value(); +} + +void PPB_Scrollbar_Impl::SetValue(uint32_t value) { + if (scrollbar_) + scrollbar_->setValue(value); +} + +void PPB_Scrollbar_Impl::SetDocumentSize(uint32_t size) { + if (scrollbar_) + scrollbar_->setDocumentSize(size); +} + +void PPB_Scrollbar_Impl::SetTickMarks(const PP_Rect* tick_marks, + uint32_t count) { + if (!scrollbar_) + return; + tickmarks_.resize(count); + for (uint32 i = 0; i < count; ++i) { + tickmarks_[i] = WebRect(tick_marks[i].point.x, + tick_marks[i].point.y, + tick_marks[i].size.width, + tick_marks[i].size.height);; + } + PP_Rect rect = location(); + Invalidate(&rect); +} + +void PPB_Scrollbar_Impl::ScrollBy(PP_ScrollBy_Dev unit, int32_t multiplier) { + if (!scrollbar_) + return; + + WebScrollbar::ScrollDirection direction = multiplier >= 0 ? + WebScrollbar::ScrollForward : WebScrollbar::ScrollBackward; + float fmultiplier = 1.0; + + WebScrollbar::ScrollGranularity granularity; + if (unit == PP_SCROLLBY_LINE) { + granularity = WebScrollbar::ScrollByLine; + } else if (unit == PP_SCROLLBY_PAGE) { + granularity = WebScrollbar::ScrollByPage; + } else if (unit == PP_SCROLLBY_DOCUMENT) { + granularity = WebScrollbar::ScrollByDocument; + } else { + granularity = WebScrollbar::ScrollByPixel; + fmultiplier = static_cast<float>(multiplier); + if (fmultiplier < 0) + fmultiplier *= -1; + } + scrollbar_->scroll(direction, granularity, fmultiplier); +} + +PP_Bool PPB_Scrollbar_Impl::PaintInternal(const gfx::Rect& rect, + PPB_ImageData_Impl* image) { + ImageDataAutoMapper mapper(image); + skia::PlatformCanvas* canvas = image->GetPlatformCanvas(); + if (!canvas || !scrollbar_) + return PP_FALSE; + canvas->save(); + canvas->scale(scale(), scale()); + scrollbar_->paint(canvas, rect); + canvas->restore(); + +#if defined(OS_WIN) + if (base::win::GetVersion() == base::win::VERSION_XP) + skia::MakeOpaque(canvas, rect.x(), rect.y(), rect.width(), rect.height()); +#endif + + return PP_TRUE; +} + +PP_Bool PPB_Scrollbar_Impl::HandleEventInternal( + const ::ppapi::InputEventData& data) { + scoped_ptr<WebInputEvent> web_input_event(CreateWebInputEvent(data)); + if (!web_input_event.get() || !scrollbar_) + return PP_FALSE; + + return PP_FromBool(scrollbar_->handleInputEvent(*web_input_event.get())); +} + +void PPB_Scrollbar_Impl::SetLocationInternal(const PP_Rect* location) { + if (!scrollbar_) + return; + scrollbar_->setLocation(WebRect(location->point.x, + location->point.y, + location->size.width, + location->size.height)); +} + +void PPB_Scrollbar_Impl::valueChanged(WebKit::WebPluginScrollbar* scrollbar) { + PluginModule* plugin_module = ResourceHelper::GetPluginModule(this); + if (!plugin_module) + return; + + const PPP_Scrollbar_Dev* ppp_scrollbar = + static_cast<const PPP_Scrollbar_Dev*>(plugin_module->GetPluginInterface( + PPP_SCROLLBAR_DEV_INTERFACE)); + if (!ppp_scrollbar) { + // Try the old version. This is ok because the old interface is a subset of + // the new one, and ValueChanged didn't change. + ppp_scrollbar = + static_cast<const PPP_Scrollbar_Dev*>(plugin_module->GetPluginInterface( + PPP_SCROLLBAR_DEV_INTERFACE_0_2)); + if (!ppp_scrollbar) + return; + } + ppp_scrollbar->ValueChanged(pp_instance(), pp_resource(), + scrollbar_->value()); +} + +void PPB_Scrollbar_Impl::overlayChanged(WebPluginScrollbar* scrollbar) { + PluginModule* plugin_module = ResourceHelper::GetPluginModule(this); + if (!plugin_module) + return; + + const PPP_Scrollbar_Dev* ppp_scrollbar = + static_cast<const PPP_Scrollbar_Dev*>(plugin_module->GetPluginInterface( + PPP_SCROLLBAR_DEV_INTERFACE)); + if (!ppp_scrollbar) + return; + ppp_scrollbar->OverlayChanged(pp_instance(), pp_resource(), + PP_FromBool(IsOverlay())); +} + +void PPB_Scrollbar_Impl::invalidateScrollbarRect( + WebKit::WebPluginScrollbar* scrollbar, + const WebKit::WebRect& rect) { + gfx::Rect gfx_rect(rect.x, + rect.y, + rect.width, + rect.height); + dirty_.Union(gfx_rect); + // Can't call into the client to tell them about the invalidate right away, + // since the PPB_Scrollbar_Impl code is still in the middle of updating its + // internal state. + // Note: we use a WeakPtrFactory here so that a lingering callback can not + // modify the lifetime of this object. Otherwise, WebKit::WebPluginScrollbar + // could outlive WebKit::WebPluginContainer, which is against its contract. + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&PPB_Scrollbar_Impl::NotifyInvalidate, + weak_ptr_factory_.GetWeakPtr())); +} + +void PPB_Scrollbar_Impl::getTickmarks( + WebKit::WebPluginScrollbar* scrollbar, + WebKit::WebVector<WebKit::WebRect>* tick_marks) const { + if (tickmarks_.empty()) { + WebRect* rects = NULL; + tick_marks->assign(rects, 0); + } else { + tick_marks->assign(&tickmarks_[0], tickmarks_.size()); + } +} + +void PPB_Scrollbar_Impl::NotifyInvalidate() { + if (dirty_.IsEmpty()) + return; + PP_Rect pp_rect; + pp_rect.point.x = dirty_.x(); + pp_rect.point.y = dirty_.y(); + pp_rect.size.width = dirty_.width(); + pp_rect.size.height = dirty_.height(); + dirty_ = gfx::Rect(); + Invalidate(&pp_rect); +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/ppb_scrollbar_impl.h b/content/renderer/pepper/ppb_scrollbar_impl.h new file mode 100644 index 0000000..eed236f --- /dev/null +++ b/content/renderer/pepper/ppb_scrollbar_impl.h @@ -0,0 +1,78 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_PEPPER_PPB_SCROLLBAR_IMPL_H_ +#define CONTENT_RENDERER_PEPPER_PPB_SCROLLBAR_IMPL_H_ + +#include <vector> + +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "content/renderer/pepper/ppb_widget_impl.h" +#include "ppapi/thunk/ppb_scrollbar_api.h" +#include "third_party/WebKit/public/platform/WebRect.h" +#include "third_party/WebKit/public/web/WebPluginScrollbarClient.h" +#include "ui/gfx/rect.h" + +namespace webkit { +namespace ppapi { + +class PPB_Scrollbar_Impl : public PPB_Widget_Impl, + public ::ppapi::thunk::PPB_Scrollbar_API, + public WebKit::WebPluginScrollbarClient { + public: + static PP_Resource Create(PP_Instance instance, bool vertical); + + // Resource overrides. + virtual PPB_Scrollbar_API* AsPPB_Scrollbar_API() OVERRIDE; + virtual void InstanceWasDeleted() OVERRIDE; + + // PPB_Scrollbar_API implementation. + virtual uint32_t GetThickness() OVERRIDE; + virtual bool IsOverlay() OVERRIDE; + virtual uint32_t GetValue() OVERRIDE; + virtual void SetValue(uint32_t value) OVERRIDE; + virtual void SetDocumentSize(uint32_t size) OVERRIDE; + virtual void SetTickMarks(const PP_Rect* tick_marks, uint32_t count) OVERRIDE; + virtual void ScrollBy(PP_ScrollBy_Dev unit, int32_t multiplier) OVERRIDE; + + private: + virtual ~PPB_Scrollbar_Impl(); + + explicit PPB_Scrollbar_Impl(PP_Instance instance); + void Init(bool vertical); + + // PPB_Widget private implementation. + virtual PP_Bool PaintInternal(const gfx::Rect& rect, + PPB_ImageData_Impl* image) OVERRIDE; + virtual PP_Bool HandleEventInternal( + const ::ppapi::InputEventData& data) OVERRIDE; + virtual void SetLocationInternal(const PP_Rect* location) OVERRIDE; + + // WebKit::WebPluginScrollbarClient implementation. + virtual void valueChanged(WebKit::WebPluginScrollbar* scrollbar) OVERRIDE; + virtual void overlayChanged(WebKit::WebPluginScrollbar* scrollbar) OVERRIDE; + virtual void invalidateScrollbarRect(WebKit::WebPluginScrollbar* scrollbar, + const WebKit::WebRect& rect) OVERRIDE; + virtual void getTickmarks( + WebKit::WebPluginScrollbar* scrollbar, + WebKit::WebVector<WebKit::WebRect>* tick_marks) const OVERRIDE; + + void NotifyInvalidate(); + + gfx::Rect dirty_; + std::vector<WebKit::WebRect> tickmarks_; + scoped_ptr<WebKit::WebPluginScrollbar> scrollbar_; + + // Used so that the post task for Invalidate doesn't keep an extra reference. + base::WeakPtrFactory<PPB_Scrollbar_Impl> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(PPB_Scrollbar_Impl); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_PPB_SCROLLBAR_IMPL_H_ diff --git a/content/renderer/pepper/ppb_tcp_server_socket_private_impl.cc b/content/renderer/pepper/ppb_tcp_server_socket_private_impl.cc new file mode 100644 index 0000000..2e8ceda --- /dev/null +++ b/content/renderer/pepper/ppb_tcp_server_socket_private_impl.cc @@ -0,0 +1,82 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/ppb_tcp_server_socket_private_impl.h" + +#include "base/logging.h" +#include "content/renderer/pepper/host_globals.h" +#include "content/renderer/pepper/plugin_delegate.h" +#include "content/renderer/pepper/ppb_tcp_socket_private_impl.h" +#include "content/renderer/pepper/resource_helper.h" + +namespace webkit { +namespace ppapi { + +PPB_TCPServerSocket_Private_Impl::PPB_TCPServerSocket_Private_Impl( + PP_Instance instance) + : ::ppapi::PPB_TCPServerSocket_Shared(instance) { +} + +PPB_TCPServerSocket_Private_Impl::~PPB_TCPServerSocket_Private_Impl() { + StopListening(); +} + +PP_Resource PPB_TCPServerSocket_Private_Impl::CreateResource( + PP_Instance instance) { + PPB_TCPServerSocket_Private_Impl* socket = + new PPB_TCPServerSocket_Private_Impl(instance); + return socket->GetReference(); +} + +void PPB_TCPServerSocket_Private_Impl::OnAcceptCompleted( + bool succeeded, + uint32 accepted_socket_id, + const PP_NetAddress_Private& local_addr, + const PP_NetAddress_Private& remote_addr) { + if (!::ppapi::TrackedCallback::IsPending(accept_callback_) || + !tcp_socket_buffer_) { + NOTREACHED(); + return; + } + + if (succeeded) { + *tcp_socket_buffer_ = + PPB_TCPSocket_Private_Impl::CreateConnectedSocket(pp_instance(), + accepted_socket_id, + local_addr, + remote_addr); + } + tcp_socket_buffer_ = NULL; + + accept_callback_->Run(succeeded ? PP_OK : PP_ERROR_FAILED); +} + +void PPB_TCPServerSocket_Private_Impl::SendListen( + const PP_NetAddress_Private& addr, + int32_t backlog) { + PluginDelegate* plugin_delegate = ResourceHelper::GetPluginDelegate(this); + if (!plugin_delegate) + return; + + plugin_delegate->TCPServerSocketListen(pp_resource(), addr, backlog); +} + +void PPB_TCPServerSocket_Private_Impl::SendAccept() { + PluginDelegate* plugin_delegate = ResourceHelper::GetPluginDelegate(this); + if (!plugin_delegate) + return; + + plugin_delegate->TCPServerSocketAccept(socket_id_); +} + +void PPB_TCPServerSocket_Private_Impl::SendStopListening() { + PluginDelegate* plugin_delegate = ResourceHelper::GetPluginDelegate(this); + if (!plugin_delegate) + return; + + plugin_delegate->TCPServerSocketStopListening(pp_resource(), socket_id_); +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/ppb_tcp_server_socket_private_impl.h b/content/renderer/pepper/ppb_tcp_server_socket_private_impl.h new file mode 100644 index 0000000..f048c7b --- /dev/null +++ b/content/renderer/pepper/ppb_tcp_server_socket_private_impl.h @@ -0,0 +1,40 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_PEPPER_PPB_TCP_SERVER_SOCKET_PRIVATE_IMPL_H_ +#define CONTENT_RENDERER_PEPPER_PPB_TCP_SERVER_SOCKET_PRIVATE_IMPL_H_ + +#include "base/compiler_specific.h" +#include "ppapi/shared_impl/private/ppb_tcp_server_socket_shared.h" + +namespace webkit { +namespace ppapi { + +class PPB_TCPServerSocket_Private_Impl + : public ::ppapi::PPB_TCPServerSocket_Shared { + public: + static PP_Resource CreateResource(PP_Instance instance); + + virtual void OnAcceptCompleted( + bool succeeded, + uint32 accepted_socket_id, + const PP_NetAddress_Private& local_addr, + const PP_NetAddress_Private& remote_addr) OVERRIDE; + + virtual void SendListen(const PP_NetAddress_Private& addr, + int32_t backlog) OVERRIDE; + virtual void SendAccept() OVERRIDE; + virtual void SendStopListening() OVERRIDE; + + private: + PPB_TCPServerSocket_Private_Impl(PP_Instance instance); + virtual ~PPB_TCPServerSocket_Private_Impl(); + + DISALLOW_COPY_AND_ASSIGN(PPB_TCPServerSocket_Private_Impl); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_PPB_TCP_SERVER_SOCKET_PRIVATE_IMPL_H_ diff --git a/content/renderer/pepper/ppb_tcp_socket_private_impl.cc b/content/renderer/pepper/ppb_tcp_socket_private_impl.cc new file mode 100644 index 0000000..7ebcc64 --- /dev/null +++ b/content/renderer/pepper/ppb_tcp_socket_private_impl.cc @@ -0,0 +1,134 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/ppb_tcp_socket_private_impl.h" + +#include "content/renderer/pepper/host_globals.h" +#include "content/renderer/pepper/plugin_delegate.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" +#include "content/renderer/pepper/resource_helper.h" +#include "ppapi/shared_impl/socket_option_data.h" + +namespace webkit { +namespace ppapi { + +PPB_TCPSocket_Private_Impl::PPB_TCPSocket_Private_Impl( + PP_Instance instance, uint32 socket_id) + : ::ppapi::TCPSocketPrivateImpl(instance, socket_id) { +} + +PPB_TCPSocket_Private_Impl::~PPB_TCPSocket_Private_Impl() { + Disconnect(); +} + +PP_Resource PPB_TCPSocket_Private_Impl::CreateResource(PP_Instance instance) { + PluginDelegate* plugin_delegate = GetPluginDelegate(instance); + if (!plugin_delegate) + return 0; + + uint32 socket_id = plugin_delegate->TCPSocketCreate(); + if (!socket_id) + return 0; + + return (new PPB_TCPSocket_Private_Impl(instance, socket_id))->GetReference(); +} + +PP_Resource PPB_TCPSocket_Private_Impl::CreateConnectedSocket( + PP_Instance instance, + uint32 socket_id, + const PP_NetAddress_Private& local_addr, + const PP_NetAddress_Private& remote_addr) { + PluginDelegate* plugin_delegate = GetPluginDelegate(instance); + if (!plugin_delegate) + return 0; + + PPB_TCPSocket_Private_Impl* socket = + new PPB_TCPSocket_Private_Impl(instance, socket_id); + + socket->connection_state_ = PPB_TCPSocket_Private_Impl::CONNECTED; + socket->local_addr_ = local_addr; + socket->remote_addr_ = remote_addr; + + plugin_delegate->RegisterTCPSocket(socket, socket_id); + + return socket->GetReference(); +} + +void PPB_TCPSocket_Private_Impl::SendConnect(const std::string& host, + uint16_t port) { + PluginDelegate* plugin_delegate = ResourceHelper::GetPluginDelegate(this); + if (!plugin_delegate) + return; + + plugin_delegate->TCPSocketConnect(this, socket_id_, host, port); +} + +void PPB_TCPSocket_Private_Impl::SendConnectWithNetAddress( + const PP_NetAddress_Private& addr) { + PluginDelegate* plugin_delegate = ResourceHelper::GetPluginDelegate(this); + if (!plugin_delegate) + return; + + plugin_delegate->TCPSocketConnectWithNetAddress(this, socket_id_, addr); +} + +void PPB_TCPSocket_Private_Impl::SendSSLHandshake( + const std::string& server_name, + uint16_t server_port, + const std::vector<std::vector<char> >& trusted_certs, + const std::vector<std::vector<char> >& untrusted_certs) { + PluginDelegate* plugin_delegate = ResourceHelper::GetPluginDelegate(this); + if (!plugin_delegate) + return; + + plugin_delegate->TCPSocketSSLHandshake(socket_id_, server_name, server_port, + trusted_certs, untrusted_certs); +} + +void PPB_TCPSocket_Private_Impl::SendRead(int32_t bytes_to_read) { + PluginDelegate* plugin_delegate = ResourceHelper::GetPluginDelegate(this); + if (!plugin_delegate) + return; + + plugin_delegate->TCPSocketRead(socket_id_, bytes_to_read); +} + + +void PPB_TCPSocket_Private_Impl::SendWrite(const std::string& buffer) { + PluginDelegate* plugin_delegate = ResourceHelper::GetPluginDelegate(this); + if (!plugin_delegate) + return; + + plugin_delegate->TCPSocketWrite(socket_id_, buffer); +} + +void PPB_TCPSocket_Private_Impl::SendDisconnect() { + PluginDelegate* plugin_delegate = ResourceHelper::GetPluginDelegate(this); + if (!plugin_delegate) + return; + + plugin_delegate->TCPSocketDisconnect(socket_id_); +} + +void PPB_TCPSocket_Private_Impl::SendSetOption( + PP_TCPSocket_Option name, + const ::ppapi::SocketOptionData& value) { + PluginDelegate* plugin_delegate = ResourceHelper::GetPluginDelegate(this); + if (!plugin_delegate) + return; + + plugin_delegate->TCPSocketSetOption(socket_id_, name, value); +} + +PluginDelegate* PPB_TCPSocket_Private_Impl::GetPluginDelegate( + PP_Instance instance) { + PluginInstanceImpl* plugin_instance = + HostGlobals::Get()->GetInstance(instance); + if (!plugin_instance) + return NULL; + return plugin_instance->delegate(); +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/ppb_tcp_socket_private_impl.h b/content/renderer/pepper/ppb_tcp_socket_private_impl.h new file mode 100644 index 0000000..7295094 --- /dev/null +++ b/content/renderer/pepper/ppb_tcp_socket_private_impl.h @@ -0,0 +1,53 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_PEPPER_PPB_TCP_SOCKET_PRIVATE_IMPL_H_ +#define CONTENT_RENDERER_PEPPER_PPB_TCP_SOCKET_PRIVATE_IMPL_H_ + +#include <vector> + +#include "base/compiler_specific.h" +#include "ppapi/shared_impl/private/tcp_socket_private_impl.h" + +namespace webkit { +namespace ppapi { + +class PluginDelegate; + +class PPB_TCPSocket_Private_Impl : public ::ppapi::TCPSocketPrivateImpl { + public: + static PP_Resource CreateResource(PP_Instance instance); + static PP_Resource CreateConnectedSocket( + PP_Instance instance, + uint32 socket_id, + const PP_NetAddress_Private& local_addr, + const PP_NetAddress_Private& remote_addr); + + virtual void SendConnect(const std::string& host, uint16_t port) OVERRIDE; + virtual void SendConnectWithNetAddress( + const PP_NetAddress_Private& addr) OVERRIDE; + virtual void SendSSLHandshake( + const std::string& server_name, + uint16_t server_port, + const std::vector<std::vector<char> >& trusted_certs, + const std::vector<std::vector<char> >& untrusted_certs) OVERRIDE; + virtual void SendRead(int32_t bytes_to_read) OVERRIDE; + virtual void SendWrite(const std::string& buffer) OVERRIDE; + virtual void SendDisconnect() OVERRIDE; + virtual void SendSetOption(PP_TCPSocket_Option name, + const ::ppapi::SocketOptionData& value) OVERRIDE; + + private: + PPB_TCPSocket_Private_Impl(PP_Instance instance, uint32 socket_id); + virtual ~PPB_TCPSocket_Private_Impl(); + + static PluginDelegate* GetPluginDelegate(PP_Instance instance); + + DISALLOW_COPY_AND_ASSIGN(PPB_TCPSocket_Private_Impl); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_PPB_TCP_SOCKET_PRIVATE_IMPL_H_ diff --git a/content/renderer/pepper/ppb_uma_private_impl.cc b/content/renderer/pepper/ppb_uma_private_impl.cc new file mode 100644 index 0000000..8468dd7 --- /dev/null +++ b/content/renderer/pepper/ppb_uma_private_impl.cc @@ -0,0 +1,100 @@ +// 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 "content/renderer/pepper/ppb_uma_private_impl.h" + +#include "base/metrics/histogram.h" +#include "ppapi/c/pp_var.h" +#include "ppapi/c/private/ppb_uma_private.h" +#include "ppapi/shared_impl/var.h" +#include "webkit/glue/webkit_glue.h" + +using ppapi::StringVar; + +namespace webkit { +namespace ppapi { + +namespace { + +#define RETURN_IF_BAD_ARGS(_name, _sample, _min, _max, _bucket_count) \ + do { \ + if (_name.type != PP_VARTYPE_STRING || _name.value.as_id == 0) \ + return; \ + if (_min >= _max) \ + return; \ + if (_bucket_count <= 1) \ + return; \ + } while (0) + +void HistogramCustomTimes(PP_Var name, + int64_t sample, + int64_t min, int64_t max, + uint32_t bucket_count) { + RETURN_IF_BAD_ARGS(name, sample, min, max, bucket_count); + + StringVar* name_string = StringVar::FromPPVar(name); + if (name_string == NULL) + return; + base::HistogramBase* counter = + base::Histogram::FactoryTimeGet( + name_string->value(), + base::TimeDelta::FromMilliseconds(min), + base::TimeDelta::FromMilliseconds(max), + bucket_count, + base::HistogramBase::kUmaTargetedHistogramFlag); + counter->AddTime(base::TimeDelta::FromMilliseconds(sample)); +} + +void HistogramCustomCounts(PP_Var name, + int32_t sample, + int32_t min, int32_t max, + uint32_t bucket_count) { + RETURN_IF_BAD_ARGS(name, sample, min, max, bucket_count); + + StringVar* name_string = StringVar::FromPPVar(name); + if (name_string == NULL) + return; + base::HistogramBase* counter = + base::Histogram::FactoryGet( + name_string->value(), + min, + max, + bucket_count, + base::HistogramBase::kUmaTargetedHistogramFlag); + counter->Add(sample); +} + +void HistogramEnumeration(PP_Var name, + int32_t sample, + int32_t boundary_value) { + RETURN_IF_BAD_ARGS(name, sample, 1, boundary_value, boundary_value + 1); + + StringVar* name_string = StringVar::FromPPVar(name); + if (name_string == NULL) + return; + base::HistogramBase* counter = + base::LinearHistogram::FactoryGet( + name_string->value(), + 1, + boundary_value, + boundary_value + 1, + base::HistogramBase::kUmaTargetedHistogramFlag); + counter->Add(sample); +} + +} // namespace + +const PPB_UMA_Private ppb_uma = { + &HistogramCustomTimes, + &HistogramCustomCounts, + &HistogramEnumeration, +}; + +// static +const PPB_UMA_Private* PPB_UMA_Private_Impl::GetInterface() { + return &ppb_uma; +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/ppb_uma_private_impl.h b/content/renderer/pepper/ppb_uma_private_impl.h new file mode 100644 index 0000000..55a94f4 --- /dev/null +++ b/content/renderer/pepper/ppb_uma_private_impl.h @@ -0,0 +1,21 @@ +// 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. + +#ifndef CONTENT_RENDERER_PEPPER_PPB_UMA_PRIVATE_IMPL_H_ +#define CONTENT_RENDERER_PEPPER_PPB_UMA_PRIVATE_IMPL_H_ + +#include "ppapi/c/private/ppb_uma_private.h" + +namespace webkit { +namespace ppapi { + +class PPB_UMA_Private_Impl { + public: + static const PPB_UMA_Private* GetInterface(); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_PPB_UMA_PRIVATE_IMPL_H_ diff --git a/content/renderer/pepper/ppb_var_deprecated_impl.cc b/content/renderer/pepper/ppb_var_deprecated_impl.cc new file mode 100644 index 0000000..cbfc6f9 --- /dev/null +++ b/content/renderer/pepper/ppb_var_deprecated_impl.cc @@ -0,0 +1,437 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/ppb_var_deprecated_impl.h" + +#include <limits> + +#include "content/renderer/pepper/common.h" +#include "content/renderer/pepper/host_globals.h" +#include "content/renderer/pepper/npapi_glue.h" +#include "content/renderer/pepper/npobject_var.h" +#include "content/renderer/pepper/plugin_module.h" +#include "content/renderer/pepper/plugin_object.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" +#include "ppapi/c/dev/ppb_var_deprecated.h" +#include "ppapi/c/ppb_var.h" +#include "ppapi/c/pp_var.h" +#include "ppapi/shared_impl/ppb_var_shared.h" +#include "third_party/WebKit/public/web/WebBindings.h" +#include "third_party/WebKit/public/web/WebScopedUserGesture.h" + +using ppapi::NPObjectVar; +using ppapi::PpapiGlobals; +using ppapi::StringVar; +using ppapi::Var; +using WebKit::WebBindings; + +namespace webkit { +namespace ppapi { + +namespace { + +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[] = + "Error: Unable to remove property"; +const char kUnableToGetAllPropertiesException[] = + "Error: Unable to get all properties"; +const char kUnableToCallMethodException[] = "Error: Unable to call method"; +const char kUnableToConstructException[] = "Error: Unable to construct"; + +// --------------------------------------------------------------------------- +// 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 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_UNDEFINED: + 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: { + 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: { + scoped_refptr<NPObjectVar> object(NPObjectVar::FromPPVar(var)); + if (!object.get()) { + VOID_TO_NPVARIANT(*result); + return false; + } + OBJECT_TO_NPVARIANT(object->np_object(), *result); + break; + } + default: + VOID_TO_NPVARIANT(*result); + return false; + } + return true; +} + +// 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 pp_module() getter is also set up properly and ready to +// use. +class ObjectAccessorTryCatch : public TryCatch { + public: + ObjectAccessorTryCatch(PP_Var object, PP_Var* exception) + : TryCatch(exception), + object_(NPObjectVar::FromPPVar(object)) { + if (!object_.get()) { + SetException(kInvalidObjectException); + } + } + + NPObjectVar* object() { return object_.get(); } + + PluginInstanceImpl* GetPluginInstance() { + return HostGlobals::Get()->GetInstance(object()->pp_instance()); + } + + protected: + scoped_refptr<NPObjectVar> object_; + + DISALLOW_COPY_AND_ASSIGN(ObjectAccessorTryCatch); +}; + +// 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 pp_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_ = PPVarToNPIdentifier(identifier); + if (!identifier_) + SetException(kInvalidPropertyException); + } + } + + NPIdentifier identifier() const { return identifier_; } + + private: + NPIdentifier identifier_; + + DISALLOW_COPY_AND_ASSIGN(ObjectAccessorWithIdentifierTryCatch); +}; + +PP_Bool HasProperty(PP_Var var, + PP_Var name, + PP_Var* exception) { + ObjectAccessorWithIdentifierTryCatch accessor(var, name, exception); + if (accessor.has_exception()) + return PP_FALSE; + return BoolToPPBool(WebBindings::hasProperty(NULL, + accessor.object()->np_object(), + accessor.identifier())); +} + +bool HasPropertyDeprecated(PP_Var var, + PP_Var name, + PP_Var* exception) { + return PPBoolToBool(HasProperty(var, name, exception)); +} + +bool HasMethodDeprecated(PP_Var var, + PP_Var name, + PP_Var* exception) { + ObjectAccessorWithIdentifierTryCatch accessor(var, name, exception); + if (accessor.has_exception()) + return false; + return WebBindings::hasMethod(NULL, accessor.object()->np_object(), + accessor.identifier()); +} + +PP_Var GetProperty(PP_Var var, + PP_Var name, + PP_Var* exception) { + ObjectAccessorWithIdentifierTryCatch accessor(var, name, exception); + if (accessor.has_exception()) + return PP_MakeUndefined(); + + NPVariant result; + if (!WebBindings::getProperty(NULL, accessor.object()->np_object(), + accessor.identifier(), &result)) { + // An exception may have been raised. + accessor.SetException(kUnableToGetPropertyException); + return PP_MakeUndefined(); + } + + PP_Var ret = NPVariantToPPVar(accessor.GetPluginInstance(), &result); + WebBindings::releaseVariantValue(&result); + return ret; +} + +void EnumerateProperties(PP_Var var, + uint32_t* property_count, + PP_Var** properties, + PP_Var* exception) { + *properties = NULL; + *property_count = 0; + + ObjectAccessorTryCatch accessor(var, exception); + if (accessor.has_exception()) + return; + + NPIdentifier* identifiers = NULL; + uint32_t count = 0; + if (!WebBindings::enumerate(NULL, accessor.object()->np_object(), + &identifiers, &count)) { + accessor.SetException(kUnableToGetAllPropertiesException); + return; + } + + if (count == 0) + return; + + *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]); + } + free(identifiers); +} + +void SetPropertyDeprecated(PP_Var var, + PP_Var name, + PP_Var value, + PP_Var* exception) { + ObjectAccessorWithIdentifierTryCatch accessor(var, name, exception); + if (accessor.has_exception()) + return; + + NPVariant variant; + if (!PPVarToNPVariantNoCopy(value, &variant)) { + accessor.SetException(kInvalidValueException); + return; + } + if (!WebBindings::setProperty(NULL, accessor.object()->np_object(), + accessor.identifier(), &variant)) + accessor.SetException(kUnableToSetPropertyException); +} + +void DeletePropertyDeprecated(PP_Var var, + PP_Var name, + PP_Var* exception) { + ObjectAccessorWithIdentifierTryCatch accessor(var, name, exception); + if (accessor.has_exception()) + return; + + if (!WebBindings::removeProperty(NULL, accessor.object()->np_object(), + accessor.identifier())) + accessor.SetException(kUnableToRemovePropertyException); +} + +PP_Var InternalCallDeprecated(ObjectAccessorTryCatch* accessor, + PP_Var method_name, + uint32_t argc, + PP_Var* argv, + PP_Var* exception) { + NPIdentifier identifier; + if (method_name.type == PP_VARTYPE_UNDEFINED) { + identifier = NULL; + } else if (method_name.type == PP_VARTYPE_STRING) { + // Specifically allow only string functions to be called. + identifier = PPVarToNPIdentifier(method_name); + if (!identifier) { + accessor->SetException(kInvalidPropertyException); + return PP_MakeUndefined(); + } + } else { + accessor->SetException(kInvalidPropertyException); + return PP_MakeUndefined(); + } + + scoped_ptr<NPVariant[]> args; + if (argc) { + args.reset(new NPVariant[argc]); + 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_MakeUndefined(); + } + } + } + + bool ok; + + NPVariant result; + if (identifier) { + ok = WebBindings::invoke(NULL, accessor->object()->np_object(), + identifier, args.get(), argc, &result); + } else { + ok = WebBindings::invokeDefault(NULL, accessor->object()->np_object(), + args.get(), argc, &result); + } + + if (!ok) { + // An exception may have been raised. + accessor->SetException(kUnableToCallMethodException); + return PP_MakeUndefined(); + } + + PP_Var ret = NPVariantToPPVar(accessor->GetPluginInstance(), &result); + WebBindings::releaseVariantValue(&result); + return ret; +} + +PP_Var CallDeprecated(PP_Var var, + PP_Var method_name, + uint32_t argc, + PP_Var* argv, + PP_Var* exception) { + ObjectAccessorTryCatch accessor(var, exception); + if (accessor.has_exception()) + return PP_MakeUndefined(); + PluginInstanceImpl* plugin = accessor.GetPluginInstance(); + if (plugin && plugin->IsProcessingUserGesture()) { + WebKit::WebScopedUserGesture user_gesture( + plugin->CurrentUserGestureToken()); + return InternalCallDeprecated(&accessor, method_name, argc, argv, + exception); + } + return InternalCallDeprecated(&accessor, method_name, argc, argv, exception); +} + +PP_Var Construct(PP_Var var, + uint32_t argc, + PP_Var* argv, + PP_Var* exception) { + ObjectAccessorTryCatch accessor(var, exception); + if (accessor.has_exception()) + return PP_MakeUndefined(); + + scoped_ptr<NPVariant[]> args; + if (argc) { + args.reset(new NPVariant[argc]); + 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_MakeUndefined(); + } + } + } + + NPVariant result; + if (!WebBindings::construct(NULL, accessor.object()->np_object(), + args.get(), argc, &result)) { + // An exception may have been raised. + accessor.SetException(kUnableToConstructException); + return PP_MakeUndefined(); + } + + PP_Var ret = NPVariantToPPVar(accessor.GetPluginInstance(), &result); + WebBindings::releaseVariantValue(&result); + return ret; +} + +bool IsInstanceOfDeprecated(PP_Var var, + const PPP_Class_Deprecated* ppp_class, + void** ppp_class_data) { + scoped_refptr<NPObjectVar> object(NPObjectVar::FromPPVar(var)); + if (!object.get()) + return false; // Not an object at all. + + return PluginObject::IsInstanceOf(object->np_object(), + ppp_class, ppp_class_data); +} + +PP_Var CreateObjectDeprecated(PP_Instance pp_instance, + const PPP_Class_Deprecated* ppp_class, + void* ppp_class_data) { + PluginInstanceImpl* instance = HostGlobals::Get()->GetInstance(pp_instance); + if (!instance) { + DLOG(ERROR) << "Create object passed an invalid instance."; + return PP_MakeNull(); + } + return PluginObject::Create(instance, ppp_class, ppp_class_data); +} + +PP_Var CreateObjectWithModuleDeprecated(PP_Module pp_module, + const PPP_Class_Deprecated* ppp_class, + void* ppp_class_data) { + PluginModule* module = HostGlobals::Get()->GetModule(pp_module); + if (!module) + return PP_MakeNull(); + return PluginObject::Create(module->GetSomeInstance(), + ppp_class, ppp_class_data); +} + +} // namespace + +// static +const PPB_Var_Deprecated* PPB_Var_Deprecated_Impl::GetVarDeprecatedInterface() { + static const PPB_Var_Deprecated var_deprecated_interface = { + ::ppapi::PPB_Var_Shared::GetVarInterface1_0()->AddRef, + ::ppapi::PPB_Var_Shared::GetVarInterface1_0()->Release, + ::ppapi::PPB_Var_Shared::GetVarInterface1_0()->VarFromUtf8, + ::ppapi::PPB_Var_Shared::GetVarInterface1_0()->VarToUtf8, + &HasPropertyDeprecated, + &HasMethodDeprecated, + &GetProperty, + &EnumerateProperties, + &SetPropertyDeprecated, + &DeletePropertyDeprecated, + &CallDeprecated, + &Construct, + &IsInstanceOfDeprecated, + &CreateObjectDeprecated, + &CreateObjectWithModuleDeprecated, + }; + + return &var_deprecated_interface; +} + +} // namespace ppapi +} // namespace webkit + diff --git a/content/renderer/pepper/ppb_var_deprecated_impl.h b/content/renderer/pepper/ppb_var_deprecated_impl.h new file mode 100644 index 0000000..459acfa --- /dev/null +++ b/content/renderer/pepper/ppb_var_deprecated_impl.h @@ -0,0 +1,21 @@ +// 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. + +#ifndef CONTENT_RENDERER_PEPPER_PPB_VAR_DEPRECATED_IMPL_H_ +#define CONTENT_RENDERER_PEPPER_PPB_VAR_DEPRECATED_IMPL_H_ + +struct PPB_Var_Deprecated; + +namespace webkit { +namespace ppapi { + +class PPB_Var_Deprecated_Impl { + public: + static const PPB_Var_Deprecated* GetVarDeprecatedInterface(); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_PPB_VAR_DEPRECATED_IMPL_H_ diff --git a/content/renderer/pepper/ppb_video_decoder_impl.cc b/content/renderer/pepper/ppb_video_decoder_impl.cc new file mode 100644 index 0000000..c71f618 --- /dev/null +++ b/content/renderer/pepper/ppb_video_decoder_impl.cc @@ -0,0 +1,303 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/ppb_video_decoder_impl.h" + +#include <string> + +#include "base/logging.h" +#include "base/message_loop/message_loop.h" +#include "base/metrics/histogram.h" +#include "content/renderer/pepper/common.h" +#include "content/renderer/pepper/plugin_module.h" +#include "content/renderer/pepper/ppb_buffer_impl.h" +#include "content/renderer/pepper/ppb_graphics_3d_impl.h" +#include "content/renderer/pepper/resource_helper.h" +#include "gpu/command_buffer/client/gles2_implementation.h" +#include "media/video/picture.h" +#include "media/video/video_decode_accelerator.h" +#include "ppapi/c/dev/pp_video_dev.h" +#include "ppapi/c/dev/ppb_video_decoder_dev.h" +#include "ppapi/c/dev/ppp_video_decoder_dev.h" +#include "ppapi/c/pp_completion_callback.h" +#include "ppapi/c/pp_errors.h" +#include "ppapi/shared_impl/resource_tracker.h" +#include "ppapi/thunk/enter.h" + +using ppapi::TrackedCallback; +using ppapi::thunk::EnterResourceNoLock; +using ppapi::thunk::PPB_Buffer_API; +using ppapi::thunk::PPB_Graphics3D_API; +using ppapi::thunk::PPB_VideoDecoder_API; + +namespace { + +// Convert PP_VideoDecoder_Profile to media::VideoCodecProfile. +media::VideoCodecProfile PPToMediaProfile( + const PP_VideoDecoder_Profile pp_profile) { + switch (pp_profile) { + case PP_VIDEODECODER_H264PROFILE_NONE: + // HACK: PPAPI contains a bogus "none" h264 profile that doesn't + // correspond to anything in h.264; but a number of released chromium + // versions silently promoted this to Baseline profile, so we retain that + // behavior here. Fall through. + case PP_VIDEODECODER_H264PROFILE_BASELINE: + return media::H264PROFILE_BASELINE; + case PP_VIDEODECODER_H264PROFILE_MAIN: + return media::H264PROFILE_MAIN; + case PP_VIDEODECODER_H264PROFILE_EXTENDED: + return media::H264PROFILE_EXTENDED; + case PP_VIDEODECODER_H264PROFILE_HIGH: + return media::H264PROFILE_HIGH; + case PP_VIDEODECODER_H264PROFILE_HIGH10PROFILE: + return media::H264PROFILE_HIGH10PROFILE; + case PP_VIDEODECODER_H264PROFILE_HIGH422PROFILE: + return media::H264PROFILE_HIGH422PROFILE; + case PP_VIDEODECODER_H264PROFILE_HIGH444PREDICTIVEPROFILE: + return media::H264PROFILE_HIGH444PREDICTIVEPROFILE; + case PP_VIDEODECODER_H264PROFILE_SCALABLEBASELINE: + return media::H264PROFILE_SCALABLEBASELINE; + case PP_VIDEODECODER_H264PROFILE_SCALABLEHIGH: + return media::H264PROFILE_SCALABLEHIGH; + case PP_VIDEODECODER_H264PROFILE_STEREOHIGH: + return media::H264PROFILE_STEREOHIGH; + case PP_VIDEODECODER_H264PROFILE_MULTIVIEWHIGH: + return media::H264PROFILE_MULTIVIEWHIGH; + case PP_VIDEODECODER_VP8PROFILE_MAIN: + return media::VP8PROFILE_MAIN; + default: + return media::VIDEO_CODEC_PROFILE_UNKNOWN; + } +} + +PP_VideoDecodeError_Dev MediaToPPError( + media::VideoDecodeAccelerator::Error error) { + switch (error) { + case media::VideoDecodeAccelerator::ILLEGAL_STATE : + return PP_VIDEODECODERERROR_ILLEGAL_STATE; + case media::VideoDecodeAccelerator::INVALID_ARGUMENT : + return PP_VIDEODECODERERROR_INVALID_ARGUMENT; + case media::VideoDecodeAccelerator::UNREADABLE_INPUT : + return PP_VIDEODECODERERROR_UNREADABLE_INPUT; + case media::VideoDecodeAccelerator::PLATFORM_FAILURE : + return PP_VIDEODECODERERROR_PLATFORM_FAILURE; + default: + NOTREACHED(); + return PP_VIDEODECODERERROR_ILLEGAL_STATE; + } +} + +} // namespace + +namespace webkit { +namespace ppapi { + +PPB_VideoDecoder_Impl::PPB_VideoDecoder_Impl(PP_Instance instance) + : PPB_VideoDecoder_Shared(instance), + ppp_videodecoder_(NULL) { + PluginModule* plugin_module = ResourceHelper::GetPluginModule(this); + if (plugin_module) { + ppp_videodecoder_ = static_cast<const PPP_VideoDecoder_Dev*>( + plugin_module->GetPluginInterface(PPP_VIDEODECODER_DEV_INTERFACE)); + } +} + +PPB_VideoDecoder_Impl::~PPB_VideoDecoder_Impl() { + Destroy(); +} + +// static +PP_Resource PPB_VideoDecoder_Impl::Create( + PP_Instance instance, + PP_Resource graphics_context, + PP_VideoDecoder_Profile profile) { + EnterResourceNoLock<PPB_Graphics3D_API> enter_context(graphics_context, true); + if (enter_context.failed()) + return 0; + PPB_Graphics3D_Impl* graphics3d_impl = + static_cast<PPB_Graphics3D_Impl*>(enter_context.object()); + + scoped_refptr<PPB_VideoDecoder_Impl> decoder( + new PPB_VideoDecoder_Impl(instance)); + if (decoder->Init(graphics_context, graphics3d_impl->platform_context(), + graphics3d_impl->gles2_impl(), profile)) + return decoder->GetReference(); + return 0; +} + +bool PPB_VideoDecoder_Impl::Init( + PP_Resource graphics_context, + PluginDelegate::PlatformContext3D* context, + gpu::gles2::GLES2Implementation* gles2_impl, + PP_VideoDecoder_Profile profile) { + InitCommon(graphics_context, gles2_impl); + + int command_buffer_route_id = context->GetCommandBufferRouteId(); + if (command_buffer_route_id == 0) + return false; + + PluginDelegate* plugin_delegate = ResourceHelper::GetPluginDelegate(this); + if (!plugin_delegate) + return false; + + platform_video_decoder_.reset(plugin_delegate->CreateVideoDecoder( + this, command_buffer_route_id)); + if (!platform_video_decoder_) + return false; + + FlushCommandBuffer(); + return platform_video_decoder_->Initialize(PPToMediaProfile(profile)); +} + +int32_t PPB_VideoDecoder_Impl::Decode( + const PP_VideoBitstreamBuffer_Dev* bitstream_buffer, + scoped_refptr<TrackedCallback> callback) { + if (!platform_video_decoder_) + return PP_ERROR_BADRESOURCE; + + EnterResourceNoLock<PPB_Buffer_API> enter(bitstream_buffer->data, true); + if (enter.failed()) + return PP_ERROR_FAILED; + + PPB_Buffer_Impl* buffer = static_cast<PPB_Buffer_Impl*>(enter.object()); + DCHECK_GE(bitstream_buffer->id, 0); + media::BitstreamBuffer decode_buffer( + bitstream_buffer->id, + buffer->shared_memory()->handle(), + bitstream_buffer->size); + if (!SetBitstreamBufferCallback(bitstream_buffer->id, callback)) + return PP_ERROR_BADARGUMENT; + + FlushCommandBuffer(); + platform_video_decoder_->Decode(decode_buffer); + return PP_OK_COMPLETIONPENDING; +} + +void PPB_VideoDecoder_Impl::AssignPictureBuffers( + uint32_t no_of_buffers, + const PP_PictureBuffer_Dev* buffers) { + if (!platform_video_decoder_) + return; + UMA_HISTOGRAM_COUNTS_100("Media.PepperVideoDecoderPictureCount", + no_of_buffers); + + std::vector<media::PictureBuffer> wrapped_buffers; + for (uint32 i = 0; i < no_of_buffers; i++) { + PP_PictureBuffer_Dev in_buf = buffers[i]; + DCHECK_GE(in_buf.id, 0); + media::PictureBuffer buffer( + in_buf.id, + gfx::Size(in_buf.size.width, in_buf.size.height), + in_buf.texture_id); + wrapped_buffers.push_back(buffer); + UMA_HISTOGRAM_COUNTS_10000("Media.PepperVideoDecoderPictureHeight", + in_buf.size.height); + } + + FlushCommandBuffer(); + platform_video_decoder_->AssignPictureBuffers(wrapped_buffers); +} + +void PPB_VideoDecoder_Impl::ReusePictureBuffer(int32_t picture_buffer_id) { + if (!platform_video_decoder_) + return; + + FlushCommandBuffer(); + platform_video_decoder_->ReusePictureBuffer(picture_buffer_id); +} + +int32_t PPB_VideoDecoder_Impl::Flush(scoped_refptr<TrackedCallback> callback) { + if (!platform_video_decoder_) + return PP_ERROR_BADRESOURCE; + + if (!SetFlushCallback(callback)) + return PP_ERROR_INPROGRESS; + + FlushCommandBuffer(); + platform_video_decoder_->Flush(); + return PP_OK_COMPLETIONPENDING; +} + +int32_t PPB_VideoDecoder_Impl::Reset(scoped_refptr<TrackedCallback> callback) { + if (!platform_video_decoder_) + return PP_ERROR_BADRESOURCE; + + if (!SetResetCallback(callback)) + return PP_ERROR_INPROGRESS; + + FlushCommandBuffer(); + platform_video_decoder_->Reset(); + return PP_OK_COMPLETIONPENDING; +} + +void PPB_VideoDecoder_Impl::Destroy() { + FlushCommandBuffer(); + + if (platform_video_decoder_) + platform_video_decoder_.release()->Destroy(); + ppp_videodecoder_ = NULL; + + ::ppapi::PPB_VideoDecoder_Shared::Destroy(); +} + +void PPB_VideoDecoder_Impl::ProvidePictureBuffers( + uint32 requested_num_of_buffers, + const gfx::Size& dimensions, + uint32 texture_target) { + if (!ppp_videodecoder_) + return; + + PP_Size out_dim = PP_MakeSize(dimensions.width(), dimensions.height()); + ppp_videodecoder_->ProvidePictureBuffers(pp_instance(), pp_resource(), + requested_num_of_buffers, &out_dim, texture_target); +} + +void PPB_VideoDecoder_Impl::PictureReady(const media::Picture& picture) { + if (!ppp_videodecoder_) + return; + + PP_Picture_Dev output; + output.picture_buffer_id = picture.picture_buffer_id(); + output.bitstream_buffer_id = picture.bitstream_buffer_id(); + ppp_videodecoder_->PictureReady(pp_instance(), pp_resource(), &output); +} + +void PPB_VideoDecoder_Impl::DismissPictureBuffer(int32 picture_buffer_id) { + if (!ppp_videodecoder_) + return; + ppp_videodecoder_->DismissPictureBuffer(pp_instance(), pp_resource(), + picture_buffer_id); +} + +void PPB_VideoDecoder_Impl::NotifyError( + media::VideoDecodeAccelerator::Error error) { + if (!ppp_videodecoder_) + return; + + PP_VideoDecodeError_Dev pp_error = MediaToPPError(error); + ppp_videodecoder_->NotifyError(pp_instance(), pp_resource(), pp_error); + UMA_HISTOGRAM_ENUMERATION( + "Media.PepperVideoDecoderError", error, + media::VideoDecodeAccelerator::LARGEST_ERROR_ENUM); +} + +void PPB_VideoDecoder_Impl::NotifyResetDone() { + RunResetCallback(PP_OK); +} + +void PPB_VideoDecoder_Impl::NotifyEndOfBitstreamBuffer( + int32 bitstream_buffer_id) { + RunBitstreamBufferCallback(bitstream_buffer_id, PP_OK); +} + +void PPB_VideoDecoder_Impl::NotifyFlushDone() { + RunFlushCallback(PP_OK); +} + +void PPB_VideoDecoder_Impl::NotifyInitializeDone() { + NOTREACHED() << "PlatformVideoDecoder::Initialize() is synchronous!"; +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/ppb_video_decoder_impl.h b/content/renderer/pepper/ppb_video_decoder_impl.h new file mode 100644 index 0000000..d2d07b8 --- /dev/null +++ b/content/renderer/pepper/ppb_video_decoder_impl.h @@ -0,0 +1,88 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_PEPPER_PPB_VIDEO_DECODER_IMPL_H_ +#define CONTENT_RENDERER_PEPPER_PPB_VIDEO_DECODER_IMPL_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "content/renderer/pepper/plugin_delegate.h" +#include "ppapi/c/dev/pp_video_dev.h" +#include "ppapi/c/dev/ppp_video_decoder_dev.h" +#include "ppapi/c/pp_var.h" +#include "ppapi/shared_impl/ppb_video_decoder_shared.h" +#include "ppapi/shared_impl/resource.h" +#include "ppapi/thunk/ppb_video_decoder_api.h" + +struct PP_PictureBuffer_Dev; +struct PP_VideoBitstreamBuffer_Dev; + +namespace gpu { +namespace gles2 { +class GLES2Implementation; +} // namespace gles2 +} // namespace gpu + +namespace webkit { +namespace ppapi { + +class PPB_VideoDecoder_Impl : public ::ppapi::PPB_VideoDecoder_Shared, + public media::VideoDecodeAccelerator::Client { + public: + // See PPB_VideoDecoder_Dev::Create. Returns 0 on failure to create & + // initialize. + static PP_Resource Create(PP_Instance instance, + PP_Resource graphics_context, + PP_VideoDecoder_Profile profile); + + // PPB_VideoDecoder_API implementation. + virtual int32_t Decode( + const PP_VideoBitstreamBuffer_Dev* bitstream_buffer, + scoped_refptr< ::ppapi::TrackedCallback> callback) OVERRIDE; + virtual void AssignPictureBuffers( + uint32_t no_of_buffers, const PP_PictureBuffer_Dev* buffers) OVERRIDE; + virtual void ReusePictureBuffer(int32_t picture_buffer_id) OVERRIDE; + virtual int32_t Flush( + scoped_refptr< ::ppapi::TrackedCallback> callback) OVERRIDE; + virtual int32_t Reset( + scoped_refptr< ::ppapi::TrackedCallback> callback) OVERRIDE; + virtual void Destroy() OVERRIDE; + + // media::VideoDecodeAccelerator::Client implementation. + virtual void ProvidePictureBuffers(uint32 requested_num_of_buffers, + const gfx::Size& dimensions, + uint32 texture_target) OVERRIDE; + virtual void DismissPictureBuffer(int32 picture_buffer_id) OVERRIDE; + virtual void PictureReady(const media::Picture& picture) OVERRIDE; + virtual void NotifyInitializeDone() OVERRIDE; + virtual void NotifyError( + media::VideoDecodeAccelerator::Error error) OVERRIDE; + virtual void NotifyFlushDone() OVERRIDE; + virtual void NotifyEndOfBitstreamBuffer(int32 buffer_id) OVERRIDE; + virtual void NotifyResetDone() OVERRIDE; + + private: + virtual ~PPB_VideoDecoder_Impl(); + + explicit PPB_VideoDecoder_Impl(PP_Instance instance); + bool Init(PP_Resource graphics_context, + PluginDelegate::PlatformContext3D* context, + gpu::gles2::GLES2Implementation* gles2_impl, + PP_VideoDecoder_Profile profile); + + // This is NULL before initialization, and if this PPB_VideoDecoder_Impl is + // swapped with another. + scoped_ptr<PluginDelegate::PlatformVideoDecoder> platform_video_decoder_; + + // Reference to the plugin requesting this interface. + const PPP_VideoDecoder_Dev* ppp_videodecoder_; + + DISALLOW_COPY_AND_ASSIGN(PPB_VideoDecoder_Impl); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_PPB_VIDEO_DECODER_IMPL_H_ diff --git a/content/renderer/pepper/ppb_widget_impl.cc b/content/renderer/pepper/ppb_widget_impl.cc new file mode 100644 index 0000000..5854f01 --- /dev/null +++ b/content/renderer/pepper/ppb_widget_impl.cc @@ -0,0 +1,80 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/ppb_widget_impl.h" + +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" +#include "content/renderer/pepper/ppb_image_data_impl.h" +#include "content/renderer/pepper/plugin_module.h" +#include "content/renderer/pepper/resource_helper.h" +#include "ppapi/c/dev/ppp_widget_dev.h" +#include "ppapi/thunk/enter.h" +#include "ppapi/thunk/ppb_input_event_api.h" +#include "ppapi/thunk/ppb_widget_api.h" + +using ppapi::thunk::EnterResourceNoLock; +using ppapi::thunk::PPB_ImageData_API; +using ppapi::thunk::PPB_InputEvent_API; +using ppapi::thunk::PPB_Widget_API; + +namespace webkit { +namespace ppapi { + +PPB_Widget_Impl::PPB_Widget_Impl(PP_Instance instance) + : Resource(::ppapi::OBJECT_IS_IMPL, instance), + scale_(1.0f) { + memset(&location_, 0, sizeof(location_)); +} + +PPB_Widget_Impl::~PPB_Widget_Impl() { +} + +PPB_Widget_API* PPB_Widget_Impl::AsPPB_Widget_API() { + return this; +} + +PP_Bool PPB_Widget_Impl::Paint(const PP_Rect* rect, PP_Resource image_id) { + EnterResourceNoLock<PPB_ImageData_API> enter(image_id, true); + if (enter.failed()) + return PP_FALSE; + return PaintInternal(gfx::Rect(rect->point.x, rect->point.y, + rect->size.width, rect->size.height), + static_cast<PPB_ImageData_Impl*>(enter.object())); +} + +PP_Bool PPB_Widget_Impl::HandleEvent(PP_Resource pp_input_event) { + EnterResourceNoLock<PPB_InputEvent_API> enter(pp_input_event, true); + if (enter.failed()) + return PP_FALSE; + return HandleEventInternal(enter.object()->GetInputEventData()); +} + +PP_Bool PPB_Widget_Impl::GetLocation(PP_Rect* location) { + *location = location_; + return PP_TRUE; +} + +void PPB_Widget_Impl::SetLocation(const PP_Rect* location) { + location_ = *location; + SetLocationInternal(location); +} + +void PPB_Widget_Impl::SetScale(float scale) { + scale_ = scale; +} + +void PPB_Widget_Impl::Invalidate(const PP_Rect* dirty) { + PluginInstanceImpl* plugin_instance = ResourceHelper::GetPluginInstance(this); + if (!plugin_instance) + return; + const PPP_Widget_Dev* widget = static_cast<const PPP_Widget_Dev*>( + plugin_instance->module()->GetPluginInterface(PPP_WIDGET_DEV_INTERFACE)); + if (!widget) + return; + widget->Invalidate(pp_instance(), pp_resource(), dirty); +} + +} // namespace ppapi +} // namespace webkit + diff --git a/content/renderer/pepper/ppb_widget_impl.h b/content/renderer/pepper/ppb_widget_impl.h new file mode 100644 index 0000000..7b1e950 --- /dev/null +++ b/content/renderer/pepper/ppb_widget_impl.h @@ -0,0 +1,65 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_PEPPER_PPB_WIDGET_IMPL_H_ +#define CONTENT_RENDERER_PEPPER_PPB_WIDGET_IMPL_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "ppapi/c/pp_rect.h" +#include "ppapi/shared_impl/resource.h" +#include "ppapi/thunk/ppb_widget_api.h" + +namespace gfx { +class Rect; +} +namespace ppapi { +struct InputEventData; +} + +namespace webkit { +namespace ppapi { + +class PPB_ImageData_Impl; + +class PPB_Widget_Impl : public ::ppapi::Resource, + public ::ppapi::thunk::PPB_Widget_API { + public: + explicit PPB_Widget_Impl(PP_Instance instance); + + // Resource overrides. + virtual ::ppapi::thunk::PPB_Widget_API* AsPPB_Widget_API() OVERRIDE; + + // PPB_WidgetAPI implementation. + virtual PP_Bool Paint(const PP_Rect* rect, PP_Resource ) OVERRIDE; + virtual PP_Bool HandleEvent(PP_Resource pp_input_event) OVERRIDE; + virtual PP_Bool GetLocation(PP_Rect* location) OVERRIDE; + virtual void SetLocation(const PP_Rect* location) OVERRIDE; + virtual void SetScale(float scale) OVERRIDE; + + // Notifies the plugin instance that the given rect needs to be repainted. + void Invalidate(const PP_Rect* dirty); + + protected: + virtual ~PPB_Widget_Impl(); + + virtual PP_Bool PaintInternal(const gfx::Rect& rect, + PPB_ImageData_Impl* image) = 0; + virtual PP_Bool HandleEventInternal(const ::ppapi::InputEventData& data) = 0; + virtual void SetLocationInternal(const PP_Rect* location) = 0; + + PP_Rect location() const { return location_; } + float scale() const { return scale_; } + + private: + PP_Rect location_; + float scale_; + + DISALLOW_COPY_AND_ASSIGN(PPB_Widget_Impl); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_PPB_WIDGET_IMPL_H_ diff --git a/content/renderer/pepper/ppb_x509_certificate_private_impl.cc b/content/renderer/pepper/ppb_x509_certificate_private_impl.cc new file mode 100644 index 0000000..0b848d2 --- /dev/null +++ b/content/renderer/pepper/ppb_x509_certificate_private_impl.cc @@ -0,0 +1,38 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/ppb_x509_certificate_private_impl.h" + +#include "content/renderer/pepper/plugin_delegate.h" +#include "content/renderer/pepper/resource_helper.h" + +namespace webkit { +namespace ppapi { + +PPB_X509Certificate_Private_Impl::PPB_X509Certificate_Private_Impl( + PP_Instance instance) : + PPB_X509Certificate_Private_Shared(::ppapi::OBJECT_IS_IMPL, instance) { +} + +// static +PP_Resource PPB_X509Certificate_Private_Impl::CreateResource( + PP_Instance instance) { + return (new PPB_X509Certificate_Private_Impl(instance))->GetReference(); +} + +bool PPB_X509Certificate_Private_Impl::ParseDER( + const std::vector<char>& der, + ::ppapi::PPB_X509Certificate_Fields* result) { + PluginDelegate* plugin_delegate = ResourceHelper::GetPluginDelegate(this); + if (!plugin_delegate) + return false; + + return plugin_delegate->X509CertificateParseDER(der, result); +} + +PPB_X509Certificate_Private_Impl::~PPB_X509Certificate_Private_Impl() { +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/ppb_x509_certificate_private_impl.h b/content/renderer/pepper/ppb_x509_certificate_private_impl.h new file mode 100644 index 0000000..6b8d856 --- /dev/null +++ b/content/renderer/pepper/ppb_x509_certificate_private_impl.h @@ -0,0 +1,37 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_PEPPER_PPB_X509_CERTIFICATE_PRIVATE_IMPL_H_ +#define CONTENT_RENDERER_PEPPER_PPB_X509_CERTIFICATE_PRIVATE_IMPL_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "ppapi/shared_impl/private/ppb_x509_certificate_private_shared.h" +#include "ppapi/shared_impl/resource.h" + +namespace ppapi { +class PPB_X509Certificate_Fields; +} + +namespace webkit { +namespace ppapi { + +class PPB_X509Certificate_Private_Impl : + public ::ppapi::PPB_X509Certificate_Private_Shared { + public: + PPB_X509Certificate_Private_Impl(PP_Instance instance); + static PP_Resource CreateResource(PP_Instance instance); + virtual bool ParseDER(const std::vector<char>& der, + ::ppapi::PPB_X509Certificate_Fields* result) OVERRIDE; + + private: + virtual ~PPB_X509Certificate_Private_Impl(); + + DISALLOW_COPY_AND_ASSIGN(PPB_X509Certificate_Private_Impl); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_PPB_X509_CERTIFICATE_PRIVATE_IMPL_H_ diff --git a/content/renderer/pepper/ppp_pdf.h b/content/renderer/pepper/ppp_pdf.h new file mode 100644 index 0000000..6ad67bf --- /dev/null +++ b/content/renderer/pepper/ppp_pdf.h @@ -0,0 +1,34 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_PEPPER_PPP_PDF_H_ +#define CONTENT_RENDERER_PEPPER_PPP_PDF_H_ + +#include "ppapi/c/pp_instance.h" +#include "ppapi/c/pp_point.h" +#include "ppapi/c/pp_var.h" + +#define PPP_PDF_INTERFACE_1 "PPP_Pdf;1" +#define PPP_PDF_INTERFACE PPP_PDF_INTERFACE_1 + +typedef enum { + // Rotates the page 90 degrees clockwise from its current orientation. + PP_PRIVATEPAGETRANSFORMTYPE_ROTATE_90_CW, + // Rotates the page 90 degrees counterclockwise from its current orientation. + PP_PRIVATEPAGETRANSFORMTYPE_ROTATE_90_CCW +} PP_PrivatePageTransformType; +PP_COMPILE_ASSERT_SIZE_IN_BYTES(PP_PrivatePageTransformType, 4); + +struct PPP_Pdf_1 { + // Returns an absolute URL if the position is over a link. + PP_Var (*GetLinkAtPosition)(PP_Instance instance, + PP_Point point); + + // Requests that the plugin apply the given transform to its view. + void (*Transform)(PP_Instance instance, PP_PrivatePageTransformType type); +}; + +typedef PPP_Pdf_1 PPP_Pdf; + +#endif // CONTENT_RENDERER_PEPPER_PPP_PDF_H_ diff --git a/content/renderer/pepper/quota_file_io.cc b/content/renderer/pepper/quota_file_io.cc new file mode 100644 index 0000000..64d26a9 --- /dev/null +++ b/content/renderer/pepper/quota_file_io.cc @@ -0,0 +1,424 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/quota_file_io.h" + +#include <algorithm> + +#include "base/bind.h" +#include "base/location.h" +#include "base/memory/weak_ptr.h" +#include "base/message_loop/message_loop_proxy.h" +#include "base/stl_util.h" +#include "base/task_runner_util.h" +#include "content/renderer/pepper/host_globals.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" +#include "content/renderer/pepper/resource_helper.h" + +using base::PlatformFile; +using base::PlatformFileError; +using quota::StorageType; + +namespace webkit { +namespace ppapi { + +namespace { +StorageType PPFileSystemTypeToQuotaStorageType(PP_FileSystemType type) { + switch (type) { + case PP_FILESYSTEMTYPE_LOCALPERSISTENT: + return quota::kStorageTypePersistent; + case PP_FILESYSTEMTYPE_LOCALTEMPORARY: + return quota::kStorageTypeTemporary; + default: + return quota::kStorageTypeUnknown; + } + NOTREACHED(); + return quota::kStorageTypeUnknown; +} + +int WriteAdapter(PlatformFile file, int64 offset, + scoped_ptr<char[]> data, int size) { + return base::WritePlatformFile(file, offset, data.get(), size); +} + +} // namespace + +class QuotaFileIO::PendingOperationBase { + public: + virtual ~PendingOperationBase() {} + + // Either one of Run() or DidFail() is called (the latter is called when + // there was more than one error during quota queries). + virtual void Run() = 0; + virtual void DidFail(PlatformFileError error) = 0; + + protected: + PendingOperationBase(QuotaFileIO* quota_io, bool is_will_operation) + : quota_io_(quota_io), is_will_operation_(is_will_operation) { + DCHECK(quota_io_); + quota_io_->WillUpdate(); + } + + QuotaFileIO* quota_io_; + const bool is_will_operation_; +}; + +class QuotaFileIO::WriteOperation : public PendingOperationBase { + public: + WriteOperation(QuotaFileIO* quota_io, + bool is_will_operation, + int64_t offset, + const char* buffer, + int32_t bytes_to_write, + const WriteCallback& callback) + : PendingOperationBase(quota_io, is_will_operation), + offset_(offset), + bytes_to_write_(bytes_to_write), + callback_(callback), + finished_(false), + status_(base::PLATFORM_FILE_OK), + bytes_written_(0), + weak_factory_(this) { + if (!is_will_operation) { + // TODO(kinuko): Check the API convention if we really need to keep a copy + // of the buffer during the async write operations. + buffer_.reset(new char[bytes_to_write]); + memcpy(buffer_.get(), buffer, bytes_to_write); + } + } + virtual ~WriteOperation() {} + virtual void Run() OVERRIDE { + DCHECK(quota_io_); + if (quota_io_->CheckIfExceedsQuota(offset_ + bytes_to_write_)) { + DidFail(base::PLATFORM_FILE_ERROR_NO_SPACE); + return; + } + if (is_will_operation_) { + // Assuming the write will succeed. + DidFinish(base::PLATFORM_FILE_OK, bytes_to_write_); + return; + } + DCHECK(buffer_.get()); + + PluginDelegate* plugin_delegate = quota_io_->GetPluginDelegate(); + if (!plugin_delegate) { + DidFail(base::PLATFORM_FILE_ERROR_FAILED); + return; + } + + if (!base::PostTaskAndReplyWithResult( + plugin_delegate->GetFileThreadMessageLoopProxy().get(), + FROM_HERE, + base::Bind(&WriteAdapter, + quota_io_->file_, + offset_, + base::Passed(&buffer_), + bytes_to_write_), + base::Bind(&WriteOperation::DidWrite, + weak_factory_.GetWeakPtr()))) { + DidFail(base::PLATFORM_FILE_ERROR_FAILED); + return; + } + } + + virtual void DidFail(PlatformFileError error) OVERRIDE { + DidFinish(error, 0); + } + + bool finished() const { return finished_; } + + virtual void WillRunCallback() { + base::MessageLoopProxy::current()->PostTask( + FROM_HERE, + base::Bind(&WriteOperation::RunCallback, weak_factory_.GetWeakPtr())); + } + + private: + void DidWrite(int bytes_written) { + base::PlatformFileError error = bytes_written > 0 ? + base::PLATFORM_FILE_OK : base::PLATFORM_FILE_ERROR_FAILED; + DidFinish(error, bytes_written); + } + + void DidFinish(PlatformFileError status, int bytes_written) { + finished_ = true; + status_ = status; + bytes_written_ = bytes_written; + int64_t max_offset = + (status != base::PLATFORM_FILE_OK) ? 0 : offset_ + bytes_written; + // This may delete itself by calling RunCallback. + quota_io_->DidWrite(this, max_offset); + } + + virtual void RunCallback() { + DCHECK_EQ(false, callback_.is_null()); + callback_.Run(status_, bytes_written_); + delete this; + } + + const int64_t offset_; + scoped_ptr<char[]> buffer_; + const int32_t bytes_to_write_; + WriteCallback callback_; + bool finished_; + PlatformFileError status_; + int64_t bytes_written_; + base::WeakPtrFactory<WriteOperation> weak_factory_; +}; + +class QuotaFileIO::SetLengthOperation : public PendingOperationBase { + public: + SetLengthOperation(QuotaFileIO* quota_io, + bool is_will_operation, + int64_t length, + const StatusCallback& callback) + : PendingOperationBase(quota_io, is_will_operation), + length_(length), + callback_(callback), + weak_factory_(this) {} + + virtual ~SetLengthOperation() {} + + virtual void Run() OVERRIDE { + DCHECK(quota_io_); + if (quota_io_->CheckIfExceedsQuota(length_)) { + DidFail(base::PLATFORM_FILE_ERROR_NO_SPACE); + return; + } + if (is_will_operation_) { + DidFinish(base::PLATFORM_FILE_OK); + return; + } + + PluginDelegate* plugin_delegate = quota_io_->GetPluginDelegate(); + if (!plugin_delegate) { + DidFail(base::PLATFORM_FILE_ERROR_FAILED); + return; + } + + if (!base::FileUtilProxy::Truncate( + plugin_delegate->GetFileThreadMessageLoopProxy().get(), + quota_io_->file_, + length_, + base::Bind(&SetLengthOperation::DidFinish, + weak_factory_.GetWeakPtr()))) { + DidFail(base::PLATFORM_FILE_ERROR_FAILED); + return; + } + } + + virtual void DidFail(PlatformFileError error) OVERRIDE { + DidFinish(error); + } + + private: + void DidFinish(PlatformFileError status) { + quota_io_->DidSetLength(status, length_); + DCHECK_EQ(false, callback_.is_null()); + callback_.Run(status); + delete this; + } + + int64_t length_; + StatusCallback callback_; + base::WeakPtrFactory<SetLengthOperation> weak_factory_; +}; + +// QuotaFileIO -------------------------------------------------------------- + +QuotaFileIO::QuotaFileIO( + PP_Instance instance, + PlatformFile file, + const GURL& file_url, + PP_FileSystemType type) + : pp_instance_(instance), + file_(file), + file_url_(file_url), + storage_type_(PPFileSystemTypeToQuotaStorageType(type)), + cached_file_size_(0), + cached_available_space_(0), + outstanding_quota_queries_(0), + outstanding_errors_(0), + max_written_offset_(0), + inflight_operations_(0), + weak_factory_(this) { + DCHECK_NE(base::kInvalidPlatformFileValue, file_); + DCHECK_NE(quota::kStorageTypeUnknown, storage_type_); +} + +QuotaFileIO::~QuotaFileIO() { + // Note that this doesn't dispatch pending callbacks. + STLDeleteContainerPointers(pending_operations_.begin(), + pending_operations_.end()); + STLDeleteContainerPointers(pending_callbacks_.begin(), + pending_callbacks_.end()); +} + +bool QuotaFileIO::Write( + int64_t offset, const char* buffer, int32_t bytes_to_write, + const WriteCallback& callback) { + if (bytes_to_write <= 0) + return false; + + WriteOperation* op = new WriteOperation( + this, false, offset, buffer, bytes_to_write, callback); + return RegisterOperationForQuotaChecks(op); +} + +bool QuotaFileIO::SetLength(int64_t length, const StatusCallback& callback) { + DCHECK(pending_operations_.empty()); + SetLengthOperation* op = new SetLengthOperation( + this, false, length, callback); + return RegisterOperationForQuotaChecks(op); +} + +bool QuotaFileIO::WillWrite( + int64_t offset, int32_t bytes_to_write, const WriteCallback& callback) { + WriteOperation* op = new WriteOperation( + this, true, offset, NULL, bytes_to_write, callback); + return RegisterOperationForQuotaChecks(op); +} + +bool QuotaFileIO::WillSetLength(int64_t length, + const StatusCallback& callback) { + DCHECK(pending_operations_.empty()); + SetLengthOperation* op = new SetLengthOperation(this, true, length, callback); + return RegisterOperationForQuotaChecks(op); +} + +PluginDelegate* QuotaFileIO::GetPluginDelegate() const { + PluginInstanceImpl* instance = HostGlobals::Get()->GetInstance(pp_instance_); + if (instance) + return instance->delegate(); + return NULL; +} + +bool QuotaFileIO::RegisterOperationForQuotaChecks( + PendingOperationBase* op_ptr) { + scoped_ptr<PendingOperationBase> op(op_ptr); + if (pending_operations_.empty()) { + // This is the first pending quota check. Run querying the file size + // and available space. + outstanding_quota_queries_ = 0; + outstanding_errors_ = 0; + + PluginDelegate* plugin_delegate = GetPluginDelegate(); + if (!plugin_delegate) + return false; + + // Query the file size. + ++outstanding_quota_queries_; + if (!base::FileUtilProxy::GetFileInfoFromPlatformFile( + plugin_delegate->GetFileThreadMessageLoopProxy().get(), + file_, + base::Bind(&QuotaFileIO::DidQueryInfoForQuota, + weak_factory_.GetWeakPtr()))) { + // This makes the call fail synchronously; we do not fire the callback + // here but just delete the operation and return false. + return false; + } + + // Query the current available space. + ++outstanding_quota_queries_; + plugin_delegate->QueryAvailableSpace( + file_url_.GetOrigin(), storage_type_, + base::Bind(&QuotaFileIO::DidQueryAvailableSpace, + weak_factory_.GetWeakPtr())); + } + pending_operations_.push_back(op.release()); + return true; +} + +void QuotaFileIO::DidQueryInfoForQuota( + base::PlatformFileError error_code, + const base::PlatformFileInfo& file_info) { + if (error_code != base::PLATFORM_FILE_OK) + ++outstanding_errors_; + cached_file_size_ = file_info.size; + DCHECK_GT(outstanding_quota_queries_, 0); + if (--outstanding_quota_queries_ == 0) + DidQueryForQuotaCheck(); +} + +void QuotaFileIO::DidQueryAvailableSpace(int64_t avail_space) { + cached_available_space_ = avail_space; + DCHECK_GT(outstanding_quota_queries_, 0); + if (--outstanding_quota_queries_ == 0) + DidQueryForQuotaCheck(); +} + +void QuotaFileIO::DidQueryForQuotaCheck() { + DCHECK(!pending_operations_.empty()); + DCHECK_GT(inflight_operations_, 0); + while (!pending_operations_.empty()) { + PendingOperationBase* op = pending_operations_.front(); + pending_operations_.pop_front(); + pending_callbacks_.push_back(op); + if (outstanding_errors_ > 0) { + op->DidFail(base::PLATFORM_FILE_ERROR_FAILED); + continue; + } + op->Run(); + } +} + +bool QuotaFileIO::CheckIfExceedsQuota(int64_t new_file_size) const { + DCHECK_GE(cached_file_size_, 0); + DCHECK_GE(cached_available_space_, 0); + return new_file_size - cached_file_size_ > cached_available_space_; +} + +void QuotaFileIO::WillUpdate() { + if (inflight_operations_++ == 0) { + PluginDelegate* plugin_delegate = GetPluginDelegate(); + if (plugin_delegate) + plugin_delegate->WillUpdateFile(file_url_); + DCHECK_EQ(0, max_written_offset_); + } +} + +void QuotaFileIO::DidWrite(WriteOperation* op, + int64_t written_offset_end) { + max_written_offset_ = std::max(max_written_offset_, written_offset_end); + DCHECK_GT(inflight_operations_, 0); + DCHECK(!pending_callbacks_.empty()); + // Fire callbacks for finished operations. + while (!pending_callbacks_.empty()) { + WriteOperation* op = static_cast<WriteOperation*>( + pending_callbacks_.front()); + if (!op->finished()) + break; + pending_callbacks_.pop_front(); + op->WillRunCallback(); + } + // If we have no more pending writes, notify the browser that we did + // update the file. + if (--inflight_operations_ == 0) { + DCHECK(pending_operations_.empty()); + int64_t growth = max_written_offset_ - cached_file_size_; + growth = growth < 0 ? 0 : growth; + + PluginDelegate* plugin_delegate = GetPluginDelegate(); + if (plugin_delegate) + plugin_delegate->DidUpdateFile(file_url_, growth); + max_written_offset_ = 0; + } +} + +void QuotaFileIO::DidSetLength(PlatformFileError error, int64_t new_file_size) { + DCHECK_EQ(1, inflight_operations_); + pending_callbacks_.pop_front(); + DCHECK(pending_callbacks_.empty()); + int64_t delta = (error != base::PLATFORM_FILE_OK) ? 0 : + new_file_size - cached_file_size_; + + + PluginDelegate* plugin_delegate = GetPluginDelegate(); + if (plugin_delegate) + plugin_delegate->DidUpdateFile(file_url_, delta); + inflight_operations_ = 0; +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/quota_file_io.h b/content/renderer/pepper/quota_file_io.h new file mode 100644 index 0000000..7c95464 --- /dev/null +++ b/content/renderer/pepper/quota_file_io.h @@ -0,0 +1,117 @@ +// 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. + +#ifndef CONTENT_RENDERER_PEPPER_QUOTA_FILE_IO_H_ +#define CONTENT_RENDERER_PEPPER_QUOTA_FILE_IO_H_ + +#include <deque> + +#include "base/files/file_util_proxy.h" +#include "base/memory/weak_ptr.h" +#include "base/platform_file.h" +#include "content/common/content_export.h" +#include "ppapi/c/pp_file_info.h" +#include "ppapi/c/pp_instance.h" +#include "url/gurl.h" +#include "webkit/common/quota/quota_types.h" + +namespace webkit { +namespace ppapi { + +class PluginDelegate; + +// This class is created per PPB_FileIO_Impl instance and provides +// write operations for quota-managed files (i.e. files of +// PP_FILESYSTEMTYPE_LOCAL{PERSISTENT,TEMPORARY}). +class QuotaFileIO { + public: + typedef base::FileUtilProxy::WriteCallback WriteCallback; + typedef base::FileUtilProxy::StatusCallback StatusCallback; + + CONTENT_EXPORT QuotaFileIO(PP_Instance instance, + base::PlatformFile file, + const GURL& path_url, + PP_FileSystemType type); + CONTENT_EXPORT ~QuotaFileIO(); + + // Performs write or setlength operation with quota checks. + // Returns true when the operation is successfully dispatched. + // |bytes_to_write| must be geater than zero. + // |callback| will be dispatched when the operation completes. + // Otherwise it returns false and |callback| will not be dispatched. + // |callback| will not be dispatched either when this instance is + // destroyed before the operation completes. + // SetLength/WillSetLength cannot be called while there're any in-flight + // operations. For Write/WillWrite it is guaranteed that |callback| are + // always dispatched in the same order as Write being called. + CONTENT_EXPORT bool Write(int64_t offset, + const char* buffer, + int32_t bytes_to_write, + const WriteCallback& callback); + CONTENT_EXPORT bool WillWrite(int64_t offset, + int32_t bytes_to_write, + const WriteCallback& callback); + + CONTENT_EXPORT bool SetLength(int64_t length, + const StatusCallback& callback); + CONTENT_EXPORT bool WillSetLength(int64_t length, + const StatusCallback& callback); + + // Returns the plugin delegate or NULL if the resource has outlived the + // instance. + PluginDelegate* GetPluginDelegate() const; + + private: + class PendingOperationBase; + class WriteOperation; + class SetLengthOperation; + + bool CheckIfExceedsQuota(int64_t new_file_size) const; + void WillUpdate(); + void DidWrite(WriteOperation* op, int64_t written_offset_end); + void DidSetLength(base::PlatformFileError error, int64_t new_file_size); + + bool RegisterOperationForQuotaChecks(PendingOperationBase* op); + void DidQueryInfoForQuota(base::PlatformFileError error_code, + const base::PlatformFileInfo& file_info); + void DidQueryAvailableSpace(int64_t avail_space); + void DidQueryForQuotaCheck(); + + // The plugin instance that owns this (via PPB_FileIO_Impl). + PP_Instance pp_instance_; + + // The file information associated to this instance. + base::PlatformFile file_; + GURL file_url_; + quota::StorageType storage_type_; + + // Pending operations that are waiting quota checks and pending + // callbacks that are to be fired after the operation; + // we use two separate queues for them so that we can safely dequeue the + // pending callbacks while enqueueing new operations. (This could + // happen when callbacks are dispatched synchronously due to error etc.) + std::deque<PendingOperationBase*> pending_operations_; + std::deque<PendingOperationBase*> pending_callbacks_; + + // Valid only while there're pending quota checks. + int64_t cached_file_size_; + int64_t cached_available_space_; + + // Quota-related queries and errors occurred during in-flight quota checks. + int outstanding_quota_queries_; + int outstanding_errors_; + + // For parallel writes bookkeeping. + int64_t max_written_offset_; + int inflight_operations_; + + base::WeakPtrFactory<QuotaFileIO> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(QuotaFileIO); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_QUOTA_FILE_IO_H_ diff --git a/content/renderer/pepper/quota_file_io_unittest.cc b/content/renderer/pepper/quota_file_io_unittest.cc new file mode 100644 index 0000000..ad66e52 --- /dev/null +++ b/content/renderer/pepper/quota_file_io_unittest.cc @@ -0,0 +1,501 @@ +// 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 <deque> +#include <limits> +#include <string> + +#include "base/basictypes.h" +#include "base/bind.h" +#include "base/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "base/memory/weak_ptr.h" +#include "base/message_loop/message_loop.h" +#include "base/platform_file.h" +#include "content/renderer/pepper/mock_plugin_delegate.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" +#include "content/renderer/pepper/ppapi_unittest.h" +#include "content/renderer/pepper/quota_file_io.h" + +using base::MessageLoopProxy; +using base::PlatformFile; +using base::PlatformFileError; + +namespace webkit { +namespace ppapi { + +namespace { +class QuotaMockPluginDelegate : public MockPluginDelegate { + public: + typedef PluginDelegate::AvailableSpaceCallback Callback; + + QuotaMockPluginDelegate() + : available_space_(0), + will_update_count_(0), + file_thread_(MessageLoopProxy::current()), + weak_factory_(this) { + } + virtual ~QuotaMockPluginDelegate() {} + + virtual scoped_refptr<MessageLoopProxy> + GetFileThreadMessageLoopProxy() OVERRIDE { + return file_thread_; + } + + virtual void QueryAvailableSpace( + const GURL& origin, + quota::StorageType type, + const Callback& callback) OVERRIDE { + DCHECK_EQ(false, callback.is_null()); + MessageLoopProxy::current()->PostTask( + FROM_HERE, base::Bind( + &QuotaMockPluginDelegate::RunAvailableSpaceCallback, + weak_factory_.GetWeakPtr(), callback)); + } + + virtual void WillUpdateFile(const GURL& file_path) OVERRIDE { + file_path_ = file_path; + ++will_update_count_; + } + + virtual void DidUpdateFile(const GURL& file_path, int64_t delta) OVERRIDE { + ASSERT_EQ(file_path_, file_path); + ASSERT_GT(will_update_count_, 0); + --will_update_count_; + available_space_ -= delta; + } + + void set_available_space(int64 available) { available_space_ = available; } + int64_t available_space() const { return available_space_; } + + private: + void RunAvailableSpaceCallback(const Callback& callback) { + callback.Run(available_space_); + } + + int64_t available_space_; + int will_update_count_; + GURL file_path_; + scoped_refptr<MessageLoopProxy> file_thread_; + base::WeakPtrFactory<QuotaMockPluginDelegate> weak_factory_; +}; +} // namespace + +class QuotaFileIOTest : public PpapiUnittest { + public: + QuotaFileIOTest() + : weak_factory_(this) {} + + virtual void SetUp() OVERRIDE { + PpapiUnittest::SetUp(); + ASSERT_TRUE(dir_.CreateUniqueTempDir()); + base::FilePath path; + ASSERT_TRUE(file_util::CreateTemporaryFileInDir(dir_.path(), &path)); + int file_flags = base::PLATFORM_FILE_OPEN | + base::PLATFORM_FILE_READ | + base::PLATFORM_FILE_WRITE | + base::PLATFORM_FILE_WRITE_ATTRIBUTES; + bool created = false; + file_ = base::kInvalidPlatformFileValue; + PlatformFileError error = base::PLATFORM_FILE_OK; + file_ = base::CreatePlatformFile(path, file_flags, &created, &error); + ASSERT_EQ(base::PLATFORM_FILE_OK, error); + ASSERT_NE(base::kInvalidPlatformFileValue, file_); + ASSERT_FALSE(created); + quota_file_io_.reset(new QuotaFileIO( + instance()->pp_instance(), file_, GURL(), + PP_FILESYSTEMTYPE_LOCALTEMPORARY)); + } + + virtual void TearDown() OVERRIDE { + quota_file_io_.reset(); + if (file_ != base::kInvalidPlatformFileValue) + base::ClosePlatformFile(file_); + PpapiUnittest::TearDown(); + } + + protected: + virtual MockPluginDelegate* NewPluginDelegate() OVERRIDE { + return static_cast<MockPluginDelegate*>(new QuotaMockPluginDelegate); + } + + void WriteTestBody(bool will_operation) { + // Attempt to write zero bytes. + EXPECT_FALSE(quota_file_io_->Write( + 0, "data", 0, + base::Bind(&QuotaFileIOTest::DidWrite, weak_factory_.GetWeakPtr()))); + // Attempt to write negative number of bytes. + EXPECT_FALSE(quota_file_io_->Write( + 0, "data", std::numeric_limits<int32_t>::min(), + base::Bind(&QuotaFileIOTest::DidWrite, weak_factory_.GetWeakPtr()))); + + quota_plugin_delegate()->set_available_space(100); + std::string read_buffer; + + // Write 8 bytes at offset 0 (-> length=8). + std::string data("12345678"); + Write(0, data, will_operation); + base::MessageLoop::current()->RunUntilIdle(); + ASSERT_EQ(1U, num_results()); + EXPECT_EQ(static_cast<int>(data.size()), bytes_written().front()); + EXPECT_EQ(base::PLATFORM_FILE_OK, status().front()); + EXPECT_EQ(100 - 8, quota_plugin_delegate()->available_space()); + reset_results(); + + if (will_operation) { + // WillWrite doesn't actually write. + EXPECT_EQ(0, GetPlatformFileSize()); + // Adjust the actual file size to 'fake' write to proceed testing. + SetPlatformFileSize(8); + } else { + EXPECT_EQ(8, GetPlatformFileSize()); + ReadPlatformFile(&read_buffer); + EXPECT_EQ(data, read_buffer); + } + + // Write 5 bytes at offset 5 (-> length=10). + data = "55555"; + Write(5, data, will_operation); + base::MessageLoop::current()->RunUntilIdle(); + ASSERT_EQ(1U, num_results()); + EXPECT_EQ(static_cast<int>(data.size()), bytes_written().front()); + EXPECT_EQ(base::PLATFORM_FILE_OK, status().front()); + EXPECT_EQ(100 - 10, quota_plugin_delegate()->available_space()); + reset_results(); + + if (will_operation) { + EXPECT_EQ(8, GetPlatformFileSize()); + SetPlatformFileSize(10); + } else { + EXPECT_EQ(10, GetPlatformFileSize()); + ReadPlatformFile(&read_buffer); + EXPECT_EQ("1234555555", read_buffer); + } + + // Write 7 bytes at offset 8 (-> length=15). + data = "9012345"; + Write(8, data, will_operation); + base::MessageLoop::current()->RunUntilIdle(); + ASSERT_EQ(1U, num_results()); + EXPECT_EQ(static_cast<int>(data.size()), bytes_written().front()); + EXPECT_EQ(base::PLATFORM_FILE_OK, status().front()); + EXPECT_EQ(100 - 15, quota_plugin_delegate()->available_space()); + reset_results(); + + if (will_operation) { + EXPECT_EQ(10, GetPlatformFileSize()); + SetPlatformFileSize(15); + } else { + EXPECT_EQ(15, GetPlatformFileSize()); + ReadPlatformFile(&read_buffer); + EXPECT_EQ("123455559012345", read_buffer); + } + + // Write 2 bytes at offset 2 (-> length=15). + data = "33"; + Write(2, data, will_operation); + base::MessageLoop::current()->RunUntilIdle(); + ASSERT_EQ(1U, num_results()); + EXPECT_EQ(static_cast<int>(data.size()), bytes_written().front()); + EXPECT_EQ(base::PLATFORM_FILE_OK, status().front()); + EXPECT_EQ(100 - 15, quota_plugin_delegate()->available_space()); + reset_results(); + + if (will_operation) { + EXPECT_EQ(15, GetPlatformFileSize()); + } else { + EXPECT_EQ(15, GetPlatformFileSize()); + ReadPlatformFile(&read_buffer); + EXPECT_EQ("123355559012345", read_buffer); + } + + // Write 4 bytes at offset 20 (-> length=24). + data = "XXXX"; + Write(20, data, will_operation); + base::MessageLoop::current()->RunUntilIdle(); + ASSERT_EQ(1U, num_results()); + EXPECT_EQ(static_cast<int>(data.size()), bytes_written().front()); + EXPECT_EQ(base::PLATFORM_FILE_OK, status().front()); + EXPECT_EQ(100 - 24, quota_plugin_delegate()->available_space()); + reset_results(); + + if (will_operation) { + EXPECT_EQ(15, GetPlatformFileSize()); + SetPlatformFileSize(24); + } else { + EXPECT_EQ(24, GetPlatformFileSize()); + ReadPlatformFile(&read_buffer); + EXPECT_EQ(std::string("123355559012345\0\0\0\0\0XXXX", 24), read_buffer); + } + + quota_plugin_delegate()->set_available_space(5); + + // Quota error case. Write 7 bytes at offset 23 (-> length is unchanged) + data = "ABCDEFG"; + Write(23, data, will_operation); + base::MessageLoop::current()->RunUntilIdle(); + ASSERT_EQ(1U, num_results()); + EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, status().front()); + EXPECT_EQ(5, quota_plugin_delegate()->available_space()); + reset_results(); + + // Overlapping write. Write 6 bytes at offset 2 (-> length is unchanged) + data = "ABCDEF"; + Write(2, data, will_operation); + base::MessageLoop::current()->RunUntilIdle(); + ASSERT_EQ(1U, num_results()); + EXPECT_EQ(static_cast<int>(data.size()), bytes_written().front()); + EXPECT_EQ(base::PLATFORM_FILE_OK, status().front()); + EXPECT_EQ(5, quota_plugin_delegate()->available_space()); + reset_results(); + + // Overlapping + extending the file size, but within the quota. + // Write 6 bytes at offset 23 (-> length=29). + Write(23, data, will_operation); + base::MessageLoop::current()->RunUntilIdle(); + ASSERT_EQ(1U, num_results()); + EXPECT_EQ(static_cast<int>(data.size()), bytes_written().front()); + EXPECT_EQ(base::PLATFORM_FILE_OK, status().front()); + EXPECT_EQ(0, quota_plugin_delegate()->available_space()); + reset_results(); + + if (!will_operation) { + EXPECT_EQ(29, GetPlatformFileSize()); + ReadPlatformFile(&read_buffer); + EXPECT_EQ(std::string("12ABCDEF9012345\0\0\0\0\0XXXABCDEF", 29), + read_buffer); + } + } + + void SetLengthTestBody(bool will_operation) { + quota_plugin_delegate()->set_available_space(100); + + SetLength(0, will_operation); + base::MessageLoop::current()->RunUntilIdle(); + ASSERT_EQ(1U, num_results()); + EXPECT_EQ(base::PLATFORM_FILE_OK, status().front()); + EXPECT_EQ(0, GetPlatformFileSize()); + EXPECT_EQ(100, quota_plugin_delegate()->available_space()); + reset_results(); + + SetLength(8, will_operation); + base::MessageLoop::current()->RunUntilIdle(); + ASSERT_EQ(1U, num_results()); + EXPECT_EQ(base::PLATFORM_FILE_OK, status().front()); + EXPECT_EQ(100 - 8, quota_plugin_delegate()->available_space()); + reset_results(); + + if (will_operation) { + EXPECT_EQ(0, GetPlatformFileSize()); + SetPlatformFileSize(8); + } else { + EXPECT_EQ(8, GetPlatformFileSize()); + } + + SetLength(16, will_operation); + base::MessageLoop::current()->RunUntilIdle(); + ASSERT_EQ(1U, num_results()); + EXPECT_EQ(base::PLATFORM_FILE_OK, status().front()); + EXPECT_EQ(100 - 16, quota_plugin_delegate()->available_space()); + reset_results(); + + if (will_operation) { + EXPECT_EQ(8, GetPlatformFileSize()); + SetPlatformFileSize(16); + } else { + EXPECT_EQ(16, GetPlatformFileSize()); + } + + SetLength(4, will_operation); + base::MessageLoop::current()->RunUntilIdle(); + ASSERT_EQ(1U, num_results()); + EXPECT_EQ(base::PLATFORM_FILE_OK, status().front()); + EXPECT_EQ(100 - 4, quota_plugin_delegate()->available_space()); + reset_results(); + + if (will_operation) { + EXPECT_EQ(16, GetPlatformFileSize()); + SetPlatformFileSize(4); + } else { + EXPECT_EQ(4, GetPlatformFileSize()); + } + + SetLength(0, will_operation); + base::MessageLoop::current()->RunUntilIdle(); + ASSERT_EQ(1U, num_results()); + EXPECT_EQ(base::PLATFORM_FILE_OK, status().front()); + EXPECT_EQ(100, quota_plugin_delegate()->available_space()); + reset_results(); + + if (will_operation) { + EXPECT_EQ(4, GetPlatformFileSize()); + SetPlatformFileSize(0); + } else { + EXPECT_EQ(0, GetPlatformFileSize()); + } + + quota_plugin_delegate()->set_available_space(5); + + // Quota error case. + SetLength(7, will_operation); + base::MessageLoop::current()->RunUntilIdle(); + ASSERT_EQ(1U, num_results()); + EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, status().front()); + EXPECT_EQ(5, quota_plugin_delegate()->available_space()); + reset_results(); + } + + QuotaMockPluginDelegate* quota_plugin_delegate() { + return static_cast<QuotaMockPluginDelegate*>(delegate()); + } + + void Write(int64_t offset, const std::string& data, bool will_operation) { + if (will_operation) { + ASSERT_TRUE(quota_file_io_->WillWrite( + offset, data.size(), + base::Bind(&QuotaFileIOTest::DidWrite, weak_factory_.GetWeakPtr()))); + } else { + ASSERT_TRUE(quota_file_io_->Write( + offset, data.c_str(), data.size(), + base::Bind(&QuotaFileIOTest::DidWrite, weak_factory_.GetWeakPtr()))); + } + } + + void SetLength(int64_t length, bool will_operation) { + if (will_operation) { + ASSERT_TRUE(quota_file_io_->WillSetLength( + length, + base::Bind(&QuotaFileIOTest::DidSetLength, + weak_factory_.GetWeakPtr()))); + } else { + ASSERT_TRUE(quota_file_io_->SetLength( + length, + base::Bind(&QuotaFileIOTest::DidSetLength, + weak_factory_.GetWeakPtr()))); + } + } + + void DidWrite(PlatformFileError status, int bytes_written) { + status_.push_back(status); + bytes_written_.push_back(bytes_written); + } + + void DidSetLength(PlatformFileError status) { + status_.push_back(status); + } + + size_t num_results() const { return status_.size(); } + const std::deque<int>& bytes_written() const { return bytes_written_; } + const std::deque<PlatformFileError>& status() const { return status_; } + + void reset_results() { + bytes_written_.clear(); + status_.clear(); + } + + void pop_result() { + bytes_written_.pop_front(); + status_.pop_front(); + } + + void ReadPlatformFile(std::string* data) { + data->clear(); + char buf[256]; + int32_t read_offset = 0; + for (;;) { + int rv = base::ReadPlatformFile(file_, read_offset, buf, sizeof(buf)); + ASSERT_GE(rv, 0); + if (rv == 0) + break; + read_offset += rv; + data->append(buf, rv); + } + } + + int64_t GetPlatformFileSize() { + base::PlatformFileInfo info; + EXPECT_TRUE(base::GetPlatformFileInfo(file_, &info)); + return info.size; + } + + void SetPlatformFileSize(int64_t length) { + EXPECT_TRUE(base::TruncatePlatformFile(file_, length)); + } + + private: + base::ScopedTempDir dir_; + PlatformFile file_; + scoped_ptr<QuotaFileIO> quota_file_io_; + std::deque<int> bytes_written_; + std::deque<PlatformFileError> status_; + base::WeakPtrFactory<QuotaFileIOTest> weak_factory_; +}; + +TEST_F(QuotaFileIOTest, Write) { + WriteTestBody(false); +} + +TEST_F(QuotaFileIOTest, WillWrite) { + WriteTestBody(true); +} + +TEST_F(QuotaFileIOTest, SetLength) { + SetLengthTestBody(false); +} + +TEST_F(QuotaFileIOTest, WillSetLength) { + SetLengthTestBody(true); +} + +TEST_F(QuotaFileIOTest, ParallelWrites) { + quota_plugin_delegate()->set_available_space(22); + std::string read_buffer; + + const std::string data1[] = { + std::string("12345678"), + std::string("55555"), + std::string("9012345"), + }; + Write(0, data1[0], false); + Write(5, data1[1], false); + Write(8, data1[2], false); + base::MessageLoop::current()->RunUntilIdle(); + + ASSERT_EQ(ARRAYSIZE_UNSAFE(data1), num_results()); + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data1); ++i) { + EXPECT_EQ(static_cast<int>(data1[i].size()), bytes_written().front()); + EXPECT_EQ(base::PLATFORM_FILE_OK, status().front()); + pop_result(); + } + + EXPECT_EQ(22 - 15, quota_plugin_delegate()->available_space()); + EXPECT_EQ(15, GetPlatformFileSize()); + ReadPlatformFile(&read_buffer); + EXPECT_EQ("123455559012345", read_buffer); + + // The second write will fail for quota error. + const std::string data2[] = { + std::string("33"), + std::string("XXXX"), + }; + Write(2, data2[0], false); + Write(20, data2[1], false); + base::MessageLoop::current()->RunUntilIdle(); + + ASSERT_EQ(ARRAYSIZE_UNSAFE(data2), num_results()); + EXPECT_EQ(static_cast<int>(data2[0].size()), bytes_written().front()); + EXPECT_EQ(base::PLATFORM_FILE_OK, status().front()); + pop_result(); + EXPECT_EQ(0, bytes_written().front()); + EXPECT_EQ(base::PLATFORM_FILE_ERROR_NO_SPACE, status().front()); + pop_result(); + + EXPECT_EQ(22 - 15, quota_plugin_delegate()->available_space()); + EXPECT_EQ(15, GetPlatformFileSize()); + ReadPlatformFile(&read_buffer); + EXPECT_EQ("123355559012345", read_buffer); +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/renderer_ppapi_host_impl.cc b/content/renderer/pepper/renderer_ppapi_host_impl.cc index 18b5859..0d6768f 100644 --- a/content/renderer/pepper/renderer_ppapi_host_impl.cc +++ b/content/renderer/pepper/renderer_ppapi_host_impl.cc @@ -8,11 +8,16 @@ #include "base/logging.h" #include "base/process/process_handle.h" #include "content/common/sandbox_util.h" +#include "content/renderer/pepper/fullscreen_container.h" +#include "content/renderer/pepper/host_globals.h" #include "content/renderer/pepper/pepper_browser_connection.h" #include "content/renderer/pepper/pepper_graphics_2d_host.h" #include "content/renderer/pepper/pepper_in_process_resource_creation.h" #include "content/renderer/pepper/pepper_in_process_router.h" #include "content/renderer/pepper/pepper_plugin_delegate_impl.h" +#include "content/renderer/pepper/plugin_delegate.h" +#include "content/renderer/pepper/plugin_module.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" #include "content/renderer/render_view_impl.h" #include "content/renderer/render_widget_fullscreen_pepper.h" #include "ipc/ipc_message.h" @@ -23,11 +28,6 @@ #include "third_party/WebKit/public/web/WebElement.h" #include "third_party/WebKit/public/web/WebPluginContainer.h" #include "ui/gfx/point.h" -#include "webkit/plugins/ppapi/fullscreen_container.h" -#include "webkit/plugins/ppapi/host_globals.h" -#include "webkit/plugins/ppapi/plugin_delegate.h" -#include "webkit/plugins/ppapi/plugin_module.h" -#include "webkit/plugins/ppapi/ppapi_plugin_instance_impl.h" using webkit::ppapi::HostGlobals; using webkit::ppapi::PluginInstance; diff --git a/content/renderer/pepper/renderer_ppapi_host_impl.h b/content/renderer/pepper/renderer_ppapi_host_impl.h index 6d0332f..6d576f7 100644 --- a/content/renderer/pepper/renderer_ppapi_host_impl.h +++ b/content/renderer/pepper/renderer_ppapi_host_impl.h @@ -9,9 +9,9 @@ #include "base/memory/scoped_ptr.h" #include "content/public/renderer/renderer_ppapi_host.h" #include "content/renderer/pepper/content_renderer_pepper_host_factory.h" +#include "content/renderer/pepper/plugin_delegate.h" +#include "content/renderer/pepper/plugin_module.h" #include "ppapi/host/ppapi_host.h" -#include "webkit/plugins/ppapi/plugin_delegate.h" -#include "webkit/plugins/ppapi/plugin_module.h" namespace IPC { class Sender; diff --git a/content/renderer/pepper/resource_creation_impl.cc b/content/renderer/pepper/resource_creation_impl.cc new file mode 100644 index 0000000..63fbe35 --- /dev/null +++ b/content/renderer/pepper/resource_creation_impl.cc @@ -0,0 +1,317 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/resource_creation_impl.h" + +#include "content/renderer/pepper/common.h" +#include "content/renderer/pepper/ppb_audio_impl.h" +#include "content/renderer/pepper/ppb_broker_impl.h" +#include "content/renderer/pepper/ppb_buffer_impl.h" +#include "content/renderer/pepper/ppb_file_ref_impl.h" +#include "content/renderer/pepper/ppb_flash_message_loop_impl.h" +#include "content/renderer/pepper/ppb_graphics_3d_impl.h" +#include "content/renderer/pepper/ppb_image_data_impl.h" +#include "content/renderer/pepper/ppb_network_monitor_private_impl.h" +#include "content/renderer/pepper/ppb_scrollbar_impl.h" +#include "content/renderer/pepper/ppb_tcp_server_socket_private_impl.h" +#include "content/renderer/pepper/ppb_tcp_socket_private_impl.h" +#include "content/renderer/pepper/ppb_video_decoder_impl.h" +#include "content/renderer/pepper/ppb_x509_certificate_private_impl.h" +#include "content/renderer/pepper/resource_helper.h" +#include "ppapi/c/pp_size.h" +#include "ppapi/shared_impl/ppb_audio_config_shared.h" +#include "ppapi/shared_impl/ppb_image_data_shared.h" +#include "ppapi/shared_impl/ppb_input_event_shared.h" +#include "ppapi/shared_impl/ppb_resource_array_shared.h" +#include "ppapi/shared_impl/var.h" + +using ppapi::InputEventData; +using ppapi::PPB_InputEvent_Shared; +using ppapi::PPB_ResourceArray_Shared; +using ppapi::StringVar; + +namespace webkit { +namespace ppapi { + +ResourceCreationImpl::ResourceCreationImpl(PluginInstanceImpl* instance) { +} + +ResourceCreationImpl::~ResourceCreationImpl() { +} + +PP_Resource ResourceCreationImpl::CreateAudio( + PP_Instance instance, + PP_Resource config_id, + PPB_Audio_Callback audio_callback, + void* user_data) { + return PPB_Audio_Impl::Create(instance, config_id, audio_callback, + user_data); +} + +PP_Resource ResourceCreationImpl::CreateAudioConfig( + PP_Instance instance, + PP_AudioSampleRate sample_rate, + uint32_t sample_frame_count) { + return ::ppapi::PPB_AudioConfig_Shared::Create( + ::ppapi::OBJECT_IS_IMPL, instance, sample_rate, sample_frame_count); +} + +PP_Resource ResourceCreationImpl::CreateAudioTrusted( + PP_Instance instance) { + return (new PPB_Audio_Impl(instance))->GetReference(); +} + +PP_Resource ResourceCreationImpl::CreateAudioInput(PP_Instance instance) { + return 0; // Not supported in-process. +} + +PP_Resource ResourceCreationImpl::CreateBroker(PP_Instance instance) { + return (new PPB_Broker_Impl(instance))->GetReference(); +} + +PP_Resource ResourceCreationImpl::CreateBuffer(PP_Instance instance, + uint32_t size) { + return PPB_Buffer_Impl::Create(instance, size); +} + +PP_Resource ResourceCreationImpl::CreateFileRef( + PP_Instance instance, + PP_Resource file_system, + const char* path) { + PPB_FileRef_Impl* res = PPB_FileRef_Impl::CreateInternal( + instance, file_system, path); + return res ? res->GetReference() : 0; +} + +PP_Resource ResourceCreationImpl::CreateFileRef( + const ::ppapi::PPB_FileRef_CreateInfo& serialized) { + // When we're in-process, the host resource in the create info *is* the + // resource, so we don't need to do anything. + return serialized.resource.host_resource(); +} + +PP_Resource ResourceCreationImpl::CreateFlashDRM(PP_Instance instance) { + return 0; // Not supported in-process. +} + +PP_Resource ResourceCreationImpl::CreateFlashFontFile( + PP_Instance instance, + const PP_BrowserFont_Trusted_Description* description, + PP_PrivateFontCharset charset) { + return 0; // Not supported in-process. +} + +PP_Resource ResourceCreationImpl::CreateFlashMenu( + PP_Instance instance, + const PP_Flash_Menu* menu_data) { + return 0; // Not supported in-process. +} + +PP_Resource ResourceCreationImpl::CreateFlashMessageLoop(PP_Instance instance) { + return PPB_Flash_MessageLoop_Impl::Create(instance); +} + +PP_Resource ResourceCreationImpl::CreateGraphics3D( + PP_Instance instance, + PP_Resource share_context, + const int32_t* attrib_list) { + return PPB_Graphics3D_Impl::Create(instance, share_context, attrib_list); +} + +PP_Resource ResourceCreationImpl::CreateGraphics3DRaw( + PP_Instance instance, + PP_Resource share_context, + const int32_t* attrib_list) { + return PPB_Graphics3D_Impl::CreateRaw(instance, share_context, attrib_list); +} + +PP_Resource ResourceCreationImpl::CreateHostResolver(PP_Instance instance) { + return 0; // Not supported in-process. +} + +PP_Resource ResourceCreationImpl::CreateHostResolverPrivate( + PP_Instance instance) { + return 0; // Not supported in-process. +} + +PP_Resource ResourceCreationImpl::CreateImageData( + PP_Instance instance, + PP_ImageDataFormat format, + const PP_Size* size, + PP_Bool init_to_zero) { + return PPB_ImageData_Impl::Create(instance, + ::ppapi::PPB_ImageData_Shared::PLATFORM, + format, *size, init_to_zero); +} + +PP_Resource ResourceCreationImpl::CreateImageDataSimple( + PP_Instance instance, + PP_ImageDataFormat format, + const PP_Size* size, + PP_Bool init_to_zero) { + return PPB_ImageData_Impl::Create(instance, + ::ppapi::PPB_ImageData_Shared::SIMPLE, + format, *size, init_to_zero); +} + +PP_Resource ResourceCreationImpl::CreateIMEInputEvent( + PP_Instance instance, + PP_InputEvent_Type type, + PP_TimeTicks time_stamp, + struct PP_Var text, + uint32_t segment_number, + const uint32_t* segment_offsets, + int32_t target_segment, + uint32_t selection_start, + uint32_t selection_end) { + return PPB_InputEvent_Shared::CreateIMEInputEvent( + ::ppapi::OBJECT_IS_IMPL, instance, type, time_stamp, text, segment_number, + segment_offsets, target_segment, selection_start, selection_end); +} + +PP_Resource ResourceCreationImpl::CreateIsolatedFileSystem(PP_Instance instance, + const char* fsid) { + NOTIMPLEMENTED(); // no need to support in-process + return 0; +} + +PP_Resource ResourceCreationImpl::CreateKeyboardInputEvent( + PP_Instance instance, + PP_InputEvent_Type type, + PP_TimeTicks time_stamp, + uint32_t modifiers, + uint32_t key_code, + struct PP_Var character_text) { + return PPB_InputEvent_Shared::CreateKeyboardInputEvent( + ::ppapi::OBJECT_IS_IMPL, instance, type, time_stamp, modifiers, key_code, + character_text); +} + +PP_Resource ResourceCreationImpl::CreateMouseInputEvent( + PP_Instance instance, + PP_InputEvent_Type type, + PP_TimeTicks time_stamp, + uint32_t modifiers, + PP_InputEvent_MouseButton mouse_button, + const PP_Point* mouse_position, + int32_t click_count, + const PP_Point* mouse_movement) { + return PPB_InputEvent_Shared::CreateMouseInputEvent( + ::ppapi::OBJECT_IS_IMPL, instance, type, time_stamp, modifiers, + mouse_button, mouse_position, click_count, mouse_movement); +} + +PP_Resource ResourceCreationImpl::CreateNetAddressFromIPv4Address( + PP_Instance instance, + const PP_NetAddress_IPv4* ipv4_addr) { + return 0; // Not supported in-process. +} + +PP_Resource ResourceCreationImpl::CreateNetAddressFromIPv6Address( + PP_Instance instance, + const PP_NetAddress_IPv6* ipv6_addr) { + return 0; // Not supported in-process. +} + +PP_Resource ResourceCreationImpl::CreateNetAddressFromNetAddressPrivate( + PP_Instance instance, + const PP_NetAddress_Private& private_addr) { + return 0; // Not supported in-process. +} + +PP_Resource ResourceCreationImpl::CreateTouchInputEvent( + PP_Instance instance, + PP_InputEvent_Type type, + PP_TimeTicks time_stamp, + uint32_t modifiers) { + return PPB_InputEvent_Shared::CreateTouchInputEvent( + ::ppapi::OBJECT_IS_IMPL, instance, type, time_stamp, modifiers); +} + +PP_Resource ResourceCreationImpl::CreateNetworkMonitor( + PP_Instance instance, + PPB_NetworkMonitor_Callback callback, + void* user_data) { + return PPB_NetworkMonitor_Private_Impl::Create(instance, callback, user_data); +} + +PP_Resource ResourceCreationImpl::CreateScrollbar(PP_Instance instance, + PP_Bool vertical) { + return PPB_Scrollbar_Impl::Create(instance, PP_ToBool(vertical)); +} + +PP_Resource ResourceCreationImpl::CreateTalk(PP_Instance /* instance */) { + return 0; // Not supported in-process. +} + +PP_Resource ResourceCreationImpl::CreateResourceArray( + PP_Instance instance, + const PP_Resource elements[], + uint32_t size) { + PPB_ResourceArray_Shared* object = new PPB_ResourceArray_Shared( + ::ppapi::OBJECT_IS_IMPL, instance, elements, size); + return object->GetReference(); +} + +PP_Resource ResourceCreationImpl::CreateTCPServerSocketPrivate( + PP_Instance instance) { + return PPB_TCPServerSocket_Private_Impl::CreateResource(instance); +} + +PP_Resource ResourceCreationImpl::CreateTCPSocket(PP_Instance instance) { + return 0; // Not supported in-process. +} + +PP_Resource ResourceCreationImpl::CreateTCPSocketPrivate(PP_Instance instance) { + return PPB_TCPSocket_Private_Impl::CreateResource(instance); +} + +PP_Resource ResourceCreationImpl::CreateUDPSocket(PP_Instance instance) { + return 0; // Not supported in-process. +} + +PP_Resource ResourceCreationImpl::CreateUDPSocketPrivate(PP_Instance instance) { + return 0; // Not supported in-process. +} + +PP_Resource ResourceCreationImpl::CreateVideoCapture(PP_Instance instance) { + return 0; // VideoCapture is not supported in process now. +} + +PP_Resource ResourceCreationImpl::CreateVideoDecoder( + PP_Instance instance, + PP_Resource graphics3d_id, + PP_VideoDecoder_Profile profile) { + return PPB_VideoDecoder_Impl::Create(instance, graphics3d_id, profile); +} + +PP_Resource ResourceCreationImpl::CreateVideoDestination( + PP_Instance instance) { + return 0; // Not supported in-process. +} + +PP_Resource ResourceCreationImpl::CreateVideoSource( + PP_Instance instance) { + return 0; // Not supported in-process. +} + +PP_Resource ResourceCreationImpl::CreateWheelInputEvent( + PP_Instance instance, + PP_TimeTicks time_stamp, + uint32_t modifiers, + const PP_FloatPoint* wheel_delta, + const PP_FloatPoint* wheel_ticks, + PP_Bool scroll_by_page) { + return PPB_InputEvent_Shared::CreateWheelInputEvent( + ::ppapi::OBJECT_IS_IMPL, instance, time_stamp, modifiers, + wheel_delta, wheel_ticks, scroll_by_page); +} + +PP_Resource ResourceCreationImpl::CreateX509CertificatePrivate( + PP_Instance instance) { + return PPB_X509Certificate_Private_Impl::CreateResource(instance); +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/resource_creation_impl.h b/content/renderer/pepper/resource_creation_impl.h new file mode 100644 index 0000000..8ed7ac4 --- /dev/null +++ b/content/renderer/pepper/resource_creation_impl.h @@ -0,0 +1,151 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_PEPPER_RESOURCE_CREATION_IMPL_H_ +#define CONTENT_RENDERER_PEPPER_RESOURCE_CREATION_IMPL_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "ppapi/thunk/resource_creation_api.h" + +namespace webkit { +namespace ppapi { + +class PluginInstanceImpl; + +// This is an abstract class. ResourceCreationAPI functions that implement +// "old-style" resources are handled here. See +// content/renderer/pepper/pepper_in_process_resource_creation.h for functions +// that implement "new-style" resources. +class ResourceCreationImpl : public ::ppapi::thunk::ResourceCreationAPI { + public: + explicit ResourceCreationImpl(PluginInstanceImpl* instance); + virtual ~ResourceCreationImpl(); + + // ResourceCreationAPI implementation. + virtual PP_Resource CreateAudio(PP_Instance instance, + PP_Resource config_id, + PPB_Audio_Callback audio_callback, + void* user_data) OVERRIDE; + virtual PP_Resource CreateAudioTrusted(PP_Instance instance) OVERRIDE; + virtual PP_Resource CreateAudioConfig(PP_Instance instance, + PP_AudioSampleRate sample_rate, + uint32_t sample_frame_count) OVERRIDE; + virtual PP_Resource CreateAudioInput(PP_Instance instance) OVERRIDE; + virtual PP_Resource CreateBroker(PP_Instance instance) OVERRIDE; + virtual PP_Resource CreateBuffer(PP_Instance instance, + uint32_t size) OVERRIDE; + virtual PP_Resource CreateFileRef(PP_Instance instance, + PP_Resource file_system, + const char* path) OVERRIDE; + virtual PP_Resource CreateFileRef( + const ::ppapi::PPB_FileRef_CreateInfo& serialized) OVERRIDE; + virtual PP_Resource CreateFlashDRM(PP_Instance instance) OVERRIDE; + virtual PP_Resource CreateFlashFontFile( + PP_Instance instance, + const PP_BrowserFont_Trusted_Description* description, + PP_PrivateFontCharset charset) OVERRIDE; + virtual PP_Resource CreateFlashMenu(PP_Instance instance, + const PP_Flash_Menu* menu_data) OVERRIDE; + virtual PP_Resource CreateFlashMessageLoop(PP_Instance instance) OVERRIDE; + virtual PP_Resource CreateGraphics3D(PP_Instance instance, + PP_Resource share_context, + const int32_t* attrib_list) OVERRIDE; + virtual PP_Resource CreateGraphics3DRaw(PP_Instance instance, + PP_Resource share_context, + const int32_t* attrib_list) OVERRIDE; + virtual PP_Resource CreateHostResolver(PP_Instance instance) OVERRIDE; + virtual PP_Resource CreateHostResolverPrivate(PP_Instance instance) OVERRIDE; + virtual PP_Resource CreateImageData(PP_Instance instance, + PP_ImageDataFormat format, + const PP_Size* size, + PP_Bool init_to_zero) OVERRIDE; + virtual PP_Resource CreateImageDataSimple(PP_Instance instance, + PP_ImageDataFormat format, + const PP_Size* size, + PP_Bool init_to_zero) OVERRIDE; + virtual PP_Resource CreateIMEInputEvent(PP_Instance instance, + PP_InputEvent_Type type, + PP_TimeTicks time_stamp, + struct PP_Var text, + uint32_t segment_number, + const uint32_t* segment_offsets, + int32_t target_segment, + uint32_t selection_start, + uint32_t selection_end) OVERRIDE; + virtual PP_Resource CreateIsolatedFileSystem(PP_Instance instance, + const char* fsid) OVERRIDE; + virtual PP_Resource CreateKeyboardInputEvent( + PP_Instance instance, + PP_InputEvent_Type type, + PP_TimeTicks time_stamp, + uint32_t modifiers, + uint32_t key_code, + PP_Var character_text) OVERRIDE; + virtual PP_Resource CreateMouseInputEvent( + PP_Instance instance, + PP_InputEvent_Type type, + PP_TimeTicks time_stamp, + uint32_t modifiers, + PP_InputEvent_MouseButton mouse_button, + const PP_Point* mouse_position, + int32_t click_count, + const PP_Point* mouse_movement) OVERRIDE; + virtual PP_Resource CreateNetAddressFromIPv4Address( + PP_Instance instance, + const PP_NetAddress_IPv4* ipv4_addr) OVERRIDE; + virtual PP_Resource CreateNetAddressFromIPv6Address( + PP_Instance instance, + const PP_NetAddress_IPv6* ipv6_addr) OVERRIDE; + virtual PP_Resource CreateNetAddressFromNetAddressPrivate( + PP_Instance instance, + const PP_NetAddress_Private& private_addr) OVERRIDE; + virtual PP_Resource CreateTouchInputEvent( + PP_Instance instance, + PP_InputEvent_Type type, + PP_TimeTicks time_stamp, + uint32_t modifiers) OVERRIDE; + virtual PP_Resource CreateNetworkMonitor( + PP_Instance instance, + PPB_NetworkMonitor_Callback callback, + void* user_data) OVERRIDE; + virtual PP_Resource CreateResourceArray(PP_Instance instance, + const PP_Resource elements[], + uint32_t size) OVERRIDE; + virtual PP_Resource CreateScrollbar(PP_Instance instance, + PP_Bool vertical) OVERRIDE; + virtual PP_Resource CreateTalk(PP_Instance instance) OVERRIDE; + virtual PP_Resource CreateTCPServerSocketPrivate( + PP_Instance instance) OVERRIDE; + virtual PP_Resource CreateTCPSocket(PP_Instance instance) OVERRIDE; + virtual PP_Resource CreateTCPSocketPrivate(PP_Instance instance) OVERRIDE; + virtual PP_Resource CreateUDPSocket(PP_Instance instance) OVERRIDE; + virtual PP_Resource CreateUDPSocketPrivate(PP_Instance instance) OVERRIDE; + virtual PP_Resource CreateVideoCapture(PP_Instance instance) OVERRIDE; + virtual PP_Resource CreateVideoDecoder( + PP_Instance instance, + PP_Resource graphics3d_id, + PP_VideoDecoder_Profile profile) OVERRIDE; + virtual PP_Resource CreateVideoDestination( + PP_Instance instance) OVERRIDE; + virtual PP_Resource CreateVideoSource( + PP_Instance instance) OVERRIDE; + virtual PP_Resource CreateWheelInputEvent( + PP_Instance instance, + PP_TimeTicks time_stamp, + uint32_t modifiers, + const PP_FloatPoint* wheel_delta, + const PP_FloatPoint* wheel_ticks, + PP_Bool scroll_by_page) OVERRIDE; + virtual PP_Resource CreateX509CertificatePrivate( + PP_Instance instance) OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(ResourceCreationImpl); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_RESOURCE_CREATION_IMPL_H_ diff --git a/content/renderer/pepper/resource_helper.cc b/content/renderer/pepper/resource_helper.cc new file mode 100644 index 0000000..c665223 --- /dev/null +++ b/content/renderer/pepper/resource_helper.cc @@ -0,0 +1,41 @@ +// 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 "content/renderer/pepper/resource_helper.h" + +#include "base/logging.h" +#include "content/renderer/pepper/host_globals.h" +#include "content/renderer/pepper/plugin_module.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" +#include "ppapi/shared_impl/resource.h" + +namespace webkit { +namespace ppapi { + +// static +PluginInstanceImpl* ResourceHelper::GetPluginInstance( + const ::ppapi::Resource* resource) { + return PPInstanceToPluginInstance(resource->pp_instance()); +} + +PluginInstanceImpl* ResourceHelper::PPInstanceToPluginInstance( + PP_Instance instance) { + return HostGlobals::Get()->GetInstance(instance); +} + +PluginModule* ResourceHelper::GetPluginModule( + const ::ppapi::Resource* resource) { + PluginInstanceImpl* instance = GetPluginInstance(resource); + return instance ? instance->module() : NULL; +} + +PluginDelegate* ResourceHelper::GetPluginDelegate( + const ::ppapi::Resource* resource) { + PluginInstanceImpl* instance = GetPluginInstance(resource); + return instance ? instance->delegate() : NULL; +} + +} // namespace ppapi +} // namespace webkit + diff --git a/content/renderer/pepper/resource_helper.h b/content/renderer/pepper/resource_helper.h new file mode 100644 index 0000000..fa8483f --- /dev/null +++ b/content/renderer/pepper/resource_helper.h @@ -0,0 +1,53 @@ +// 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. + +#ifndef CONTENT_RENDERER_PEPPER_RESOURCE_HELPER_H_ +#define CONTENT_RENDERER_PEPPER_RESOURCE_HELPER_H_ + +#include "base/basictypes.h" +#include "ppapi/c/pp_instance.h" +#include "ppapi/c/pp_resource.h" + +namespace ppapi { +class Resource; +} + +namespace webkit { +namespace ppapi { + +class PluginInstanceImpl; +class PluginModule; +class PluginDelegate; + +// Helper functions for Resoruce implementations. +// +// This is specifically not designed to be a base class that derives from +// ppapi::Resource to avoid diamond inheritance if most of a resource class +// is implemented in the shared_impl (to share code with the proxy). +class ResourceHelper { + public: + // Returns the instance implementation object for the given resource, or NULL + // if the resource has outlived its instance. + static PluginInstanceImpl* GetPluginInstance( + const ::ppapi::Resource* resource); + + // Returns the module for the given resource, or NULL if the resource has + // outlived its instance. + static PluginModule* GetPluginModule(const ::ppapi::Resource* resource); + + // Returns the plugin delegate for the given resource, or NULL if the + // resource has outlived its instance. + static PluginDelegate* GetPluginDelegate(const ::ppapi::Resource* resource); + + // Returns the instance implementation object for the pp_instance. + static PluginInstanceImpl* PPInstanceToPluginInstance(PP_Instance instance); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(ResourceHelper); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_RESOURCE_IMPL_HELPER_H_ diff --git a/content/renderer/pepper/url_request_info_util.cc b/content/renderer/pepper/url_request_info_util.cc new file mode 100644 index 0000000..08fc076 --- /dev/null +++ b/content/renderer/pepper/url_request_info_util.cc @@ -0,0 +1,205 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/url_request_info_util.h" + +#include "base/logging.h" +#include "base/strings/string_util.h" +#include "content/renderer/pepper/common.h" +#include "content/renderer/pepper/plugin_module.h" +#include "content/renderer/pepper/ppb_file_ref_impl.h" +#include "content/renderer/pepper/resource_helper.h" +#include "net/http/http_util.h" +#include "ppapi/shared_impl/url_request_info_data.h" +#include "ppapi/shared_impl/var.h" +#include "ppapi/thunk/enter.h" +#include "third_party/WebKit/public/platform/WebData.h" +#include "third_party/WebKit/public/platform/WebHTTPBody.h" +#include "third_party/WebKit/public/platform/WebURL.h" +#include "third_party/WebKit/public/platform/WebURLRequest.h" +#include "third_party/WebKit/public/web/WebDocument.h" +#include "third_party/WebKit/public/web/WebFrame.h" +#include "url/gurl.h" +#include "url/url_util.h" +#include "webkit/child/weburlrequest_extradata_impl.h" + +using ppapi::URLRequestInfoData; +using ppapi::Resource; +using ppapi::thunk::EnterResourceNoLock; +using ppapi::thunk::PPB_FileRef_API; +using WebKit::WebData; +using WebKit::WebHTTPBody; +using WebKit::WebString; +using WebKit::WebFrame; +using WebKit::WebURL; +using WebKit::WebURLRequest; + +namespace webkit { +namespace ppapi { + +namespace { + +// Appends the file ref given the Resource pointer associated with it to the +// given HTTP body, returning true on success. +bool AppendFileRefToBody( + Resource* file_ref_resource, + int64_t start_offset, + int64_t number_of_bytes, + PP_Time expected_last_modified_time, + WebHTTPBody *http_body) { + // Get the underlying file ref impl. + if (!file_ref_resource) + return false; + PPB_FileRef_API* file_ref_api = file_ref_resource->AsPPB_FileRef_API(); + if (!file_ref_api) + return false; + const PPB_FileRef_Impl* file_ref = + static_cast<PPB_FileRef_Impl*>(file_ref_api); + + PluginDelegate* plugin_delegate = + ResourceHelper::GetPluginDelegate(file_ref_resource); + if (!plugin_delegate) + return false; + + base::FilePath platform_path; + switch (file_ref->GetFileSystemType()) { + case PP_FILESYSTEMTYPE_LOCALTEMPORARY: + case PP_FILESYSTEMTYPE_LOCALPERSISTENT: + // TODO(kinuko): remove this sync IPC when we fully support + // AppendURLRange for FileSystem URL. + plugin_delegate->SyncGetFileSystemPlatformPath( + file_ref->GetFileSystemURL(), &platform_path); + break; + case PP_FILESYSTEMTYPE_EXTERNAL: + platform_path = file_ref->GetSystemPath(); + break; + default: + NOTREACHED(); + } + http_body->appendFileRange( + platform_path.AsUTF16Unsafe(), + start_offset, + number_of_bytes, + expected_last_modified_time); + return true; +} + +// Checks that the request data is valid. Returns false on failure. Note that +// method and header validation is done by the URL loader when the request is +// opened, and any access errors are returned asynchronously. +bool ValidateURLRequestData(const ::ppapi::URLRequestInfoData& data) { + if (data.prefetch_buffer_lower_threshold < 0 || + data.prefetch_buffer_upper_threshold < 0 || + data.prefetch_buffer_upper_threshold <= + data.prefetch_buffer_lower_threshold) { + return false; + } + return true; +} + +// Ensures that the file_ref members of the given request info data are +// populated from the resource IDs. Returns true on success. +bool EnsureFileRefObjectsPopulated(::ppapi::URLRequestInfoData* data) { + // Get the Resource objects for any file refs with only host resource (this + // is the state of the request as it comes off IPC). + for (size_t i = 0; i < data->body.size(); ++i) { + URLRequestInfoData::BodyItem& item = data->body[i]; + if (item.is_file && !item.file_ref.get()) { + EnterResourceNoLock<PPB_FileRef_API> enter( + item.file_ref_host_resource.host_resource(), false); + if (!enter.succeeded()) + return false; + item.file_ref = enter.resource(); + } + } + return true; +} + +} // namespace + +bool CreateWebURLRequest(::ppapi::URLRequestInfoData* data, + WebFrame* frame, + WebURLRequest* dest) { + // In the out-of-process case, we've received the URLRequestInfoData + // from the untrusted plugin and done no validation on it. We need to be + // sure it's not being malicious by checking everything for consistency. + if (!ValidateURLRequestData(*data) || !EnsureFileRefObjectsPopulated(data)) + return false; + + dest->initialize(); + dest->setTargetType(WebURLRequest::TargetIsObject); + dest->setURL(frame->document().completeURL(WebString::fromUTF8( + data->url))); + dest->setDownloadToFile(data->stream_to_file); + dest->setReportUploadProgress(data->record_upload_progress); + + if (!data->method.empty()) + dest->setHTTPMethod(WebString::fromUTF8(data->method)); + + dest->setFirstPartyForCookies(frame->document().firstPartyForCookies()); + + const std::string& headers = data->headers; + if (!headers.empty()) { + net::HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\n\r"); + while (it.GetNext()) { + dest->addHTTPHeaderField( + WebString::fromUTF8(it.name()), + WebString::fromUTF8(it.values())); + } + } + + // Append the upload data. + if (!data->body.empty()) { + WebHTTPBody http_body; + http_body.initialize(); + for (size_t i = 0; i < data->body.size(); ++i) { + const URLRequestInfoData::BodyItem& item = data->body[i]; + if (item.is_file) { + if (!AppendFileRefToBody(item.file_ref.get(), + item.start_offset, + item.number_of_bytes, + item.expected_last_modified_time, + &http_body)) + return false; + } else { + DCHECK(!item.data.empty()); + http_body.appendData(WebData(item.data)); + } + } + dest->setHTTPBody(http_body); + } + + // Add the "Referer" header if there is a custom referrer. Such requests + // require universal access. For all other requests, "Referer" will be set + // after header security checks are done in AssociatedURLLoader. + if (data->has_custom_referrer_url && !data->custom_referrer_url.empty()) + frame->setReferrerForRequest(*dest, GURL(data->custom_referrer_url)); + + if (data->has_custom_content_transfer_encoding && + !data->custom_content_transfer_encoding.empty()) { + dest->addHTTPHeaderField( + WebString::fromUTF8("Content-Transfer-Encoding"), + WebString::fromUTF8(data->custom_content_transfer_encoding)); + } + + if (data->has_custom_user_agent) { + dest->setExtraData(new webkit_glue::WebURLRequestExtraDataImpl( + WebKit::WebReferrerPolicyDefault, // Ignored. + WebString::fromUTF8(data->custom_user_agent))); + } + + return true; +} + +bool URLRequestRequiresUniversalAccess( + const ::ppapi::URLRequestInfoData& data) { + return + data.has_custom_referrer_url || + data.has_custom_content_transfer_encoding || + data.has_custom_user_agent || + url_util::FindAndCompareScheme(data.url, "javascript", NULL); +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/url_request_info_util.h b/content/renderer/pepper/url_request_info_util.h new file mode 100644 index 0000000..88cabc8 --- /dev/null +++ b/content/renderer/pepper/url_request_info_util.h @@ -0,0 +1,38 @@ +// 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. + +#ifndef CONTENT_RENDERER_PEPPER_URL_REQUEST_INFO_UTIL_H_ +#define CONTENT_RENDERER_PEPPER_URL_REQUEST_INFO_UTIL_H_ + +#include "base/memory/ref_counted.h" +#include "content/common/content_export.h" + +namespace ppapi { +struct URLRequestInfoData; +} + +namespace WebKit { +class WebFrame; +class WebURLRequest; +} + +namespace webkit { +namespace ppapi { + +// Creates the WebKit URL request from the current request info. Returns true +// on success, false if the request is invalid (in which case *dest may be +// partially initialized). Any upload files with only resource IDs (no file ref +// pointers) will be populated by this function on success. +CONTENT_EXPORT bool CreateWebURLRequest(::ppapi::URLRequestInfoData* data, + WebKit::WebFrame* frame, + WebKit::WebURLRequest* dest); + +// Returns true if universal access is required to use the given request. +CONTENT_EXPORT bool URLRequestRequiresUniversalAccess( + const ::ppapi::URLRequestInfoData& data); + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_PPB_URL_REQUEST_INFO_UTIL_H_ diff --git a/content/renderer/pepper/url_response_info_util.cc b/content/renderer/pepper/url_response_info_util.cc index ddfa0f2..f07a9b9 100644 --- a/content/renderer/pepper/url_response_info_util.cc +++ b/content/renderer/pepper/url_response_info_util.cc @@ -5,13 +5,13 @@ #include "content/renderer/pepper/url_response_info_util.h" #include "base/files/file_path.h" +#include "content/renderer/pepper/ppb_file_ref_impl.h" #include "ppapi/shared_impl/url_response_info_data.h" #include "third_party/WebKit/public/platform/WebCString.h" #include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.h" #include "third_party/WebKit/public/platform/WebString.h" #include "third_party/WebKit/public/platform/WebURL.h" #include "third_party/WebKit/public/platform/WebURLResponse.h" -#include "webkit/plugins/ppapi/ppb_file_ref_impl.h" using webkit::ppapi::PPB_FileRef_Impl; using WebKit::WebHTTPHeaderVisitor; diff --git a/content/renderer/pepper/usb_key_code_conversion.cc b/content/renderer/pepper/usb_key_code_conversion.cc new file mode 100644 index 0000000..2640417 --- /dev/null +++ b/content/renderer/pepper/usb_key_code_conversion.cc @@ -0,0 +1,23 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/usb_key_code_conversion.h" + +#include "build/build_config.h" + +using WebKit::WebKeyboardEvent; + +namespace webkit { +namespace ppapi { + +#if !defined(OS_LINUX) && !defined(OS_MACOSX) && !defined(OS_WIN) + +uint32_t UsbKeyCodeForKeyboardEvent(const WebKeyboardEvent& key_event) { + return 0; +} + +#endif + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/usb_key_code_conversion.h b/content/renderer/pepper/usb_key_code_conversion.h new file mode 100644 index 0000000..459e662 --- /dev/null +++ b/content/renderer/pepper/usb_key_code_conversion.h @@ -0,0 +1,27 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_PEPPER_USB_KEY_CODE_CONVERSION_H_ +#define CONTENT_RENDERER_PEPPER_USB_KEY_CODE_CONVERSION_H_ + +#include "ppapi/c/pp_stdint.h" + +namespace WebKit { +class WebKeyboardEvent; +} // namespace WebKit + +namespace webkit { +namespace ppapi { + +// Returns a 32-bit "USB Key Code" for the key identifier by the supplied +// WebKeyboardEvent. The supplied event must be a KeyDown or KeyUp. +// The code consists of the USB Page (in the high-order 16-bit word) and +// USB Usage Id of the key. If no translation can be performed then zero +// is returned. +uint32_t UsbKeyCodeForKeyboardEvent(const WebKit::WebKeyboardEvent& key_event); + +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_USB_KEY_CODE_CONVERSION_H_ diff --git a/content/renderer/pepper/usb_key_code_conversion_linux.cc b/content/renderer/pepper/usb_key_code_conversion_linux.cc new file mode 100644 index 0000000..fd5e2b1 --- /dev/null +++ b/content/renderer/pepper/usb_key_code_conversion_linux.cc @@ -0,0 +1,32 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/usb_key_code_conversion.h" + +#include "base/basictypes.h" +#include "third_party/WebKit/public/web/WebInputEvent.h" + +using WebKit::WebKeyboardEvent; + +namespace webkit { +namespace ppapi { + +namespace { + +#define USB_KEYMAP(usb, xkb, win, mac) {usb, xkb} +#include "ui/base/keycodes/usb_keycode_map.h" +#undef USB_KEYMAP + +} // anonymous namespace + +uint32_t UsbKeyCodeForKeyboardEvent(const WebKeyboardEvent& key_event) { + // TODO(garykac): This code assumes that on Linux we're receiving events via + // the XKB driver. We should detect between "XKB", "kbd" and "evdev" at + // run-time and re-map accordingly, but that's not possible here, inside the + // sandbox. + return NativeKeycodeToUsbKeycode(key_event.nativeKeyCode); +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/usb_key_code_conversion_mac.cc b/content/renderer/pepper/usb_key_code_conversion_mac.cc new file mode 100644 index 0000000..4f55366 --- /dev/null +++ b/content/renderer/pepper/usb_key_code_conversion_mac.cc @@ -0,0 +1,28 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/usb_key_code_conversion.h" + +#include "base/basictypes.h" +#include "third_party/WebKit/public/web/WebInputEvent.h" + +using WebKit::WebKeyboardEvent; + +namespace webkit { +namespace ppapi { + +namespace { + +#define USB_KEYMAP(usb, xkb, win, mac) {usb, mac} +#include "ui/base/keycodes/usb_keycode_map.h" +#undef USB_KEYMAP + +} // anonymous namespace + +uint32_t UsbKeyCodeForKeyboardEvent(const WebKeyboardEvent& key_event) { + return NativeKeycodeToUsbKeycode(key_event.nativeKeyCode); +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/usb_key_code_conversion_win.cc b/content/renderer/pepper/usb_key_code_conversion_win.cc new file mode 100644 index 0000000..5ef1d81 --- /dev/null +++ b/content/renderer/pepper/usb_key_code_conversion_win.cc @@ -0,0 +1,33 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/usb_key_code_conversion.h" + +#include "base/basictypes.h" +#include "third_party/WebKit/public/web/WebInputEvent.h" + +using WebKit::WebKeyboardEvent; + +namespace webkit { +namespace ppapi { + +namespace { + +#define USB_KEYMAP(usb, xkb, win, mac) {usb, win} +#include "ui/base/keycodes/usb_keycode_map.h" +#undef USB_KEYMAP + +} // anonymous namespace + +uint32_t UsbKeyCodeForKeyboardEvent(const WebKeyboardEvent& key_event) { + // Extract the scancode and extended bit from the native key event's lParam. + int scancode = (key_event.nativeKeyCode >> 16) & 0x000000FF; + if ((key_event.nativeKeyCode & (1 << 24)) != 0) + scancode |= 0xe000; + + return NativeKeycodeToUsbKeycode(scancode); +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/v8_var_converter.cc b/content/renderer/pepper/v8_var_converter.cc new file mode 100644 index 0000000..202a7cc --- /dev/null +++ b/content/renderer/pepper/v8_var_converter.cc @@ -0,0 +1,470 @@ +// Copyright (c) 2013 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 "content/renderer/pepper/v8_var_converter.h" + +#include <map> +#include <stack> +#include <string> + +#include "base/containers/hash_tables.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "content/renderer/pepper/host_array_buffer_var.h" +#include "ppapi/shared_impl/array_var.h" +#include "ppapi/shared_impl/dictionary_var.h" +#include "ppapi/shared_impl/var.h" +#include "ppapi/shared_impl/var_tracker.h" +#include "third_party/WebKit/public/platform/WebArrayBuffer.h" + +using ppapi::ArrayBufferVar; +using ppapi::ArrayVar; +using ppapi::DictionaryVar; +using ppapi::ScopedPPVar; +using ppapi::StringVar; +using std::make_pair; + +namespace { + +template <class T> +struct StackEntry { + StackEntry(T v) : val(v), sentinel(false) {} + T val; + // Used to track parent nodes on the stack while traversing the graph. + bool sentinel; +}; + +struct HashedHandle { + HashedHandle(v8::Handle<v8::Object> h) : handle(h) {} + size_t hash() const { return handle->GetIdentityHash(); } + bool operator==(const HashedHandle& h) const { return handle == h.handle; } + bool operator<(const HashedHandle& h) const { return hash() < h.hash(); } + v8::Handle<v8::Object> handle; +}; + +} // namespace + +namespace BASE_HASH_NAMESPACE { +#if defined(COMPILER_GCC) +template <> +struct hash<HashedHandle> { + size_t operator()(const HashedHandle& handle) const { + return handle.hash(); + } +}; +#elif defined(COMPILER_MSVC) +inline size_t hash_value(const HashedHandle& handle) { + return handle.hash(); +} +#endif +} // namespace BASE_HASH_NAMESPACE + +namespace webkit { +namespace ppapi { +namespace V8VarConverter { + +namespace { + +// Maps PP_Var IDs to the V8 value handle they correspond to. +typedef base::hash_map<int64_t, v8::Handle<v8::Value> > VarHandleMap; +typedef base::hash_set<int64_t> ParentVarSet; + +// Maps V8 value handles to the PP_Var they correspond to. +typedef base::hash_map<HashedHandle, ScopedPPVar> HandleVarMap; +typedef base::hash_set<HashedHandle> ParentHandleSet; + +// Returns a V8 value which corresponds to a given PP_Var. If |var| is a +// reference counted PP_Var type, and it exists in |visited_ids|, the V8 value +// associated with it in the map will be returned, otherwise a new V8 value will +// be created and added to the map. |did_create| indicates whether a new v8 +// value was created as a result of calling the function. +bool GetOrCreateV8Value(const PP_Var& var, + v8::Handle<v8::Value>* result, + bool* did_create, + VarHandleMap* visited_ids, + ParentVarSet* parent_ids) { + *did_create = false; + + if (::ppapi::VarTracker::IsVarTypeRefcounted(var.type)) { + if (parent_ids->count(var.value.as_id) != 0) + return false; + VarHandleMap::iterator it = visited_ids->find(var.value.as_id); + if (it != visited_ids->end()) { + *result = it->second; + return true; + } + } + + switch (var.type) { + case PP_VARTYPE_UNDEFINED: + *result = v8::Undefined(); + break; + case PP_VARTYPE_NULL: + *result = v8::Null(); + break; + case PP_VARTYPE_BOOL: + *result = (var.value.as_bool == PP_TRUE) ? v8::True() : v8::False(); + break; + case PP_VARTYPE_INT32: + *result = v8::Integer::New(var.value.as_int); + break; + case PP_VARTYPE_DOUBLE: + *result = v8::Number::New(var.value.as_double); + break; + case PP_VARTYPE_STRING: { + StringVar* string = StringVar::FromPPVar(var); + if (!string) { + NOTREACHED(); + result->Clear(); + return false; + } + const std::string& value = string->value(); + // Create a string object rather than a string primitive. This allows us + // to have multiple references to the same string in javascript, which + // matches the reference behavior of PP_Vars. + *result = v8::String::New(value.c_str(), value.size())->ToObject(); + break; + } + case PP_VARTYPE_ARRAY_BUFFER: { + ArrayBufferVar* buffer = ArrayBufferVar::FromPPVar(var); + if (!buffer) { + NOTREACHED(); + result->Clear(); + return false; + } + HostArrayBufferVar* host_buffer = + static_cast<HostArrayBufferVar*>(buffer); + *result = + v8::Local<v8::Value>::New(host_buffer->webkit_buffer().toV8Value()); + break; + } + case PP_VARTYPE_ARRAY: + *result = v8::Array::New(); + break; + case PP_VARTYPE_DICTIONARY: + *result = v8::Object::New(); + break; + case PP_VARTYPE_OBJECT: + NOTREACHED(); + result->Clear(); + return false; + } + + *did_create = true; + if (::ppapi::VarTracker::IsVarTypeRefcounted(var.type)) + (*visited_ids)[var.value.as_id] = *result; + return true; +} + +// For a given V8 value handle, this returns a PP_Var which corresponds to it. +// If the handle already exists in |visited_handles|, the PP_Var associated with +// it will be returned, otherwise a new V8 value will be created and added to +// the map. |did_create| indicates if a new PP_Var was created as a result of +// calling the function. +bool GetOrCreateVar(v8::Handle<v8::Value> val, + PP_Var* result, + bool* did_create, + HandleVarMap* visited_handles, + ParentHandleSet* parent_handles) { + CHECK(!val.IsEmpty()); + *did_create = false; + + // Even though every v8 string primitive encountered will be a unique object, + // we still add them to |visited_handles| so that the corresponding string + // PP_Var created will be properly refcounted. + if (val->IsObject() || val->IsString()) { + if (parent_handles->count(HashedHandle(val->ToObject())) != 0) + return false; + + HandleVarMap::const_iterator it = visited_handles->find( + HashedHandle(val->ToObject())); + if (it != visited_handles->end()) { + *result = it->second.get(); + return true; + } + } + + if (val->IsUndefined()) { + *result = PP_MakeUndefined(); + } else if (val->IsNull()) { + *result = PP_MakeNull(); + } else if (val->IsBoolean() || val->IsBooleanObject()) { + *result = PP_MakeBool(PP_FromBool(val->ToBoolean()->Value())); + } else if (val->IsInt32()) { + *result = PP_MakeInt32(val->ToInt32()->Value()); + } else if (val->IsNumber() || val->IsNumberObject()) { + *result = PP_MakeDouble(val->ToNumber()->Value()); + } else if (val->IsString() || val->IsStringObject()) { + v8::String::Utf8Value utf8(val->ToString()); + *result = StringVar::StringToPPVar(std::string(*utf8, utf8.length())); + } else if (val->IsArray()) { + *result = (new ArrayVar())->GetPPVar(); + } else if (val->IsObject()) { + scoped_ptr<WebKit::WebArrayBuffer> web_array_buffer( + WebKit::WebArrayBuffer::createFromV8Value(val)); + if (web_array_buffer.get()) { + scoped_refptr<HostArrayBufferVar> buffer_var(new HostArrayBufferVar( + *web_array_buffer)); + *result = buffer_var->GetPPVar(); + } else { + *result = (new DictionaryVar())->GetPPVar(); + } + } else { + // Silently ignore the case where we can't convert to a Var as we may + // be trying to convert a type that doesn't have a corresponding + // PP_Var type. + return true; + } + + *did_create = true; + if (val->IsObject() || val->IsString()) { + visited_handles->insert(make_pair( + HashedHandle(val->ToObject()), + ScopedPPVar(ScopedPPVar::PassRef(), *result))); + } + return true; +} + +bool CanHaveChildren(PP_Var var) { + return var.type == PP_VARTYPE_ARRAY || var.type == PP_VARTYPE_DICTIONARY; +} + +} // namespace + +// To/FromV8Value use a stack-based DFS search to traverse V8/Var graph. Each +// iteration, the top node on the stack examined. If the node has not been +// visited yet (i.e. sentinel == false) then it is added to the list of parents +// which contains all of the nodes on the path from the start node to the +// current node. Each of the current nodes children are examined. If they appear +// in the list of parents it means we have a cycle and we return NULL. +// Otherwise, if they can have children, we add them to the stack. If the +// node at the top of the stack has already been visited, then we pop it off the +// stack and erase it from the list of parents. +// static +bool ToV8Value(const PP_Var& var, + v8::Handle<v8::Context> context, + v8::Handle<v8::Value>* result) { + v8::Context::Scope context_scope(context); + v8::HandleScope handle_scope; + + VarHandleMap visited_ids; + ParentVarSet parent_ids; + + std::stack<StackEntry<PP_Var> > stack; + stack.push(StackEntry<PP_Var>(var)); + v8::Handle<v8::Value> root; + bool is_root = true; + + while (!stack.empty()) { + const PP_Var& current_var = stack.top().val; + v8::Handle<v8::Value> current_v8; + + if (stack.top().sentinel) { + stack.pop(); + if (CanHaveChildren(current_var)) + parent_ids.erase(current_var.value.as_id); + continue; + } else { + stack.top().sentinel = true; + } + + bool did_create = false; + if (!GetOrCreateV8Value(current_var, ¤t_v8, &did_create, + &visited_ids, &parent_ids)) { + return false; + } + + if (is_root) { + is_root = false; + root = current_v8; + } + + // Add child nodes to the stack. + if (current_var.type == PP_VARTYPE_ARRAY) { + parent_ids.insert(current_var.value.as_id); + ArrayVar* array_var = ArrayVar::FromPPVar(current_var); + if (!array_var) { + NOTREACHED(); + return false; + } + DCHECK(current_v8->IsArray()); + v8::Handle<v8::Array> v8_array = current_v8.As<v8::Array>(); + + for (size_t i = 0; i < array_var->elements().size(); ++i) { + const PP_Var& child_var = array_var->elements()[i].get(); + v8::Handle<v8::Value> child_v8; + if (!GetOrCreateV8Value(child_var, &child_v8, &did_create, + &visited_ids, &parent_ids)) { + return false; + } + if (did_create && CanHaveChildren(child_var)) + stack.push(child_var); + v8::TryCatch try_catch; + v8_array->Set(static_cast<uint32>(i), child_v8); + if (try_catch.HasCaught()) { + LOG(ERROR) << "Setter for index " << i << " threw an exception."; + return false; + } + } + } else if (current_var.type == PP_VARTYPE_DICTIONARY) { + parent_ids.insert(current_var.value.as_id); + DictionaryVar* dict_var = DictionaryVar::FromPPVar(current_var); + if (!dict_var) { + NOTREACHED(); + return false; + } + DCHECK(current_v8->IsObject()); + v8::Handle<v8::Object> v8_object = current_v8->ToObject(); + + for (DictionaryVar::KeyValueMap::const_iterator iter = + dict_var->key_value_map().begin(); + iter != dict_var->key_value_map().end(); + ++iter) { + const std::string& key = iter->first; + const PP_Var& child_var = iter->second.get(); + v8::Handle<v8::Value> child_v8; + if (!GetOrCreateV8Value(child_var, &child_v8, &did_create, + &visited_ids, &parent_ids)) { + return false; + } + if (did_create && CanHaveChildren(child_var)) + stack.push(child_var); + v8::TryCatch try_catch; + v8_object->Set(v8::String::New(key.c_str(), key.length()), child_v8); + if (try_catch.HasCaught()) { + LOG(ERROR) << "Setter for property " << key.c_str() << " threw an " + << "exception."; + return false; + } + } + } + } + + *result = handle_scope.Close(root); + return true; +} + +bool FromV8Value(v8::Handle<v8::Value> val, + v8::Handle<v8::Context> context, + PP_Var* result) { + v8::Context::Scope context_scope(context); + v8::HandleScope handle_scope; + + HandleVarMap visited_handles; + ParentHandleSet parent_handles; + + std::stack<StackEntry<v8::Handle<v8::Value> > > stack; + stack.push(StackEntry<v8::Handle<v8::Value> >(val)); + ScopedPPVar root; + bool is_root = true; + + while (!stack.empty()) { + v8::Handle<v8::Value> current_v8 = stack.top().val; + PP_Var current_var; + + if (stack.top().sentinel) { + stack.pop(); + if (current_v8->IsObject()) + parent_handles.erase(HashedHandle(current_v8->ToObject())); + continue; + } else { + stack.top().sentinel = true; + } + + bool did_create = false; + if (!GetOrCreateVar(current_v8, ¤t_var, &did_create, + &visited_handles, &parent_handles)) { + return false; + } + + if (is_root) { + is_root = false; + root = current_var; + } + + // Add child nodes to the stack. + if (current_var.type == PP_VARTYPE_ARRAY) { + DCHECK(current_v8->IsArray()); + v8::Handle<v8::Array> v8_array = current_v8.As<v8::Array>(); + parent_handles.insert(HashedHandle(v8_array)); + + ArrayVar* array_var = ArrayVar::FromPPVar(current_var); + if (!array_var) { + NOTREACHED(); + return false; + } + + for (uint32 i = 0; i < v8_array->Length(); ++i) { + v8::TryCatch try_catch; + v8::Handle<v8::Value> child_v8 = v8_array->Get(i); + if (try_catch.HasCaught()) + return false; + + if (!v8_array->HasRealIndexedProperty(i)) + continue; + + PP_Var child_var; + if (!GetOrCreateVar(child_v8, &child_var, &did_create, + &visited_handles, &parent_handles)) { + return false; + } + if (did_create && child_v8->IsObject()) + stack.push(child_v8); + + array_var->Set(i, child_var); + } + } else if (current_var.type == PP_VARTYPE_DICTIONARY) { + DCHECK(current_v8->IsObject()); + v8::Handle<v8::Object> v8_object = current_v8->ToObject(); + parent_handles.insert(HashedHandle(v8_object)); + + DictionaryVar* dict_var = DictionaryVar::FromPPVar(current_var); + if (!dict_var) { + NOTREACHED(); + return false; + } + + v8::Handle<v8::Array> property_names(v8_object->GetOwnPropertyNames()); + for (uint32 i = 0; i < property_names->Length(); ++i) { + v8::Handle<v8::Value> key(property_names->Get(i)); + + // Extend this test to cover more types as necessary and if sensible. + if (!key->IsString() && !key->IsNumber()) { + NOTREACHED() << "Key \"" << *v8::String::AsciiValue(key) << "\" " + "is neither a string nor a number"; + return false; + } + + // Skip all callbacks: crbug.com/139933 + if (v8_object->HasRealNamedCallbackProperty(key->ToString())) + continue; + + v8::String::Utf8Value name_utf8(key->ToString()); + + v8::TryCatch try_catch; + v8::Handle<v8::Value> child_v8 = v8_object->Get(key); + if (try_catch.HasCaught()) + return false; + + PP_Var child_var; + if (!GetOrCreateVar(child_v8, &child_var, &did_create, + &visited_handles, &parent_handles)) { + return false; + } + if (did_create && child_v8->IsObject()) + stack.push(child_v8); + + bool success = dict_var->SetWithStringKey( + std::string(*name_utf8, name_utf8.length()), child_var); + DCHECK(success); + } + } + } + *result = root.Release(); + return true; +} + +} // namespace V8VarConverter +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/pepper/v8_var_converter.h b/content/renderer/pepper/v8_var_converter.h new file mode 100644 index 0000000..2a07bfb --- /dev/null +++ b/content/renderer/pepper/v8_var_converter.h @@ -0,0 +1,35 @@ +// Copyright (c) 2013 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 CONTENT_RENDERER_PEPPER_V8_VAR_CONVERTER_H +#define CONTENT_RENDERER_PEPPER_V8_VAR_CONVERTER_H + + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "ppapi/c/pp_var.h" +#include "v8/include/v8.h" +#include "content/common/content_export.h" + +namespace webkit { +namespace ppapi { +namespace V8VarConverter { + +// Converts the given PP_Var to a v8::Value. True is returned upon success. +bool CONTENT_EXPORT ToV8Value(const PP_Var& var, + v8::Handle<v8::Context> context, + v8::Handle<v8::Value>* result); +// Converts the given v8::Value to a PP_Var. True is returned upon success. +// Every PP_Var in the reference graph of which |result| is apart will have +// a refcount equal to the number of references to it in the graph. |result| +// will have one additional reference. +bool CONTENT_EXPORT FromV8Value(v8::Handle<v8::Value> val, + v8::Handle<v8::Context> context, + PP_Var* result); + +} // namespace V8VarConverter +} // namespace ppapi +} // namespace webkit + +#endif // CONTENT_RENDERER_PEPPER_V8_VAR_CONVERTER_H diff --git a/content/renderer/pepper/v8_var_converter_unittest.cc b/content/renderer/pepper/v8_var_converter_unittest.cc new file mode 100644 index 0000000..794c665 --- /dev/null +++ b/content/renderer/pepper/v8_var_converter_unittest.cc @@ -0,0 +1,387 @@ +// Copyright (c) 2012 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 "content/renderer/pepper/v8_var_converter.h" + +#include <cmath> + +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/values.h" +#include "ppapi/c/pp_bool.h" +#include "ppapi/c/pp_var.h" +#include "ppapi/shared_impl/array_var.h" +#include "ppapi/shared_impl/dictionary_var.h" +#include "ppapi/shared_impl/ppapi_globals.h" +#include "ppapi/shared_impl/proxy_lock.h" +#include "ppapi/shared_impl/scoped_pp_var.h" +#include "ppapi/shared_impl/test_globals.h" +#include "ppapi/shared_impl/unittest_utils.h" +#include "ppapi/shared_impl/var.h" +#include "ppapi/shared_impl/var_tracker.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "v8/include/v8.h" + +using ppapi::ArrayBufferVar; +using ppapi::ArrayVar; +using ppapi::DictionaryVar; +using ppapi::PpapiGlobals; +using ppapi::ProxyLock; +using ppapi::ScopedPPVar; +using ppapi::StringVar; +using ppapi::TestGlobals; +using ppapi::TestEqual; +using ppapi::VarTracker; + +namespace webkit { +namespace ppapi { + +namespace { + +// Maps PP_Var IDs to the V8 value handle they correspond to. +typedef base::hash_map<int64_t, v8::Handle<v8::Value> > VarHandleMap; + +bool Equals(const PP_Var& var, + v8::Handle<v8::Value> val, + VarHandleMap* visited_ids) { + if (::ppapi::VarTracker::IsVarTypeRefcounted(var.type)) { + VarHandleMap::iterator it = visited_ids->find(var.value.as_id); + if (it != visited_ids->end()) + return it->second == val; + (*visited_ids)[var.value.as_id] = val; + } + + if (val->IsUndefined()) { + return var.type == PP_VARTYPE_UNDEFINED; + } else if (val->IsNull()) { + return var.type == PP_VARTYPE_NULL; + } else if (val->IsBoolean() || val->IsBooleanObject()) { + return var.type == PP_VARTYPE_BOOL && + PP_FromBool(val->ToBoolean()->Value()) == var.value.as_bool; + } else if (val->IsInt32()) { + return var.type == PP_VARTYPE_INT32 && + val->ToInt32()->Value() == var.value.as_int; + } else if (val->IsNumber() || val->IsNumberObject()) { + return var.type == PP_VARTYPE_DOUBLE && + fabs(val->ToNumber()->Value() - var.value.as_double) <= 1.0e-4; + } else if (val->IsString() || val->IsStringObject()) { + if (var.type != PP_VARTYPE_STRING) + return false; + StringVar* string_var = StringVar::FromPPVar(var); + DCHECK(string_var); + v8::String::Utf8Value utf8(val->ToString()); + return std::string(*utf8, utf8.length()) == string_var->value(); + } else if (val->IsArray()) { + if (var.type != PP_VARTYPE_ARRAY) + return false; + ArrayVar* array_var = ArrayVar::FromPPVar(var); + DCHECK(array_var); + v8::Handle<v8::Array> v8_array = val.As<v8::Array>(); + if (v8_array->Length() != array_var->elements().size()) + return false; + for (uint32 i = 0; i < v8_array->Length(); ++i) { + v8::Handle<v8::Value> child_v8 = v8_array->Get(i); + if (!Equals(array_var->elements()[i].get(), child_v8, visited_ids)) + return false; + } + return true; + } else if (val->IsObject()) { + if (var.type == PP_VARTYPE_ARRAY_BUFFER) { + // TODO(raymes): Implement this when we have tests for array buffers. + NOTIMPLEMENTED(); + return false; + } else { + v8::Handle<v8::Object> v8_object = val->ToObject(); + if (var.type != PP_VARTYPE_DICTIONARY) + return false; + DictionaryVar* dict_var = DictionaryVar::FromPPVar(var); + DCHECK(dict_var); + v8::Handle<v8::Array> property_names(v8_object->GetOwnPropertyNames()); + if (property_names->Length() != dict_var->key_value_map().size()) + return false; + for (uint32 i = 0; i < property_names->Length(); ++i) { + v8::Handle<v8::Value> key(property_names->Get(i)); + + if (!key->IsString() && !key->IsNumber()) + return false; + v8::Handle<v8::Value> child_v8 = v8_object->Get(key); + + v8::String::Utf8Value name_utf8(key->ToString()); + ScopedPPVar release_key(ScopedPPVar::PassRef(), + StringVar::StringToPPVar( + std::string(*name_utf8, name_utf8.length()))); + if (!dict_var->HasKey(release_key.get())) + return false; + ScopedPPVar release_value(ScopedPPVar::PassRef(), + dict_var->Get(release_key.get())); + if (!Equals(release_value.get(), child_v8, visited_ids)) + return false; + } + return true; + } + } + return false; +} + +bool Equals(const PP_Var& var, + v8::Handle<v8::Value> val) { + VarHandleMap var_handle_map; + return Equals(var, val, &var_handle_map); +} + +class V8VarConverterTest : public testing::Test { + public: + V8VarConverterTest() + : isolate_(v8::Isolate::GetCurrent()) {} + virtual ~V8VarConverterTest() {} + + // testing::Test implementation. + virtual void SetUp() { + ProxyLock::Acquire(); + v8::HandleScope handle_scope(isolate_); + v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(); + context_.Reset(isolate_, v8::Context::New(isolate_, NULL, global)); + } + virtual void TearDown() { + context_.Dispose(); + ASSERT_TRUE(PpapiGlobals::Get()->GetVarTracker()->GetLiveVars().empty()); + ProxyLock::Release(); + } + + protected: + bool RoundTrip(const PP_Var& var, PP_Var* result) { + v8::HandleScope handle_scope(isolate_); + v8::Context::Scope context_scope(isolate_, context_); + v8::Local<v8::Context> context = + v8::Local<v8::Context>::New(isolate_, context_); + v8::Handle<v8::Value> v8_result; + if (!V8VarConverter::ToV8Value(var, context, &v8_result)) + return false; + if (!Equals(var, v8_result)) + return false; + if (!V8VarConverter::FromV8Value(v8_result, context, result)) + return false; + return true; + } + + // Assumes a ref for var. + bool RoundTripAndCompare(const PP_Var& var) { + ScopedPPVar expected(ScopedPPVar::PassRef(), var); + PP_Var actual_var; + if (!RoundTrip(expected.get(), &actual_var)) + return false; + ScopedPPVar actual(ScopedPPVar::PassRef(), actual_var); + return TestEqual(expected.get(), actual.get()); + } + + v8::Isolate* isolate_; + + // Context for the JavaScript in the test. + v8::Persistent<v8::Context> context_; + + private: + TestGlobals globals_; +}; + +} // namespace + +TEST_F(V8VarConverterTest, SimpleRoundTripTest) { + EXPECT_TRUE(RoundTripAndCompare(PP_MakeUndefined())); + EXPECT_TRUE(RoundTripAndCompare(PP_MakeNull())); + EXPECT_TRUE(RoundTripAndCompare(PP_MakeInt32(100))); + EXPECT_TRUE(RoundTripAndCompare(PP_MakeBool(PP_TRUE))); + EXPECT_TRUE(RoundTripAndCompare(PP_MakeDouble(53.75))); +} + +TEST_F(V8VarConverterTest, StringRoundTripTest) { + EXPECT_TRUE(RoundTripAndCompare(StringVar::StringToPPVar(""))); + EXPECT_TRUE(RoundTripAndCompare(StringVar::StringToPPVar("hello world!"))); +} + +TEST_F(V8VarConverterTest, ArrayBufferRoundTripTest) { + // TODO(raymes): Testing this here requires spinning up some of WebKit. + // Work out how to do this. +} + +TEST_F(V8VarConverterTest, DictionaryArrayRoundTripTest) { + // Empty array. + scoped_refptr<ArrayVar> array(new ArrayVar); + ScopedPPVar release_array(ScopedPPVar::PassRef(), array->GetPPVar()); + EXPECT_TRUE(RoundTripAndCompare(array->GetPPVar())); + + size_t index = 0; + + // Array with primitives. + array->Set(index++, PP_MakeUndefined()); + array->Set(index++, PP_MakeNull()); + array->Set(index++, PP_MakeInt32(100)); + array->Set(index++, PP_MakeBool(PP_FALSE)); + array->Set(index++, PP_MakeDouble(0.123)); + EXPECT_TRUE(RoundTripAndCompare(array->GetPPVar())); + + // Array with 2 references to the same string. + ScopedPPVar release_string( + ScopedPPVar::PassRef(), StringVar::StringToPPVar("abc")); + array->Set(index++, release_string.get()); + array->Set(index++, release_string.get()); + EXPECT_TRUE(RoundTripAndCompare(array->GetPPVar())); + + // Array with nested array that references the same string. + scoped_refptr<ArrayVar> array2(new ArrayVar); + ScopedPPVar release_array2(ScopedPPVar::PassRef(), array2->GetPPVar()); + array2->Set(0, release_string.get()); + array->Set(index++, release_array2.get()); + EXPECT_TRUE(RoundTripAndCompare(array->GetPPVar())); + + // Empty dictionary. + scoped_refptr<DictionaryVar> dictionary(new DictionaryVar); + ScopedPPVar release_dictionary(ScopedPPVar::PassRef(), + dictionary->GetPPVar()); + EXPECT_TRUE(RoundTripAndCompare(dictionary->GetPPVar())); + + // Dictionary with primitives. + dictionary->SetWithStringKey("1", PP_MakeUndefined()); + dictionary->SetWithStringKey("2", PP_MakeNull()); + dictionary->SetWithStringKey("3", PP_MakeInt32(-100)); + dictionary->SetWithStringKey("4", PP_MakeBool(PP_TRUE)); + dictionary->SetWithStringKey("5", PP_MakeDouble(-103.52)); + EXPECT_TRUE(RoundTripAndCompare(dictionary->GetPPVar())); + + // Dictionary with 2 references to the same string. + dictionary->SetWithStringKey("6", release_string.get()); + dictionary->SetWithStringKey("7", release_string.get()); + EXPECT_TRUE(RoundTripAndCompare(dictionary->GetPPVar())); + + // Dictionary with nested dictionary that references the same string. + scoped_refptr<DictionaryVar> dictionary2(new DictionaryVar); + ScopedPPVar release_dictionary2(ScopedPPVar::PassRef(), + dictionary2->GetPPVar()); + dictionary2->SetWithStringKey("abc", release_string.get()); + dictionary->SetWithStringKey("8", release_dictionary2.get()); + EXPECT_TRUE(RoundTripAndCompare(dictionary->GetPPVar())); + + // Array with dictionary. + array->Set(index++, release_dictionary.get()); + EXPECT_TRUE(RoundTripAndCompare(array->GetPPVar())); + + // Array with dictionary with array. + array2->Set(0, PP_MakeInt32(100)); + dictionary->SetWithStringKey("9", release_array2.get()); + EXPECT_TRUE(RoundTripAndCompare(array->GetPPVar())); +} + +TEST_F(V8VarConverterTest, Cycles) { + // Check that cycles aren't converted. + v8::HandleScope handle_scope(isolate_); + v8::Context::Scope context_scope(isolate_, context_); + v8::Local<v8::Context> context = + v8::Local<v8::Context>::New(isolate_, context_); + + // Var->V8 conversion. + { + scoped_refptr<DictionaryVar> dictionary(new DictionaryVar); + ScopedPPVar release_dictionary(ScopedPPVar::PassRef(), + dictionary->GetPPVar()); + scoped_refptr<ArrayVar> array(new ArrayVar); + ScopedPPVar release_array(ScopedPPVar::PassRef(), array->GetPPVar()); + + dictionary->SetWithStringKey("1", release_array.get()); + array->Set(0, release_dictionary.get()); + + v8::Handle<v8::Value> v8_result; + + // Array <-> dictionary cycle. + dictionary->SetWithStringKey("1", release_array.get()); + ASSERT_FALSE(V8VarConverter::ToV8Value(release_dictionary.get(), + context, &v8_result)); + // Break the cycle. + // TODO(raymes): We need some better machinery for releasing vars with + // cycles. Remove the code below once we have that. + dictionary->DeleteWithStringKey("1"); + + // Array with self reference. + array->Set(0, release_array.get()); + ASSERT_FALSE(V8VarConverter::ToV8Value(release_array.get(), + context, &v8_result)); + // Break the self reference. + array->Set(0, PP_MakeUndefined()); + } + + // V8->Var conversion. + { + v8::Handle<v8::Object> object = v8::Object::New(); + v8::Handle<v8::Array> array = v8::Array::New(); + + PP_Var var_result; + + // Array <-> dictionary cycle. + std::string key = "1"; + object->Set(v8::String::New(key.c_str(), key.length()), array); + array->Set(0, object); + + ASSERT_FALSE(V8VarConverter::FromV8Value(object, context, &var_result)); + + // Array with self reference. + array->Set(0, array); + ASSERT_FALSE(V8VarConverter::FromV8Value(array, context, &var_result)); + } +} + +TEST_F(V8VarConverterTest, StrangeDictionaryKeyTest) { + { + // Test keys with '.'. + scoped_refptr<DictionaryVar> dictionary(new DictionaryVar); + dictionary->SetWithStringKey(".", PP_MakeUndefined()); + dictionary->SetWithStringKey("x.y", PP_MakeUndefined()); + EXPECT_TRUE(RoundTripAndCompare(dictionary->GetPPVar())); + } + + { + // Test non-string key types. They should be cast to strings. + v8::HandleScope handle_scope(isolate_); + v8::Context::Scope context_scope(isolate_, context_); + + const char* source = "(function() {" + "return {" + "1: 'foo'," + "'2': 'bar'," + "true: 'baz'," + "false: 'qux'," + "null: 'quux'," + "undefined: 'oops'" + "};" + "})();"; + + v8::Handle<v8::Script> script(v8::Script::New(v8::String::New(source))); + v8::Handle<v8::Object> object = script->Run().As<v8::Object>(); + ASSERT_FALSE(object.IsEmpty()); + + PP_Var actual; + ASSERT_TRUE(V8VarConverter::FromV8Value(object, + v8::Local<v8::Context>::New(isolate_, context_), &actual)); + ScopedPPVar release_actual(ScopedPPVar::PassRef(), actual); + + scoped_refptr<DictionaryVar> expected(new DictionaryVar); + ScopedPPVar foo(ScopedPPVar::PassRef(), StringVar::StringToPPVar("foo")); + expected->SetWithStringKey("1", foo.get()); + ScopedPPVar bar(ScopedPPVar::PassRef(), StringVar::StringToPPVar("bar")); + expected->SetWithStringKey("2", bar.get()); + ScopedPPVar baz(ScopedPPVar::PassRef(), StringVar::StringToPPVar("baz")); + expected->SetWithStringKey("true", baz.get()); + ScopedPPVar qux(ScopedPPVar::PassRef(), StringVar::StringToPPVar("qux")); + expected->SetWithStringKey("false", qux.get()); + ScopedPPVar quux(ScopedPPVar::PassRef(), StringVar::StringToPPVar("quux")); + expected->SetWithStringKey("null", quux.get()); + ScopedPPVar oops(ScopedPPVar::PassRef(), StringVar::StringToPPVar("oops")); + expected->SetWithStringKey("undefined", oops.get()); + ScopedPPVar release_expected( + ScopedPPVar::PassRef(), expected->GetPPVar()); + + ASSERT_TRUE(TestEqual(release_expected.get(), release_actual.get())); + } +} + +} // namespace ppapi +} // namespace webkit diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc index e3976f6..fa49316 100644 --- a/content/renderer/render_widget.cc +++ b/content/renderer/render_widget.cc @@ -31,6 +31,7 @@ #include "content/renderer/gpu/mailbox_output_surface.h" #include "content/renderer/gpu/render_widget_compositor.h" #include "content/renderer/ime_event_guard.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" #include "content/renderer/render_process.h" #include "content/renderer/render_process_visibility_manager.h" #include "content/renderer/render_thread_impl.h" @@ -57,7 +58,6 @@ #include "ui/gfx/skia_util.h" #include "ui/gl/gl_switches.h" #include "ui/surface/transport_dib.h" -#include "webkit/plugins/ppapi/ppapi_plugin_instance_impl.h" #include "webkit/renderer/compositor_bindings/web_rendering_stats_impl.h" #include "webkit/renderer/cursor_utils.h" @@ -971,6 +971,7 @@ void RenderWidget::PaintRect(const gfx::Rect& rect, &optimized_copy_rect, &dib_scale_factor); if (optimized_instance) { +#if defined(ENABLE_PLUGINS) // This plugin can be optimize-painted and we can just ask it to paint // itself. We don't actually need the TransportDIB in this case. // @@ -1005,6 +1006,7 @@ void RenderWidget::PaintRect(const gfx::Rect& rect, if (!is_accelerated_compositing_active_) software_stats_.total_paint_time += paint_time; } +#endif } else { // Normal painting case. base::TimeTicks paint_begin_ticks; diff --git a/content/renderer/render_widget_fullscreen_pepper.cc b/content/renderer/render_widget_fullscreen_pepper.cc index 80a6128..1eb0ff9 100644 --- a/content/renderer/render_widget_fullscreen_pepper.cc +++ b/content/renderer/render_widget_fullscreen_pepper.cc @@ -14,6 +14,8 @@ #include "content/public/common/content_switches.h" #include "content/renderer/gpu/render_widget_compositor.h" #include "content/renderer/pepper/pepper_platform_context_3d_impl.h" +#include "content/renderer/pepper/plugin_delegate.h" +#include "content/renderer/pepper/ppapi_plugin_instance_impl.h" #include "content/renderer/render_thread_impl.h" #include "gpu/command_buffer/client/gles2_implementation.h" #include "skia/ext/platform_canvas.h" @@ -25,8 +27,6 @@ #include "third_party/WebKit/public/web/WebWidget.h" #include "ui/gfx/size_conversions.h" #include "ui/gl/gpu_preference.h" -#include "webkit/plugins/ppapi/plugin_delegate.h" -#include "webkit/plugins/ppapi/ppapi_plugin_instance_impl.h" using WebKit::WebCanvas; using WebKit::WebCompositionUnderline; diff --git a/content/renderer/render_widget_fullscreen_pepper.h b/content/renderer/render_widget_fullscreen_pepper.h index 9e54548..840a979 100644 --- a/content/renderer/render_widget_fullscreen_pepper.h +++ b/content/renderer/render_widget_fullscreen_pepper.h @@ -7,9 +7,9 @@ #include "base/memory/scoped_ptr.h" #include "content/renderer/mouse_lock_dispatcher.h" +#include "content/renderer/pepper/fullscreen_container.h" #include "content/renderer/render_widget_fullscreen.h" #include "third_party/WebKit/public/web/WebWidget.h" -#include "webkit/plugins/ppapi/fullscreen_container.h" namespace webkit { namespace ppapi { diff --git a/content/renderer/renderer_main.cc b/content/renderer/renderer_main.cc index 5c59ad1..5d34342 100644 --- a/content/renderer/renderer_main.cc +++ b/content/renderer/renderer_main.cc @@ -28,13 +28,13 @@ #include "content/public/common/main_function_params.h" #include "content/public/renderer/content_renderer_client.h" #include "content/renderer/browser_plugin/browser_plugin_manager_impl.h" +#include "content/renderer/pepper/ppapi_interface_factory.h" #include "content/renderer/render_process_impl.h" #include "content/renderer/render_thread_impl.h" #include "content/renderer/renderer_main_platform_delegate.h" #include "ui/base/ui_base_switches.h" #include "webkit/child/webkit_child_helpers.h" #include "webkit/glue/webkit_glue.h" -#include "webkit/plugins/ppapi/ppapi_interface_factory.h" #if defined(OS_MACOSX) #include <Carbon/Carbon.h> @@ -148,9 +148,6 @@ int RendererMain(const MainFunctionParams& parameters) { RendererMainPlatformDelegate platform(parameters); - webkit::ppapi::PpapiInterfaceFactoryManager* factory_manager = - webkit::ppapi::PpapiInterfaceFactoryManager::GetInstance(); - factory_manager->RegisterFactory(ContentPPAPIInterfaceFactory); base::StatsCounterTimer stats_counter_timer("Content.RendererInit"); base::StatsScope<base::StatsCounterTimer> startup_timer(stats_counter_timer); @@ -205,6 +202,10 @@ int RendererMain(const MainFunctionParams& parameters) { } #if defined(ENABLE_PLUGINS) + webkit::ppapi::PpapiInterfaceFactoryManager* factory_manager = + webkit::ppapi::PpapiInterfaceFactoryManager::GetInstance(); + factory_manager->RegisterFactory(ContentPPAPIInterfaceFactory); + // Load pepper plugins before engaging the sandbox. PepperPluginRegistry::GetInstance(); #endif |