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