blob: 437397b24e619f1d1419934abcb92af8245159b5 [file] [log] [blame]
// 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 "chrome/renderer/pepper_plugin_delegate_impl.h"
#include "app/surface/transport_dib.h"
#include "base/logging.h"
#include "base/scoped_ptr.h"
#include "chrome/common/render_messages.h"
#include "chrome/renderer/audio_message_filter.h"
#include "chrome/renderer/render_view.h"
#include "third_party/WebKit/WebKit/chromium/public/WebFileChooserCompletion.h"
#include "third_party/WebKit/WebKit/chromium/public/WebFileChooserParams.h"
#include "webkit/glue/plugins/pepper_plugin_instance.h"
#if defined(OS_MACOSX)
#include "chrome/common/render_messages.h"
#include "chrome/renderer/render_thread.h"
#endif
namespace {
// Implements the Image2D using a TransportDIB.
class PlatformImage2DImpl : public pepper::PluginDelegate::PlatformImage2D {
public:
// This constructor will take ownership of the dib pointer.
PlatformImage2DImpl(int width, int height, TransportDIB* dib)
: width_(width),
height_(height),
dib_(dib) {
}
virtual skia::PlatformCanvas* Map() {
return dib_->GetPlatformCanvas(width_, height_);
}
virtual intptr_t GetSharedMemoryHandle() const {
return reinterpret_cast<intptr_t>(dib_.get());
}
private:
int width_;
int height_;
scoped_ptr<TransportDIB> dib_;
DISALLOW_COPY_AND_ASSIGN(PlatformImage2DImpl);
};
class PlatformAudioImpl
: public pepper::PluginDelegate::PlatformAudio,
public AudioMessageFilter::Delegate {
public:
explicit PlatformAudioImpl(scoped_refptr<AudioMessageFilter> filter)
: client_(NULL), filter_(filter), stream_id_(0) {
DCHECK(filter_);
}
virtual ~PlatformAudioImpl() {
// Make sure we have been shut down.
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,
pepper::PluginDelegate::PlatformAudio::Client* client);
virtual bool StartPlayback() {
return filter_ && filter_->Send(
new ViewHostMsg_PlayAudioStream(0, stream_id_));
}
virtual bool StopPlayback() {
return filter_ && filter_->Send(
new ViewHostMsg_PauseAudioStream(0, stream_id_));
}
virtual void ShutDown();
private:
virtual void OnRequestPacket(uint32 bytes_in_buffer,
const base::Time& message_timestamp) {
LOG(FATAL) << "Should never get OnRequestPacket in PlatformAudioImpl";
}
virtual void OnStateChanged(const ViewMsg_AudioStreamState_Params& 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.
pepper::PluginDelegate::PlatformAudio::Client* client_;
// MessageFilter used to send/receive IPC.
scoped_refptr<AudioMessageFilter> filter_;
// Our ID on the MessageFilter.
int32 stream_id_;
DISALLOW_COPY_AND_ASSIGN(PlatformAudioImpl);
};
bool PlatformAudioImpl::Initialize(
uint32_t sample_rate, uint32_t sample_count,
pepper::PluginDelegate::PlatformAudio::Client* client) {
DCHECK(client);
// Make sure we don't call init more than once.
DCHECK_EQ(0, stream_id_);
client_ = client;
ViewHostMsg_Audio_CreateStream_Params params;
params.format = AudioManager::AUDIO_PCM_LINEAR;
params.channels = 2;
params.sample_rate = sample_rate;
params.bits_per_sample = 16;
params.packet_size = sample_count * params.channels *
(params.bits_per_sample >> 3);
stream_id_ = filter_->AddDelegate(this);
return filter_->Send(new ViewHostMsg_CreateAudioStream(0, stream_id_, params,
true));
}
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);
client_->StreamCreated(handle, length, socket_handle);
}
void PlatformAudioImpl::ShutDown() {
// Make sure we don't call shutdown more than once.
if (!stream_id_) {
return;
}
filter_->Send(new ViewHostMsg_CloseAudioStream(0, stream_id_));
filter_->RemoveDelegate(stream_id_);
stream_id_ = 0;
client_ = NULL;
}
// Implements the VideoDecoder.
class PlatformVideoDecoderImpl
: public pepper::PluginDelegate::PlatformVideoDecoder {
public:
PlatformVideoDecoderImpl()
: input_buffer_size_(0),
next_dib_id_(0),
dib_(NULL) {
memset(&decoder_config_, 0, sizeof(decoder_config_));
memset(&flush_callback_, 0, sizeof(flush_callback_));
}
virtual bool Init(const PP_VideoDecoderConfig& decoder_config) {
decoder_config_ = decoder_config;
input_buffer_size_ = 1024 << 4;
// Allocate the transport DIB.
TransportDIB* dib = TransportDIB::Create(input_buffer_size_,
next_dib_id_++);
if (!dib)
return false;
// TODO(wjia): Create video decoder in GPU process.
return true;
}
virtual bool Decode(PP_VideoCompressedDataBuffer& input_buffer) {
// TODO(wjia): Implement me!
NOTIMPLEMENTED();
input_buffers_.push(&input_buffer);
// Copy input data to dib_ and send it to GPU video decoder.
return false;
}
virtual int32_t Flush(PP_CompletionCallback& callback) {
// TODO(wjia): Implement me!
NOTIMPLEMENTED();
// Do nothing if there is a flush pending.
if (flush_callback_.func)
return PP_ERROR_BADARGUMENT;
flush_callback_ = callback;
// Call GPU video decoder to flush.
return PP_ERROR_WOULDBLOCK;
}
virtual bool ReturnUncompressedDataBuffer(
PP_VideoUncompressedDataBuffer& buffer) {
// TODO(wjia): Implement me!
NOTIMPLEMENTED();
// Deliver the buffer to GPU video decoder.
return false;
}
void OnFlushDone() {
if (!flush_callback_.func)
return;
flush_callback_.func(flush_callback_.user_data, PP_OK);
flush_callback_.func = NULL;
}
virtual intptr_t GetSharedMemoryHandle() const {
return reinterpret_cast<intptr_t>(dib_.get());
}
private:
size_t input_buffer_size_;
int next_dib_id_;
scoped_ptr<TransportDIB> dib_;
PP_VideoDecoderConfig decoder_config_;
std::queue<PP_VideoCompressedDataBuffer*> input_buffers_;
PP_CompletionCallback flush_callback_;
DISALLOW_COPY_AND_ASSIGN(PlatformVideoDecoderImpl);
};
} // namespace
PepperPluginDelegateImpl::PepperPluginDelegateImpl(RenderView* render_view)
: render_view_(render_view) {
}
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<pepper::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<pepper::PluginInstance*> plugins = active_instances_;
for (std::set<pepper::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();
}
}
void PepperPluginDelegateImpl::InstanceCreated(
pepper::PluginInstance* instance) {
active_instances_.insert(instance);
}
void PepperPluginDelegateImpl::InstanceDeleted(
pepper::PluginInstance* instance) {
active_instances_.erase(instance);
}
pepper::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). Note that the
// TransportDIB is _not_ cached in the browser; this is because this memory
// gets flushed by the renderer into another TransportDIB that represents the
// page, which is then in turn flushed to the screen by the browser process.
// When |transport_dib_| goes out of scope in the dtor, all of its shared
// memory gets reclaimed.
TransportDIB::Handle dib_handle;
IPC::Message* msg = new ViewHostMsg_AllocTransportDIB(buffer_size,
false,
&dib_handle);
if (!RenderThread::current()->Send(msg))
return NULL;
if (!TransportDIB::is_valid(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);
}
pepper::PluginDelegate::PlatformVideoDecoder*
PepperPluginDelegateImpl::CreateVideoDecoder(
const PP_VideoDecoderConfig& decoder_config) {
scoped_ptr<PlatformVideoDecoderImpl> decoder(new PlatformVideoDecoderImpl());
if (!decoder->Init(decoder_config))
return NULL;
return decoder.release();
}
void PepperPluginDelegateImpl::DidChangeNumberOfFindResults(int identifier,
int total,
bool final_result) {
render_view_->reportFindInPageMatchCount(identifier, total, final_result);
}
void PepperPluginDelegateImpl::DidChangeSelectedFindResult(int identifier,
int index) {
render_view_->reportFindInPageSelection(
identifier, index + 1, WebKit::WebRect());
}
pepper::PluginDelegate::PlatformAudio* PepperPluginDelegateImpl::CreateAudio(
uint32_t sample_rate, uint32_t sample_count,
pepper::PluginDelegate::PlatformAudio::Client* client) {
scoped_ptr<PlatformAudioImpl> audio(
new PlatformAudioImpl(render_view_->audio_message_filter()));
if (audio->Initialize(sample_rate, sample_count, client)) {
return audio.release();
} else {
return NULL;
}
}
bool PepperPluginDelegateImpl::RunFileChooser(
const WebKit::WebFileChooserParams& params,
WebKit::WebFileChooserCompletion* chooser_completion) {
return render_view_->runFileChooser(params, chooser_completion);
}