Home | History | Annotate | Download | only in pepper
      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