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.
5 #include "content/browser/renderer_host/clipboard_message_filter.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"
29 void SanitizeObjectMap(ui::Clipboard::ObjectMap
* objects
,
30 BitmapPolicy bitmap_policy
) {
31 if (bitmap_policy
!= kAllowBitmap
)
32 objects
->erase(ui::Clipboard::CBF_SMBITMAP
);
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
);
55 ClipboardMessageFilter::ClipboardMessageFilter()
56 : BrowserMessageFilter(ClipboardMsgStart
) {}
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.
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
70 if (IPC_MESSAGE_CLASS(message
) == ClipboardMsgStart
)
71 *thread
= BrowserThread::UI
;
75 if (message
.type() == ClipboardHostMsg_ReadImage::ID
)
76 *thread
= BrowserThread::FILE;
80 bool ClipboardMessageFilter::OnMessageReceived(const IPC::Message
& message
,
81 bool* message_was_ok
) {
83 IPC_BEGIN_MESSAGE_MAP_EX(ClipboardMessageFilter
, message
, *message_was_ok
)
84 IPC_MESSAGE_HANDLER(ClipboardHostMsg_WriteObjectsAsync
, OnWriteObjectsAsync
)
85 IPC_MESSAGE_HANDLER(ClipboardHostMsg_WriteObjectsSync
, OnWriteObjectsSync
)
86 IPC_MESSAGE_HANDLER(ClipboardHostMsg_GetSequenceNumber
, OnGetSequenceNumber
)
87 IPC_MESSAGE_HANDLER(ClipboardHostMsg_IsFormatAvailable
, OnIsFormatAvailable
)
88 IPC_MESSAGE_HANDLER(ClipboardHostMsg_Clear
, OnClear
)
89 IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadAvailableTypes
,
91 IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadText
, OnReadText
)
92 IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadHTML
, OnReadHTML
)
93 IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadRTF
, OnReadRTF
)
94 IPC_MESSAGE_HANDLER_DELAY_REPLY(ClipboardHostMsg_ReadImage
, OnReadImage
)
95 IPC_MESSAGE_HANDLER(ClipboardHostMsg_ReadCustomData
, OnReadCustomData
)
96 #if defined(OS_MACOSX)
97 IPC_MESSAGE_HANDLER(ClipboardHostMsg_FindPboardWriteStringAsync
,
98 OnFindPboardWriteString
)
100 IPC_MESSAGE_UNHANDLED(handled
= false)
101 IPC_END_MESSAGE_MAP()
105 ClipboardMessageFilter::~ClipboardMessageFilter() {
108 void ClipboardMessageFilter::OnWriteObjectsSync(
109 const ui::Clipboard::ObjectMap
& objects
,
110 base::SharedMemoryHandle bitmap_handle
) {
111 DCHECK(base::SharedMemory::IsHandleValid(bitmap_handle
))
112 << "Bad bitmap handle";
114 // On Windows, we can't write directly from the IO thread, so we copy the data
115 // into a heap allocated map and post a task to the UI thread. On other
116 // platforms, to lower the amount of time the renderer has to wait for the
117 // sync IPC to complete, we also take a copy and post a task to flush the data
118 // to the clipboard later.
119 scoped_ptr
<ui::Clipboard::ObjectMap
> long_living_objects(
120 new ui::Clipboard::ObjectMap(objects
));
121 SanitizeObjectMap(long_living_objects
.get(), kAllowBitmap
);
122 // Splice the shared memory handle into the data. |long_living_objects| now
123 // contains a heap-allocated SharedMemory object that references
124 // |bitmap_handle|. This reference will keep the shared memory section alive
125 // when this IPC returns, and the SharedMemory object will eventually be
126 // freed by ui::Clipboard::WriteObjects().
127 if (!ui::Clipboard::ReplaceSharedMemHandle(
128 long_living_objects
.get(), bitmap_handle
, PeerHandle()))
131 BrowserThread::PostTask(
134 base::Bind(&ClipboardMessageFilter::WriteObjectsOnUIThread
,
135 base::Owned(long_living_objects
.release())));
138 // On Windows, the write must be performed on the UI thread because the
139 // clipboard object from the IO thread cannot create windows so it cannot be
140 // the "owner" of the clipboard's contents. See http://crbug.com/5823.
141 // TODO(dcheng): Temporarily a member of ClipboardMessageFilter so it can access
142 // ui::Clipboard::WriteObjects().
143 void ClipboardMessageFilter::WriteObjectsOnUIThread(
144 const ui::Clipboard::ObjectMap
* objects
) {
145 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
146 static ui::Clipboard
* clipboard
= ui::Clipboard::GetForCurrentThread();
147 clipboard
->WriteObjects(ui::CLIPBOARD_TYPE_COPY_PASTE
, *objects
);
150 void ClipboardMessageFilter::OnWriteObjectsAsync(
151 const ui::Clipboard::ObjectMap
& objects
) {
152 // This async message doesn't support shared-memory based bitmaps; they must
153 // be removed otherwise we might dereference a rubbish pointer.
154 scoped_ptr
<ui::Clipboard::ObjectMap
> sanitized_objects(
155 new ui::Clipboard::ObjectMap(objects
));
156 SanitizeObjectMap(sanitized_objects
.get(), kFilterBitmap
);
159 // We cannot write directly from the IO thread, and cannot service the IPC
160 // on the UI thread. We'll copy the relevant data and post a task to preform
161 // the write on the UI thread.
162 BrowserThread::PostTask(
166 &WriteObjectsOnUIThread
, base::Owned(sanitized_objects
.release())));
168 GetClipboard()->WriteObjects(
169 ui::CLIPBOARD_TYPE_COPY_PASTE
, *sanitized_objects
.get());
173 void ClipboardMessageFilter::OnGetSequenceNumber(ui::ClipboardType type
,
174 uint64
* sequence_number
) {
175 *sequence_number
= GetClipboard()->GetSequenceNumber(type
);
178 void ClipboardMessageFilter::OnReadAvailableTypes(
179 ui::ClipboardType type
,
180 std::vector
<base::string16
>* types
,
181 bool* contains_filenames
) {
182 GetClipboard()->ReadAvailableTypes(type
, types
, contains_filenames
);
185 void ClipboardMessageFilter::OnIsFormatAvailable(ClipboardFormat format
,
186 ui::ClipboardType type
,
189 case CLIPBOARD_FORMAT_PLAINTEXT
:
190 *result
= GetClipboard()->IsFormatAvailable(
191 ui::Clipboard::GetPlainTextWFormatType(), type
) ||
192 GetClipboard()->IsFormatAvailable(
193 ui::Clipboard::GetPlainTextFormatType(), type
);
195 case CLIPBOARD_FORMAT_HTML
:
196 *result
= GetClipboard()->IsFormatAvailable(
197 ui::Clipboard::GetHtmlFormatType(), type
);
199 case CLIPBOARD_FORMAT_SMART_PASTE
:
200 *result
= GetClipboard()->IsFormatAvailable(
201 ui::Clipboard::GetWebKitSmartPasteFormatType(), type
);
203 case CLIPBOARD_FORMAT_BOOKMARK
:
204 #if defined(OS_WIN) || defined(OS_MACOSX)
205 *result
= GetClipboard()->IsFormatAvailable(
206 ui::Clipboard::GetUrlWFormatType(), type
);
214 void ClipboardMessageFilter::OnClear(ui::ClipboardType type
) {
215 GetClipboard()->Clear(type
);
218 void ClipboardMessageFilter::OnReadText(ui::ClipboardType type
,
219 base::string16
* result
) {
220 if (GetClipboard()->IsFormatAvailable(
221 ui::Clipboard::GetPlainTextWFormatType(), type
)) {
222 GetClipboard()->ReadText(type
, result
);
223 } else if (GetClipboard()->IsFormatAvailable(
224 ui::Clipboard::GetPlainTextFormatType(), type
)) {
226 GetClipboard()->ReadAsciiText(type
, &ascii
);
227 *result
= base::ASCIIToUTF16(ascii
);
233 void ClipboardMessageFilter::OnReadHTML(ui::ClipboardType type
,
234 base::string16
* markup
,
236 uint32
* fragment_start
,
237 uint32
* fragment_end
) {
238 std::string src_url_str
;
239 GetClipboard()->ReadHTML(type
, markup
, &src_url_str
, fragment_start
,
241 *url
= GURL(src_url_str
);
244 void ClipboardMessageFilter::OnReadRTF(ui::ClipboardType type
,
245 std::string
* result
) {
246 GetClipboard()->ReadRTF(type
, result
);
249 void ClipboardMessageFilter::OnReadImage(ui::ClipboardType type
,
250 IPC::Message
* reply_msg
) {
251 SkBitmap bitmap
= GetClipboard()->ReadImage(type
);
254 BrowserThread::PostTask(
255 BrowserThread::FILE, FROM_HERE
,
257 &ClipboardMessageFilter::OnReadImageReply
, this, bitmap
, reply_msg
));
259 OnReadImageReply(bitmap
, reply_msg
);
263 void ClipboardMessageFilter::OnReadImageReply(
264 const SkBitmap
& bitmap
, IPC::Message
* reply_msg
) {
265 base::SharedMemoryHandle image_handle
= base::SharedMemory::NULLHandle();
266 uint32 image_size
= 0;
267 if (!bitmap
.isNull()) {
268 std::vector
<unsigned char> png_data
;
269 if (gfx::PNGCodec::FastEncodeBGRASkBitmap(bitmap
, false, &png_data
)) {
270 base::SharedMemory buffer
;
271 if (buffer
.CreateAndMapAnonymous(png_data
.size())) {
272 memcpy(buffer
.memory(), vector_as_array(&png_data
), png_data
.size());
273 if (buffer
.GiveToProcess(PeerHandle(), &image_handle
)) {
274 image_size
= png_data
.size();
279 ClipboardHostMsg_ReadImage::WriteReplyParams(reply_msg
, image_handle
,
284 void ClipboardMessageFilter::OnReadCustomData(ui::ClipboardType clipboard_type
,
285 const base::string16
& type
,
286 base::string16
* result
) {
287 GetClipboard()->ReadCustomData(clipboard_type
, type
, result
);
291 ui::Clipboard
* ClipboardMessageFilter::GetClipboard() {
292 // We have a static instance of the clipboard service for use by all message
293 // filters. This instance lives for the life of the browser processes.
294 static ui::Clipboard
* clipboard
= ui::Clipboard::GetForCurrentThread();
298 } // namespace content