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