blob: 46475dfd4d7b42c2431fc4f551dbc58cbe68a15e [file] [log] [blame]
// 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/browser/renderer_host/clipboard_message_filter.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/stl_util.h"
#include "content/common/clipboard_messages.h"
#include "content/public/browser/browser_context.h"
#include "ipc/ipc_message_macros.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/zlib/zlib.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/size.h"
#include "url/gurl.h"
namespace content {
#if defined(OS_WIN)
namespace {
// The write must be performed on the UI thread because the clipboard object
// from the IO thread cannot create windows so it cannot be the "owner" of the
// clipboard's contents. // See http://crbug.com/5823.
void WriteObjectsOnUIThread(ui::Clipboard::ObjectMap* objects) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
static ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
clipboard->WriteObjects(ui::Clipboard::BUFFER_STANDARD, *objects);
}
} // namespace
#endif
ClipboardMessageFilter::ClipboardMessageFilter() {}
void ClipboardMessageFilter::OverrideThreadForMessage(
const IPC::Message& message, BrowserThread::ID* thread) {
// Clipboard writes should always occur on the UI thread due the restrictions
// of various platform APIs. In general, the clipboard is not thread-safe, so
// all clipboard calls should be serviced from the UI thread.
//
// Windows needs clipboard reads to be serviced from the IO thread because
// these are sync IPCs which can result in deadlocks with NPAPI plugins if
// serviced from the UI thread. Note that Windows clipboard calls ARE
// thread-safe so it is ok for reads and writes to be serviced from different
// threads.
#if !defined(OS_WIN)
if (IPC_MESSAGE_CLASS(message) == ClipboardMsgStart)
*thread = BrowserThread::UI;
#endif
#if defined(OS_WIN)
if (message.type() == ClipboardHostMsg_ReadImage::ID)
*thread = BrowserThread::FILE;
#endif
}
bool ClipboardMessageFilter::OnMessageReceived(const IPC::Message& message,
bool* message_was_ok) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP_EX(ClipboardMessageFilter, message, *message_was_ok)
IPC_MESSAGE_HANDLER(ClipboardHostMsg_WriteObjectsAsync, OnWriteObjectsAsync)
IPC_MESSAGE_HANDLER(ClipboardHostMsg_WriteObjectsSync, OnWriteObjectsSync)
IPC_MESSAGE_HANDLER(ClipboardHostMsg_GetSequenceNumber, OnGetSequenceNumber)
IPC_MESSAGE_HANDLER(ClipboardHostMsg_IsFormatAvailable, OnIsFormatAvailable)
IPC_MESSAGE_HANDLER(ClipboardHostMsg_Clear, OnClear)
IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadAvailableTypes,
OnReadAvailableTypes)
IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadText, OnReadText)
IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadAsciiText, OnReadAsciiText)
IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadHTML, OnReadHTML)
IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadRTF, OnReadRTF)
IPC_MESSAGE_HANDLER_DELAY_REPLY(ClipboardHostMsg_ReadImage, OnReadImage)
IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadCustomData, OnReadCustomData)
IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadData, OnReadData)
#if defined(OS_MACOSX)
IPC_MESSAGE_HANDLER(ClipboardHostMsg_FindPboardWriteStringAsync,
OnFindPboardWriteString)
#endif
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
ClipboardMessageFilter::~ClipboardMessageFilter() {
}
void ClipboardMessageFilter::OnWriteObjectsSync(
ui::Clipboard::ObjectMap objects,
base::SharedMemoryHandle bitmap_handle) {
DCHECK(base::SharedMemory::IsHandleValid(bitmap_handle))
<< "Bad bitmap handle";
// Splice the shared memory handle into the clipboard data.
ui::Clipboard::ReplaceSharedMemHandle(&objects, bitmap_handle, PeerHandle());
#if defined(OS_WIN)
// We cannot write directly from the IO thread, and cannot service the IPC
// on the UI thread. We'll copy the relevant data and get a handle to any
// shared memory so it doesn't go away when we resume the renderer, and post
// a task to perform the write on the UI thread.
ui::Clipboard::ObjectMap* long_living_objects = new ui::Clipboard::ObjectMap;
long_living_objects->swap(objects);
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(&WriteObjectsOnUIThread, base::Owned(long_living_objects)));
#else
GetClipboard()->WriteObjects(ui::Clipboard::BUFFER_STANDARD, objects);
#endif
}
void ClipboardMessageFilter::OnWriteObjectsAsync(
const ui::Clipboard::ObjectMap& objects) {
#if defined(OS_WIN)
// We cannot write directly from the IO thread, and cannot service the IPC
// on the UI thread. We'll copy the relevant data and post a task to preform
// the write on the UI thread.
ui::Clipboard::ObjectMap* long_living_objects =
new ui::Clipboard::ObjectMap(objects);
// This async message doesn't support shared-memory based bitmaps; they must
// be removed otherwise we might dereference a rubbish pointer.
long_living_objects->erase(ui::Clipboard::CBF_SMBITMAP);
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(&WriteObjectsOnUIThread, base::Owned(long_living_objects)));
#else
GetClipboard()->WriteObjects(ui::Clipboard::BUFFER_STANDARD, objects);
#endif
}
void ClipboardMessageFilter::OnGetSequenceNumber(
ui::Clipboard::Buffer buffer, uint64* sequence_number) {
*sequence_number = GetClipboard()->GetSequenceNumber(buffer);
}
void ClipboardMessageFilter::OnReadAvailableTypes(
ui::Clipboard::Buffer buffer, std::vector<string16>* types,
bool* contains_filenames) {
GetClipboard()->ReadAvailableTypes(buffer, types, contains_filenames);
}
void ClipboardMessageFilter::OnIsFormatAvailable(
const ui::Clipboard::FormatType& format, ui::Clipboard::Buffer buffer,
bool* result) {
*result = GetClipboard()->IsFormatAvailable(format, buffer);
}
void ClipboardMessageFilter::OnClear(ui::Clipboard::Buffer buffer) {
GetClipboard()->Clear(buffer);
}
void ClipboardMessageFilter::OnReadText(
ui::Clipboard::Buffer buffer, string16* result) {
GetClipboard()->ReadText(buffer, result);
}
void ClipboardMessageFilter::OnReadAsciiText(
ui::Clipboard::Buffer buffer, std::string* result) {
GetClipboard()->ReadAsciiText(buffer, result);
}
void ClipboardMessageFilter::OnReadHTML(
ui::Clipboard::Buffer buffer, string16* markup, GURL* url,
uint32* fragment_start, uint32* fragment_end) {
std::string src_url_str;
GetClipboard()->ReadHTML(buffer, markup, &src_url_str, fragment_start,
fragment_end);
*url = GURL(src_url_str);
}
void ClipboardMessageFilter::OnReadRTF(
ui::Clipboard::Buffer buffer, std::string* result) {
GetClipboard()->ReadRTF(buffer, result);
}
void ClipboardMessageFilter::OnReadImage(
ui::Clipboard::Buffer buffer, IPC::Message* reply_msg) {
SkBitmap bitmap = GetClipboard()->ReadImage(buffer);
#if defined(USE_X11)
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
base::Bind(
&ClipboardMessageFilter::OnReadImageReply, this, bitmap, reply_msg));
#else
OnReadImageReply(bitmap, reply_msg);
#endif
}
void ClipboardMessageFilter::OnReadImageReply(
const SkBitmap& bitmap, IPC::Message* reply_msg) {
base::SharedMemoryHandle image_handle = base::SharedMemory::NULLHandle();
uint32 image_size = 0;
std::string reply_data;
if (!bitmap.isNull()) {
std::vector<unsigned char> png_data;
SkAutoLockPixels lock(bitmap);
if (gfx::PNGCodec::EncodeWithCompressionLevel(
static_cast<const unsigned char*>(bitmap.getPixels()),
gfx::PNGCodec::FORMAT_BGRA,
gfx::Size(bitmap.width(), bitmap.height()),
bitmap.rowBytes(),
false,
std::vector<gfx::PNGCodec::Comment>(),
Z_BEST_SPEED,
&png_data)) {
base::SharedMemory buffer;
if (buffer.CreateAndMapAnonymous(png_data.size())) {
memcpy(buffer.memory(), vector_as_array(&png_data), png_data.size());
if (buffer.GiveToProcess(PeerHandle(), &image_handle)) {
image_size = png_data.size();
}
}
}
}
ClipboardHostMsg_ReadImage::WriteReplyParams(reply_msg, image_handle,
image_size);
Send(reply_msg);
}
void ClipboardMessageFilter::OnReadCustomData(
ui::Clipboard::Buffer buffer, const string16& type, string16* result) {
GetClipboard()->ReadCustomData(buffer, type, result);
}
void ClipboardMessageFilter::OnReadData(const ui::Clipboard::FormatType& format,
std::string* data) {
GetClipboard()->ReadData(format, data);
}
// static
ui::Clipboard* ClipboardMessageFilter::GetClipboard() {
// We have a static instance of the clipboard service for use by all message
// filters. This instance lives for the life of the browser processes.
static ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
return clipboard;
}
} // namespace content