• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "ui/base/clipboard/clipboard.h"
6 
7 #include <iterator>
8 #include <limits>
9 
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"
16 
17 namespace ui {
18 
19 namespace {
20 
21 // Valides a shared bitmap on the clipboard.
22 // Returns true if the clipboard data makes sense and it's safe to access the
23 // bitmap.
ValidateAndMapSharedBitmap(size_t bitmap_bytes,base::SharedMemory * bitmap_data)24 bool ValidateAndMapSharedBitmap(size_t bitmap_bytes,
25                                 base::SharedMemory* bitmap_data) {
26   using base::SharedMemory;
27 
28   if (!bitmap_data || !SharedMemory::IsHandleValid(bitmap_data->handle()))
29     return false;
30 
31   if (!bitmap_data->Map(bitmap_bytes)) {
32     PLOG(ERROR) << "Failed to map bitmap memory";
33     return false;
34   }
35   return true;
36 }
37 
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;
44 
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;
49 
50 // Mutex that controls access to |g_clipboard_map|.
51 static base::LazyInstance<base::Lock>::Leaky
52     g_clipboard_map_lock = LAZY_INSTANCE_INITIALIZER;
53 
54 }  // namespace
55 
56 // static
SetAllowedThreads(const std::vector<base::PlatformThreadId> & allowed_threads)57 void Clipboard::SetAllowedThreads(
58     const std::vector<base::PlatformThreadId>& allowed_threads) {
59   base::AutoLock lock(g_clipboard_map_lock.Get());
60 
61   g_allowed_threads.Get().clear();
62   std::copy(allowed_threads.begin(), allowed_threads.end(),
63             std::back_inserter(g_allowed_threads.Get()));
64 }
65 
66 // static
GetForCurrentThread()67 Clipboard* Clipboard::GetForCurrentThread() {
68   base::AutoLock lock(g_clipboard_map_lock.Get());
69 
70   base::PlatformThreadId id = base::PlatformThread::CurrentId();
71 
72   AllowedThreadsVector* allowed_threads = g_allowed_threads.Pointer();
73   if (!allowed_threads->empty()) {
74     bool found = false;
75     for (AllowedThreadsVector::const_iterator it = allowed_threads->begin();
76          it != allowed_threads->end(); ++it) {
77       if (*it == id) {
78         found = true;
79         break;
80       }
81     }
82 
83     DCHECK(found);
84   }
85 
86   ClipboardMap* clipboard_map = g_clipboard_map.Pointer();
87   ClipboardMap::iterator it = clipboard_map->find(id);
88   if (it != clipboard_map->end())
89     return it->second;
90 
91   Clipboard* clipboard = new ui::Clipboard;
92   clipboard_map->insert(std::make_pair(id, clipboard));
93   return clipboard;
94 }
95 
DestroyClipboardForCurrentThread()96 void Clipboard::DestroyClipboardForCurrentThread() {
97   base::AutoLock lock(g_clipboard_map_lock.Get());
98 
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()) {
103     delete it->second;
104     clipboard_map->erase(it);
105   }
106 }
107 
DispatchObject(ObjectType type,const ObjectMapParams & params)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()))
111     return;
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()))
115     return;
116   switch (type) {
117     case CBF_TEXT:
118       WriteText(&(params[0].front()), params[0].size());
119       break;
120 
121     case CBF_HTML:
122       if (params.size() == 2) {
123         if (params[1].empty())
124           return;
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);
129       }
130       break;
131 
132     case CBF_RTF:
133       WriteRTF(&(params[0].front()), params[0].size());
134       break;
135 
136     case CBF_BOOKMARK:
137       WriteBookmark(&(params[0].front()), params[0].size(),
138                     &(params[1].front()), params[1].size());
139       break;
140 
141     case CBF_WEBKIT:
142       WriteWebSmartPaste();
143       break;
144 
145     case CBF_SMBITMAP: {
146       using base::SharedMemory;
147       using base::SharedMemoryHandle;
148 
149       if (params[0].size() != sizeof(SharedMemory*) ||
150           params[1].size() != sizeof(gfx::Size)) {
151         return;
152       }
153 
154       SkBitmap bitmap;
155       const gfx::Size* unvalidated_size =
156           reinterpret_cast<const gfx::Size*>(&params[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())) {
162         return;
163       }
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()))
167         return;
168 
169       // It's OK to cast away constness here since we map the handle as
170       // read-only.
171       const char* raw_bitmap_data_const =
172           reinterpret_cast<const char*>(&params[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));
176 
177       if (!ValidateAndMapSharedBitmap(bitmap.getSize(), bitmap_data.get()))
178         return;
179       bitmap.setPixels(bitmap_data->memory());
180 
181       WriteBitmap(bitmap);
182       break;
183     }
184 
185     case CBF_DATA:
186       WriteData(
187           FormatType::Deserialize(
188               std::string(&(params[0].front()), params[0].size())),
189           &(params[1].front()),
190           params[1].size());
191       break;
192 
193     default:
194       NOTREACHED();
195   }
196 }
197 
198 // static
ReplaceSharedMemHandle(ObjectMap * objects,base::SharedMemoryHandle bitmap_handle,base::ProcessHandle process)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;
204 
205   for (ObjectMap::iterator iter = objects->begin(); iter != objects->end();
206        ++iter) {
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)
211         return false;
212 
213 #if defined(OS_WIN)
214       SharedMemory* bitmap = new SharedMemory(bitmap_handle, true, process);
215 #else
216       SharedMemory* bitmap = new SharedMemory(bitmap_handle, true);
217 #endif
218 
219       // There must always be two parameters associated with each shmem bitmap.
220       if (iter->second.size() != 2)
221         return false;
222 
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;
229     }
230   }
231   return true;
232 }
233 
234 }  // namespace ui
235