blob: 5fc1914dd696f80c64b3651e34e6467ef4d6ce8f [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 "chrome/browser/renderer_host/pepper/pepper_flash_clipboard_message_filter.h"
#include "base/pickle.h"
#include "base/strings/utf_string_conversions.h"
#include "content/public/browser/browser_thread.h"
#include "ipc/ipc_message.h"
#include "ipc/ipc_message_macros.h"
#include "ppapi/c/pp_errors.h"
#include "ppapi/c/private/ppb_flash_clipboard.h"
#include "ppapi/host/dispatch_host_message.h"
#include "ppapi/host/host_message_context.h"
#include "ppapi/host/ppapi_host.h"
#include "ppapi/proxy/ppapi_messages.h"
#include "ppapi/proxy/resource_message_params.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
using content::BrowserThread;
namespace chrome {
namespace {
const size_t kMaxClipboardWriteSize = 1000000;
ui::ClipboardType ConvertClipboardType(uint32_t type) {
switch (type) {
case PP_FLASH_CLIPBOARD_TYPE_STANDARD:
return ui::CLIPBOARD_TYPE_COPY_PASTE;
case PP_FLASH_CLIPBOARD_TYPE_SELECTION:
return ui::CLIPBOARD_TYPE_SELECTION;
}
NOTREACHED();
return ui::CLIPBOARD_TYPE_COPY_PASTE;
}
// Functions to pack/unpack custom data from a pickle. See the header file for
// more detail on custom formats in Pepper.
// TODO(raymes): Currently pepper custom formats are stored in their own
// native format type. However we should be able to store them in the same way
// as "Web Custom" formats are. This would allow clipboard data to be shared
// between pepper applications and web applications. However currently web apps
// assume all data that is placed on the clipboard is UTF16 and pepper allows
// arbitrary data so this change would require some reworking of the chrome
// clipboard interface for custom data.
bool JumpToFormatInPickle(const string16& format, PickleIterator* iter) {
uint64 size = 0;
if (!iter->ReadUInt64(&size))
return false;
for (uint64 i = 0; i < size; ++i) {
string16 stored_format;
if (!iter->ReadString16(&stored_format))
return false;
if (stored_format == format)
return true;
int skip_length;
if (!iter->ReadLength(&skip_length))
return false;
if (!iter->SkipBytes(skip_length))
return false;
}
return false;
}
bool IsFormatAvailableInPickle(const string16& format, const Pickle& pickle) {
PickleIterator iter(pickle);
return JumpToFormatInPickle(format, &iter);
}
std::string ReadDataFromPickle(const string16& format, const Pickle& pickle) {
std::string result;
PickleIterator iter(pickle);
if (!JumpToFormatInPickle(format, &iter) || !iter.ReadString(&result))
return std::string();
return result;
}
bool WriteDataToPickle(const std::map<string16, std::string>& data,
Pickle* pickle) {
pickle->WriteUInt64(data.size());
for (std::map<string16, std::string>::const_iterator it = data.begin();
it != data.end(); ++it) {
if (!pickle->WriteString16(it->first))
return false;
if (!pickle->WriteString(it->second))
return false;
}
return true;
}
} // namespace
PepperFlashClipboardMessageFilter::PepperFlashClipboardMessageFilter() {
}
PepperFlashClipboardMessageFilter::~PepperFlashClipboardMessageFilter() {
}
scoped_refptr<base::TaskRunner>
PepperFlashClipboardMessageFilter::OverrideTaskRunnerForMessage(
const IPC::Message& msg) {
// Clipboard writes should always occur on the UI thread due to 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.
if (msg.type() == PpapiHostMsg_FlashClipboard_WriteData::ID)
return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
// Windows needs clipboard reads to be serviced from the IO thread because
// these are sync IPCs which can result in deadlocks with 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)
return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI);
#else
return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);
#endif
}
int32_t PepperFlashClipboardMessageFilter::OnResourceMessageReceived(
const IPC::Message& msg,
ppapi::host::HostMessageContext* context) {
IPC_BEGIN_MESSAGE_MAP(PepperFlashClipboardMessageFilter, msg)
PPAPI_DISPATCH_HOST_RESOURCE_CALL(
PpapiHostMsg_FlashClipboard_RegisterCustomFormat,
OnMsgRegisterCustomFormat);
PPAPI_DISPATCH_HOST_RESOURCE_CALL(
PpapiHostMsg_FlashClipboard_IsFormatAvailable,
OnMsgIsFormatAvailable);
PPAPI_DISPATCH_HOST_RESOURCE_CALL(
PpapiHostMsg_FlashClipboard_ReadData,
OnMsgReadData);
PPAPI_DISPATCH_HOST_RESOURCE_CALL(
PpapiHostMsg_FlashClipboard_WriteData,
OnMsgWriteData);
IPC_END_MESSAGE_MAP()
return PP_ERROR_FAILED;
}
int32_t PepperFlashClipboardMessageFilter::OnMsgRegisterCustomFormat(
ppapi::host::HostMessageContext* host_context,
const std::string& format_name) {
uint32_t format = custom_formats_.RegisterFormat(format_name);
if (format == PP_FLASH_CLIPBOARD_FORMAT_INVALID)
return PP_ERROR_FAILED;
host_context->reply_msg =
PpapiPluginMsg_FlashClipboard_RegisterCustomFormatReply(format);
return PP_OK;
}
int32_t PepperFlashClipboardMessageFilter::OnMsgIsFormatAvailable(
ppapi::host::HostMessageContext* host_context,
uint32_t clipboard_type,
uint32_t format) {
if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) {
NOTIMPLEMENTED();
return PP_ERROR_FAILED;
}
ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
ui::ClipboardType type = ConvertClipboardType(clipboard_type);
bool available = false;
switch (format) {
case PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT: {
bool plain = clipboard->IsFormatAvailable(
ui::Clipboard::GetPlainTextFormatType(), type);
bool plainw = clipboard->IsFormatAvailable(
ui::Clipboard::GetPlainTextWFormatType(), type);
available = plain || plainw;
break;
}
case PP_FLASH_CLIPBOARD_FORMAT_HTML:
available = clipboard->IsFormatAvailable(
ui::Clipboard::GetHtmlFormatType(), type);
break;
case PP_FLASH_CLIPBOARD_FORMAT_RTF:
available =
clipboard->IsFormatAvailable(ui::Clipboard::GetRtfFormatType(), type);
break;
case PP_FLASH_CLIPBOARD_FORMAT_INVALID:
break;
default:
if (custom_formats_.IsFormatRegistered(format)) {
std::string format_name = custom_formats_.GetFormatName(format);
std::string clipboard_data;
clipboard->ReadData(
ui::Clipboard::GetPepperCustomDataFormatType(), &clipboard_data);
Pickle pickle(clipboard_data.data(), clipboard_data.size());
available = IsFormatAvailableInPickle(UTF8ToUTF16(format_name), pickle);
}
break;
}
return available ? PP_OK : PP_ERROR_FAILED;
}
int32_t PepperFlashClipboardMessageFilter::OnMsgReadData(
ppapi::host::HostMessageContext* host_context,
uint32_t clipboard_type,
uint32_t format) {
if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) {
NOTIMPLEMENTED();
return PP_ERROR_FAILED;
}
ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
ui::ClipboardType type = ConvertClipboardType(clipboard_type);
std::string clipboard_string;
int32_t result = PP_ERROR_FAILED;
switch (format) {
case PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT: {
if (clipboard->IsFormatAvailable(
ui::Clipboard::GetPlainTextWFormatType(), type)) {
string16 text;
clipboard->ReadText(type, &text);
if (!text.empty()) {
result = PP_OK;
clipboard_string = UTF16ToUTF8(text);
break;
}
}
// If the PlainTextW format isn't available or is empty, take the
// ASCII text format.
if (clipboard->IsFormatAvailable(ui::Clipboard::GetPlainTextFormatType(),
type)) {
result = PP_OK;
clipboard->ReadAsciiText(type, &clipboard_string);
}
break;
}
case PP_FLASH_CLIPBOARD_FORMAT_HTML: {
if (!clipboard->IsFormatAvailable(ui::Clipboard::GetHtmlFormatType(),
type)) {
break;
}
string16 html;
std::string url;
uint32 fragment_start;
uint32 fragment_end;
clipboard->ReadHTML(type, &html, &url, &fragment_start, &fragment_end);
result = PP_OK;
clipboard_string = UTF16ToUTF8(
html.substr(fragment_start, fragment_end - fragment_start));
break;
}
case PP_FLASH_CLIPBOARD_FORMAT_RTF: {
if (!clipboard->IsFormatAvailable(
ui::Clipboard::GetRtfFormatType(), type)) {
break;
}
result = PP_OK;
clipboard->ReadRTF(type, &clipboard_string);
break;
}
case PP_FLASH_CLIPBOARD_FORMAT_INVALID:
break;
default: {
if (custom_formats_.IsFormatRegistered(format)) {
string16 format_name =
UTF8ToUTF16(custom_formats_.GetFormatName(format));
std::string clipboard_data;
clipboard->ReadData(
ui::Clipboard::GetPepperCustomDataFormatType(), &clipboard_data);
Pickle pickle(clipboard_data.data(), clipboard_data.size());
if (IsFormatAvailableInPickle(format_name, pickle)) {
result = PP_OK;
clipboard_string = ReadDataFromPickle(format_name, pickle);
}
}
break;
}
}
if (result == PP_OK) {
host_context->reply_msg =
PpapiPluginMsg_FlashClipboard_ReadDataReply(clipboard_string);
}
return result;
}
int32_t PepperFlashClipboardMessageFilter::OnMsgWriteData(
ppapi::host::HostMessageContext* host_context,
uint32_t clipboard_type,
const std::vector<uint32_t>& formats,
const std::vector<std::string>& data) {
if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) {
NOTIMPLEMENTED();
return PP_ERROR_FAILED;
}
if (formats.size() != data.size())
return PP_ERROR_FAILED;
ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
ui::ClipboardType type = ConvertClipboardType(clipboard_type);
// If no formats are passed in clear the clipboard.
if (formats.size() == 0) {
clipboard->Clear(type);
return PP_OK;
}
ui::ScopedClipboardWriter scw(clipboard, type);
std::map<string16, std::string> custom_data_map;
int32_t res = PP_OK;
for (uint32_t i = 0; i < formats.size(); ++i) {
if (data[i].length() > kMaxClipboardWriteSize) {
res = PP_ERROR_NOSPACE;
break;
}
switch (formats[i]) {
case PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT:
scw.WriteText(UTF8ToUTF16(data[i]));
break;
case PP_FLASH_CLIPBOARD_FORMAT_HTML:
scw.WriteHTML(UTF8ToUTF16(data[i]), std::string());
break;
case PP_FLASH_CLIPBOARD_FORMAT_RTF:
scw.WriteRTF(data[i]);
break;
case PP_FLASH_CLIPBOARD_FORMAT_INVALID:
res = PP_ERROR_BADARGUMENT;
break;
default:
if (custom_formats_.IsFormatRegistered(formats[i])) {
std::string format_name = custom_formats_.GetFormatName(formats[i]);
custom_data_map[UTF8ToUTF16(format_name)] = data[i];
} else {
// Invalid format.
res = PP_ERROR_BADARGUMENT;
break;
}
}
if (res != PP_OK)
break;
}
if (custom_data_map.size() > 0) {
Pickle pickle;
if (WriteDataToPickle(custom_data_map, &pickle)) {
scw.WritePickledData(pickle,
ui::Clipboard::GetPepperCustomDataFormatType());
} else {
res = PP_ERROR_BADARGUMENT;
}
}
if (res != PP_OK) {
// Need to clear the objects so nothing is written.
scw.Reset();
}
return res;
}
} // namespace chrome