OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/renderer_host/clipboard_message_filter.h" | 5 #include "content/browser/renderer_host/clipboard_message_filter.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/bind_helpers.h" | 8 #include "base/bind_helpers.h" |
9 #include "base/location.h" | 9 #include "base/location.h" |
10 #include "base/macros.h" | |
11 #include "base/memory/scoped_ptr.h" | 10 #include "base/memory/scoped_ptr.h" |
12 #include "base/pickle.h" | |
13 #include "base/stl_util.h" | 11 #include "base/stl_util.h" |
14 #include "base/strings/utf_string_conversions.h" | 12 #include "base/strings/utf_string_conversions.h" |
15 #include "content/common/clipboard_messages.h" | 13 #include "content/common/clipboard_messages.h" |
16 #include "content/public/browser/browser_context.h" | 14 #include "content/public/browser/browser_context.h" |
17 #include "ipc/ipc_message_macros.h" | 15 #include "ipc/ipc_message_macros.h" |
18 #include "third_party/skia/include/core/SkBitmap.h" | 16 #include "third_party/skia/include/core/SkBitmap.h" |
19 #include "ui/base/clipboard/custom_data_helper.h" | |
20 #include "ui/base/clipboard/scoped_clipboard_writer.h" | |
21 #include "ui/gfx/codec/png_codec.h" | 17 #include "ui/gfx/codec/png_codec.h" |
22 #include "ui/gfx/size.h" | 18 #include "ui/gfx/size.h" |
23 #include "url/gurl.h" | 19 #include "url/gurl.h" |
24 | 20 |
25 namespace content { | 21 namespace content { |
26 | 22 |
27 namespace { | 23 namespace { |
28 | 24 |
29 void ReleaseSharedMemoryPixels(void* addr, void* context) { | 25 enum BitmapPolicy { |
30 delete reinterpret_cast<base::SharedMemory*>(context); | 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 } |
31 } | 50 } |
32 | 51 |
33 } // namespace | 52 } // namespace |
34 | 53 |
| 54 |
35 ClipboardMessageFilter::ClipboardMessageFilter() | 55 ClipboardMessageFilter::ClipboardMessageFilter() |
36 : BrowserMessageFilter(ClipboardMsgStart), | 56 : BrowserMessageFilter(ClipboardMsgStart) {} |
37 clipboard_writer_( | |
38 new ui::ScopedClipboardWriter(ui::CLIPBOARD_TYPE_COPY_PASTE)) { | |
39 } | |
40 | 57 |
41 void ClipboardMessageFilter::OverrideThreadForMessage( | 58 void ClipboardMessageFilter::OverrideThreadForMessage( |
42 const IPC::Message& message, BrowserThread::ID* thread) { | 59 const IPC::Message& message, BrowserThread::ID* thread) { |
43 // Clipboard writes should always occur on the UI thread due the restrictions | 60 // Clipboard writes should always occur on the UI thread due the restrictions |
44 // of various platform APIs. In general, the clipboard is not thread-safe, so | 61 // of various platform APIs. In general, the clipboard is not thread-safe, so |
45 // all clipboard calls should be serviced from the UI thread. | 62 // all clipboard calls should be serviced from the UI thread. |
46 // | 63 // |
47 // Windows needs clipboard reads to be serviced from the IO thread because | 64 // Windows needs clipboard reads to be serviced from the IO thread because |
48 // these are sync IPCs which can result in deadlocks with NPAPI plugins if | 65 // these are sync IPCs which can result in deadlocks with NPAPI plugins if |
49 // serviced from the UI thread. Note that Windows clipboard calls ARE | 66 // serviced from the UI thread. Note that Windows clipboard calls ARE |
50 // thread-safe so it is ok for reads and writes to be serviced from different | 67 // thread-safe so it is ok for reads and writes to be serviced from different |
51 // threads. | 68 // threads. |
52 #if !defined(OS_WIN) | 69 #if !defined(OS_WIN) |
53 if (IPC_MESSAGE_CLASS(message) == ClipboardMsgStart) | 70 if (IPC_MESSAGE_CLASS(message) == ClipboardMsgStart) |
54 *thread = BrowserThread::UI; | 71 *thread = BrowserThread::UI; |
55 #endif | 72 #endif |
56 | 73 |
57 #if defined(OS_WIN) | 74 #if defined(OS_WIN) |
58 if (message.type() == ClipboardHostMsg_ReadImage::ID) | 75 if (message.type() == ClipboardHostMsg_ReadImage::ID) |
59 *thread = BrowserThread::FILE; | 76 *thread = BrowserThread::FILE; |
60 #endif | 77 #endif |
61 } | 78 } |
62 | 79 |
63 bool ClipboardMessageFilter::OnMessageReceived(const IPC::Message& message) { | 80 bool ClipboardMessageFilter::OnMessageReceived(const IPC::Message& message) { |
64 bool handled = true; | 81 bool handled = true; |
65 IPC_BEGIN_MESSAGE_MAP(ClipboardMessageFilter, message) | 82 IPC_BEGIN_MESSAGE_MAP(ClipboardMessageFilter, message) |
| 83 IPC_MESSAGE_HANDLER(ClipboardHostMsg_WriteObjectsAsync, OnWriteObjectsAsync) |
| 84 IPC_MESSAGE_HANDLER(ClipboardHostMsg_WriteObjectsSync, OnWriteObjectsSync) |
66 IPC_MESSAGE_HANDLER(ClipboardHostMsg_GetSequenceNumber, OnGetSequenceNumber) | 85 IPC_MESSAGE_HANDLER(ClipboardHostMsg_GetSequenceNumber, OnGetSequenceNumber) |
67 IPC_MESSAGE_HANDLER(ClipboardHostMsg_IsFormatAvailable, OnIsFormatAvailable) | 86 IPC_MESSAGE_HANDLER(ClipboardHostMsg_IsFormatAvailable, OnIsFormatAvailable) |
68 IPC_MESSAGE_HANDLER(ClipboardHostMsg_Clear, OnClear) | 87 IPC_MESSAGE_HANDLER(ClipboardHostMsg_Clear, OnClear) |
69 IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadAvailableTypes, | 88 IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadAvailableTypes, |
70 OnReadAvailableTypes) | 89 OnReadAvailableTypes) |
71 IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadText, OnReadText) | 90 IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadText, OnReadText) |
72 IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadHTML, OnReadHTML) | 91 IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadHTML, OnReadHTML) |
73 IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadRTF, OnReadRTF) | 92 IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadRTF, OnReadRTF) |
74 IPC_MESSAGE_HANDLER_DELAY_REPLY(ClipboardHostMsg_ReadImage, OnReadImage) | 93 IPC_MESSAGE_HANDLER_DELAY_REPLY(ClipboardHostMsg_ReadImage, OnReadImage) |
75 IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadCustomData, OnReadCustomData) | 94 IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadCustomData, OnReadCustomData) |
76 IPC_MESSAGE_HANDLER(ClipboardHostMsg_WriteText, OnWriteText) | |
77 IPC_MESSAGE_HANDLER(ClipboardHostMsg_WriteHTML, OnWriteHTML) | |
78 IPC_MESSAGE_HANDLER(ClipboardHostMsg_WriteSmartPasteMarker, | |
79 OnWriteSmartPasteMarker) | |
80 IPC_MESSAGE_HANDLER(ClipboardHostMsg_WriteCustomData, OnWriteCustomData) | |
81 IPC_MESSAGE_HANDLER(ClipboardHostMsg_WriteBookmark, OnWriteBookmark) | |
82 IPC_MESSAGE_HANDLER(ClipboardHostMsg_WriteImage, OnWriteImage) | |
83 IPC_MESSAGE_HANDLER(ClipboardHostMsg_CommitWrite, OnCommitWrite); | |
84 #if defined(OS_MACOSX) | 95 #if defined(OS_MACOSX) |
85 IPC_MESSAGE_HANDLER(ClipboardHostMsg_FindPboardWriteStringAsync, | 96 IPC_MESSAGE_HANDLER(ClipboardHostMsg_FindPboardWriteStringAsync, |
86 OnFindPboardWriteString) | 97 OnFindPboardWriteString) |
87 #endif | 98 #endif |
88 IPC_MESSAGE_UNHANDLED(handled = false) | 99 IPC_MESSAGE_UNHANDLED(handled = false) |
89 IPC_END_MESSAGE_MAP() | 100 IPC_END_MESSAGE_MAP() |
90 return handled; | 101 return handled; |
91 } | 102 } |
92 | 103 |
93 ClipboardMessageFilter::~ClipboardMessageFilter() { | 104 ClipboardMessageFilter::~ClipboardMessageFilter() { |
94 } | 105 } |
95 | 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 |
96 void ClipboardMessageFilter::OnGetSequenceNumber(ui::ClipboardType type, | 172 void ClipboardMessageFilter::OnGetSequenceNumber(ui::ClipboardType type, |
97 uint64* sequence_number) { | 173 uint64* sequence_number) { |
98 *sequence_number = GetClipboard()->GetSequenceNumber(type); | 174 *sequence_number = GetClipboard()->GetSequenceNumber(type); |
99 } | 175 } |
100 | 176 |
101 void ClipboardMessageFilter::OnReadAvailableTypes( | 177 void ClipboardMessageFilter::OnReadAvailableTypes( |
102 ui::ClipboardType type, | 178 ui::ClipboardType type, |
103 std::vector<base::string16>* types, | 179 std::vector<base::string16>* types, |
104 bool* contains_filenames) { | 180 bool* contains_filenames) { |
105 GetClipboard()->ReadAvailableTypes(type, types, contains_filenames); | 181 GetClipboard()->ReadAvailableTypes(type, types, contains_filenames); |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
203 image_size); | 279 image_size); |
204 Send(reply_msg); | 280 Send(reply_msg); |
205 } | 281 } |
206 | 282 |
207 void ClipboardMessageFilter::OnReadCustomData(ui::ClipboardType clipboard_type, | 283 void ClipboardMessageFilter::OnReadCustomData(ui::ClipboardType clipboard_type, |
208 const base::string16& type, | 284 const base::string16& type, |
209 base::string16* result) { | 285 base::string16* result) { |
210 GetClipboard()->ReadCustomData(clipboard_type, type, result); | 286 GetClipboard()->ReadCustomData(clipboard_type, type, result); |
211 } | 287 } |
212 | 288 |
213 void ClipboardMessageFilter::OnWriteText(ui::ClipboardType clipboard_type, | |
214 const base::string16& text) { | |
215 clipboard_writer_->WriteText(text); | |
216 } | |
217 | |
218 void ClipboardMessageFilter::OnWriteHTML(ui::ClipboardType clipboard_type, | |
219 const base::string16& markup, | |
220 const GURL& url) { | |
221 clipboard_writer_->WriteHTML(markup, url.spec()); | |
222 } | |
223 | |
224 void ClipboardMessageFilter::OnWriteSmartPasteMarker( | |
225 ui::ClipboardType clipboard_type) { | |
226 clipboard_writer_->WriteWebSmartPaste(); | |
227 } | |
228 | |
229 void ClipboardMessageFilter::OnWriteCustomData( | |
230 ui::ClipboardType clipboard_type, | |
231 const std::map<base::string16, base::string16>& data) { | |
232 Pickle pickle; | |
233 ui::WriteCustomDataToPickle(data, &pickle); | |
234 clipboard_writer_->WritePickledData( | |
235 pickle, ui::Clipboard::GetWebCustomDataFormatType()); | |
236 } | |
237 | |
238 void ClipboardMessageFilter::OnWriteBookmark(ui::ClipboardType clipboard_type, | |
239 const GURL& url, | |
240 const base::string16& title) { | |
241 clipboard_writer_->WriteBookmark(title, url.spec()); | |
242 } | |
243 | |
244 void ClipboardMessageFilter::OnWriteImage(ui::ClipboardType clipboard_type, | |
245 const gfx::Size& size, | |
246 base::SharedMemoryHandle handle) { | |
247 if (!base::SharedMemory::IsHandleValid(handle)) { | |
248 return; | |
249 } | |
250 | |
251 scoped_ptr<base::SharedMemory> bitmap_buffer( | |
252 #if defined(OS_WIN) | |
253 new base::SharedMemory(handle, true, PeerHandle())); | |
254 #else | |
255 new base::SharedMemory(handle, true)); | |
256 #endif | |
257 | |
258 SkBitmap bitmap; | |
259 // Let Skia do some sanity checking for (no negative widths/heights, no | |
260 // overflows while calculating bytes per row, etc). | |
261 if (!bitmap.setInfo(SkImageInfo::MakeN32Premul( | |
262 size.width(), size.height()))) { | |
263 return; | |
264 } | |
265 | |
266 // Make sure the size is representable as a signed 32-bit int, so | |
267 // SkBitmap::getSize() won't be truncated. | |
268 if (!sk_64_isS32(bitmap.computeSize64())) | |
269 return; | |
270 | |
271 if (!bitmap_buffer->Map(bitmap.getSize())) | |
272 return; | |
273 | |
274 if (!bitmap.installPixels(bitmap.info(), | |
275 bitmap_buffer->memory(), | |
276 bitmap.rowBytes(), | |
277 NULL, | |
278 &ReleaseSharedMemoryPixels, | |
279 bitmap_buffer.get())) | |
280 return; | |
281 | |
282 // On success, SkBitmap now owns the SharedMemory. | |
283 ignore_result(bitmap_buffer.release()); | |
284 clipboard_writer_->WriteImage(bitmap); | |
285 } | |
286 | |
287 void ClipboardMessageFilter::OnCommitWrite(ui::ClipboardType clipboard_type) { | |
288 #if defined(OS_WIN) | |
289 // On non-Windows platforms, all clipboard IPCs are handled on the UI thread. | |
290 // However, Windows handles the clipboard IPCs on the IO thread to prevent | |
291 // deadlocks. Clipboard writes must still occur on the UI thread because the | |
292 // clipboard object from the IO thread cannot create windows so it cannot be | |
293 // the "owner" of the clipboard's contents. See http://crbug.com/5823. | |
294 BrowserThread::DeleteSoon( | |
295 BrowserThread::UI, FROM_HERE, clipboard_writer_.release()); | |
296 #endif | |
297 clipboard_writer_.reset( | |
298 new ui::ScopedClipboardWriter(ui::CLIPBOARD_TYPE_COPY_PASTE)); | |
299 } | |
300 | |
301 // static | 289 // static |
302 ui::Clipboard* ClipboardMessageFilter::GetClipboard() { | 290 ui::Clipboard* ClipboardMessageFilter::GetClipboard() { |
303 // We have a static instance of the clipboard service for use by all message | 291 // We have a static instance of the clipboard service for use by all message |
304 // filters. This instance lives for the life of the browser processes. | 292 // filters. This instance lives for the life of the browser processes. |
305 static ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); | 293 static ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); |
306 return clipboard; | 294 return clipboard; |
307 } | 295 } |
308 | 296 |
309 } // namespace content | 297 } // namespace content |
OLD | NEW |