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 "ui/base/clipboard/clipboard.h"
10 #include "base/lazy_instance.h"
11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/synchronization/lock.h"
14 #include "third_party/skia/include/core/SkBitmap.h"
15 #include "ui/gfx/size.h"
21 // Valides a shared bitmap on the clipboard.
22 // Returns true if the clipboard data makes sense and it's safe to access the
24 bool ValidateAndMapSharedBitmap(size_t bitmap_bytes
,
25 base::SharedMemory
* bitmap_data
) {
26 using base::SharedMemory
;
28 if (!bitmap_data
|| !SharedMemory::IsHandleValid(bitmap_data
->handle()))
31 if (!bitmap_data
->Map(bitmap_bytes
)) {
32 PLOG(ERROR
) << "Failed to map bitmap memory";
38 // A list of allowed threads. By default, this is empty and no thread checking
39 // is done (in the unit test case), but a user (like content) can set which
40 // threads are allowed to call this method.
41 typedef std::vector
<base::PlatformThreadId
> AllowedThreadsVector
;
42 static base::LazyInstance
<AllowedThreadsVector
> g_allowed_threads
=
43 LAZY_INSTANCE_INITIALIZER
;
45 // Mapping from threads to clipboard objects.
46 typedef std::map
<base::PlatformThreadId
, Clipboard
*> ClipboardMap
;
47 static base::LazyInstance
<ClipboardMap
> g_clipboard_map
=
48 LAZY_INSTANCE_INITIALIZER
;
50 // Mutex that controls access to |g_clipboard_map|.
51 static base::LazyInstance
<base::Lock
>::Leaky
52 g_clipboard_map_lock
= LAZY_INSTANCE_INITIALIZER
;
57 void Clipboard::SetAllowedThreads(
58 const std::vector
<base::PlatformThreadId
>& allowed_threads
) {
59 base::AutoLock
lock(g_clipboard_map_lock
.Get());
61 g_allowed_threads
.Get().clear();
62 std::copy(allowed_threads
.begin(), allowed_threads
.end(),
63 std::back_inserter(g_allowed_threads
.Get()));
67 Clipboard
* Clipboard::GetForCurrentThread() {
68 base::AutoLock
lock(g_clipboard_map_lock
.Get());
70 base::PlatformThreadId id
= base::PlatformThread::CurrentId();
72 AllowedThreadsVector
* allowed_threads
= g_allowed_threads
.Pointer();
73 if (!allowed_threads
->empty()) {
75 for (AllowedThreadsVector::const_iterator it
= allowed_threads
->begin();
76 it
!= allowed_threads
->end(); ++it
) {
86 ClipboardMap
* clipboard_map
= g_clipboard_map
.Pointer();
87 ClipboardMap::iterator it
= clipboard_map
->find(id
);
88 if (it
!= clipboard_map
->end())
91 Clipboard
* clipboard
= new ui::Clipboard
;
92 clipboard_map
->insert(std::make_pair(id
, clipboard
));
96 void Clipboard::DestroyClipboardForCurrentThread() {
97 base::AutoLock
lock(g_clipboard_map_lock
.Get());
99 ClipboardMap
* clipboard_map
= g_clipboard_map
.Pointer();
100 base::PlatformThreadId id
= base::PlatformThread::CurrentId();
101 ClipboardMap::iterator it
= clipboard_map
->find(id
);
102 if (it
!= clipboard_map
->end()) {
104 clipboard_map
->erase(it
);
108 void Clipboard::DispatchObject(ObjectType type
, const ObjectMapParams
& params
) {
109 // All types apart from CBF_WEBKIT need at least 1 non-empty param.
110 if (type
!= CBF_WEBKIT
&& (params
.empty() || params
[0].empty()))
112 // Some other types need a non-empty 2nd param.
113 if ((type
== CBF_BOOKMARK
|| type
== CBF_SMBITMAP
|| type
== CBF_DATA
) &&
114 (params
.size() != 2 || params
[1].empty()))
118 WriteText(&(params
[0].front()), params
[0].size());
122 if (params
.size() == 2) {
123 if (params
[1].empty())
125 WriteHTML(&(params
[0].front()), params
[0].size(),
126 &(params
[1].front()), params
[1].size());
127 } else if (params
.size() == 1) {
128 WriteHTML(&(params
[0].front()), params
[0].size(), NULL
, 0);
133 WriteRTF(&(params
[0].front()), params
[0].size());
137 WriteBookmark(&(params
[0].front()), params
[0].size(),
138 &(params
[1].front()), params
[1].size());
142 WriteWebSmartPaste();
146 using base::SharedMemory
;
147 using base::SharedMemoryHandle
;
149 if (params
[0].size() != sizeof(SharedMemory
*) ||
150 params
[1].size() != sizeof(gfx::Size
)) {
155 const gfx::Size
* unvalidated_size
=
156 reinterpret_cast<const gfx::Size
*>(¶ms
[1].front());
157 // Let Skia do some sanity checking for us (no negative widths/heights, no
158 // overflows while calculating bytes per row, etc).
159 if (!bitmap
.setConfig(SkBitmap::kARGB_8888_Config
,
160 unvalidated_size
->width(),
161 unvalidated_size
->height())) {
164 // Make sure the size is representable as a signed 32-bit int, so
165 // SkBitmap::getSize() won't be truncated.
166 if (!sk_64_isS32(bitmap
.computeSize64()))
169 // It's OK to cast away constness here since we map the handle as
171 const char* raw_bitmap_data_const
=
172 reinterpret_cast<const char*>(¶ms
[0].front());
173 char* raw_bitmap_data
= const_cast<char*>(raw_bitmap_data_const
);
174 scoped_ptr
<SharedMemory
> bitmap_data(
175 *reinterpret_cast<SharedMemory
**>(raw_bitmap_data
));
177 if (!ValidateAndMapSharedBitmap(bitmap
.getSize(), bitmap_data
.get()))
179 bitmap
.setPixels(bitmap_data
->memory());
187 FormatType::Deserialize(
188 std::string(&(params
[0].front()), params
[0].size())),
189 &(params
[1].front()),
199 bool Clipboard::ReplaceSharedMemHandle(ObjectMap
* objects
,
200 base::SharedMemoryHandle bitmap_handle
,
201 base::ProcessHandle process
) {
202 using base::SharedMemory
;
203 bool has_shared_bitmap
= false;
205 for (ObjectMap::iterator iter
= objects
->begin(); iter
!= objects
->end();
207 if (iter
->first
== CBF_SMBITMAP
) {
208 // The code currently only accepts sending a single bitmap over this way.
209 // Fail if we ever encounter more than one shmem bitmap structure to fill.
210 if (has_shared_bitmap
)
214 SharedMemory
* bitmap
= new SharedMemory(bitmap_handle
, true, process
);
216 SharedMemory
* bitmap
= new SharedMemory(bitmap_handle
, true);
219 // There must always be two parameters associated with each shmem bitmap.
220 if (iter
->second
.size() != 2)
223 // We store the shared memory object pointer so it can be retrieved by the
224 // UI thread (see DispatchObject()).
225 iter
->second
[0].clear();
226 for (size_t i
= 0; i
< sizeof(SharedMemory
*); ++i
)
227 iter
->second
[0].push_back(reinterpret_cast<char*>(&bitmap
)[i
]);
228 has_shared_bitmap
= true;