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 21 using content::BrowserThread; 22 23 namespace chrome { 24 25 namespace { 26 27 const size_t kMaxClipboardWriteSize = 1000000; 28 29 ui::Clipboard::Buffer ConvertClipboardType(uint32_t type) { 30 switch (type) { 31 case PP_FLASH_CLIPBOARD_TYPE_STANDARD: 32 return ui::Clipboard::BUFFER_STANDARD; 33 case PP_FLASH_CLIPBOARD_TYPE_SELECTION: 34 return ui::Clipboard::BUFFER_SELECTION; 35 } 36 NOTREACHED(); 37 return ui::Clipboard::BUFFER_STANDARD; 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. 49 bool JumpToFormatInPickle(const 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 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 68 bool IsFormatAvailableInPickle(const string16& format, const Pickle& pickle) { 69 PickleIterator iter(pickle); 70 return JumpToFormatInPickle(format, &iter); 71 } 72 73 std::string ReadDataFromPickle(const string16& format, const Pickle& pickle) { 74 std::string result; 75 PickleIterator iter(pickle); 76 if (!JumpToFormatInPickle(format, &iter) || !iter.ReadString(&result)) 77 return std::string(); 78 return result; 79 } 80 81 bool WriteDataToPickle(const std::map<string16, std::string>& data, 82 Pickle* pickle) { 83 pickle->WriteUInt64(data.size()); 84 for (std::map<string16, std::string>::const_iterator it = data.begin(); 85 it != data.end(); ++it) { 86 if (!pickle->WriteString16(it->first)) 87 return false; 88 if (!pickle->WriteString(it->second)) 89 return false; 90 } 91 return true; 92 } 93 94 } // namespace 95 96 PepperFlashClipboardMessageFilter::PepperFlashClipboardMessageFilter() { 97 } 98 99 PepperFlashClipboardMessageFilter::~PepperFlashClipboardMessageFilter() { 100 } 101 102 scoped_refptr<base::TaskRunner> 103 PepperFlashClipboardMessageFilter::OverrideTaskRunnerForMessage( 104 const IPC::Message& msg) { 105 // Clipboard writes should always occur on the UI thread due to the 106 // restrictions of various platform APIs. In general, the clipboard is not 107 // thread-safe, so all clipboard calls should be serviced from the UI thread. 108 if (msg.type() == PpapiHostMsg_FlashClipboard_WriteData::ID) 109 return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI); 110 111 // Windows needs clipboard reads to be serviced from the IO thread because 112 // these are sync IPCs which can result in deadlocks with plugins if serviced 113 // from the UI thread. Note that Windows clipboard calls ARE thread-safe so it 114 // is ok for reads and writes to be serviced from different threads. 115 #if !defined(OS_WIN) 116 return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI); 117 #else 118 return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO); 119 #endif 120 } 121 122 int32_t PepperFlashClipboardMessageFilter::OnResourceMessageReceived( 123 const IPC::Message& msg, 124 ppapi::host::HostMessageContext* context) { 125 IPC_BEGIN_MESSAGE_MAP(PepperFlashClipboardMessageFilter, msg) 126 PPAPI_DISPATCH_HOST_RESOURCE_CALL( 127 PpapiHostMsg_FlashClipboard_RegisterCustomFormat, 128 OnMsgRegisterCustomFormat); 129 PPAPI_DISPATCH_HOST_RESOURCE_CALL( 130 PpapiHostMsg_FlashClipboard_IsFormatAvailable, 131 OnMsgIsFormatAvailable); 132 PPAPI_DISPATCH_HOST_RESOURCE_CALL( 133 PpapiHostMsg_FlashClipboard_ReadData, 134 OnMsgReadData); 135 PPAPI_DISPATCH_HOST_RESOURCE_CALL( 136 PpapiHostMsg_FlashClipboard_WriteData, 137 OnMsgWriteData); 138 IPC_END_MESSAGE_MAP() 139 return PP_ERROR_FAILED; 140 } 141 142 int32_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 153 int32_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::Clipboard::Buffer buffer_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(), buffer_type); 169 bool plainw = clipboard->IsFormatAvailable( 170 ui::Clipboard::GetPlainTextWFormatType(), buffer_type); 171 available = plain || plainw; 172 break; 173 } 174 case PP_FLASH_CLIPBOARD_FORMAT_HTML: 175 available = clipboard->IsFormatAvailable( 176 ui::Clipboard::GetHtmlFormatType(), buffer_type); 177 break; 178 case PP_FLASH_CLIPBOARD_FORMAT_RTF: 179 available = clipboard->IsFormatAvailable( 180 ui::Clipboard::GetRtfFormatType(), buffer_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( 189 ui::Clipboard::GetPepperCustomDataFormatType(), &clipboard_data); 190 Pickle pickle(clipboard_data.data(), clipboard_data.size()); 191 available = IsFormatAvailableInPickle(UTF8ToUTF16(format_name), pickle); 192 } 193 break; 194 } 195 196 return available ? PP_OK : PP_ERROR_FAILED; 197 } 198 199 int32_t PepperFlashClipboardMessageFilter::OnMsgReadData( 200 ppapi::host::HostMessageContext* host_context, 201 uint32_t clipboard_type, 202 uint32_t format) { 203 if (clipboard_type != PP_FLASH_CLIPBOARD_TYPE_STANDARD) { 204 NOTIMPLEMENTED(); 205 return PP_ERROR_FAILED; 206 } 207 208 ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); 209 ui::Clipboard::Buffer buffer_type = ConvertClipboardType(clipboard_type); 210 std::string clipboard_string; 211 int32_t result = PP_ERROR_FAILED; 212 switch (format) { 213 case PP_FLASH_CLIPBOARD_FORMAT_PLAINTEXT: { 214 if (clipboard->IsFormatAvailable( 215 ui::Clipboard::GetPlainTextWFormatType(), buffer_type)) { 216 string16 text; 217 clipboard->ReadText(buffer_type, &text); 218 if (!text.empty()) { 219 result = PP_OK; 220 clipboard_string = UTF16ToUTF8(text); 221 break; 222 } 223 } 224 // If the PlainTextW format isn't available or is empty, take the 225 // ASCII text format. 226 if (clipboard->IsFormatAvailable( 227 ui::Clipboard::GetPlainTextFormatType(), buffer_type)) { 228 result = PP_OK; 229 clipboard->ReadAsciiText(buffer_type, &clipboard_string); 230 } 231 break; 232 } 233 case PP_FLASH_CLIPBOARD_FORMAT_HTML: { 234 if (!clipboard->IsFormatAvailable( 235 ui::Clipboard::GetHtmlFormatType(), buffer_type)) { 236 break; 237 } 238 239 string16 html; 240 std::string url; 241 uint32 fragment_start; 242 uint32 fragment_end; 243 clipboard->ReadHTML(buffer_type, &html, &url, &fragment_start, 244 &fragment_end); 245 result = PP_OK; 246 clipboard_string = UTF16ToUTF8( 247 html.substr(fragment_start, fragment_end - fragment_start)); 248 break; 249 } 250 case PP_FLASH_CLIPBOARD_FORMAT_RTF: { 251 if (!clipboard->IsFormatAvailable( 252 ui::Clipboard::GetRtfFormatType(), buffer_type)) { 253 break; 254 } 255 result = PP_OK; 256 clipboard->ReadRTF(buffer_type, &clipboard_string); 257 break; 258 } 259 case PP_FLASH_CLIPBOARD_FORMAT_INVALID: 260 break; 261 default: { 262 if (custom_formats_.IsFormatRegistered(format)) { 263 string16 format_name = UTF8ToUTF16( 264 custom_formats_.GetFormatName(format)); 265 std::string clipboard_data; 266 clipboard->ReadData( 267 ui::Clipboard::GetPepperCustomDataFormatType(), &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 285 int32_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::Clipboard::Buffer buffer_type = ConvertClipboardType(clipboard_type); 299 // If no formats are passed in clear the clipboard. 300 if (formats.size() == 0) { 301 clipboard->Clear(buffer_type); 302 return PP_OK; 303 } 304 305 ui::ScopedClipboardWriter scw(clipboard, buffer_type); 306 std::map<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(UTF8ToUTF16(data[i])); 317 break; 318 case PP_FLASH_CLIPBOARD_FORMAT_HTML: 319 scw.WriteHTML(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[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 360 } // namespace chrome 361