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 = ¶ms[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