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
9#include "base/lazy_instance.h"
10#include "base/logging.h"
11#include "base/memory/scoped_ptr.h"
12#include "base/synchronization/lock.h"
13#include "ui/gfx/size.h"
14
15namespace ui {
16
17namespace {
18
19// A compromised renderer could send us bad data, so validate it.
20// This function only checks that the size parameter makes sense, the caller
21// is responsible for further validating the bitmap buffer against
22// |bitmap_bytes|.
23//
24// |params| - Clipboard bitmap contents to validate.
25// |bitmap_bytes| - On return contains the number of bytes needed to store
26// the bitmap data or -1 if the data is invalid.
27// returns: true if the bitmap size is valid, false otherwise.
28bool IsBitmapSafe(const Clipboard::ObjectMapParams& params,
29                  uint32* bitmap_bytes) {
30  *bitmap_bytes = -1;
31  if (params[1].size() != sizeof(gfx::Size))
32    return false;
33  const gfx::Size* size =
34      reinterpret_cast<const gfx::Size*>(&(params[1].front()));
35  uint32 total_size = size->width();
36  // Using INT_MAX not SIZE_T_MAX to put a reasonable bound on things.
37  if (INT_MAX / size->width() <= size->height())
38    return false;
39  total_size *= size->height();
40  if (INT_MAX / total_size <= 4)
41    return false;
42  total_size *= 4;
43  *bitmap_bytes = total_size;
44  return true;
45}
46
47// Validates a plain bitmap on the clipboard.
48// Returns true if the clipboard data makes sense and it's safe to access the
49// bitmap.
50bool ValidatePlainBitmap(const Clipboard::ObjectMapParams& params) {
51  uint32 bitmap_bytes = -1;
52  if (!IsBitmapSafe(params, &bitmap_bytes))
53    return false;
54  if (bitmap_bytes != params[0].size())
55    return false;
56  return true;
57}
58
59// Valides a shared bitmap on the clipboard.
60// Returns true if the clipboard data makes sense and it's safe to access the
61// bitmap.
62bool ValidateAndMapSharedBitmap(const Clipboard::ObjectMapParams& params,
63                                base::SharedMemory* bitmap_data) {
64  using base::SharedMemory;
65  uint32 bitmap_bytes = -1;
66  if (!IsBitmapSafe(params, &bitmap_bytes))
67    return false;
68
69  if (!bitmap_data || !SharedMemory::IsHandleValid(bitmap_data->handle()))
70    return false;
71
72  if (!bitmap_data->Map(bitmap_bytes)) {
73    PLOG(ERROR) << "Failed to map bitmap memory";
74    return false;
75  }
76  return true;
77}
78
79// A list of allowed threads. By default, this is empty and no thread checking
80// is done (in the unit test case), but a user (like content) can set which
81// threads are allowed to call this method.
82typedef std::vector<base::PlatformThreadId> AllowedThreadsVector;
83static base::LazyInstance<AllowedThreadsVector> g_allowed_threads =
84    LAZY_INSTANCE_INITIALIZER;
85
86// Mapping from threads to clipboard objects.
87typedef std::map<base::PlatformThreadId, Clipboard*> ClipboardMap;
88static base::LazyInstance<ClipboardMap> g_clipboard_map =
89    LAZY_INSTANCE_INITIALIZER;
90
91// Mutex that controls access to |g_clipboard_map|.
92static base::LazyInstance<base::Lock>::Leaky
93    g_clipboard_map_lock = LAZY_INSTANCE_INITIALIZER;
94
95}  // namespace
96
97// static
98void Clipboard::SetAllowedThreads(
99    const std::vector<base::PlatformThreadId>& allowed_threads) {
100  base::AutoLock lock(g_clipboard_map_lock.Get());
101
102  g_allowed_threads.Get().clear();
103  std::copy(allowed_threads.begin(), allowed_threads.end(),
104            std::back_inserter(g_allowed_threads.Get()));
105}
106
107// static
108Clipboard* Clipboard::GetForCurrentThread() {
109  base::AutoLock lock(g_clipboard_map_lock.Get());
110
111  base::PlatformThreadId id = base::PlatformThread::CurrentId();
112
113  AllowedThreadsVector* allowed_threads = g_allowed_threads.Pointer();
114  if (!allowed_threads->empty()) {
115    bool found = false;
116    for (AllowedThreadsVector::const_iterator it = allowed_threads->begin();
117         it != allowed_threads->end(); ++it) {
118      if (*it == id) {
119        found = true;
120        break;
121      }
122    }
123
124    DCHECK(found);
125  }
126
127  ClipboardMap* clipboard_map = g_clipboard_map.Pointer();
128  ClipboardMap::iterator it = clipboard_map->find(id);
129  if (it != clipboard_map->end())
130    return it->second;
131
132  Clipboard* clipboard = new ui::Clipboard;
133  clipboard_map->insert(std::make_pair(id, clipboard));
134  return clipboard;
135}
136
137void Clipboard::DestroyClipboardForCurrentThread() {
138  base::AutoLock lock(g_clipboard_map_lock.Get());
139
140  ClipboardMap* clipboard_map = g_clipboard_map.Pointer();
141  base::PlatformThreadId id = base::PlatformThread::CurrentId();
142  ClipboardMap::iterator it = clipboard_map->find(id);
143  if (it != clipboard_map->end()) {
144    delete it->second;
145    clipboard_map->erase(it);
146  }
147}
148
149void Clipboard::DispatchObject(ObjectType type, const ObjectMapParams& params) {
150  // All types apart from CBF_WEBKIT need at least 1 non-empty param.
151  if (type != CBF_WEBKIT && (params.empty() || params[0].empty()))
152    return;
153  // Some other types need a non-empty 2nd param.
154  if ((type == CBF_BOOKMARK || type == CBF_BITMAP ||
155       type == CBF_SMBITMAP || type == CBF_DATA) &&
156      (params.size() != 2 || params[1].empty()))
157    return;
158  switch (type) {
159    case CBF_TEXT:
160      WriteText(&(params[0].front()), params[0].size());
161      break;
162
163    case CBF_HTML:
164      if (params.size() == 2) {
165        if (params[1].empty())
166          return;
167        WriteHTML(&(params[0].front()), params[0].size(),
168                  &(params[1].front()), params[1].size());
169      } else if (params.size() == 1) {
170        WriteHTML(&(params[0].front()), params[0].size(), NULL, 0);
171      }
172      break;
173
174    case CBF_RTF:
175      WriteRTF(&(params[0].front()), params[0].size());
176      break;
177
178    case CBF_BOOKMARK:
179      WriteBookmark(&(params[0].front()), params[0].size(),
180                    &(params[1].front()), params[1].size());
181      break;
182
183    case CBF_WEBKIT:
184      WriteWebSmartPaste();
185      break;
186
187    case CBF_BITMAP:
188      if (!ValidatePlainBitmap(params))
189        return;
190
191      WriteBitmap(&(params[0].front()), &(params[1].front()));
192      break;
193
194    case CBF_SMBITMAP: {
195      using base::SharedMemory;
196      using base::SharedMemoryHandle;
197
198      if (params[0].size() != sizeof(SharedMemory*))
199        return;
200
201      // It's OK to cast away constness here since we map the handle as
202      // read-only.
203      const char* raw_bitmap_data_const =
204          reinterpret_cast<const char*>(&(params[0].front()));
205      char* raw_bitmap_data = const_cast<char*>(raw_bitmap_data_const);
206      scoped_ptr<SharedMemory> bitmap_data(
207          *reinterpret_cast<SharedMemory**>(raw_bitmap_data));
208
209      if (!ValidateAndMapSharedBitmap(params, bitmap_data.get()))
210        return;
211      WriteBitmap(static_cast<const char*>(bitmap_data->memory()),
212                  &(params[1].front()));
213      break;
214    }
215
216    case CBF_DATA:
217      WriteData(
218          FormatType::Deserialize(
219              std::string(&(params[0].front()), params[0].size())),
220          &(params[1].front()),
221          params[1].size());
222      break;
223
224    default:
225      NOTREACHED();
226  }
227}
228
229// static
230void Clipboard::ReplaceSharedMemHandle(ObjectMap* objects,
231                                       base::SharedMemoryHandle bitmap_handle,
232                                       base::ProcessHandle process) {
233  using base::SharedMemory;
234  bool has_shared_bitmap = false;
235
236  for (ObjectMap::iterator iter = objects->begin(); iter != objects->end();
237       ++iter) {
238    if (iter->first == CBF_SMBITMAP) {
239      // The code currently only accepts sending a single bitmap over this way.
240      // Fail hard if we ever encounter more than one shared bitmap structure to
241      // fill.
242      CHECK(!has_shared_bitmap);
243
244#if defined(OS_WIN)
245      SharedMemory* bitmap = new SharedMemory(bitmap_handle, true, process);
246#else
247      SharedMemory* bitmap = new SharedMemory(bitmap_handle, true);
248#endif
249
250      // We store the shared memory object pointer so it can be retrieved by the
251      // UI thread (see DispatchObject()).
252      iter->second[0].clear();
253      for (size_t i = 0; i < sizeof(SharedMemory*); ++i)
254        iter->second[0].push_back(reinterpret_cast<char*>(&bitmap)[i]);
255      has_shared_bitmap = true;
256    }
257  }
258}
259
260}  // namespace ui
261