pepper_flash_clipboard_message_filter.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/renderer_host/pepper/pepper_flash_clipboard_message_filter.h" 6 7#include "base/pickle.h" 8#include "base/strings/utf_string_conversions.h" 9#include "content/public/browser/browser_thread.h" 10#include "ipc/ipc_message.h" 11#include "ipc/ipc_message_macros.h" 12#include "ppapi/c/pp_errors.h" 13#include "ppapi/c/private/ppb_flash_clipboard.h" 14#include "ppapi/host/dispatch_host_message.h" 15#include "ppapi/host/host_message_context.h" 16#include "ppapi/host/ppapi_host.h" 17#include "ppapi/proxy/ppapi_messages.h" 18#include "ppapi/proxy/resource_message_params.h" 19#include "ui/base/clipboard/scoped_clipboard_writer.h" 20 21using content::BrowserThread; 22 23namespace chrome { 24 25namespace { 26 27const size_t kMaxClipboardWriteSize = 1000000; 28 29ui::ClipboardType ConvertClipboardType(uint32_t type) { 30 switch (type) { 31 case PP_FLASH_CLIPBOARD_TYPE_STANDARD: 32 return ui::CLIPBOARD_TYPE_COPY_PASTE; 33 case PP_FLASH_CLIPBOARD_TYPE_SELECTION: 34 return ui::CLIPBOARD_TYPE_SELECTION; 35 } 36 NOTREACHED(); 37 return ui::CLIPBOARD_TYPE_COPY_PASTE; 38} 39 40// Functions to pack/unpack custom data from a pickle. See the header file for 41// more detail on custom formats in Pepper. 42// TODO(raymes): Currently pepper custom formats are stored in their own 43// native format type. However we should be able to store them in the same way 44// as "Web Custom" formats are. This would allow clipboard data to be shared 45// between pepper applications and web applications. However currently web apps 46// assume all data that is placed on the clipboard is UTF16 and pepper allows 47// arbitrary data so this change would require some reworking of the chrome 48// clipboard interface for custom data. 49bool JumpToFormatInPickle(const base::string16& format, PickleIterator* iter) { 50 uint64 size = 0; 51 if (!iter->ReadUInt64(&size)) 52 return false; 53 for (uint64 i = 0; i < size; ++i) { 54 base::string16 stored_format; 55 if (!iter->ReadString16(&stored_format)) 56 return false; 57 if (stored_format == format) 58 return true; 59 int skip_length; 60 if (!iter->ReadLength(&skip_length)) 61 return false; 62 if (!iter->SkipBytes(skip_length)) 63 return false; 64 } 65 return false; 66} 67 68bool IsFormatAvailableInPickle(const base::string16& format, 69 const Pickle& pickle) { 70 PickleIterator iter(pickle); 71 return JumpToFormatInPickle(format, &iter); 72} 73 74std::string ReadDataFromPickle(const base::string16& format, 75 const Pickle& pickle) { 76 std::string result; 77 PickleIterator iter(pickle); 78 if (!JumpToFormatInPickle(format, &iter) || !iter.ReadString(&result)) 79 return std::string(); 80 return result; 81} 82 83bool WriteDataToPickle(const std::map<string16, std::string>& data, 84 Pickle* pickle) { 85 pickle->WriteUInt64(data.size()); 86 for (std::map<string16, std::string>::const_iterator it = data.begin(); 87 it != data.end(); ++it) { 88 if (!pickle->WriteString16(it->first)) 89 return false; 90 if (!pickle->WriteString(it->second)) 91 return false; 92 } 93 return true; 94} 95 96} // namespace 97 98PepperFlashClipboardMessageFilter::PepperFlashClipboardMessageFilter() { 99} 100 101PepperFlashClipboardMessageFilter::~PepperFlashClipboardMessageFilter() { 102} 103 104scoped_refptr<base::TaskRunner> 105PepperFlashClipboardMessageFilter::OverrideTaskRunnerForMessage( 106 const IPC::Message& msg) { 107 // Clipboard writes should always occur on the UI thread due to the 108 // restrictions of various platform APIs. In general, the clipboard is not 109 // thread-safe, so all clipboard calls should be serviced from the UI thread. 110 if (msg.type() == PpapiHostMsg_FlashClipboard_WriteData::ID) 111 return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI); 112 113 // Windows needs clipboard reads to be serviced from the IO thread because 114 // these are sync IPCs which can result in deadlocks with plugins if serviced 115 // from the UI thread. Note that Windows clipboard calls ARE thread-safe so it 116 // is ok for reads and writes to be serviced from different threads. 117#if !defined(OS_WIN) 118 return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI); 119#else 120 return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO); 121#endif 122} 123 124int32_t PepperFlashClipboardMessageFilter::OnResourceMessageReceived( 125 const IPC::Message& msg, 126 ppapi::host::HostMessageContext* context) { 127 IPC_BEGIN_MESSAGE_MAP(PepperFlashClipboardMessageFilter, msg) 128 PPAPI_DISPATCH_HOST_RESOURCE_CALL( 129 PpapiHostMsg_FlashClipboard_RegisterCustomFormat, 130 OnMsgRegisterCustomFormat); 131 PPAPI_DISPATCH_HOST_RESOURCE_CALL( 132 PpapiHostMsg_FlashClipboard_IsFormatAvailable, 133 OnMsgIsFormatAvailable); 134 PPAPI_DISPATCH_HOST_RESOURCE_CALL( 135 PpapiHostMsg_FlashClipboard_ReadData, 136 OnMsgReadData); 137 PPAPI_DISPATCH_HOST_RESOURCE_CALL( 138 PpapiHostMsg_FlashClipboard_WriteData, 139 OnMsgWriteData); 140 IPC_END_MESSAGE_MAP() 141 return PP_ERROR_FAILED; 142} 143 144int32_t PepperFlashClipboardMessageFilter::OnMsgRegisterCustomFormat( 145 ppapi::host::HostMessageContext* host_context, 146 const std::string& format_name) { 147 uint32_t format = custom_formats_.RegisterFormat(format_name); 148 if (format == PP_FLASH_CLIPBOARD_FORMAT_INVALID) 149 return PP_ERROR_FAILED; 150 host_context->reply_msg = 151 PpapiPluginMsg_FlashClipboard_RegisterCustomFormatReply(format); 152 return PP_OK; 153} 154 155int32_t PepperFlashClipboardMessageFilter::OnMsgIsFormatAvailable( 156 ppapi::host::HostMessageContext* host_context, 157 uint32_t clipboard_type, 158 uint32_t format) { 159 if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) { 160 NOTIMPLEMENTED(); 161 return PP_ERROR_FAILED; 162 } 163 164 ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); 165 ui::ClipboardType type = ConvertClipboardType(clipboard_type); 166 bool available = false; 167 switch (format) { 168 case PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT: { 169 bool plain = clipboard->IsFormatAvailable( 170 ui::Clipboard::GetPlainTextFormatType(), type); 171 bool plainw = clipboard->IsFormatAvailable( 172 ui::Clipboard::GetPlainTextWFormatType(), type); 173 available = plain || plainw; 174 break; 175 } 176 case PP_FLASH_CLIPBOARD_FORMAT_HTML: 177 available = clipboard->IsFormatAvailable( 178 ui::Clipboard::GetHtmlFormatType(), type); 179 break; 180 case PP_FLASH_CLIPBOARD_FORMAT_RTF: 181 available = 182 clipboard->IsFormatAvailable(ui::Clipboard::GetRtfFormatType(), type); 183 break; 184 case PP_FLASH_CLIPBOARD_FORMAT_INVALID: 185 break; 186 default: 187 if (custom_formats_.IsFormatRegistered(format)) { 188 std::string format_name = custom_formats_.GetFormatName(format); 189 std::string clipboard_data; 190 clipboard->ReadData( 191 ui::Clipboard::GetPepperCustomDataFormatType(), &clipboard_data); 192 Pickle pickle(clipboard_data.data(), clipboard_data.size()); 193 available = IsFormatAvailableInPickle(UTF8ToUTF16(format_name), pickle); 194 } 195 break; 196 } 197 198 return available ? PP_OK : PP_ERROR_FAILED; 199} 200 201int32_t PepperFlashClipboardMessageFilter::OnMsgReadData( 202 ppapi::host::HostMessageContext* host_context, 203 uint32_t clipboard_type, 204 uint32_t format) { 205 if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) { 206 NOTIMPLEMENTED(); 207 return PP_ERROR_FAILED; 208 } 209 210 ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); 211 ui::ClipboardType type = ConvertClipboardType(clipboard_type); 212 std::string clipboard_string; 213 int32_t result = PP_ERROR_FAILED; 214 switch (format) { 215 case PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT: { 216 if (clipboard->IsFormatAvailable( 217 ui::Clipboard::GetPlainTextWFormatType(), type)) { 218 base::string16 text; 219 clipboard->ReadText(type, &text); 220 if (!text.empty()) { 221 result = PP_OK; 222 clipboard_string = UTF16ToUTF8(text); 223 break; 224 } 225 } 226 // If the PlainTextW format isn't available or is empty, take the 227 // ASCII text format. 228 if (clipboard->IsFormatAvailable(ui::Clipboard::GetPlainTextFormatType(), 229 type)) { 230 result = PP_OK; 231 clipboard->ReadAsciiText(type, &clipboard_string); 232 } 233 break; 234 } 235 case PP_FLASH_CLIPBOARD_FORMAT_HTML: { 236 if (!clipboard->IsFormatAvailable(ui::Clipboard::GetHtmlFormatType(), 237 type)) { 238 break; 239 } 240 241 base::string16 html; 242 std::string url; 243 uint32 fragment_start; 244 uint32 fragment_end; 245 clipboard->ReadHTML(type, &html, &url, &fragment_start, &fragment_end); 246 result = PP_OK; 247 clipboard_string = UTF16ToUTF8( 248 html.substr(fragment_start, fragment_end - fragment_start)); 249 break; 250 } 251 case PP_FLASH_CLIPBOARD_FORMAT_RTF: { 252 if (!clipboard->IsFormatAvailable( 253 ui::Clipboard::GetRtfFormatType(), type)) { 254 break; 255 } 256 result = PP_OK; 257 clipboard->ReadRTF(type, &clipboard_string); 258 break; 259 } 260 case PP_FLASH_CLIPBOARD_FORMAT_INVALID: 261 break; 262 default: { 263 if (custom_formats_.IsFormatRegistered(format)) { 264 base::string16 format_name = 265 UTF8ToUTF16(custom_formats_.GetFormatName(format)); 266 std::string clipboard_data; 267 clipboard->ReadData( 268 ui::Clipboard::GetPepperCustomDataFormatType(), &clipboard_data); 269 Pickle pickle(clipboard_data.data(), clipboard_data.size()); 270 if (IsFormatAvailableInPickle(format_name, pickle)) { 271 result = PP_OK; 272 clipboard_string = ReadDataFromPickle(format_name, pickle); 273 } 274 } 275 break; 276 } 277 } 278 279 if (result == PP_OK) { 280 host_context->reply_msg = 281 PpapiPluginMsg_FlashClipboard_ReadDataReply(clipboard_string); 282 } 283 return result; 284} 285 286int32_t PepperFlashClipboardMessageFilter::OnMsgWriteData( 287 ppapi::host::HostMessageContext* host_context, 288 uint32_t clipboard_type, 289 const std::vector<uint32_t>& formats, 290 const std::vector<std::string>& data) { 291 if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) { 292 NOTIMPLEMENTED(); 293 return PP_ERROR_FAILED; 294 } 295 if (formats.size() != data.size()) 296 return PP_ERROR_FAILED; 297 298 ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); 299 ui::ClipboardType type = ConvertClipboardType(clipboard_type); 300 // If no formats are passed in clear the clipboard. 301 if (formats.size() == 0) { 302 clipboard->Clear(type); 303 return PP_OK; 304 } 305 306 ui::ScopedClipboardWriter scw(clipboard, type); 307 std::map<string16, std::string> custom_data_map; 308 int32_t res = PP_OK; 309 for (uint32_t i = 0; i < formats.size(); ++i) { 310 if (data[i].length() > kMaxClipboardWriteSize) { 311 res = PP_ERROR_NOSPACE; 312 break; 313 } 314 315 switch (formats[i]) { 316 case PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT: 317 scw.WriteText(UTF8ToUTF16(data[i])); 318 break; 319 case PP_FLASH_CLIPBOARD_FORMAT_HTML: 320 scw.WriteHTML(UTF8ToUTF16(data[i]), std::string()); 321 break; 322 case PP_FLASH_CLIPBOARD_FORMAT_RTF: 323 scw.WriteRTF(data[i]); 324 break; 325 case PP_FLASH_CLIPBOARD_FORMAT_INVALID: 326 res = PP_ERROR_BADARGUMENT; 327 break; 328 default: 329 if (custom_formats_.IsFormatRegistered(formats[i])) { 330 std::string format_name = custom_formats_.GetFormatName(formats[i]); 331 custom_data_map[UTF8ToUTF16(format_name)] = data[i]; 332 } else { 333 // Invalid format. 334 res = PP_ERROR_BADARGUMENT; 335 break; 336 } 337 } 338 339 if (res != PP_OK) 340 break; 341 } 342 343 if (custom_data_map.size() > 0) { 344 Pickle pickle; 345 if (WriteDataToPickle(custom_data_map, &pickle)) { 346 scw.WritePickledData(pickle, 347 ui::Clipboard::GetPepperCustomDataFormatType()); 348 } else { 349 res = PP_ERROR_BADARGUMENT; 350 } 351 } 352 353 if (res != PP_OK) { 354 // Need to clear the objects so nothing is written. 355 scw.Reset(); 356 } 357 358 return res; 359} 360 361} // namespace chrome 362