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/stl_util.h"
     10 #include "content/common/clipboard_messages.h"
     11 #include "content/public/browser/browser_context.h"
     12 #include "ipc/ipc_message_macros.h"
     13 #include "third_party/skia/include/core/SkBitmap.h"
     14 #include "third_party/zlib/zlib.h"
     15 #include "ui/gfx/codec/png_codec.h"
     16 #include "ui/gfx/size.h"
     17 #include "url/gurl.h"
     18 
     19 namespace content {
     20 
     21 #if defined(OS_WIN)
     22 
     23 namespace {
     24 
     25 // The write must be performed on the UI thread because the clipboard object
     26 // from the IO thread cannot create windows so it cannot be the "owner" of the
     27 // clipboard's contents.  // See http://crbug.com/5823.
     28 void WriteObjectsOnUIThread(ui::Clipboard::ObjectMap* objects) {
     29   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     30   static ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
     31   clipboard->WriteObjects(ui::Clipboard::BUFFER_STANDARD, *objects);
     32 }
     33 
     34 }  // namespace
     35 
     36 #endif
     37 
     38 ClipboardMessageFilter::ClipboardMessageFilter() {}
     39 
     40 void ClipboardMessageFilter::OverrideThreadForMessage(
     41     const IPC::Message& message, BrowserThread::ID* thread) {
     42   // Clipboard writes should always occur on the UI thread due the restrictions
     43   // of various platform APIs. In general, the clipboard is not thread-safe, so
     44   // all clipboard calls should be serviced from the UI thread.
     45   //
     46   // Windows needs clipboard reads to be serviced from the IO thread because
     47   // these are sync IPCs which can result in deadlocks with NPAPI plugins if
     48   // serviced from the UI thread. Note that Windows clipboard calls ARE
     49   // thread-safe so it is ok for reads and writes to be serviced from different
     50   // threads.
     51 #if !defined(OS_WIN)
     52   if (IPC_MESSAGE_CLASS(message) == ClipboardMsgStart)
     53     *thread = BrowserThread::UI;
     54 #endif
     55 
     56 #if defined(OS_WIN)
     57   if (message.type() == ClipboardHostMsg_ReadImage::ID)
     58     *thread = BrowserThread::FILE;
     59 #endif
     60 }
     61 
     62 bool ClipboardMessageFilter::OnMessageReceived(const IPC::Message& message,
     63                                                bool* message_was_ok) {
     64   bool handled = true;
     65   IPC_BEGIN_MESSAGE_MAP_EX(ClipboardMessageFilter, message, *message_was_ok)
     66     IPC_MESSAGE_HANDLER(ClipboardHostMsg_WriteObjectsAsync, OnWriteObjectsAsync)
     67     IPC_MESSAGE_HANDLER(ClipboardHostMsg_WriteObjectsSync, OnWriteObjectsSync)
     68     IPC_MESSAGE_HANDLER(ClipboardHostMsg_GetSequenceNumber, OnGetSequenceNumber)
     69     IPC_MESSAGE_HANDLER(ClipboardHostMsg_IsFormatAvailable, OnIsFormatAvailable)
     70     IPC_MESSAGE_HANDLER(ClipboardHostMsg_Clear, OnClear)
     71     IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadAvailableTypes,
     72                         OnReadAvailableTypes)
     73     IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadText, OnReadText)
     74     IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadAsciiText, OnReadAsciiText)
     75     IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadHTML, OnReadHTML)
     76     IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadRTF, OnReadRTF)
     77     IPC_MESSAGE_HANDLER_DELAY_REPLY(ClipboardHostMsg_ReadImage, OnReadImage)
     78     IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadCustomData, OnReadCustomData)
     79     IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadData, OnReadData)
     80 #if defined(OS_MACOSX)
     81     IPC_MESSAGE_HANDLER(ClipboardHostMsg_FindPboardWriteStringAsync,
     82                         OnFindPboardWriteString)
     83 #endif
     84     IPC_MESSAGE_UNHANDLED(handled = false)
     85   IPC_END_MESSAGE_MAP()
     86   return handled;
     87 }
     88 
     89 ClipboardMessageFilter::~ClipboardMessageFilter() {
     90 }
     91 
     92 void ClipboardMessageFilter::OnWriteObjectsSync(
     93     ui::Clipboard::ObjectMap objects,
     94     base::SharedMemoryHandle bitmap_handle) {
     95   DCHECK(base::SharedMemory::IsHandleValid(bitmap_handle))
     96       << "Bad bitmap handle";
     97   // Splice the shared memory handle into the clipboard data.
     98   ui::Clipboard::ReplaceSharedMemHandle(&objects, bitmap_handle, PeerHandle());
     99 #if defined(OS_WIN)
    100   // We cannot write directly from the IO thread, and cannot service the IPC
    101   // on the UI thread. We'll copy the relevant data and get a handle to any
    102   // shared memory so it doesn't go away when we resume the renderer, and post
    103   // a task to perform the write on the UI thread.
    104   ui::Clipboard::ObjectMap* long_living_objects = new ui::Clipboard::ObjectMap;
    105   long_living_objects->swap(objects);
    106 
    107   BrowserThread::PostTask(
    108       BrowserThread::UI,
    109       FROM_HERE,
    110       base::Bind(&WriteObjectsOnUIThread, base::Owned(long_living_objects)));
    111 #else
    112   GetClipboard()->WriteObjects(ui::Clipboard::BUFFER_STANDARD, objects);
    113 #endif
    114 }
    115 
    116 void ClipboardMessageFilter::OnWriteObjectsAsync(
    117     const ui::Clipboard::ObjectMap& objects) {
    118 #if defined(OS_WIN)
    119   // We cannot write directly from the IO thread, and cannot service the IPC
    120   // on the UI thread. We'll copy the relevant data and post a task to preform
    121   // the write on the UI thread.
    122   ui::Clipboard::ObjectMap* long_living_objects =
    123       new ui::Clipboard::ObjectMap(objects);
    124 
    125   // This async message doesn't support shared-memory based bitmaps; they must
    126   // be removed otherwise we might dereference a rubbish pointer.
    127   long_living_objects->erase(ui::Clipboard::CBF_SMBITMAP);
    128 
    129   BrowserThread::PostTask(
    130       BrowserThread::UI,
    131       FROM_HERE,
    132       base::Bind(&WriteObjectsOnUIThread, base::Owned(long_living_objects)));
    133 #else
    134   GetClipboard()->WriteObjects(ui::Clipboard::BUFFER_STANDARD, objects);
    135 #endif
    136 }
    137 
    138 void ClipboardMessageFilter::OnGetSequenceNumber(
    139     ui::Clipboard::Buffer buffer, uint64* sequence_number) {
    140   *sequence_number = GetClipboard()->GetSequenceNumber(buffer);
    141 }
    142 
    143 void ClipboardMessageFilter::OnReadAvailableTypes(
    144     ui::Clipboard::Buffer buffer, std::vector<string16>* types,
    145     bool* contains_filenames) {
    146   GetClipboard()->ReadAvailableTypes(buffer, types, contains_filenames);
    147 }
    148 
    149 void ClipboardMessageFilter::OnIsFormatAvailable(
    150     const ui::Clipboard::FormatType& format, ui::Clipboard::Buffer buffer,
    151     bool* result) {
    152   *result = GetClipboard()->IsFormatAvailable(format, buffer);
    153 }
    154 
    155 void ClipboardMessageFilter::OnClear(ui::Clipboard::Buffer buffer) {
    156   GetClipboard()->Clear(buffer);
    157 }
    158 
    159 void ClipboardMessageFilter::OnReadText(
    160     ui::Clipboard::Buffer buffer, string16* result) {
    161   GetClipboard()->ReadText(buffer, result);
    162 }
    163 
    164 void ClipboardMessageFilter::OnReadAsciiText(
    165     ui::Clipboard::Buffer buffer, std::string* result) {
    166   GetClipboard()->ReadAsciiText(buffer, result);
    167 }
    168 
    169 void ClipboardMessageFilter::OnReadHTML(
    170     ui::Clipboard::Buffer buffer, string16* markup, GURL* url,
    171     uint32* fragment_start, uint32* fragment_end) {
    172   std::string src_url_str;
    173   GetClipboard()->ReadHTML(buffer, markup, &src_url_str, fragment_start,
    174                            fragment_end);
    175   *url = GURL(src_url_str);
    176 }
    177 
    178 void ClipboardMessageFilter::OnReadRTF(
    179     ui::Clipboard::Buffer buffer, std::string* result) {
    180   GetClipboard()->ReadRTF(buffer, result);
    181 }
    182 
    183 void ClipboardMessageFilter::OnReadImage(
    184     ui::Clipboard::Buffer buffer, IPC::Message* reply_msg) {
    185   SkBitmap bitmap = GetClipboard()->ReadImage(buffer);
    186 
    187 #if defined(USE_X11)
    188   BrowserThread::PostTask(
    189       BrowserThread::FILE, FROM_HERE,
    190       base::Bind(
    191           &ClipboardMessageFilter::OnReadImageReply, this, bitmap, reply_msg));
    192 #else
    193   OnReadImageReply(bitmap, reply_msg);
    194 #endif
    195 }
    196 
    197 void ClipboardMessageFilter::OnReadImageReply(
    198     const SkBitmap& bitmap, IPC::Message* reply_msg) {
    199   base::SharedMemoryHandle image_handle = base::SharedMemory::NULLHandle();
    200   uint32 image_size = 0;
    201   std::string reply_data;
    202   if (!bitmap.isNull()) {
    203     std::vector<unsigned char> png_data;
    204     SkAutoLockPixels lock(bitmap);
    205     if (gfx::PNGCodec::EncodeWithCompressionLevel(
    206             static_cast<const unsigned char*>(bitmap.getPixels()),
    207             gfx::PNGCodec::FORMAT_BGRA,
    208             gfx::Size(bitmap.width(), bitmap.height()),
    209             bitmap.rowBytes(),
    210             false,
    211             std::vector<gfx::PNGCodec::Comment>(),
    212             Z_BEST_SPEED,
    213             &png_data)) {
    214       base::SharedMemory buffer;
    215       if (buffer.CreateAndMapAnonymous(png_data.size())) {
    216         memcpy(buffer.memory(), vector_as_array(&png_data), png_data.size());
    217         if (buffer.GiveToProcess(PeerHandle(), &image_handle)) {
    218           image_size = png_data.size();
    219         }
    220       }
    221     }
    222   }
    223   ClipboardHostMsg_ReadImage::WriteReplyParams(reply_msg, image_handle,
    224                                                image_size);
    225   Send(reply_msg);
    226 }
    227 
    228 void ClipboardMessageFilter::OnReadCustomData(
    229     ui::Clipboard::Buffer buffer, const string16& type, string16* result) {
    230   GetClipboard()->ReadCustomData(buffer, type, result);
    231 }
    232 
    233 void ClipboardMessageFilter::OnReadData(const ui::Clipboard::FormatType& format,
    234                                         std::string* data) {
    235   GetClipboard()->ReadData(format, data);
    236 }
    237 
    238 // static
    239 ui::Clipboard* ClipboardMessageFilter::GetClipboard() {
    240   // We have a static instance of the clipboard service for use by all message
    241   // filters.  This instance lives for the life of the browser processes.
    242   static ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread();
    243   return clipboard;
    244 }
    245 
    246 }  // namespace content
    247