Home | History | Annotate | Download | only in renderer_host
      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 "content/browser/renderer_host/clipboard_message_filter.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/bind_helpers.h"
      9 #include "base/location.h"
     10 #include "base/memory/scoped_ptr.h"
     11 #include "base/stl_util.h"
     12 #include "base/strings/utf_string_conversions.h"
     13 #include "content/common/clipboard_messages.h"
     14 #include "content/public/browser/browser_context.h"
     15 #include "ipc/ipc_message_macros.h"
     16 #include "third_party/skia/include/core/SkBitmap.h"
     17 #include "ui/gfx/codec/png_codec.h"
     18 #include "ui/gfx/size.h"
     19 #include "url/gurl.h"
     20 
     21 namespace content {
     22 
     23 namespace {
     24 
     25 enum BitmapPolicy {
     26   kFilterBitmap,
     27   kAllowBitmap,
     28 };
     29 void SanitizeObjectMap(ui::Clipboard::ObjectMap* objects,
     30                        BitmapPolicy bitmap_policy) {
     31   if (bitmap_policy != kAllowBitmap)
     32     objects->erase(ui::Clipboard::CBF_SMBITMAP);
     33 
     34   ui::Clipboard::ObjectMap::iterator data_it =
     35       objects->find(ui::Clipboard::CBF_DATA);
     36   if (data_it != objects->end()) {
     37     const ui::Clipboard::FormatType& web_custom_format =
     38         ui::Clipboard::GetWebCustomDataFormatType();
     39     if (data_it->second.size() != 2 ||
     40         !web_custom_format.Equals(
     41             ui::Clipboard::FormatType::Deserialize(std::string(
     42                 &data_it->second[0].front(),
     43                 data_it->second[0].size())))) {
     44       // CBF_DATA should always have two parameters associated with it, and the
     45       // associated FormatType should always be web custom data. If not, then
     46       // data is malformed and we'll ignore it.
     47       objects->erase(ui::Clipboard::CBF_DATA);
     48     }
     49   }
     50 }
     51 
     52 }  // namespace
     53 
     54 
     55 ClipboardMessageFilter::ClipboardMessageFilter()
     56     : BrowserMessageFilter(ClipboardMsgStart) {}
     57 
     58 void ClipboardMessageFilter::OverrideThreadForMessage(
     59     const IPC::Message& message, BrowserThread::ID* thread) {
     60   // Clipboard writes should always occur on the UI thread due the restrictions
     61   // of various platform APIs. In general, the clipboard is not thread-safe, so
     62   // all clipboard calls should be serviced from the UI thread.
     63   //
     64   // Windows needs clipboard reads to be serviced from the IO thread because
     65   // these are sync IPCs which can result in deadlocks with NPAPI plugins if
     66   // serviced from the UI thread. Note that Windows clipboard calls ARE
     67   // thread-safe so it is ok for reads and writes to be serviced from different
     68   // threads.
     69 #if !defined(OS_WIN)
     70   if (IPC_MESSAGE_CLASS(message) == ClipboardMsgStart)
     71     *thread = BrowserThread::UI;
     72 #endif
     73 
     74 #if defined(OS_WIN)
     75   if (message.type() == ClipboardHostMsg_ReadImage::ID)
     76     *thread = BrowserThread::FILE;
     77 #endif
     78 }
     79 
     80 bool ClipboardMessageFilter::OnMessageReceived(const IPC::Message& message) {
     81   bool handled = true;
     82   IPC_BEGIN_MESSAGE_MAP(ClipboardMessageFilter, message)
     83     IPC_MESSAGE_HANDLER(ClipboardHostMsg_WriteObjectsAsync, OnWriteObjectsAsync)
     84     IPC_MESSAGE_HANDLER(ClipboardHostMsg_WriteObjectsSync, OnWriteObjectsSync)
     85     IPC_MESSAGE_HANDLER(ClipboardHostMsg_GetSequenceNumber, OnGetSequenceNumber)
     86     IPC_MESSAGE_HANDLER(ClipboardHostMsg_IsFormatAvailable, OnIsFormatAvailable)
     87     IPC_MESSAGE_HANDLER(ClipboardHostMsg_Clear, OnClear)
     88     IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadAvailableTypes,
     89                         OnReadAvailableTypes)
     90     IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadText, OnReadText)
     91     IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadHTML, OnReadHTML)
     92     IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadRTF, OnReadRTF)
     93     IPC_MESSAGE_HANDLER_DELAY_REPLY(ClipboardHostMsg_ReadImage, OnReadImage)
     94     IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadCustomData, OnReadCustomData)
     95 #if defined(OS_MACOSX)
     96     IPC_MESSAGE_HANDLER(ClipboardHostMsg_FindPboardWriteStringAsync,
     97                         OnFindPboardWriteString)
     98 #endif
     99     IPC_MESSAGE_UNHANDLED(handled = false)
    100   IPC_END_MESSAGE_MAP()
    101   return handled;
    102 }
    103 
    104 ClipboardMessageFilter::~ClipboardMessageFilter() {
    105 }
    106 
    107 void ClipboardMessageFilter::OnWriteObjectsSync(
    108     const ui::Clipboard::ObjectMap& objects,
    109     base::SharedMemoryHandle bitmap_handle) {
    110   DCHECK(base::SharedMemory::IsHandleValid(bitmap_handle))
    111       << "Bad bitmap handle";
    112 
    113   // On Windows, we can't write directly from the IO thread, so we copy the data
    114   // into a heap allocated map and post a task to the UI thread. On other
    115   // platforms, to lower the amount of time the renderer has to wait for the
    116   // sync IPC to complete, we also take a copy and post a task to flush the data
    117   // to the clipboard later.
    118   scoped_ptr<ui::Clipboard::ObjectMap> long_living_objects(
    119       new ui::Clipboard::ObjectMap(objects));
    120   SanitizeObjectMap(long_living_objects.get(), kAllowBitmap);
    121   // Splice the shared memory handle into the data. |long_living_objects| now
    122   // contains a heap-allocated SharedMemory object that references
    123   // |bitmap_handle|. This reference will keep the shared memory section alive
    124   // when this IPC returns, and the SharedMemory object will eventually be
    125   // freed by ui::Clipboard::WriteObjects().
    126   if (!ui::Clipboard::ReplaceSharedMemHandle(
    127            long_living_objects.get(), bitmap_handle, PeerHandle()))
    128     return;
    129 
    130   BrowserThread::PostTask(
    131       BrowserThread::UI,
    132       FROM_HERE,
    133       base::Bind(&ClipboardMessageFilter::WriteObjectsOnUIThread,
    134                  base::Owned(long_living_objects.release())));
    135 }
    136 
    137 // On Windows, the write must be performed on the UI thread because the
    138 // clipboard object from the IO thread cannot create windows so it cannot be
    139 // the "owner" of the clipboard's contents. See http://crbug.com/5823.
    140 // TODO(dcheng): Temporarily a member of ClipboardMessageFilter so it can access
    141 // ui::Clipboard::WriteObjects().
    142 void ClipboardMessageFilter::WriteObjectsOnUIThread(
    143     const ui::Clipboard::ObjectMap* objects) {
    144   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    145   static ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
    146   clipboard->WriteObjects(ui::CLIPBOARD_TYPE_COPY_PASTE, *objects);
    147 }
    148 
    149 void ClipboardMessageFilter::OnWriteObjectsAsync(
    150     const ui::Clipboard::ObjectMap& objects) {
    151   // This async message doesn't support shared-memory based bitmaps; they must
    152   // be removed otherwise we might dereference a rubbish pointer.
    153   scoped_ptr<ui::Clipboard::ObjectMap> sanitized_objects(
    154       new ui::Clipboard::ObjectMap(objects));
    155   SanitizeObjectMap(sanitized_objects.get(), kFilterBitmap);
    156 
    157 #if defined(OS_WIN)
    158   // We cannot write directly from the IO thread, and cannot service the IPC
    159   // on the UI thread. We'll copy the relevant data and post a task to preform
    160   // the write on the UI thread.
    161   BrowserThread::PostTask(
    162       BrowserThread::UI,
    163       FROM_HERE,
    164       base::Bind(
    165           &WriteObjectsOnUIThread, base::Owned(sanitized_objects.release())));
    166 #else
    167   GetClipboard()->WriteObjects(
    168       ui::CLIPBOARD_TYPE_COPY_PASTE, *sanitized_objects.get());
    169 #endif
    170 }
    171 
    172 void ClipboardMessageFilter::OnGetSequenceNumber(ui::ClipboardType type,
    173                                                  uint64* sequence_number) {
    174   *sequence_number = GetClipboard()->GetSequenceNumber(type);
    175 }
    176 
    177 void ClipboardMessageFilter::OnReadAvailableTypes(
    178     ui::ClipboardType type,
    179     std::vector<base::string16>* types,
    180     bool* contains_filenames) {
    181   GetClipboard()->ReadAvailableTypes(type, types, contains_filenames);
    182 }
    183 
    184 void ClipboardMessageFilter::OnIsFormatAvailable(ClipboardFormat format,
    185                                                  ui::ClipboardType type,
    186                                                  bool* result) {
    187   switch (format) {
    188     case CLIPBOARD_FORMAT_PLAINTEXT:
    189       *result = GetClipboard()->IsFormatAvailable(
    190                     ui::Clipboard::GetPlainTextWFormatType(), type) ||
    191                 GetClipboard()->IsFormatAvailable(
    192                     ui::Clipboard::GetPlainTextFormatType(), type);
    193       break;
    194     case CLIPBOARD_FORMAT_HTML:
    195       *result = GetClipboard()->IsFormatAvailable(
    196           ui::Clipboard::GetHtmlFormatType(), type);
    197       break;
    198     case CLIPBOARD_FORMAT_SMART_PASTE:
    199       *result = GetClipboard()->IsFormatAvailable(
    200           ui::Clipboard::GetWebKitSmartPasteFormatType(), type);
    201       break;
    202     case CLIPBOARD_FORMAT_BOOKMARK:
    203 #if defined(OS_WIN) || defined(OS_MACOSX)
    204       *result = GetClipboard()->IsFormatAvailable(
    205           ui::Clipboard::GetUrlWFormatType(), type);
    206 #else
    207       *result = false;
    208 #endif
    209       break;
    210   }
    211 }
    212 
    213 void ClipboardMessageFilter::OnClear(ui::ClipboardType type) {
    214   GetClipboard()->Clear(type);
    215 }
    216 
    217 void ClipboardMessageFilter::OnReadText(ui::ClipboardType type,
    218                                         base::string16* result) {
    219   if (GetClipboard()->IsFormatAvailable(
    220           ui::Clipboard::GetPlainTextWFormatType(), type)) {
    221     GetClipboard()->ReadText(type, result);
    222   } else if (GetClipboard()->IsFormatAvailable(
    223                  ui::Clipboard::GetPlainTextFormatType(), type)) {
    224     std::string ascii;
    225     GetClipboard()->ReadAsciiText(type, &ascii);
    226     *result = base::ASCIIToUTF16(ascii);
    227   } else {
    228     result->clear();
    229   }
    230 }
    231 
    232 void ClipboardMessageFilter::OnReadHTML(ui::ClipboardType type,
    233                                         base::string16* markup,
    234                                         GURL* url,
    235                                         uint32* fragment_start,
    236                                         uint32* fragment_end) {
    237   std::string src_url_str;
    238   GetClipboard()->ReadHTML(type, markup, &src_url_str, fragment_start,
    239                            fragment_end);
    240   *url = GURL(src_url_str);
    241 }
    242 
    243 void ClipboardMessageFilter::OnReadRTF(ui::ClipboardType type,
    244                                        std::string* result) {
    245   GetClipboard()->ReadRTF(type, result);
    246 }
    247 
    248 void ClipboardMessageFilter::OnReadImage(ui::ClipboardType type,
    249                                          IPC::Message* reply_msg) {
    250   SkBitmap bitmap = GetClipboard()->ReadImage(type);
    251 
    252 #if defined(USE_X11)
    253   BrowserThread::PostTask(
    254       BrowserThread::FILE, FROM_HERE,
    255       base::Bind(
    256           &ClipboardMessageFilter::OnReadImageReply, this, bitmap, reply_msg));
    257 #else
    258   OnReadImageReply(bitmap, reply_msg);
    259 #endif
    260 }
    261 
    262 void ClipboardMessageFilter::OnReadImageReply(
    263     const SkBitmap& bitmap, IPC::Message* reply_msg) {
    264   base::SharedMemoryHandle image_handle = base::SharedMemory::NULLHandle();
    265   uint32 image_size = 0;
    266   if (!bitmap.isNull()) {
    267     std::vector<unsigned char> png_data;
    268     if (gfx::PNGCodec::FastEncodeBGRASkBitmap(bitmap, false, &png_data)) {
    269       base::SharedMemory buffer;
    270       if (buffer.CreateAndMapAnonymous(png_data.size())) {
    271         memcpy(buffer.memory(), vector_as_array(&png_data), png_data.size());
    272         if (buffer.GiveToProcess(PeerHandle(), &image_handle)) {
    273           image_size = png_data.size();
    274         }
    275       }
    276     }
    277   }
    278   ClipboardHostMsg_ReadImage::WriteReplyParams(reply_msg, image_handle,
    279                                                image_size);
    280   Send(reply_msg);
    281 }
    282 
    283 void ClipboardMessageFilter::OnReadCustomData(ui::ClipboardType clipboard_type,
    284                                               const base::string16& type,
    285                                               base::string16* result) {
    286   GetClipboard()->ReadCustomData(clipboard_type, type, result);
    287 }
    288 
    289 // static
    290 ui::Clipboard* ClipboardMessageFilter::GetClipboard() {
    291   // We have a static instance of the clipboard service for use by all message
    292   // filters.  This instance lives for the life of the browser processes.
    293   static ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
    294   return clipboard;
    295 }
    296 
    297 }  // namespace content
    298