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" |
10 #include "base/memory/scoped_ptr.h" | 11 #include "base/memory/scoped_ptr.h" |
| 12 #include "base/pickle.h" |
11 #include "base/stl_util.h" | 13 #include "base/stl_util.h" |
12 #include "base/strings/utf_string_conversions.h" | 14 #include "base/strings/utf_string_conversions.h" |
13 #include "content/common/clipboard_messages.h" | 15 #include "content/common/clipboard_messages.h" |
14 #include "content/public/browser/browser_context.h" | 16 #include "content/public/browser/browser_context.h" |
15 #include "ipc/ipc_message_macros.h" | 17 #include "ipc/ipc_message_macros.h" |
16 #include "third_party/skia/include/core/SkBitmap.h" | 18 #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" |
17 #include "ui/gfx/codec/png_codec.h" | 21 #include "ui/gfx/codec/png_codec.h" |
18 #include "ui/gfx/size.h" | 22 #include "ui/gfx/size.h" |
19 #include "url/gurl.h" | 23 #include "url/gurl.h" |
20 | 24 |
21 namespace content { | 25 namespace content { |
22 | 26 |
23 namespace { | 27 namespace { |
24 | 28 |
25 enum BitmapPolicy { | 29 void ReleaseSharedMemoryPixels(void* addr, void* context) { |
26 kFilterBitmap, | 30 delete reinterpret_cast<base::SharedMemory*>(context); |
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 } | 31 } |
51 | 32 |
52 } // namespace | 33 } // namespace |
53 | 34 |
54 | |
55 ClipboardMessageFilter::ClipboardMessageFilter() | 35 ClipboardMessageFilter::ClipboardMessageFilter() |
56 : BrowserMessageFilter(ClipboardMsgStart) {} | 36 : BrowserMessageFilter(ClipboardMsgStart), |
| 37 clipboard_writer_( |
| 38 new ui::ScopedClipboardWriter(ui::CLIPBOARD_TYPE_COPY_PASTE)) { |
| 39 } |
57 | 40 |
58 void ClipboardMessageFilter::OverrideThreadForMessage( | 41 void ClipboardMessageFilter::OverrideThreadForMessage( |
59 const IPC::Message& message, BrowserThread::ID* thread) { | 42 const IPC::Message& message, BrowserThread::ID* thread) { |
60 // Clipboard writes should always occur on the UI thread due the restrictions | 43 // 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 | 44 // of various platform APIs. In general, the clipboard is not thread-safe, so |
62 // all clipboard calls should be serviced from the UI thread. | 45 // all clipboard calls should be serviced from the UI thread. |
63 // | 46 // |
64 // Windows needs clipboard reads to be serviced from the IO thread because | 47 // 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 | 48 // 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 | 49 // 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 | 50 // thread-safe so it is ok for reads and writes to be serviced from different |
68 // threads. | 51 // threads. |
69 #if !defined(OS_WIN) | 52 #if !defined(OS_WIN) |
70 if (IPC_MESSAGE_CLASS(message) == ClipboardMsgStart) | 53 if (IPC_MESSAGE_CLASS(message) == ClipboardMsgStart) |
71 *thread = BrowserThread::UI; | 54 *thread = BrowserThread::UI; |
72 #endif | 55 #endif |
73 | 56 |
74 #if defined(OS_WIN) | 57 #if defined(OS_WIN) |
75 if (message.type() == ClipboardHostMsg_ReadImage::ID) | 58 if (message.type() == ClipboardHostMsg_ReadImage::ID) |
76 *thread = BrowserThread::FILE; | 59 *thread = BrowserThread::FILE; |
77 #endif | 60 #endif |
78 } | 61 } |
79 | 62 |
80 bool ClipboardMessageFilter::OnMessageReceived(const IPC::Message& message) { | 63 bool ClipboardMessageFilter::OnMessageReceived(const IPC::Message& message) { |
81 bool handled = true; | 64 bool handled = true; |
82 IPC_BEGIN_MESSAGE_MAP(ClipboardMessageFilter, message) | 65 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) | 66 IPC_MESSAGE_HANDLER(ClipboardHostMsg_GetSequenceNumber, OnGetSequenceNumber) |
86 IPC_MESSAGE_HANDLER(ClipboardHostMsg_IsFormatAvailable, OnIsFormatAvailable) | 67 IPC_MESSAGE_HANDLER(ClipboardHostMsg_IsFormatAvailable, OnIsFormatAvailable) |
87 IPC_MESSAGE_HANDLER(ClipboardHostMsg_Clear, OnClear) | 68 IPC_MESSAGE_HANDLER(ClipboardHostMsg_Clear, OnClear) |
88 IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadAvailableTypes, | 69 IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadAvailableTypes, |
89 OnReadAvailableTypes) | 70 OnReadAvailableTypes) |
90 IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadText, OnReadText) | 71 IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadText, OnReadText) |
91 IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadHTML, OnReadHTML) | 72 IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadHTML, OnReadHTML) |
92 IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadRTF, OnReadRTF) | 73 IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadRTF, OnReadRTF) |
93 IPC_MESSAGE_HANDLER_DELAY_REPLY(ClipboardHostMsg_ReadImage, OnReadImage) | 74 IPC_MESSAGE_HANDLER_DELAY_REPLY(ClipboardHostMsg_ReadImage, OnReadImage) |
94 IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadCustomData, OnReadCustomData) | 75 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); |
95 #if defined(OS_MACOSX) | 84 #if defined(OS_MACOSX) |
96 IPC_MESSAGE_HANDLER(ClipboardHostMsg_FindPboardWriteStringAsync, | 85 IPC_MESSAGE_HANDLER(ClipboardHostMsg_FindPboardWriteStringAsync, |
97 OnFindPboardWriteString) | 86 OnFindPboardWriteString) |
98 #endif | 87 #endif |
99 IPC_MESSAGE_UNHANDLED(handled = false) | 88 IPC_MESSAGE_UNHANDLED(handled = false) |
100 IPC_END_MESSAGE_MAP() | 89 IPC_END_MESSAGE_MAP() |
101 return handled; | 90 return handled; |
102 } | 91 } |
103 | 92 |
104 ClipboardMessageFilter::~ClipboardMessageFilter() { | 93 ClipboardMessageFilter::~ClipboardMessageFilter() { |
105 } | 94 } |
106 | 95 |
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, | 96 void ClipboardMessageFilter::OnGetSequenceNumber(ui::ClipboardType type, |
173 uint64* sequence_number) { | 97 uint64* sequence_number) { |
174 *sequence_number = GetClipboard()->GetSequenceNumber(type); | 98 *sequence_number = GetClipboard()->GetSequenceNumber(type); |
175 } | 99 } |
176 | 100 |
177 void ClipboardMessageFilter::OnReadAvailableTypes( | 101 void ClipboardMessageFilter::OnReadAvailableTypes( |
178 ui::ClipboardType type, | 102 ui::ClipboardType type, |
179 std::vector<base::string16>* types, | 103 std::vector<base::string16>* types, |
180 bool* contains_filenames) { | 104 bool* contains_filenames) { |
181 GetClipboard()->ReadAvailableTypes(type, types, contains_filenames); | 105 GetClipboard()->ReadAvailableTypes(type, types, contains_filenames); |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
279 image_size); | 203 image_size); |
280 Send(reply_msg); | 204 Send(reply_msg); |
281 } | 205 } |
282 | 206 |
283 void ClipboardMessageFilter::OnReadCustomData(ui::ClipboardType clipboard_type, | 207 void ClipboardMessageFilter::OnReadCustomData(ui::ClipboardType clipboard_type, |
284 const base::string16& type, | 208 const base::string16& type, |
285 base::string16* result) { | 209 base::string16* result) { |
286 GetClipboard()->ReadCustomData(clipboard_type, type, result); | 210 GetClipboard()->ReadCustomData(clipboard_type, type, result); |
287 } | 211 } |
288 | 212 |
| 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 |
289 // static | 301 // static |
290 ui::Clipboard* ClipboardMessageFilter::GetClipboard() { | 302 ui::Clipboard* ClipboardMessageFilter::GetClipboard() { |
291 // We have a static instance of the clipboard service for use by all message | 303 // 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. | 304 // filters. This instance lives for the life of the browser processes. |
293 static ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); | 305 static ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); |
294 return clipboard; | 306 return clipboard; |
295 } | 307 } |
296 | 308 |
297 } // namespace content | 309 } // namespace content |
OLD | NEW |