pepper_flash_clipboard_message_filter.cc revision a02191e04bc25c4935f804f2c080ae28663d096d
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(); 88 ++it) { 89 if (!pickle->WriteString16(it->first)) 90 return false; 91 if (!pickle->WriteString(it->second)) 92 return false; 93 } 94 return true; 95} 96 97} // namespace 98 99PepperFlashClipboardMessageFilter::PepperFlashClipboardMessageFilter() {} 100 101PepperFlashClipboardMessageFilter::~PepperFlashClipboardMessageFilter() {} 102 103scoped_refptr<base::TaskRunner> 104PepperFlashClipboardMessageFilter::OverrideTaskRunnerForMessage( 105 const IPC::Message& msg) { 106 // Clipboard writes should always occur on the UI thread due to the 107 // restrictions of various platform APIs. In general, the clipboard is not 108 // thread-safe, so all clipboard calls should be serviced from the UI thread. 109 if (msg.type() == PpapiHostMsg_FlashClipboard_WriteData::ID) 110 return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI); 111 112// Windows needs clipboard reads to be serviced from the IO thread because 113// these are sync IPCs which can result in deadlocks with plugins if serviced 114// from the UI thread. Note that Windows clipboard calls ARE thread-safe so it 115// is ok for reads and writes to be serviced from different threads. 116#if !defined(OS_WIN) 117 return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI); 118#else 119 return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO); 120#endif 121} 122 123int32_t PepperFlashClipboardMessageFilter::OnResourceMessageReceived( 124 const IPC::Message& msg, 125 ppapi::host::HostMessageContext* context) { 126 IPC_BEGIN_MESSAGE_MAP(PepperFlashClipboardMessageFilter, msg) 127 PPAPI_DISPATCH_HOST_RESOURCE_CALL( 128 PpapiHostMsg_FlashClipboard_RegisterCustomFormat, 129 OnMsgRegisterCustomFormat); 130 PPAPI_DISPATCH_HOST_RESOURCE_CALL( 131 PpapiHostMsg_FlashClipboard_IsFormatAvailable, OnMsgIsFormatAvailable); 132 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FlashClipboard_ReadData, 133 OnMsgReadData); 134 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FlashClipboard_WriteData, 135 OnMsgWriteData); 136 PPAPI_DISPATCH_HOST_RESOURCE_CALL( 137 PpapiHostMsg_FlashClipboard_GetSequenceNumber, OnMsgGetSequenceNumber); 138 IPC_END_MESSAGE_MAP() 139 return PP_ERROR_FAILED; 140} 141 142int32_t PepperFlashClipboardMessageFilter::OnMsgRegisterCustomFormat( 143 ppapi::host::HostMessageContext* host_context, 144 const std::string& format_name) { 145 uint32_t format = custom_formats_.RegisterFormat(format_name); 146 if (format == PP_FLASH_CLIPBOARD_FORMAT_INVALID) 147 return PP_ERROR_FAILED; 148 host_context->reply_msg = 149 PpapiPluginMsg_FlashClipboard_RegisterCustomFormatReply(format); 150 return PP_OK; 151} 152 153int32_t PepperFlashClipboardMessageFilter::OnMsgIsFormatAvailable( 154 ppapi::host::HostMessageContext* host_context, 155 uint32_t clipboard_type, 156 uint32_t format) { 157 if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) { 158 NOTIMPLEMENTED(); 159 return PP_ERROR_FAILED; 160 } 161 162 ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); 163 ui::ClipboardType type = ConvertClipboardType(clipboard_type); 164 bool available = false; 165 switch (format) { 166 case PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT: { 167 bool plain = clipboard->IsFormatAvailable( 168 ui::Clipboard::GetPlainTextFormatType(), type); 169 bool plainw = clipboard->IsFormatAvailable( 170 ui::Clipboard::GetPlainTextWFormatType(), type); 171 available = plain || plainw; 172 break; 173 } 174 case PP_FLASH_CLIPBOARD_FORMAT_HTML: 175 available = clipboard->IsFormatAvailable( 176 ui::Clipboard::GetHtmlFormatType(), type); 177 break; 178 case PP_FLASH_CLIPBOARD_FORMAT_RTF: 179 available = 180 clipboard->IsFormatAvailable(ui::Clipboard::GetRtfFormatType(), type); 181 break; 182 case PP_FLASH_CLIPBOARD_FORMAT_INVALID: 183 break; 184 default: 185 if (custom_formats_.IsFormatRegistered(format)) { 186 std::string format_name = custom_formats_.GetFormatName(format); 187 std::string clipboard_data; 188 clipboard->ReadData(ui::Clipboard::GetPepperCustomDataFormatType(), 189 &clipboard_data); 190 Pickle pickle(clipboard_data.data(), clipboard_data.size()); 191 available = 192 IsFormatAvailableInPickle(base::UTF8ToUTF16(format_name), pickle); 193 } 194 break; 195 } 196 197 return available ? PP_OK : PP_ERROR_FAILED; 198} 199 200int32_t PepperFlashClipboardMessageFilter::OnMsgReadData( 201 ppapi::host::HostMessageContext* host_context, 202 uint32_t clipboard_type, 203 uint32_t format) { 204 if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) { 205 NOTIMPLEMENTED(); 206 return PP_ERROR_FAILED; 207 } 208 209 ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); 210 ui::ClipboardType type = ConvertClipboardType(clipboard_type); 211 std::string clipboard_string; 212 int32_t result = PP_ERROR_FAILED; 213 switch (format) { 214 case PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT: { 215 if (clipboard->IsFormatAvailable(ui::Clipboard::GetPlainTextWFormatType(), 216 type)) { 217 base::string16 text; 218 clipboard->ReadText(type, &text); 219 if (!text.empty()) { 220 result = PP_OK; 221 clipboard_string = base::UTF16ToUTF8(text); 222 break; 223 } 224 } 225 // If the PlainTextW format isn't available or is empty, take the 226 // ASCII text format. 227 if (clipboard->IsFormatAvailable(ui::Clipboard::GetPlainTextFormatType(), 228 type)) { 229 result = PP_OK; 230 clipboard->ReadAsciiText(type, &clipboard_string); 231 } 232 break; 233 } 234 case PP_FLASH_CLIPBOARD_FORMAT_HTML: { 235 if (!clipboard->IsFormatAvailable(ui::Clipboard::GetHtmlFormatType(), 236 type)) { 237 break; 238 } 239 240 base::string16 html; 241 std::string url; 242 uint32 fragment_start; 243 uint32 fragment_end; 244 clipboard->ReadHTML(type, &html, &url, &fragment_start, &fragment_end); 245 result = PP_OK; 246 clipboard_string = base::UTF16ToUTF8( 247 html.substr(fragment_start, fragment_end - fragment_start)); 248 break; 249 } 250 case PP_FLASH_CLIPBOARD_FORMAT_RTF: { 251 if (!clipboard->IsFormatAvailable(ui::Clipboard::GetRtfFormatType(), 252 type)) { 253 break; 254 } 255 result = PP_OK; 256 clipboard->ReadRTF(type, &clipboard_string); 257 break; 258 } 259 case PP_FLASH_CLIPBOARD_FORMAT_INVALID: 260 break; 261 default: { 262 if (custom_formats_.IsFormatRegistered(format)) { 263 base::string16 format_name = 264 base::UTF8ToUTF16(custom_formats_.GetFormatName(format)); 265 std::string clipboard_data; 266 clipboard->ReadData(ui::Clipboard::GetPepperCustomDataFormatType(), 267 &clipboard_data); 268 Pickle pickle(clipboard_data.data(), clipboard_data.size()); 269 if (IsFormatAvailableInPickle(format_name, pickle)) { 270 result = PP_OK; 271 clipboard_string = ReadDataFromPickle(format_name, pickle); 272 } 273 } 274 break; 275 } 276 } 277 278 if (result == PP_OK) { 279 host_context->reply_msg = 280 PpapiPluginMsg_FlashClipboard_ReadDataReply(clipboard_string); 281 } 282 return result; 283} 284 285int32_t PepperFlashClipboardMessageFilter::OnMsgWriteData( 286 ppapi::host::HostMessageContext* host_context, 287 uint32_t clipboard_type, 288 const std::vector<uint32_t>& formats, 289 const std::vector<std::string>& data) { 290 if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) { 291 NOTIMPLEMENTED(); 292 return PP_ERROR_FAILED; 293 } 294 if (formats.size() != data.size()) 295 return PP_ERROR_FAILED; 296 297 ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); 298 ui::ClipboardType type = ConvertClipboardType(clipboard_type); 299 // If no formats are passed in clear the clipboard. 300 if (formats.size() == 0) { 301 clipboard->Clear(type); 302 return PP_OK; 303 } 304 305 ui::ScopedClipboardWriter scw(clipboard, type); 306 std::map<base::string16, std::string> custom_data_map; 307 int32_t res = PP_OK; 308 for (uint32_t i = 0; i < formats.size(); ++i) { 309 if (data[i].length() > kMaxClipboardWriteSize) { 310 res = PP_ERROR_NOSPACE; 311 break; 312 } 313 314 switch (formats[i]) { 315 case PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT: 316 scw.WriteText(base::UTF8ToUTF16(data[i])); 317 break; 318 case PP_FLASH_CLIPBOARD_FORMAT_HTML: 319 scw.WriteHTML(base::UTF8ToUTF16(data[i]), std::string()); 320 break; 321 case PP_FLASH_CLIPBOARD_FORMAT_RTF: 322 scw.WriteRTF(data[i]); 323 break; 324 case PP_FLASH_CLIPBOARD_FORMAT_INVALID: 325 res = PP_ERROR_BADARGUMENT; 326 break; 327 default: 328 if (custom_formats_.IsFormatRegistered(formats[i])) { 329 std::string format_name = custom_formats_.GetFormatName(formats[i]); 330 custom_data_map[base::UTF8ToUTF16(format_name)] = data[i]; 331 } else { 332 // Invalid format. 333 res = PP_ERROR_BADARGUMENT; 334 break; 335 } 336 } 337 338 if (res != PP_OK) 339 break; 340 } 341 342 if (custom_data_map.size() > 0) { 343 Pickle pickle; 344 if (WriteDataToPickle(custom_data_map, &pickle)) { 345 scw.WritePickledData(pickle, 346 ui::Clipboard::GetPepperCustomDataFormatType()); 347 } else { 348 res = PP_ERROR_BADARGUMENT; 349 } 350 } 351 352 if (res != PP_OK) { 353 // Need to clear the objects so nothing is written. 354 scw.Reset(); 355 } 356 357 return res; 358} 359 360int32_t PepperFlashClipboardMessageFilter::OnMsgGetSequenceNumber( 361 ppapi::host::HostMessageContext* host_context, 362 uint32_t clipboard_type) { 363 if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) { 364 NOTIMPLEMENTED(); 365 return PP_ERROR_FAILED; 366 } 367 368 ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); 369 ui::ClipboardType type = ConvertClipboardType(clipboard_type); 370 int64_t sequence_number = clipboard->GetSequenceNumber(type); 371 host_context->reply_msg = 372 PpapiPluginMsg_FlashClipboard_GetSequenceNumberReply(sequence_number); 373 return PP_OK; 374} 375 376} // namespace chrome 377