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 #include <memory>
10 
11 #include "base/logging.h"
12 #include "base/memory/ptr_util.h"
13 #include "base/stl_util.h"
14 #include "third_party/skia/include/core/SkBitmap.h"
15 #include "ui/gfx/geometry/size.h"
16 
17 namespace ui {
18 
19 // static
SetAllowedThreads(const std::vector<base::PlatformThreadId> & allowed_threads)20 void Clipboard::SetAllowedThreads(
21     const std::vector<base::PlatformThreadId>& allowed_threads) {
22   base::AutoLock lock(ClipboardMapLock());
23 
24   AllowedThreads().clear();
25   std::copy(allowed_threads.begin(), allowed_threads.end(),
26             std::back_inserter(AllowedThreads()));
27 }
28 
29 // static
SetClipboardForCurrentThread(std::unique_ptr<Clipboard> platform_clipboard)30 void Clipboard::SetClipboardForCurrentThread(
31     std::unique_ptr<Clipboard> platform_clipboard) {
32   base::AutoLock lock(ClipboardMapLock());
33   base::PlatformThreadId id = Clipboard::GetAndValidateThreadID();
34 
35   ClipboardMap* clipboard_map = ClipboardMapPtr();
36   // This shouldn't happen. The clipboard should not already exist.
37   DCHECK(!base::Contains(*clipboard_map, id));
38   clipboard_map->insert({id, std::move(platform_clipboard)});
39 }
40 
41 // static
GetForCurrentThread()42 Clipboard* Clipboard::GetForCurrentThread() {
43   base::AutoLock lock(ClipboardMapLock());
44   base::PlatformThreadId id = GetAndValidateThreadID();
45 
46   ClipboardMap* clipboard_map = ClipboardMapPtr();
47   auto it = clipboard_map->find(id);
48   if (it != clipboard_map->end())
49     return it->second.get();
50 
51   Clipboard* clipboard = Clipboard::Create();
52   clipboard_map->insert({id, base::WrapUnique(clipboard)});
53   return clipboard;
54 }
55 
56 // static
TakeForCurrentThread()57 std::unique_ptr<Clipboard> Clipboard::TakeForCurrentThread() {
58   base::AutoLock lock(ClipboardMapLock());
59 
60   ClipboardMap* clipboard_map = ClipboardMapPtr();
61   base::PlatformThreadId id = base::PlatformThread::CurrentId();
62 
63   Clipboard* clipboard = nullptr;
64 
65   auto it = clipboard_map->find(id);
66   if (it != clipboard_map->end()) {
67     clipboard = it->second.release();
68     clipboard_map->erase(it);
69   }
70 
71   return base::WrapUnique(clipboard);
72 }
73 
74 // static
OnPreShutdownForCurrentThread()75 void Clipboard::OnPreShutdownForCurrentThread() {
76   base::AutoLock lock(ClipboardMapLock());
77   base::PlatformThreadId id = GetAndValidateThreadID();
78 
79   ClipboardMap* clipboard_map = ClipboardMapPtr();
80   auto it = clipboard_map->find(id);
81   if (it != clipboard_map->end())
82     it->second->OnPreShutdown();
83 }
84 
85 // static
DestroyClipboardForCurrentThread()86 void Clipboard::DestroyClipboardForCurrentThread() {
87   base::AutoLock lock(ClipboardMapLock());
88 
89   ClipboardMap* clipboard_map = ClipboardMapPtr();
90   base::PlatformThreadId id = base::PlatformThread::CurrentId();
91   auto it = clipboard_map->find(id);
92   if (it != clipboard_map->end())
93     clipboard_map->erase(it);
94 }
95 
GetLastModifiedTime() const96 base::Time Clipboard::GetLastModifiedTime() const {
97   return base::Time();
98 }
99 
ClearLastModifiedTime()100 void Clipboard::ClearLastModifiedTime() {}
101 
102 Clipboard::Clipboard() = default;
103 Clipboard::~Clipboard() = default;
104 
DispatchPortableRepresentation(PortableFormat format,const ObjectMapParams & params)105 void Clipboard::DispatchPortableRepresentation(PortableFormat format,
106                                                const ObjectMapParams& params) {
107   // Ignore writes with empty parameters.
108   for (const auto& param : params) {
109     if (param.empty())
110       return;
111   }
112 
113   switch (format) {
114     case PortableFormat::kText:
115       WriteText(&(params[0].front()), params[0].size());
116       break;
117 
118     case PortableFormat::kHtml:
119       if (params.size() == 2) {
120         if (params[1].empty())
121           return;
122         WriteHTML(&(params[0].front()), params[0].size(),
123                   &(params[1].front()), params[1].size());
124       } else if (params.size() == 1) {
125         WriteHTML(&(params[0].front()), params[0].size(), nullptr, 0);
126       }
127       break;
128 
129     case PortableFormat::kRtf:
130       WriteRTF(&(params[0].front()), params[0].size());
131       break;
132 
133     case PortableFormat::kBookmark:
134       WriteBookmark(&(params[0].front()), params[0].size(),
135                     &(params[1].front()), params[1].size());
136       break;
137 
138     case PortableFormat::kWebkit:
139       WriteWebSmartPaste();
140       break;
141 
142     case PortableFormat::kBitmap: {
143       // Usually, the params are just UTF-8 strings. However, for images,
144       // ScopedClipboardWriter actually sizes the buffer to sizeof(SkBitmap*),
145       // aliases the contents of the vector to a SkBitmap**, and writes the
146       // pointer to the actual SkBitmap in the clipboard object param.
147       const char* packed_pointer_buffer = &params[0].front();
148       WriteBitmap(**reinterpret_cast<SkBitmap* const*>(packed_pointer_buffer));
149       break;
150     }
151 
152     case PortableFormat::kData:
153       WriteData(ClipboardFormatType::Deserialize(
154                     std::string(&(params[0].front()), params[0].size())),
155                 &(params[1].front()), params[1].size());
156       break;
157 
158     default:
159       NOTREACHED();
160   }
161 }
162 
DispatchPlatformRepresentations(std::vector<Clipboard::PlatformRepresentation> platform_representations)163 void Clipboard::DispatchPlatformRepresentations(
164     std::vector<Clipboard::PlatformRepresentation> platform_representations) {
165   for (const auto& representation : platform_representations) {
166     WriteData(ClipboardFormatType::GetType(representation.format),
167               reinterpret_cast<const char*>(representation.data.data()),
168               representation.data.size());
169   }
170 }
171 
GetAndValidateThreadID()172 base::PlatformThreadId Clipboard::GetAndValidateThreadID() {
173   ClipboardMapLock().AssertAcquired();
174 
175   const base::PlatformThreadId id = base::PlatformThread::CurrentId();
176 
177   // A Clipboard instance must be allocated for every thread that uses the
178   // clipboard. To prevented unbounded memory use, CHECK that the current thread
179   // was whitelisted to use the clipboard. This is a CHECK rather than a DCHECK
180   // to catch incorrect usage in production (e.g. https://crbug.com/872737).
181   CHECK(AllowedThreads().empty() || base::Contains(AllowedThreads(), id));
182 
183   return id;
184 }
185 
186 // static
AllowedThreads()187 std::vector<base::PlatformThreadId>& Clipboard::AllowedThreads() {
188   static base::NoDestructor<std::vector<base::PlatformThreadId>>
189       allowed_threads;
190   return *allowed_threads;
191 }
192 
193 // static
ClipboardMapPtr()194 Clipboard::ClipboardMap* Clipboard::ClipboardMapPtr() {
195   static base::NoDestructor<ClipboardMap> clipboard_map;
196   return clipboard_map.get();
197 }
198 
199 // static
ClipboardMapLock()200 base::Lock& Clipboard::ClipboardMapLock() {
201   static base::NoDestructor<base::Lock> clipboard_map_lock;
202   return *clipboard_map_lock;
203 }
204 
205 }  // namespace ui
206