Home | History | Annotate | Download | only in clipboard
      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.
     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
     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
     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 
     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 
    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
    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