blob: 9c798a138224cac5c34e2dce8eb176ac4bd48d71 [file] [log] [blame]
// 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_delegate_impl.h"
#include <cmath>
#include <queue>
#include "base/bind.h"
#include "base/callback.h"
#include "base/command_line.h"
#include "base/file_path.h"
#include "base/file_util_proxy.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/string_split.h"
#include "base/sync_socket.h"
#include "base/task.h"
#include "base/time.h"
#include "content/common/child_process.h"
#include "content/common/child_process_messages.h"
#include "content/common/child_thread.h"
#include "content/common/file_system/file_system_dispatcher.h"
#include "content/common/file_system_messages.h"
#include "content/common/media/audio_messages.h"
#include "content/common/pepper_file_messages.h"
#include "content/common/pepper_plugin_registry.h"
#include "content/common/pepper_messages.h"
#include "content/common/quota_dispatcher.h"
#include "content/common/view_messages.h"
#include "content/public/common/content_switches.h"
#include "content/public/renderer/content_renderer_client.h"
#include "content/renderer/gpu/command_buffer_proxy.h"
#include "content/renderer/gpu/gpu_channel_host.h"
#include "content/renderer/gpu/renderer_gl_context.h"
#include "content/renderer/gpu/webgraphicscontext3d_command_buffer_impl.h"
#include "content/renderer/media/audio_message_filter.h"
#include "content/renderer/media/video_capture_impl_manager.h"
#include "content/renderer/p2p/p2p_transport_impl.h"
#include "content/renderer/pepper_platform_context_3d_impl.h"
#include "content/renderer/pepper_platform_video_decoder_impl.h"
#include "content/renderer/render_thread_impl.h"
#include "content/renderer/render_view_impl.h"
#include "content/renderer/render_widget_fullscreen_pepper.h"
#include "content/renderer/webplugin_delegate_proxy.h"
#include "ipc/ipc_channel_handle.h"
#include "media/video/capture/video_capture_proxy.h"
#include "ppapi/c/dev/pp_video_dev.h"
#include "ppapi/c/pp_errors.h"
#include "ppapi/c/private/ppb_flash.h"
#include "ppapi/c/private/ppb_flash_net_connector.h"
#include "ppapi/proxy/host_dispatcher.h"
#include "ppapi/proxy/ppapi_messages.h"
#include "ppapi/shared_impl/ppapi_preferences.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebCursorInfo.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFileChooserCompletion.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFileChooserParams.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginContainer.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebScreenInfo.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
#include "ui/gfx/size.h"
#include "ui/gfx/surface/transport_dib.h"
#include "webkit/fileapi/file_system_callback_dispatcher.h"
#include "webkit/glue/context_menu.h"
#include "webkit/plugins/npapi/webplugin.h"
#include "webkit/plugins/ppapi/file_path.h"
#include "webkit/plugins/ppapi/ppb_file_io_impl.h"
#include "webkit/plugins/ppapi/plugin_module.h"
#include "webkit/plugins/ppapi/ppapi_plugin_instance.h"
#include "webkit/plugins/ppapi/ppb_broker_impl.h"
#include "webkit/plugins/ppapi/ppb_flash_impl.h"
#include "webkit/plugins/ppapi/ppb_flash_net_connector_impl.h"
#include "webkit/plugins/ppapi/resource_helper.h"
#include "webkit/plugins/webplugininfo.h"
using WebKit::WebView;
namespace {
int32_t PlatformFileToInt(base::PlatformFile handle) {
#if defined(OS_WIN)
return static_cast<int32_t>(reinterpret_cast<intptr_t>(handle));
#elif defined(OS_POSIX)
return handle;
#else
#error Not implemented.
#endif
}
base::SyncSocket::Handle DuplicateHandle(base::SyncSocket::Handle handle) {
base::SyncSocket::Handle out_handle = base::kInvalidPlatformFileValue;
#if defined(OS_WIN)
DWORD options = DUPLICATE_SAME_ACCESS;
if (!::DuplicateHandle(::GetCurrentProcess(),
handle,
::GetCurrentProcess(),
&out_handle,
0,
FALSE,
options)) {
out_handle = base::kInvalidPlatformFileValue;
}
#elif defined(OS_POSIX)
// If asked to close the source, we can simply re-use the source fd instead of
// dup()ing and close()ing.
out_handle = ::dup(handle);
#else
#error Not implemented.
#endif
return out_handle;
}
// Implements the Image2D using a TransportDIB.
class PlatformImage2DImpl
: public webkit::ppapi::PluginDelegate::PlatformImage2D {
public:
// This constructor will take ownership of the dib pointer.
// On Mac, we assume that the dib is cached by the browser, so on destruction
// we'll tell the browser to free it.
PlatformImage2DImpl(int width, int height, TransportDIB* dib)
: width_(width),
height_(height),
dib_(dib) {
}
#if defined(OS_MACOSX)
// On Mac, we have to tell the browser to free the transport DIB.
virtual ~PlatformImage2DImpl() {
if (dib_.get()) {
RenderThreadImpl::current()->Send(
new ViewHostMsg_FreeTransportDIB(dib_->id()));
}
}
#endif
virtual skia::PlatformCanvas* Map() {
return dib_->GetPlatformCanvas(width_, height_);
}
virtual intptr_t GetSharedMemoryHandle(uint32* byte_count) const {
*byte_count = dib_->size();
#if defined(OS_WIN)
return reinterpret_cast<intptr_t>(dib_->handle());
#elif defined(OS_MACOSX)
return static_cast<intptr_t>(dib_->handle().fd);
#elif defined(OS_LINUX)
return static_cast<intptr_t>(dib_->handle());
#endif
}
virtual TransportDIB* GetTransportDIB() const {
return dib_.get();
}
private:
int width_;
int height_;
scoped_ptr<TransportDIB> dib_;
DISALLOW_COPY_AND_ASSIGN(PlatformImage2DImpl);
};
class PlatformAudioImpl
: public webkit::ppapi::PluginDelegate::PlatformAudio,
public AudioMessageFilter::Delegate,
public base::RefCountedThreadSafe<PlatformAudioImpl> {
public:
PlatformAudioImpl()
: client_(NULL), stream_id_(0),
main_message_loop_(MessageLoop::current()) {
filter_ = RenderThreadImpl::current()->audio_message_filter();
}
virtual ~PlatformAudioImpl() {
// Make sure we have been shut down. Warning: this will usually happen on
// the I/O thread!
DCHECK_EQ(0, stream_id_);
DCHECK(!client_);
}
// Initialize this audio context. StreamCreated() will be called when the
// stream is created.
bool Initialize(uint32_t sample_rate, uint32_t sample_count,
webkit::ppapi::PluginDelegate::PlatformAudio::Client* client);
// PlatformAudio implementation (called on main thread).
virtual bool StartPlayback();
virtual bool StopPlayback();
virtual void ShutDown();
private:
// I/O thread backends to above functions.
void InitializeOnIOThread(const AudioParameters& params);
void StartPlaybackOnIOThread();
void StopPlaybackOnIOThread();
void ShutDownOnIOThread();
virtual void OnRequestPacket(AudioBuffersState buffers_state) {
LOG(FATAL) << "Should never get OnRequestPacket in PlatformAudioImpl";
}
virtual void OnStateChanged(AudioStreamState state) {}
virtual void OnCreated(base::SharedMemoryHandle handle, uint32 length) {
LOG(FATAL) << "Should never get OnCreated in PlatformAudioImpl";
}
virtual void OnLowLatencyCreated(base::SharedMemoryHandle handle,
base::SyncSocket::Handle socket_handle,
uint32 length);
virtual void OnVolume(double volume) {}
// The client to notify when the stream is created. THIS MUST ONLY BE
// ACCESSED ON THE MAIN THREAD.
webkit::ppapi::PluginDelegate::PlatformAudio::Client* client_;
// MessageFilter used to send/receive IPC. THIS MUST ONLY BE ACCESSED ON THE
// I/O thread except to send messages and get the message loop.
scoped_refptr<AudioMessageFilter> filter_;
// Our ID on the MessageFilter. THIS MUST ONLY BE ACCESSED ON THE I/O THREAD
// or else you could race with the initialize function which sets it.
int32 stream_id_;
MessageLoop* main_message_loop_;
DISALLOW_COPY_AND_ASSIGN(PlatformAudioImpl);
};
bool PlatformAudioImpl::Initialize(
uint32_t sample_rate, uint32_t sample_count,
webkit::ppapi::PluginDelegate::PlatformAudio::Client* client) {
DCHECK(client);
// Make sure we don't call init more than once.
DCHECK_EQ(0, stream_id_);
client_ = client;
AudioParameters params;
params.format = AudioParameters::AUDIO_PCM_LINEAR;
params.channels = 2;
params.sample_rate = sample_rate;
params.bits_per_sample = 16;
params.samples_per_packet = sample_count;
ChildProcess::current()->io_message_loop()->PostTask(
FROM_HERE,
NewRunnableMethod(this, &PlatformAudioImpl::InitializeOnIOThread,
params));
return true;
}
bool PlatformAudioImpl::StartPlayback() {
if (filter_) {
ChildProcess::current()->io_message_loop()->PostTask(
FROM_HERE,
NewRunnableMethod(this, &PlatformAudioImpl::StartPlaybackOnIOThread));
return true;
}
return false;
}
bool PlatformAudioImpl::StopPlayback() {
if (filter_) {
ChildProcess::current()->io_message_loop()->PostTask(
FROM_HERE,
NewRunnableMethod(this, &PlatformAudioImpl::StopPlaybackOnIOThread));
return true;
}
return false;
}
void PlatformAudioImpl::ShutDown() {
// Called on the main thread to stop all audio callbacks. We must only change
// the client on the main thread, and the delegates from the I/O thread.
client_ = NULL;
ChildProcess::current()->io_message_loop()->PostTask(
FROM_HERE,
base::Bind(&PlatformAudioImpl::ShutDownOnIOThread, this));
}
void PlatformAudioImpl::InitializeOnIOThread(const AudioParameters& params) {
stream_id_ = filter_->AddDelegate(this);
filter_->Send(new AudioHostMsg_CreateStream(stream_id_, params, true));
}
void PlatformAudioImpl::StartPlaybackOnIOThread() {
if (stream_id_)
filter_->Send(new AudioHostMsg_PlayStream(stream_id_));
}
void PlatformAudioImpl::StopPlaybackOnIOThread() {
if (stream_id_)
filter_->Send(new AudioHostMsg_PauseStream(stream_id_));
}
void PlatformAudioImpl::ShutDownOnIOThread() {
// Make sure we don't call shutdown more than once.
if (!stream_id_)
return;
filter_->Send(new AudioHostMsg_CloseStream(stream_id_));
filter_->RemoveDelegate(stream_id_);
stream_id_ = 0;
Release(); // Release for the delegate, balances out the reference taken in
// PepperPluginDelegateImpl::CreateAudio.
}
void PlatformAudioImpl::OnLowLatencyCreated(
base::SharedMemoryHandle handle, base::SyncSocket::Handle socket_handle,
uint32 length) {
#if defined(OS_WIN)
DCHECK(handle);
DCHECK(socket_handle);
#else
DCHECK_NE(-1, handle.fd);
DCHECK_NE(-1, socket_handle);
#endif
DCHECK(length);
if (MessageLoop::current() == main_message_loop_) {
// Must dereference the client only on the main thread. Shutdown may have
// occurred while the request was in-flight, so we need to NULL check.
if (client_)
client_->StreamCreated(handle, length, socket_handle);
} else {
main_message_loop_->PostTask(FROM_HERE,
NewRunnableMethod(this, &PlatformAudioImpl::OnLowLatencyCreated,
handle, socket_handle, length));
}
}
class DispatcherDelegate : public ppapi::proxy::ProxyChannel::Delegate {
public:
virtual ~DispatcherDelegate() {}
// ProxyChannel::Delegate implementation.
virtual base::MessageLoopProxy* GetIPCMessageLoop() {
// This is called only in the renderer so we know we have a child process.
DCHECK(ChildProcess::current()) << "Must be in the renderer.";
return ChildProcess::current()->io_message_loop_proxy();
}
virtual base::WaitableEvent* GetShutdownEvent() {
DCHECK(ChildProcess::current()) << "Must be in the renderer.";
return ChildProcess::current()->GetShutDownEvent();
}
};
class HostDispatcherWrapper
: public webkit::ppapi::PluginDelegate::OutOfProcessProxy {
public:
HostDispatcherWrapper() {}
virtual ~HostDispatcherWrapper() {}
bool Init(base::ProcessHandle plugin_process_handle,
const IPC::ChannelHandle& channel_handle,
PP_Module pp_module,
ppapi::proxy::Dispatcher::GetInterfaceFunc local_get_interface,
const ppapi::Preferences& preferences) {
dispatcher_delegate_.reset(new DispatcherDelegate);
dispatcher_.reset(new ppapi::proxy::HostDispatcher(
plugin_process_handle, pp_module, local_get_interface));
if (!dispatcher_->InitHostWithChannel(
dispatcher_delegate_.get(),
channel_handle, true, preferences)) {
dispatcher_.reset();
dispatcher_delegate_.reset();
return false;
}
dispatcher_->channel()->SetRestrictDispatchToSameChannel(true);
return true;
}
// OutOfProcessProxy implementation.
virtual const void* GetProxiedInterface(const char* name) {
return dispatcher_->GetProxiedInterface(name);
}
virtual void AddInstance(PP_Instance instance) {
ppapi::proxy::HostDispatcher::SetForInstance(instance, dispatcher_.get());
}
virtual void RemoveInstance(PP_Instance instance) {
ppapi::proxy::HostDispatcher::RemoveForInstance(instance);
}
private:
scoped_ptr<ppapi::proxy::HostDispatcher> dispatcher_;
scoped_ptr<ppapi::proxy::ProxyChannel::Delegate> dispatcher_delegate_;
};
class QuotaCallbackTranslator : public QuotaDispatcher::Callback {
public:
typedef webkit::ppapi::PluginDelegate::AvailableSpaceCallback PluginCallback;
explicit QuotaCallbackTranslator(const PluginCallback& cb) : callback_(cb) {}
virtual void DidQueryStorageUsageAndQuota(int64 usage, int64 quota) OVERRIDE {
callback_.Run(std::max(static_cast<int64>(0), quota - usage));
}
virtual void DidGrantStorageQuota(int64 granted_quota) OVERRIDE {
NOTREACHED();
}
virtual void DidFail(quota::QuotaStatusCode error) OVERRIDE {
callback_.Run(0);
}
private:
PluginCallback callback_;
};
class PlatformVideoCaptureImpl
: public webkit::ppapi::PluginDelegate::PlatformVideoCapture {
public:
PlatformVideoCaptureImpl(media::VideoCapture::EventHandler* handler)
: handler_proxy_(new media::VideoCaptureHandlerProxy(
handler, base::MessageLoopProxy::current())) {
VideoCaptureImplManager* manager =
RenderThreadImpl::current()->video_capture_impl_manager();
// 1 means the "default" video capture device.
// TODO(piman): Add a way to enumerate devices and pass them through the
// API.
video_capture_ = manager->AddDevice(1, handler_proxy_.get());
}
// Overrides from media::VideoCapture::EventHandler
virtual ~PlatformVideoCaptureImpl() {
VideoCaptureImplManager* manager =
RenderThreadImpl::current()->video_capture_impl_manager();
manager->RemoveDevice(1, handler_proxy_.get());
}
virtual void StartCapture(
EventHandler* handler,
const VideoCaptureCapability& capability) OVERRIDE {
DCHECK(handler == handler_proxy_->proxied());
video_capture_->StartCapture(handler_proxy_.get(), capability);
}
virtual void StopCapture(EventHandler* handler) OVERRIDE {
DCHECK(handler == handler_proxy_->proxied());
video_capture_->StopCapture(handler_proxy_.get());
}
virtual void FeedBuffer(scoped_refptr<VideoFrameBuffer> buffer) OVERRIDE {
video_capture_->FeedBuffer(buffer);
}
virtual bool CaptureStarted() OVERRIDE {
return handler_proxy_->state().started;
}
virtual int CaptureWidth() OVERRIDE {
return handler_proxy_->state().width;
}
virtual int CaptureHeight() OVERRIDE {
return handler_proxy_->state().height;
}
virtual int CaptureFrameRate() OVERRIDE {
return handler_proxy_->state().frame_rate;
}
private:
scoped_ptr<media::VideoCaptureHandlerProxy> handler_proxy_;
media::VideoCapture* video_capture_;
};
} // namespace
BrokerDispatcherWrapper::BrokerDispatcherWrapper() {
}
BrokerDispatcherWrapper::~BrokerDispatcherWrapper() {
}
bool BrokerDispatcherWrapper::Init(
base::ProcessHandle plugin_process_handle,
const IPC::ChannelHandle& channel_handle) {
dispatcher_delegate_.reset(new DispatcherDelegate);
dispatcher_.reset(
new ppapi::proxy::BrokerHostDispatcher(plugin_process_handle));
if (!dispatcher_->InitBrokerWithChannel(dispatcher_delegate_.get(),
channel_handle,
true)) {
dispatcher_.reset();
dispatcher_delegate_.reset();
return false;
}
dispatcher_->channel()->SetRestrictDispatchToSameChannel(true);
return true;
}
// Does not take ownership of the local pipe.
int32_t BrokerDispatcherWrapper::SendHandleToBroker(
PP_Instance instance,
base::SyncSocket::Handle handle) {
IPC::PlatformFileForTransit foreign_socket_handle =
dispatcher_->ShareHandleWithRemote(handle, false);
if (foreign_socket_handle == IPC::InvalidPlatformFileForTransit())
return PP_ERROR_FAILED;
int32_t result;
if (!dispatcher_->Send(
new PpapiMsg_ConnectToPlugin(instance, foreign_socket_handle, &result))) {
// The plugin did not receive the handle, so it must be closed.
// The easiest way to clean it up is to just put it in an object
// and then close it. This failure case is not performance critical.
// The handle could still leak if Send succeeded but the IPC later failed.
base::SyncSocket temp_socket(
IPC::PlatformFileForTransitToPlatformFile(foreign_socket_handle));
return PP_ERROR_FAILED;
}
return result;
}
PpapiBrokerImpl::PpapiBrokerImpl(webkit::ppapi::PluginModule* plugin_module,
PepperPluginDelegateImpl* delegate)
: plugin_module_(plugin_module),
delegate_(delegate->AsWeakPtr()) {
DCHECK(plugin_module_);
DCHECK(delegate_);
plugin_module_->SetBroker(this);
}
PpapiBrokerImpl::~PpapiBrokerImpl() {
// Report failure to all clients that had pending operations.
for (ClientMap::iterator i = pending_connects_.begin();
i != pending_connects_.end(); ++i) {
base::WeakPtr<webkit::ppapi::PPB_Broker_Impl>& weak_ptr = i->second;
if (weak_ptr) {
weak_ptr->BrokerConnected(
PlatformFileToInt(base::kInvalidPlatformFileValue), PP_ERROR_ABORTED);
}
}
pending_connects_.clear();
plugin_module_->SetBroker(NULL);
plugin_module_ = NULL;
}
// If the channel is not ready, queue the connection.
void PpapiBrokerImpl::Connect(webkit::ppapi::PPB_Broker_Impl* client) {
DCHECK(pending_connects_.find(client) == pending_connects_.end())
<< "Connect was already called for this client";
// Ensure this object and the associated broker exist as long as the
// client exists. There is a corresponding Release() call in Disconnect(),
// which is called when the PPB_Broker_Impl is destroyed. The only other
// possible reference is in pending_connect_broker_, which only holds a
// transient reference. This ensures the broker is available as long as the
// plugin needs it and allows the plugin to release the broker when it is no
// longer using it.
AddRef();
if (!dispatcher_.get()) {
pending_connects_[client] = client->AsWeakPtr();
return;
}
DCHECK(pending_connects_.empty());
ConnectPluginToBroker(client);
}
void PpapiBrokerImpl::Disconnect(webkit::ppapi::PPB_Broker_Impl* client) {
// Remove the pending connect if one exists. This class will not call client's
// callback.
pending_connects_.erase(client);
// TODO(ddorwin): Send message disconnect message using dispatcher_.
if (pending_connects_.empty()) {
// There are no more clients of this broker. Ensure it will be deleted even
// if the IPC response never comes and OnPpapiBrokerChannelCreated is not
// called to remove this object from pending_connect_broker_.
// Before the broker is connected, clients must either be in
// pending_connects_ or not yet associated with this object. Thus, if this
// object is in pending_connect_broker_, there can be no associated clients
// once pending_connects_ is empty and it is thus safe to remove this from
// pending_connect_broker_. Doing so will cause this object to be deleted,
// removing it from the PluginModule. Any new clients will create a new
// instance of this object.
// This doesn't solve all potential problems, but it helps with the ones
// we can influence.
if (delegate_) {
bool stopped = delegate_->StopWaitingForPpapiBrokerConnection(this);
// Verify the assumption that there are no references other than the one
// client holds, which will be released below.
DCHECK(!stopped || HasOneRef());
}
}
// Release the reference added in Connect().
// This must be the last statement because it may delete this object.
Release();
}
void PpapiBrokerImpl::OnBrokerChannelConnected(
base::ProcessHandle broker_process_handle,
const IPC::ChannelHandle& channel_handle) {
scoped_ptr<BrokerDispatcherWrapper> dispatcher(new BrokerDispatcherWrapper);
if (dispatcher->Init(broker_process_handle, channel_handle)) {
dispatcher_.reset(dispatcher.release());
// Process all pending channel requests from the renderers.
for (ClientMap::iterator i = pending_connects_.begin();
i != pending_connects_.end(); ++i) {
base::WeakPtr<webkit::ppapi::PPB_Broker_Impl>& weak_ptr = i->second;
if (weak_ptr)
ConnectPluginToBroker(weak_ptr);
}
} else {
// Report failure to all clients.
for (ClientMap::iterator i = pending_connects_.begin();
i != pending_connects_.end(); ++i) {
base::WeakPtr<webkit::ppapi::PPB_Broker_Impl>& weak_ptr = i->second;
if (weak_ptr) {
weak_ptr->BrokerConnected(
PlatformFileToInt(base::kInvalidPlatformFileValue),
PP_ERROR_FAILED);
}
}
}
pending_connects_.clear();
}
void PpapiBrokerImpl::ConnectPluginToBroker(
webkit::ppapi::PPB_Broker_Impl* client) {
base::SyncSocket::Handle plugin_handle = base::kInvalidPlatformFileValue;
int32_t result = PP_OK;
base::SyncSocket* sockets[2] = {0};
if (base::SyncSocket::CreatePair(sockets)) {
// The socket objects will be deleted when this function exits, closing the
// handles. Any uses of the socket must duplicate them.
scoped_ptr<base::SyncSocket> broker_socket(sockets[0]);
scoped_ptr<base::SyncSocket> plugin_socket(sockets[1]);
result = dispatcher_->SendHandleToBroker(client->pp_instance(),
broker_socket->handle());
// If the broker has its pipe handle, duplicate the plugin's handle.
// Otherwise, the plugin's handle will be automatically closed.
if (result == PP_OK)
plugin_handle = DuplicateHandle(plugin_socket->handle());
} else {
result = PP_ERROR_FAILED;
}
// TOOD(ddorwin): Change the IPC to asynchronous: Queue an object containing
// client and plugin_socket.release(), then return.
// That message handler will then call client->BrokerConnected() with the
// saved pipe handle.
// Temporarily, just call back.
client->BrokerConnected(PlatformFileToInt(plugin_handle), result);
}
PepperPluginDelegateImpl::PepperPluginDelegateImpl(RenderViewImpl* render_view)
: render_view_(render_view),
has_saved_context_menu_action_(false),
saved_context_menu_action_(0),
focused_plugin_(NULL),
mouse_lock_owner_(NULL),
mouse_locked_(false),
pending_lock_request_(false),
pending_unlock_request_(false),
last_mouse_event_target_(NULL) {
}
PepperPluginDelegateImpl::~PepperPluginDelegateImpl() {
DCHECK(!mouse_lock_owner_);
}
scoped_refptr<webkit::ppapi::PluginModule>
PepperPluginDelegateImpl::CreatePepperPluginModule(
const webkit::WebPluginInfo& webplugin_info,
bool* pepper_plugin_was_registered) {
*pepper_plugin_was_registered = true;
// See if a module has already been loaded for this plugin.
FilePath path(webplugin_info.path);
scoped_refptr<webkit::ppapi::PluginModule> module =
PepperPluginRegistry::GetInstance()->GetLiveModule(path);
if (module)
return module;
// In-process plugins will have always been created up-front to avoid the
// sandbox restrictions. So getting here implies it doesn't exist or should
// be out of process.
const content::PepperPluginInfo* info =
PepperPluginRegistry::GetInstance()->GetInfoForPlugin(webplugin_info);
if (!info) {
*pepper_plugin_was_registered = false;
return scoped_refptr<webkit::ppapi::PluginModule>();
} else if (!info->is_out_of_process) {
// In-process plugin not preloaded, it probably couldn't be initialized.
return scoped_refptr<webkit::ppapi::PluginModule>();
}
// Out of process: have the browser start the plugin process for us.
base::ProcessHandle plugin_process_handle = base::kNullProcessHandle;
IPC::ChannelHandle channel_handle;
render_view_->Send(new ViewHostMsg_OpenChannelToPepperPlugin(
path, &plugin_process_handle, &channel_handle));
if (channel_handle.name.empty()) {
// Couldn't be initialized.
return scoped_refptr<webkit::ppapi::PluginModule>();
}
// Create a new HostDispatcher for the proxying, and hook it to a new
// PluginModule. Note that AddLiveModule must be called before any early
// returns since the module's destructor will remove itself.
module = new webkit::ppapi::PluginModule(info->name, path,
PepperPluginRegistry::GetInstance());
PepperPluginRegistry::GetInstance()->AddLiveModule(path, module);
scoped_ptr<HostDispatcherWrapper> dispatcher(new HostDispatcherWrapper);
if (!dispatcher->Init(
plugin_process_handle,
channel_handle,
module->pp_module(),
webkit::ppapi::PluginModule::GetLocalGetInterfaceFunc(),
GetPreferences()))
return scoped_refptr<webkit::ppapi::PluginModule>();
module->InitAsProxied(dispatcher.release());
return module;
}
scoped_refptr<PpapiBrokerImpl> PepperPluginDelegateImpl::CreatePpapiBroker(
webkit::ppapi::PluginModule* plugin_module) {
DCHECK(plugin_module);
DCHECK(!plugin_module->GetBroker());
// The broker path is the same as the plugin.
const FilePath& broker_path = plugin_module->path();
scoped_refptr<PpapiBrokerImpl> broker =
new PpapiBrokerImpl(plugin_module, this);
int request_id =
pending_connect_broker_.Add(new scoped_refptr<PpapiBrokerImpl>(broker));
// Have the browser start the broker process for us.
IPC::Message* msg =
new ViewHostMsg_OpenChannelToPpapiBroker(render_view_->routing_id(),
request_id,
broker_path);
if (!render_view_->Send(msg)) {
pending_connect_broker_.Remove(request_id);
return scoped_refptr<PpapiBrokerImpl>();
}
return broker;
}
void PepperPluginDelegateImpl::OnPpapiBrokerChannelCreated(
int request_id,
base::ProcessHandle broker_process_handle,
const IPC::ChannelHandle& handle) {
scoped_refptr<PpapiBrokerImpl>* broker_ptr =
pending_connect_broker_.Lookup(request_id);
if (broker_ptr) {
scoped_refptr<PpapiBrokerImpl> broker = *broker_ptr;
pending_connect_broker_.Remove(request_id);
broker->OnBrokerChannelConnected(broker_process_handle, handle);
} else {
// There is no broker waiting for this channel. Close it so the broker can
// clean up and possibly exit.
// The easiest way to clean it up is to just put it in an object
// and then close them. This failure case is not performance critical.
BrokerDispatcherWrapper temp_dispatcher;
temp_dispatcher.Init(broker_process_handle, handle);
}
}
// Iterates through pending_connect_broker_ to find the broker.
// Cannot use Lookup() directly because pending_connect_broker_ does not store
// the raw pointer to the broker. Assumes maximum of one copy of broker exists.
bool PepperPluginDelegateImpl::StopWaitingForPpapiBrokerConnection(
PpapiBrokerImpl* broker) {
for (BrokerMap::iterator i(&pending_connect_broker_);
!i.IsAtEnd(); i.Advance()) {
if (i.GetCurrentValue()->get() == broker) {
pending_connect_broker_.Remove(i.GetCurrentKey());
return true;
}
}
return false;
}
void PepperPluginDelegateImpl::ViewInitiatedPaint() {
// Notify all of our instances that we started painting. This is used for
// internal bookkeeping only, so we know that the set can not change under
// us.
for (std::set<webkit::ppapi::PluginInstance*>::iterator i =
active_instances_.begin();
i != active_instances_.end(); ++i)
(*i)->ViewInitiatedPaint();
}
void PepperPluginDelegateImpl::ViewFlushedPaint() {
// Notify all instances that we painted. This will call into the plugin, and
// we it may ask to close itself as a result. This will, in turn, modify our
// set, possibly invalidating the iterator. So we iterate on a copy that
// won't change out from under us.
std::set<webkit::ppapi::PluginInstance*> plugins = active_instances_;
for (std::set<webkit::ppapi::PluginInstance*>::iterator i = plugins.begin();
i != plugins.end(); ++i) {
// The copy above makes sure our iterator is never invalid if some plugins
// are destroyed. But some plugin may decide to close all of its views in
// response to a paint in one of them, so we need to make sure each one is
// still "current" before using it.
//
// It's possible that a plugin was destroyed, but another one was created
// with the same address. In this case, we'll call ViewFlushedPaint on that
// new plugin. But that's OK for this particular case since we're just
// notifying all of our instances that the view flushed, and the new one is
// one of our instances.
//
// What about the case where a new one is created in a callback at a new
// address and we don't issue the callback? We're still OK since this
// callback is used for flush callbacks and we could not have possibly
// started a new paint (ViewInitiatedPaint) for the new plugin while
// processing a previous paint for an existing one.
if (active_instances_.find(*i) != active_instances_.end())
(*i)->ViewFlushedPaint();
}
}
webkit::ppapi::PluginInstance*
PepperPluginDelegateImpl::GetBitmapForOptimizedPluginPaint(
const gfx::Rect& paint_bounds,
TransportDIB** dib,
gfx::Rect* location,
gfx::Rect* clip) {
for (std::set<webkit::ppapi::PluginInstance*>::iterator i =
active_instances_.begin();
i != active_instances_.end(); ++i) {
webkit::ppapi::PluginInstance* instance = *i;
if (instance->GetBitmapForOptimizedPluginPaint(
paint_bounds, dib, location, clip))
return *i;
}
return NULL;
}
void PepperPluginDelegateImpl::PluginFocusChanged(
webkit::ppapi::PluginInstance* instance,
bool focused) {
if (focused)
focused_plugin_ = instance;
else if (focused_plugin_ == instance)
focused_plugin_ = NULL;
if (render_view_)
render_view_->PpapiPluginFocusChanged();
}
void PepperPluginDelegateImpl::PluginTextInputTypeChanged(
webkit::ppapi::PluginInstance* instance) {
if (focused_plugin_ == instance && render_view_)
render_view_->PpapiPluginTextInputTypeChanged();
}
void PepperPluginDelegateImpl::PluginCaretPositionChanged(
webkit::ppapi::PluginInstance* instance) {
if (focused_plugin_ == instance && render_view_)
render_view_->PpapiPluginCaretPositionChanged();
}
void PepperPluginDelegateImpl::PluginRequestedCancelComposition(
webkit::ppapi::PluginInstance* instance) {
if (focused_plugin_ == instance && render_view_)
render_view_->PpapiPluginCancelComposition();
}
void PepperPluginDelegateImpl::OnImeSetComposition(
const string16& text,
const std::vector<WebKit::WebCompositionUnderline>& underlines,
int selection_start,
int selection_end) {
if (!IsPluginAcceptingCompositionEvents()) {
composition_text_ = text;
} else {
// TODO(kinaba) currently all composition events are sent directly to
// plugins. Use DOM event mechanism after WebKit is made aware about
// plugins that support composition.
// The code below mimics the behavior of WebCore::Editor::setComposition.
// Empty -> nonempty: composition started.
if (composition_text_.empty() && !text.empty())
focused_plugin_->HandleCompositionStart(string16());
// Nonempty -> empty: composition canceled.
if (!composition_text_.empty() && text.empty())
focused_plugin_->HandleCompositionEnd(string16());
composition_text_ = text;
// Nonempty: composition is ongoing.
if (!composition_text_.empty()) {
focused_plugin_->HandleCompositionUpdate(composition_text_, underlines,
selection_start, selection_end);
}
}
}
void PepperPluginDelegateImpl::OnImeConfirmComposition(const string16& text) {
// Here, text.empty() has a special meaning. It means to commit the last
// update of composition text (see RenderWidgetHost::ImeConfirmComposition()).
const string16& last_text = text.empty() ? composition_text_ : text;
// last_text is empty only when both text and composition_text_ is. Ignore it.
if (last_text.empty())
return;
if (!IsPluginAcceptingCompositionEvents()) {
for (size_t i = 0; i < text.size(); ++i) {
WebKit::WebKeyboardEvent char_event;
char_event.type = WebKit::WebInputEvent::Char;
char_event.timeStampSeconds = base::Time::Now().ToDoubleT();
char_event.modifiers = 0;
char_event.windowsKeyCode = last_text[i];
char_event.nativeKeyCode = last_text[i];
char_event.text[0] = last_text[i];
char_event.unmodifiedText[0] = last_text[i];
if (render_view_->webwidget())
render_view_->webwidget()->handleInputEvent(char_event);
}
} else {
// Mimics the order of events sent by WebKit.
// See WebCore::Editor::setComposition() for the corresponding code.
focused_plugin_->HandleCompositionEnd(last_text);
focused_plugin_->HandleTextInput(last_text);
}
composition_text_.clear();
}
gfx::Rect PepperPluginDelegateImpl::GetCaretBounds() const {
if (!focused_plugin_)
return gfx::Rect(0, 0, 0, 0);
return focused_plugin_->GetCaretBounds();
}
ui::TextInputType PepperPluginDelegateImpl::GetTextInputType() const {
if (!focused_plugin_)
return ui::TEXT_INPUT_TYPE_NONE;
return focused_plugin_->text_input_type();
}
bool PepperPluginDelegateImpl::IsPluginAcceptingCompositionEvents() const {
if (!focused_plugin_)
return false;
return focused_plugin_->IsPluginAcceptingCompositionEvents();
}
bool PepperPluginDelegateImpl::CanComposeInline() const {
return IsPluginAcceptingCompositionEvents();
}
void PepperPluginDelegateImpl::PluginCrashed(
webkit::ppapi::PluginInstance* instance) {
render_view_->PluginCrashed(instance->module()->path());
UnlockMouse(instance);
}
void PepperPluginDelegateImpl::InstanceCreated(
webkit::ppapi::PluginInstance* instance) {
active_instances_.insert(instance);
// Set the initial focus.
instance->SetContentAreaFocus(render_view_->has_focus());
}
void PepperPluginDelegateImpl::InstanceDeleted(
webkit::ppapi::PluginInstance* instance) {
active_instances_.erase(instance);
if (mouse_lock_owner_ && mouse_lock_owner_ == instance) {
// UnlockMouse() will determine whether a ViewHostMsg_UnlockMouse needs to
// be sent, and set internal state properly. We only need to forget about
// the current |mouse_lock_owner_|.
UnlockMouse(mouse_lock_owner_);
mouse_lock_owner_ = NULL;
}
if (last_mouse_event_target_ == instance)
last_mouse_event_target_ = NULL;
if (focused_plugin_ == instance)
PluginFocusChanged(instance, false);
}
SkBitmap* PepperPluginDelegateImpl::GetSadPluginBitmap() {
return content::GetContentClient()->renderer()->GetSadPluginBitmap();
}
webkit::ppapi::PluginDelegate::PlatformImage2D*
PepperPluginDelegateImpl::CreateImage2D(int width, int height) {
uint32 buffer_size = width * height * 4;
// Allocate the transport DIB and the PlatformCanvas pointing to it.
#if defined(OS_MACOSX)
// On the Mac, shared memory has to be created in the browser in order to
// work in the sandbox. Do this by sending a message to the browser
// requesting a TransportDIB (see also
// chrome/renderer/webplugin_delegate_proxy.cc, method
// WebPluginDelegateProxy::CreateBitmap() for similar code). The TransportDIB
// is cached in the browser, and is freed (in typical cases) by the
// PlatformImage2DImpl's destructor.
TransportDIB::Handle dib_handle;
IPC::Message* msg = new ViewHostMsg_AllocTransportDIB(buffer_size,
true,
&dib_handle);
if (!RenderThreadImpl::current()->Send(msg))
return NULL;
if (!TransportDIB::is_valid_handle(dib_handle))
return NULL;
TransportDIB* dib = TransportDIB::Map(dib_handle);
#else
static int next_dib_id = 0;
TransportDIB* dib = TransportDIB::Create(buffer_size, next_dib_id++);
if (!dib)
return NULL;
#endif
return new PlatformImage2DImpl(width, height, dib);
}
webkit::ppapi::PluginDelegate::PlatformContext3D*
PepperPluginDelegateImpl::CreateContext3D() {
#ifdef ENABLE_GPU
// If accelerated compositing of plugins is disabled, fail to create a 3D
// context, because it won't be visible. This allows graceful fallback in the
// modules.
if (!render_view_->webkit_preferences().accelerated_plugins_enabled)
return NULL;
return new PlatformContext3DImpl(this);
#else
return NULL;
#endif
}
webkit::ppapi::PluginDelegate::PlatformVideoCapture*
PepperPluginDelegateImpl::CreateVideoCapture(
media::VideoCapture::EventHandler* handler) {
return new PlatformVideoCaptureImpl(handler);
}
webkit::ppapi::PluginDelegate::PlatformVideoDecoder*
PepperPluginDelegateImpl::CreateVideoDecoder(
media::VideoDecodeAccelerator::Client* client,
int32 command_buffer_route_id) {
return new PlatformVideoDecoderImpl(client, command_buffer_route_id);
}
void PepperPluginDelegateImpl::NumberOfFindResultsChanged(int identifier,
int total,
bool final_result) {
render_view_->reportFindInPageMatchCount(identifier, total, final_result);
}
void PepperPluginDelegateImpl::SelectedFindResultChanged(int identifier,
int index) {
render_view_->reportFindInPageSelection(
identifier, index + 1, WebKit::WebRect());
}
webkit::ppapi::PluginDelegate::PlatformAudio*
PepperPluginDelegateImpl::CreateAudio(
uint32_t sample_rate, uint32_t sample_count,
webkit::ppapi::PluginDelegate::PlatformAudio::Client* client) {
scoped_refptr<PlatformAudioImpl> audio(new PlatformAudioImpl());
if (audio->Initialize(sample_rate, sample_count, client)) {
// Balanced by Release invoked in PlatformAudioImpl::ShutDownOnIOThread().
return audio.release();
} else {
return NULL;
}
}
// If a broker has not already been created for this plugin, creates one.
webkit::ppapi::PluginDelegate::PpapiBroker*
PepperPluginDelegateImpl::ConnectToPpapiBroker(
webkit::ppapi::PPB_Broker_Impl* client) {
CHECK(client);
// If a broker needs to be created, this will ensure it does not get deleted
// before Connect() adds a reference.
scoped_refptr<PpapiBrokerImpl> broker_impl;
webkit::ppapi::PluginModule* plugin_module =
webkit::ppapi::ResourceHelper::GetPluginModule(client);
PpapiBroker* broker = plugin_module->GetBroker();
if (!broker) {
broker_impl = CreatePpapiBroker(plugin_module);
if (!broker_impl.get())
return NULL;
broker = broker_impl;
}
// Adds a reference, ensuring not deleted when broker_impl goes out of scope.
broker->Connect(client);
return broker;
}
bool PepperPluginDelegateImpl::RunFileChooser(
const WebKit::WebFileChooserParams& params,
WebKit::WebFileChooserCompletion* chooser_completion) {
return render_view_->runFileChooser(params, chooser_completion);
}
bool PepperPluginDelegateImpl::AsyncOpenFile(
const FilePath& path,
int flags,
const AsyncOpenFileCallback& callback) {
int message_id = pending_async_open_files_.Add(
new AsyncOpenFileCallback(callback));
IPC::Message* msg = new ViewHostMsg_AsyncOpenFile(
render_view_->routing_id(), path, flags, message_id);
return render_view_->Send(msg);
}
void PepperPluginDelegateImpl::OnAsyncFileOpened(
base::PlatformFileError error_code,
base::PlatformFile file,
int message_id) {
AsyncOpenFileCallback* callback =
pending_async_open_files_.Lookup(message_id);
DCHECK(callback);
pending_async_open_files_.Remove(message_id);
callback->Run(error_code, base::PassPlatformFile(&file));
// Make sure we won't leak file handle if the requester has died.
if (file != base::kInvalidPlatformFileValue)
base::FileUtilProxy::Close(GetFileThreadMessageLoopProxy(), file,
base::FileUtilProxy::StatusCallback());
delete callback;
}
void PepperPluginDelegateImpl::OnSetFocus(bool has_focus) {
for (std::set<webkit::ppapi::PluginInstance*>::iterator i =
active_instances_.begin();
i != active_instances_.end(); ++i)
(*i)->SetContentAreaFocus(has_focus);
}
bool PepperPluginDelegateImpl::IsPluginFocused() const {
return focused_plugin_ != NULL;
}
void PepperPluginDelegateImpl::OnLockMouseACK(bool succeeded) {
DCHECK(!mouse_locked_ && pending_lock_request_);
mouse_locked_ = succeeded;
pending_lock_request_ = false;
if (pending_unlock_request_ && !succeeded) {
// We have sent an unlock request after the lock request. However, since
// the lock request has failed, the unlock request will be ignored by the
// browser side and there won't be any response to it.
pending_unlock_request_ = false;
}
// If the PluginInstance has been deleted, |mouse_lock_owner_| can be NULL.
if (mouse_lock_owner_) {
webkit::ppapi::PluginInstance* last_mouse_lock_owner = mouse_lock_owner_;
if (!succeeded) {
// Reset |mouse_lock_owner_| to NULL before calling OnLockMouseACK(), so
// that if OnLockMouseACK() results in calls to any mouse lock method
// (e.g., LockMouse()), the method will see consistent internal state.
mouse_lock_owner_ = NULL;
}
last_mouse_lock_owner->OnLockMouseACK(succeeded ? PP_OK : PP_ERROR_FAILED);
}
}
void PepperPluginDelegateImpl::OnMouseLockLost() {
DCHECK(mouse_locked_ && !pending_lock_request_);
mouse_locked_ = false;
pending_unlock_request_ = false;
// If the PluginInstance has been deleted, |mouse_lock_owner_| can be NULL.
if (mouse_lock_owner_) {
// Reset |mouse_lock_owner_| to NULL before calling OnMouseLockLost(), so
// that if OnMouseLockLost() results in calls to any mouse lock method
// (e.g., LockMouse()), the method will see consistent internal state.
webkit::ppapi::PluginInstance* last_mouse_lock_owner = mouse_lock_owner_;
mouse_lock_owner_ = NULL;
last_mouse_lock_owner->OnMouseLockLost();
}
}
bool PepperPluginDelegateImpl::HandleMouseEvent(
const WebKit::WebMouseEvent& event) {
// This method is called for every mouse event that the render view receives.
// And then the mouse event is forwarded to WebKit, which dispatches it to the
// event target. Potentially a Pepper plugin will receive the event.
// In order to tell whether a plugin gets the last mouse event and which it
// is, we set |last_mouse_event_target_| to NULL here. If a plugin gets the
// event, it will notify us via DidReceiveMouseEvent() and set itself as
// |last_mouse_event_target_|.
last_mouse_event_target_ = NULL;
if (mouse_locked_) {
if (mouse_lock_owner_) {
// |cursor_info| is ignored since it is hidden when the mouse is locked.
WebKit::WebCursorInfo cursor_info;
mouse_lock_owner_->HandleInputEvent(event, &cursor_info);
}
// If the mouse is locked, only the current owner of the mouse lock can
// process mouse events.
return true;
}
return false;
}
bool PepperPluginDelegateImpl::OpenFileSystem(
const GURL& url,
fileapi::FileSystemType type,
long long size,
fileapi::FileSystemCallbackDispatcher* dispatcher) {
FileSystemDispatcher* file_system_dispatcher =
ChildThread::current()->file_system_dispatcher();
return file_system_dispatcher->OpenFileSystem(
url.GetWithEmptyPath(), type, size, true /* create */, dispatcher);
}
bool PepperPluginDelegateImpl::MakeDirectory(
const GURL& path,
bool recursive,
fileapi::FileSystemCallbackDispatcher* dispatcher) {
FileSystemDispatcher* file_system_dispatcher =
ChildThread::current()->file_system_dispatcher();
return file_system_dispatcher->Create(
path, false, true, recursive, dispatcher);
}
bool PepperPluginDelegateImpl::Query(
const GURL& path,
fileapi::FileSystemCallbackDispatcher* dispatcher) {
FileSystemDispatcher* file_system_dispatcher =
ChildThread::current()->file_system_dispatcher();
return file_system_dispatcher->ReadMetadata(path, dispatcher);
}
bool PepperPluginDelegateImpl::Touch(
const GURL& path,
const base::Time& last_access_time,
const base::Time& last_modified_time,
fileapi::FileSystemCallbackDispatcher* dispatcher) {
FileSystemDispatcher* file_system_dispatcher =
ChildThread::current()->file_system_dispatcher();
return file_system_dispatcher->TouchFile(path, last_access_time,
last_modified_time, dispatcher);
}
bool PepperPluginDelegateImpl::Delete(
const GURL& path,
fileapi::FileSystemCallbackDispatcher* dispatcher) {
FileSystemDispatcher* file_system_dispatcher =
ChildThread::current()->file_system_dispatcher();
return file_system_dispatcher->Remove(path, false /* recursive */,
dispatcher);
}
bool PepperPluginDelegateImpl::Rename(
const GURL& file_path,
const GURL& new_file_path,
fileapi::FileSystemCallbackDispatcher* dispatcher) {
FileSystemDispatcher* file_system_dispatcher =
ChildThread::current()->file_system_dispatcher();
return file_system_dispatcher->Move(file_path, new_file_path, dispatcher);
}
bool PepperPluginDelegateImpl::ReadDirectory(
const GURL& directory_path,
fileapi::FileSystemCallbackDispatcher* dispatcher) {
FileSystemDispatcher* file_system_dispatcher =
ChildThread::current()->file_system_dispatcher();
return file_system_dispatcher->ReadDirectory(directory_path, dispatcher);
}
void PepperPluginDelegateImpl::QueryAvailableSpace(
const GURL& origin, quota::StorageType type,
const AvailableSpaceCallback& callback) {
ChildThread::current()->quota_dispatcher()->QueryStorageUsageAndQuota(
origin, type, new QuotaCallbackTranslator(callback));
}
void PepperPluginDelegateImpl::WillUpdateFile(const GURL& path) {
ChildThread::current()->Send(new FileSystemHostMsg_WillUpdate(path));
}
void PepperPluginDelegateImpl::DidUpdateFile(const GURL& path, int64_t delta) {
ChildThread::current()->Send(new FileSystemHostMsg_DidUpdate(path, delta));
}
class AsyncOpenFileSystemURLCallbackTranslator
: public fileapi::FileSystemCallbackDispatcher {
public:
AsyncOpenFileSystemURLCallbackTranslator(
const webkit::ppapi::PluginDelegate::AsyncOpenFileCallback& callback)
: callback_(callback) {
}
virtual ~AsyncOpenFileSystemURLCallbackTranslator() {}
virtual void DidSucceed() {
NOTREACHED();
}
virtual void DidReadMetadata(
const base::PlatformFileInfo& file_info,
const FilePath& platform_path) {
NOTREACHED();
}
virtual void DidReadDirectory(
const std::vector<base::FileUtilProxy::Entry>& entries,
bool has_more) {
NOTREACHED();
}
virtual void DidOpenFileSystem(const std::string& name,
const GURL& root) {
NOTREACHED();
}
virtual void DidFail(base::PlatformFileError error_code) {
base::PlatformFile invalid_file = base::kInvalidPlatformFileValue;
callback_.Run(error_code, base::PassPlatformFile(&invalid_file));
}
virtual void DidWrite(int64 bytes, bool complete) {
NOTREACHED();
}
virtual void DidOpenFile(
base::PlatformFile file,
base::ProcessHandle unused) {
callback_.Run(base::PLATFORM_FILE_OK, base::PassPlatformFile(&file));
// Make sure we won't leak file handle if the requester has died.
if (file != base::kInvalidPlatformFileValue) {
base::FileUtilProxy::Close(
RenderThreadImpl::current()->GetFileThreadMessageLoopProxy(), file,
base::FileUtilProxy::StatusCallback());
}
}
private: // TODO(ericu): Delete this?
webkit::ppapi::PluginDelegate::AsyncOpenFileCallback callback_;
};
bool PepperPluginDelegateImpl::AsyncOpenFileSystemURL(
const GURL& path, int flags, const AsyncOpenFileCallback& callback) {
FileSystemDispatcher* file_system_dispatcher =
ChildThread::current()->file_system_dispatcher();
return file_system_dispatcher->OpenFile(path, flags,
new AsyncOpenFileSystemURLCallbackTranslator(callback));
}
base::PlatformFileError PepperPluginDelegateImpl::OpenFile(
const webkit::ppapi::PepperFilePath& path,
int flags,
base::PlatformFile* file) {
IPC::PlatformFileForTransit transit_file;
base::PlatformFileError error;
IPC::Message* msg = new PepperFileMsg_OpenFile(
path, flags, &error, &transit_file);
if (!render_view_->Send(msg)) {
*file = base::kInvalidPlatformFileValue;
return base::PLATFORM_FILE_ERROR_FAILED;
}
*file = IPC::PlatformFileForTransitToPlatformFile(transit_file);
return error;
}
base::PlatformFileError PepperPluginDelegateImpl::RenameFile(
const webkit::ppapi::PepperFilePath& from_path,
const webkit::ppapi::PepperFilePath& to_path) {
base::PlatformFileError error;
IPC::Message* msg = new PepperFileMsg_RenameFile(from_path, to_path, &error);
if (!render_view_->Send(msg))
return base::PLATFORM_FILE_ERROR_FAILED;
return error;
}
base::PlatformFileError PepperPluginDelegateImpl::DeleteFileOrDir(
const webkit::ppapi::PepperFilePath& path,
bool recursive) {
base::PlatformFileError error;
IPC::Message* msg = new PepperFileMsg_DeleteFileOrDir(
path, recursive, &error);
if (!render_view_->Send(msg))
return base::PLATFORM_FILE_ERROR_FAILED;
return error;
}
base::PlatformFileError PepperPluginDelegateImpl::CreateDir(
const webkit::ppapi::PepperFilePath& path) {
base::PlatformFileError error;
IPC::Message* msg = new PepperFileMsg_CreateDir(path, &error);
if (!render_view_->Send(msg))
return base::PLATFORM_FILE_ERROR_FAILED;
return error;
}
base::PlatformFileError PepperPluginDelegateImpl::QueryFile(
const webkit::ppapi::PepperFilePath& path,
base::PlatformFileInfo* info) {
base::PlatformFileError error;
IPC::Message* msg = new PepperFileMsg_QueryFile(path, info, &error);
if (!render_view_->Send(msg))
return base::PLATFORM_FILE_ERROR_FAILED;
return error;
}
base::PlatformFileError PepperPluginDelegateImpl::GetDirContents(
const webkit::ppapi::PepperFilePath& path,
webkit::ppapi::DirContents* contents) {
base::PlatformFileError error;
IPC::Message* msg = new PepperFileMsg_GetDirContents(path, contents, &error);
if (!render_view_->Send(msg))
return base::PLATFORM_FILE_ERROR_FAILED;
return error;
}
void PepperPluginDelegateImpl::SyncGetFileSystemPlatformPath(
const GURL& url, FilePath* platform_path) {
RenderThreadImpl::current()->Send(new FileSystemHostMsg_SyncGetPlatformPath(
url, platform_path));
}
scoped_refptr<base::MessageLoopProxy>
PepperPluginDelegateImpl::GetFileThreadMessageLoopProxy() {
return RenderThreadImpl::current()->GetFileThreadMessageLoopProxy();
}
int32_t PepperPluginDelegateImpl::ConnectTcp(
webkit::ppapi::PPB_Flash_NetConnector_Impl* connector,
const char* host,
uint16_t port) {
int request_id = pending_connect_tcps_.Add(
new scoped_refptr<webkit::ppapi::PPB_Flash_NetConnector_Impl>(connector));
IPC::Message* msg =
new PepperMsg_ConnectTcp(render_view_->routing_id(),
request_id,
std::string(host),
port);
if (!render_view_->Send(msg)) {
pending_connect_tcps_.Remove(request_id);
return PP_ERROR_FAILED;
}
return PP_OK_COMPLETIONPENDING;
}
int32_t PepperPluginDelegateImpl::ConnectTcpAddress(
webkit::ppapi::PPB_Flash_NetConnector_Impl* connector,
const struct PP_Flash_NetAddress* addr) {
int request_id = pending_connect_tcps_.Add(
new scoped_refptr<webkit::ppapi::PPB_Flash_NetConnector_Impl>(connector));
IPC::Message* msg =
new PepperMsg_ConnectTcpAddress(render_view_->routing_id(),
request_id,
*addr);
if (!render_view_->Send(msg)) {
pending_connect_tcps_.Remove(request_id);
return PP_ERROR_FAILED;
}
return PP_OK_COMPLETIONPENDING;
}
void PepperPluginDelegateImpl::OnConnectTcpACK(
int request_id,
base::PlatformFile socket,
const PP_Flash_NetAddress& local_addr,
const PP_Flash_NetAddress& remote_addr) {
scoped_refptr<webkit::ppapi::PPB_Flash_NetConnector_Impl> connector =
*pending_connect_tcps_.Lookup(request_id);
pending_connect_tcps_.Remove(request_id);
connector->CompleteConnectTcp(socket, local_addr, remote_addr);
}
int32_t PepperPluginDelegateImpl::ShowContextMenu(
webkit::ppapi::PluginInstance* instance,
webkit::ppapi::PPB_Flash_Menu_Impl* menu,
const gfx::Point& position) {
int32 render_widget_id = render_view_->routing_id();
if (instance->FlashIsFullscreen(instance->pp_instance())) {
webkit::ppapi::FullscreenContainer* container =
instance->fullscreen_container();
DCHECK(container);
render_widget_id =
static_cast<RenderWidgetFullscreenPepper*>(container)->routing_id();
}
int request_id = pending_context_menus_.Add(
new scoped_refptr<webkit::ppapi::PPB_Flash_Menu_Impl>(menu));
ContextMenuParams params;
params.x = position.x();
params.y = position.y();
params.custom_context.is_pepper_menu = true;
params.custom_context.request_id = request_id;
params.custom_context.render_widget_id = render_widget_id;
params.custom_items = menu->menu_data();
// Transform the position to be in render view's coordinates.
if (instance->IsFullscreen(instance->pp_instance()) ||
instance->FlashIsFullscreen(instance->pp_instance())) {
WebKit::WebRect rect = render_view_->windowRect();
params.x -= rect.x;
params.y -= rect.y;
} else {
params.x += instance->position().x();
params.y += instance->position().y();
}
IPC::Message* msg = new ViewHostMsg_ContextMenu(render_view_->routing_id(),
params);
if (!render_view_->Send(msg)) {
pending_context_menus_.Remove(request_id);
return PP_ERROR_FAILED;
}
return PP_OK_COMPLETIONPENDING;
}
void PepperPluginDelegateImpl::OnContextMenuClosed(
const webkit_glue::CustomContextMenuContext& custom_context) {
int request_id = custom_context.request_id;
scoped_refptr<webkit::ppapi::PPB_Flash_Menu_Impl>* menu_ptr =
pending_context_menus_.Lookup(request_id);
if (!menu_ptr) {
NOTREACHED() << "CompleteShowContextMenu() called twice for the same menu.";
return;
}
scoped_refptr<webkit::ppapi::PPB_Flash_Menu_Impl> menu = *menu_ptr;
DCHECK(menu.get());
pending_context_menus_.Remove(request_id);
if (has_saved_context_menu_action_) {
menu->CompleteShow(PP_OK, saved_context_menu_action_);
has_saved_context_menu_action_ = false;
saved_context_menu_action_ = 0;
} else {
menu->CompleteShow(PP_ERROR_USERCANCEL, 0);
}
}
void PepperPluginDelegateImpl::OnCustomContextMenuAction(
const webkit_glue::CustomContextMenuContext& custom_context,
unsigned action) {
// Just save the action.
DCHECK(!has_saved_context_menu_action_);
has_saved_context_menu_action_ = true;
saved_context_menu_action_ = action;
}
webkit::ppapi::FullscreenContainer*
PepperPluginDelegateImpl::CreateFullscreenContainer(
webkit::ppapi::PluginInstance* instance) {
return render_view_->CreatePepperFullscreenContainer(instance);
}
gfx::Size PepperPluginDelegateImpl::GetScreenSize() {
WebKit::WebScreenInfo info = render_view_->screenInfo();
return gfx::Size(info.rect.width, info.rect.height);
}
std::string PepperPluginDelegateImpl::GetDefaultEncoding() {
// TODO(brettw) bug 56615: Somehow get the preference for the default
// encoding here rather than using the global default for the UI language.
return content::GetContentClient()->renderer()->GetDefaultEncoding();
}
void PepperPluginDelegateImpl::ZoomLimitsChanged(double minimum_factor,
double maximum_factor) {
double minimum_level = WebView::zoomFactorToZoomLevel(minimum_factor);
double maximum_level = WebView::zoomFactorToZoomLevel(maximum_factor);
render_view_->webview()->zoomLimitsChanged(minimum_level, maximum_level);
}
std::string PepperPluginDelegateImpl::ResolveProxy(const GURL& url) {
bool result;
std::string proxy_result;
RenderThreadImpl::current()->Send(
new ViewHostMsg_ResolveProxy(url, &result, &proxy_result));
return proxy_result;
}
void PepperPluginDelegateImpl::DidStartLoading() {
render_view_->DidStartLoadingForPlugin();
}
void PepperPluginDelegateImpl::DidStopLoading() {
render_view_->DidStopLoadingForPlugin();
}
void PepperPluginDelegateImpl::SetContentRestriction(int restrictions) {
render_view_->Send(new ViewHostMsg_UpdateContentRestrictions(
render_view_->routing_id(), restrictions));
}
void PepperPluginDelegateImpl::SaveURLAs(const GURL& url) {
render_view_->Send(new ViewHostMsg_SaveURLAs(
render_view_->routing_id(), url));
}
webkit_glue::P2PTransport* PepperPluginDelegateImpl::CreateP2PTransport() {
#if defined(ENABLE_P2P_APIS)
return new content::P2PTransportImpl(render_view_->p2p_socket_dispatcher());
#else
return NULL;
#endif
}
double PepperPluginDelegateImpl::GetLocalTimeZoneOffset(base::Time t) {
double result = 0.0;
render_view_->Send(new PepperMsg_GetLocalTimeZoneOffset(
t, &result));
return result;
}
std::string PepperPluginDelegateImpl::GetFlashCommandLineArgs() {
return CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
switches::kPpapiFlashArgs);
}
base::SharedMemory* PepperPluginDelegateImpl::CreateAnonymousSharedMemory(
uint32_t size) {
if (size == 0)
return NULL;
base::SharedMemoryHandle handle;
if (!render_view_->Send(
new ChildProcessHostMsg_SyncAllocateSharedMemory(size, &handle))) {
DLOG(WARNING) << "Browser allocation request message failed";
return NULL;
}
if (!base::SharedMemory::IsHandleValid(handle)) {
DLOG(WARNING) << "Browser failed to allocate shared memory";
return NULL;
}
return new base::SharedMemory(handle, false);
}
ppapi::Preferences PepperPluginDelegateImpl::GetPreferences() {
return ppapi::Preferences(render_view_->webkit_preferences());
}
void PepperPluginDelegateImpl::LockMouse(
webkit::ppapi::PluginInstance* instance) {
DCHECK(instance);
if (!MouseLockedOrPending()) {
DCHECK(!mouse_lock_owner_);
pending_lock_request_ = true;
mouse_lock_owner_ = instance;
render_view_->Send(
new ViewHostMsg_LockMouse(render_view_->routing_id()));
} else if (instance != mouse_lock_owner_) {
// Another plugin instance is using mouse lock. Fail immediately.
instance->OnLockMouseACK(PP_ERROR_FAILED);
} else {
if (mouse_locked_) {
instance->OnLockMouseACK(PP_OK);
} else if (pending_lock_request_) {
instance->OnLockMouseACK(PP_ERROR_INPROGRESS);
} else {
// The only case left here is
// !mouse_locked_ && !pending_lock_request_ && pending_unlock_request_,
// which is not possible.
NOTREACHED();
instance->OnLockMouseACK(PP_ERROR_FAILED);
}
}
}
void PepperPluginDelegateImpl::UnlockMouse(
webkit::ppapi::PluginInstance* instance) {
DCHECK(instance);
// If no one is using mouse lock or the user is not |instance|, ignore
// the unlock request.
if (MouseLockedOrPending() && mouse_lock_owner_ == instance) {
if (mouse_locked_ || pending_lock_request_) {
DCHECK(!mouse_locked_ || !pending_lock_request_);
if (!pending_unlock_request_) {
pending_unlock_request_ = true;
render_view_->Send(
new ViewHostMsg_UnlockMouse(render_view_->routing_id()));
}
} else {
// The only case left here is
// !mouse_locked_ && !pending_lock_request_ && pending_unlock_request_,
// which is not possible.
NOTREACHED();
}
}
}
void PepperPluginDelegateImpl::DidChangeCursor(
webkit::ppapi::PluginInstance* instance,
const WebKit::WebCursorInfo& cursor) {
// Update the cursor appearance immediately if the requesting plugin is the
// one which receives the last mouse event. Otherwise, the new cursor won't be
// picked up until the plugin gets the next input event. That is bad if, e.g.,
// the plugin would like to set an invisible cursor when there isn't any user
// input for a while.
if (instance == last_mouse_event_target_)
render_view_->didChangeCursor(cursor);
}
void PepperPluginDelegateImpl::DidReceiveMouseEvent(
webkit::ppapi::PluginInstance* instance) {
last_mouse_event_target_ = instance;
}
bool PepperPluginDelegateImpl::IsInFullscreenMode() {
return render_view_->is_fullscreen();
}
int PepperPluginDelegateImpl::GetRoutingId() const {
return render_view_->routing_id();
}
RendererGLContext*
PepperPluginDelegateImpl::GetParentContextForPlatformContext3D() {
WebGraphicsContext3DCommandBufferImpl* context =
static_cast<WebGraphicsContext3DCommandBufferImpl*>(
render_view_->webview()->graphicsContext3D());
if (!context)
return NULL;
if (!context->makeContextCurrent() || context->isContextLost())
return NULL;
RendererGLContext* parent_context = context->context();
if (!parent_context)
return NULL;
return parent_context;
}