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*>(¶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())) { 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 (bitmap.getSize64().is64()) 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*>(¶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)); 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