pepper_flash_clipboard_message_filter.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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<base::string16, std::string>& data, 84 Pickle* pickle) { 85 pickle->WriteUInt64(data.size()); 86 for (std::map<base::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 PPAPI_DISPATCH_HOST_RESOURCE_CALL( 141 PpapiHostMsg_FlashClipboard_GetSequenceNumber, 142 OnMsgGetSequenceNumber); 143 IPC_END_MESSAGE_MAP() 144 return PP_ERROR_FAILED; 145} 146 147int32_t PepperFlashClipboardMessageFilter::OnMsgRegisterCustomFormat( 148 ppapi::host::HostMessageContext* host_context, 149 const std::string& format_name) { 150 uint32_t format = custom_formats_.RegisterFormat(format_name); 151 if (format == PP_FLASH_CLIPBOARD_FORMAT_INVALID) 152 return PP_ERROR_FAILED; 153 host_context->reply_msg = 154 PpapiPluginMsg_FlashClipboard_RegisterCustomFormatReply(format); 155 return PP_OK; 156} 157 158int32_t PepperFlashClipboardMessageFilter::OnMsgIsFormatAvailable( 159 ppapi::host::HostMessageContext* host_context, 160 uint32_t clipboard_type, 161 uint32_t format) { 162 if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) { 163 NOTIMPLEMENTED(); 164 return PP_ERROR_FAILED; 165 } 166 167 ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); 168 ui::ClipboardType type = ConvertClipboardType(clipboard_type); 169 bool available = false; 170 switch (format) { 171 case PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT: { 172 bool plain = clipboard->IsFormatAvailable( 173 ui::Clipboard::GetPlainTextFormatType(), type); 174 bool plainw = clipboard->IsFormatAvailable( 175 ui::Clipboard::GetPlainTextWFormatType(), type); 176 available = plain || plainw; 177 break; 178 } 179 case PP_FLASH_CLIPBOARD_FORMAT_HTML: 180 available = clipboard->IsFormatAvailable( 181 ui::Clipboard::GetHtmlFormatType(), type); 182 break; 183 case PP_FLASH_CLIPBOARD_FORMAT_RTF: 184 available = 185 clipboard->IsFormatAvailable(ui::Clipboard::GetRtfFormatType(), type); 186 break; 187 case PP_FLASH_CLIPBOARD_FORMAT_INVALID: 188 break; 189 default: 190 if (custom_formats_.IsFormatRegistered(format)) { 191 std::string format_name = custom_formats_.GetFormatName(format); 192 std::string clipboard_data; 193 clipboard->ReadData( 194 ui::Clipboard::GetPepperCustomDataFormatType(), &clipboard_data); 195 Pickle pickle(clipboard_data.data(), clipboard_data.size()); 196 available = 197 IsFormatAvailableInPickle(base::UTF8ToUTF16(format_name), pickle); 198 } 199 break; 200 } 201 202 return available ? PP_OK : PP_ERROR_FAILED; 203} 204 205int32_t PepperFlashClipboardMessageFilter::OnMsgReadData( 206 ppapi::host::HostMessageContext* host_context, 207 uint32_t clipboard_type, 208 uint32_t format) { 209 if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) { 210 NOTIMPLEMENTED(); 211 return PP_ERROR_FAILED; 212 } 213 214 ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); 215 ui::ClipboardType type = ConvertClipboardType(clipboard_type); 216 std::string clipboard_string; 217 int32_t result = PP_ERROR_FAILED; 218 switch (format) { 219 case PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT: { 220 if (clipboard->IsFormatAvailable( 221 ui::Clipboard::GetPlainTextWFormatType(), type)) { 222 base::string16 text; 223 clipboard->ReadText(type, &text); 224 if (!text.empty()) { 225 result = PP_OK; 226 clipboard_string = base::UTF16ToUTF8(text); 227 break; 228 } 229 } 230 // If the PlainTextW format isn't available or is empty, take the 231 // ASCII text format. 232 if (clipboard->IsFormatAvailable(ui::Clipboard::GetPlainTextFormatType(), 233 type)) { 234 result = PP_OK; 235 clipboard->ReadAsciiText(type, &clipboard_string); 236 } 237 break; 238 } 239 case PP_FLASH_CLIPBOARD_FORMAT_HTML: { 240 if (!clipboard->IsFormatAvailable(ui::Clipboard::GetHtmlFormatType(), 241 type)) { 242 break; 243 } 244 245 base::string16 html; 246 std::string url; 247 uint32 fragment_start; 248 uint32 fragment_end; 249 clipboard->ReadHTML(type, &html, &url, &fragment_start, &fragment_end); 250 result = PP_OK; 251 clipboard_string = base::UTF16ToUTF8( 252 html.substr(fragment_start, fragment_end - fragment_start)); 253 break; 254 } 255 case PP_FLASH_CLIPBOARD_FORMAT_RTF: { 256 if (!clipboard->IsFormatAvailable( 257 ui::Clipboard::GetRtfFormatType(), type)) { 258 break; 259 } 260 result = PP_OK; 261 clipboard->ReadRTF(type, &clipboard_string); 262 break; 263 } 264 case PP_FLASH_CLIPBOARD_FORMAT_INVALID: 265 break; 266 default: { 267 if (custom_formats_.IsFormatRegistered(format)) { 268 base::string16 format_name = 269 base::UTF8ToUTF16(custom_formats_.GetFormatName(format)); 270 std::string clipboard_data; 271 clipboard->ReadData( 272 ui::Clipboard::GetPepperCustomDataFormatType(), &clipboard_data); 273 Pickle pickle(clipboard_data.data(), clipboard_data.size()); 274 if (IsFormatAvailableInPickle(format_name, pickle)) { 275 result = PP_OK; 276 clipboard_string = ReadDataFromPickle(format_name, pickle); 277 } 278 } 279 break; 280 } 281 } 282 283 if (result == PP_OK) { 284 host_context->reply_msg = 285 PpapiPluginMsg_FlashClipboard_ReadDataReply(clipboard_string); 286 } 287 return result; 288} 289 290int32_t PepperFlashClipboardMessageFilter::OnMsgWriteData( 291 ppapi::host::HostMessageContext* host_context, 292 uint32_t clipboard_type, 293 const std::vector<uint32_t>& formats, 294 const std::vector<std::string>& data) { 295 if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) { 296 NOTIMPLEMENTED(); 297 return PP_ERROR_FAILED; 298 } 299 if (formats.size() != data.size()) 300 return PP_ERROR_FAILED; 301 302 ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); 303 ui::ClipboardType type = ConvertClipboardType(clipboard_type); 304 // If no formats are passed in clear the clipboard. 305 if (formats.size() == 0) { 306 clipboard->Clear(type); 307 return PP_OK; 308 } 309 310 ui::ScopedClipboardWriter scw(clipboard, type); 311 std::map<base::string16, std::string> custom_data_map; 312 int32_t res = PP_OK; 313 for (uint32_t i = 0; i < formats.size(); ++i) { 314 if (data[i].length() > kMaxClipboardWriteSize) { 315 res = PP_ERROR_NOSPACE; 316 break; 317 } 318 319 switch (formats[i]) { 320 case PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT: 321 scw.WriteText(base::UTF8ToUTF16(data[i])); 322 break; 323 case PP_FLASH_CLIPBOARD_FORMAT_HTML: 324 scw.WriteHTML(base::UTF8ToUTF16(data[i]), std::string()); 325 break; 326 case PP_FLASH_CLIPBOARD_FORMAT_RTF: 327 scw.WriteRTF(data[i]); 328 break; 329 case PP_FLASH_CLIPBOARD_FORMAT_INVALID: 330 res = PP_ERROR_BADARGUMENT; 331 break; 332 default: 333 if (custom_formats_.IsFormatRegistered(formats[i])) { 334 std::string format_name = custom_formats_.GetFormatName(formats[i]); 335 custom_data_map[base::UTF8ToUTF16(format_name)] = data[i]; 336 } else { 337 // Invalid format. 338 res = PP_ERROR_BADARGUMENT; 339 break; 340 } 341 } 342 343 if (res != PP_OK) 344 break; 345 } 346 347 if (custom_data_map.size() > 0) { 348 Pickle pickle; 349 if (WriteDataToPickle(custom_data_map, &pickle)) { 350 scw.WritePickledData(pickle, 351 ui::Clipboard::GetPepperCustomDataFormatType()); 352 } else { 353 res = PP_ERROR_BADARGUMENT; 354 } 355 } 356 357 if (res != PP_OK) { 358 // Need to clear the objects so nothing is written. 359 scw.Reset(); 360 } 361 362 return res; 363} 364 365int32_t PepperFlashClipboardMessageFilter::OnMsgGetSequenceNumber( 366 ppapi::host::HostMessageContext* host_context, 367 uint32_t clipboard_type) { 368 if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) { 369 NOTIMPLEMENTED(); 370 return PP_ERROR_FAILED; 371 } 372 373 ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); 374 ui::ClipboardType type = ConvertClipboardType(clipboard_type); 375 int64_t sequence_number = clipboard->GetSequenceNumber(type); 376 host_context->reply_msg = 377 PpapiPluginMsg_FlashClipboard_GetSequenceNumberReply(sequence_number); 378 return PP_OK; 379} 380 381} // namespace chrome 382