Copy Smart Lock user preferences to local state so we can access them on the sign...
[chromium-blink-merge.git] / remoting / host / clipboard_win.cc
blobdd2cc28395326d9019a2578b862dee9a8cb26d67
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.
5 #include "remoting/host/clipboard.h"
7 #include <windows.h>
9 #include "base/basictypes.h"
10 #include "base/bind.h"
11 #include "base/logging.h"
12 #include "base/strings/string16.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/threading/platform_thread.h"
15 #include "base/win/message_window.h"
16 #include "base/win/scoped_hglobal.h"
17 #include "base/win/windows_version.h"
18 #include "remoting/base/constants.h"
19 #include "remoting/base/util.h"
20 #include "remoting/proto/event.pb.h"
21 #include "remoting/protocol/clipboard_stub.h"
23 namespace {
25 // A scoper class that opens and closes the clipboard.
26 // This class was adapted from the ScopedClipboard class in
27 // ui/base/clipboard/clipboard_win.cc.
28 class ScopedClipboard {
29 public:
30 ScopedClipboard() : opened_(false) {
33 ~ScopedClipboard() {
34 if (opened_) {
35 // CloseClipboard() must be called with anonymous access token. See
36 // crbug.com/441834 .
37 BOOL result = ::ImpersonateAnonymousToken(::GetCurrentThread());
38 CHECK(result);
39 ::CloseClipboard();
40 result = ::RevertToSelf();
41 CHECK(result);
45 bool Init(HWND owner) {
46 const int kMaxAttemptsToOpenClipboard = 5;
47 const base::TimeDelta kSleepTimeBetweenAttempts =
48 base::TimeDelta::FromMilliseconds(5);
50 if (opened_) {
51 NOTREACHED();
52 return true;
55 // This code runs on the UI thread, so we can block only very briefly.
56 for (int attempt = 0; attempt < kMaxAttemptsToOpenClipboard; ++attempt) {
57 if (attempt > 0) {
58 base::PlatformThread::Sleep(kSleepTimeBetweenAttempts);
60 if (::OpenClipboard(owner)) {
61 opened_ = true;
62 return true;
65 return false;
68 BOOL Empty() {
69 if (!opened_) {
70 NOTREACHED();
71 return false;
73 return ::EmptyClipboard();
76 void SetData(UINT uFormat, HANDLE hMem) {
77 if (!opened_) {
78 NOTREACHED();
79 return;
81 // The caller must not close the handle that ::SetClipboardData returns.
82 ::SetClipboardData(uFormat, hMem);
85 // The caller must not free the handle. The caller should lock the handle,
86 // copy the clipboard data, and unlock the handle. All this must be done
87 // before this ScopedClipboard is destroyed.
88 HANDLE GetData(UINT format) {
89 if (!opened_) {
90 NOTREACHED();
91 return nullptr;
93 return ::GetClipboardData(format);
96 private:
97 bool opened_;
100 typedef BOOL (WINAPI AddClipboardFormatListenerFn)(HWND);
101 typedef BOOL (WINAPI RemoveClipboardFormatListenerFn)(HWND);
103 } // namespace
105 namespace remoting {
107 class ClipboardWin : public Clipboard {
108 public:
109 ClipboardWin();
111 virtual void Start(
112 scoped_ptr<protocol::ClipboardStub> client_clipboard) override;
113 virtual void InjectClipboardEvent(
114 const protocol::ClipboardEvent& event) override;
115 virtual void Stop() override;
117 private:
118 void OnClipboardUpdate();
120 // Handles messages received by |window_|.
121 bool HandleMessage(UINT message,
122 WPARAM wparam,
123 LPARAM lparam,
124 LRESULT* result);
126 scoped_ptr<protocol::ClipboardStub> client_clipboard_;
127 AddClipboardFormatListenerFn* add_clipboard_format_listener_;
128 RemoveClipboardFormatListenerFn* remove_clipboard_format_listener_;
130 // Used to subscribe to WM_CLIPBOARDUPDATE messages.
131 scoped_ptr<base::win::MessageWindow> window_;
133 DISALLOW_COPY_AND_ASSIGN(ClipboardWin);
136 ClipboardWin::ClipboardWin()
137 : add_clipboard_format_listener_(nullptr),
138 remove_clipboard_format_listener_(nullptr) {
141 void ClipboardWin::Start(
142 scoped_ptr<protocol::ClipboardStub> client_clipboard) {
143 DCHECK(!add_clipboard_format_listener_);
144 DCHECK(!remove_clipboard_format_listener_);
145 DCHECK(!window_);
147 client_clipboard_.swap(client_clipboard);
149 // user32.dll is statically linked.
150 HMODULE user32 = GetModuleHandle(L"user32.dll");
151 CHECK(user32);
153 add_clipboard_format_listener_ =
154 reinterpret_cast<AddClipboardFormatListenerFn*>(
155 GetProcAddress(user32, "AddClipboardFormatListener"));
156 if (add_clipboard_format_listener_) {
157 remove_clipboard_format_listener_ =
158 reinterpret_cast<RemoveClipboardFormatListenerFn*>(
159 GetProcAddress(user32, "RemoveClipboardFormatListener"));
160 // If AddClipboardFormatListener() present, RemoveClipboardFormatListener()
161 // should be available too.
162 CHECK(remove_clipboard_format_listener_);
163 } else {
164 LOG(WARNING) << "AddClipboardFormatListener() is not available.";
167 window_.reset(new base::win::MessageWindow());
168 if (!window_->Create(base::Bind(&ClipboardWin::HandleMessage,
169 base::Unretained(this)))) {
170 LOG(ERROR) << "Couldn't create clipboard window.";
171 window_.reset();
172 return;
175 if (add_clipboard_format_listener_) {
176 if (!(*add_clipboard_format_listener_)(window_->hwnd())) {
177 LOG(WARNING) << "AddClipboardFormatListener() failed: " << GetLastError();
182 void ClipboardWin::Stop() {
183 client_clipboard_.reset();
185 if (window_ && remove_clipboard_format_listener_)
186 (*remove_clipboard_format_listener_)(window_->hwnd());
188 window_.reset();
191 void ClipboardWin::InjectClipboardEvent(
192 const protocol::ClipboardEvent& event) {
193 if (!window_)
194 return;
196 // Currently we only handle UTF-8 text.
197 if (event.mime_type().compare(kMimeTypeTextUtf8) != 0)
198 return;
199 if (!StringIsUtf8(event.data().c_str(), event.data().length())) {
200 LOG(ERROR) << "ClipboardEvent: data is not UTF-8 encoded.";
201 return;
204 base::string16 text = base::UTF8ToUTF16(ReplaceLfByCrLf(event.data()));
206 ScopedClipboard clipboard;
207 if (!clipboard.Init(window_->hwnd())) {
208 LOG(WARNING) << "Couldn't open the clipboard.";
209 return;
212 clipboard.Empty();
214 HGLOBAL text_global =
215 ::GlobalAlloc(GMEM_MOVEABLE, (text.size() + 1) * sizeof(WCHAR));
216 if (!text_global) {
217 LOG(WARNING) << "Couldn't allocate global memory.";
218 return;
221 LPWSTR text_global_locked =
222 reinterpret_cast<LPWSTR>(::GlobalLock(text_global));
223 memcpy(text_global_locked, text.data(), text.size() * sizeof(WCHAR));
224 text_global_locked[text.size()] = (WCHAR)0;
225 ::GlobalUnlock(text_global);
227 clipboard.SetData(CF_UNICODETEXT, text_global);
230 void ClipboardWin::OnClipboardUpdate() {
231 DCHECK(window_);
233 if (::IsClipboardFormatAvailable(CF_UNICODETEXT)) {
234 base::string16 text;
235 // Add a scope, so that we keep the clipboard open for as short a time as
236 // possible.
238 ScopedClipboard clipboard;
239 if (!clipboard.Init(window_->hwnd())) {
240 LOG(WARNING) << "Couldn't open the clipboard." << GetLastError();
241 return;
244 HGLOBAL text_global = clipboard.GetData(CF_UNICODETEXT);
245 if (!text_global) {
246 LOG(WARNING) << "Couldn't get data from the clipboard: "
247 << GetLastError();
248 return;
251 base::win::ScopedHGlobal<WCHAR*> text_lock(text_global);
252 if (!text_lock.get()) {
253 LOG(WARNING) << "Couldn't lock clipboard data: " << GetLastError();
254 return;
256 text.assign(text_lock.get());
259 protocol::ClipboardEvent event;
260 event.set_mime_type(kMimeTypeTextUtf8);
261 event.set_data(ReplaceCrLfByLf(base::UTF16ToUTF8(text)));
263 if (client_clipboard_.get()) {
264 client_clipboard_->InjectClipboardEvent(event);
269 bool ClipboardWin::HandleMessage(
270 UINT message, WPARAM wparam, LPARAM lparam, LRESULT* result) {
271 if (message == WM_CLIPBOARDUPDATE) {
272 OnClipboardUpdate();
273 *result = 0;
274 return true;
277 return false;
280 scoped_ptr<Clipboard> Clipboard::Create() {
281 return make_scoped_ptr(new ClipboardWin());
284 } // namespace remoting