<webview>: Move name attribute to chrome
[chromium-blink-merge.git] / remoting / host / clipboard_win.cc
blobdfb627013aa483d075802901a10c0a2084133622
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();
39 bool Init(HWND owner) {
40 const int kMaxAttemptsToOpenClipboard = 5;
41 const base::TimeDelta kSleepTimeBetweenAttempts =
42 base::TimeDelta::FromMilliseconds(5);
44 if (opened_) {
45 NOTREACHED();
46 return true;
49 // This code runs on the UI thread, so we can block only very briefly.
50 for (int attempt = 0; attempt < kMaxAttemptsToOpenClipboard; ++attempt) {
51 if (attempt > 0) {
52 base::PlatformThread::Sleep(kSleepTimeBetweenAttempts);
54 if (::OpenClipboard(owner)) {
55 opened_ = true;
56 return true;
59 return false;
62 BOOL Empty() {
63 if (!opened_) {
64 NOTREACHED();
65 return false;
67 return ::EmptyClipboard();
70 void SetData(UINT uFormat, HANDLE hMem) {
71 if (!opened_) {
72 NOTREACHED();
73 return;
75 // The caller must not close the handle that ::SetClipboardData returns.
76 ::SetClipboardData(uFormat, hMem);
79 // The caller must not free the handle. The caller should lock the handle,
80 // copy the clipboard data, and unlock the handle. All this must be done
81 // before this ScopedClipboard is destroyed.
82 HANDLE GetData(UINT format) {
83 if (!opened_) {
84 NOTREACHED();
85 return NULL;
87 return ::GetClipboardData(format);
90 private:
91 bool opened_;
94 typedef BOOL (WINAPI AddClipboardFormatListenerFn)(HWND);
95 typedef BOOL (WINAPI RemoveClipboardFormatListenerFn)(HWND);
97 } // namespace
99 namespace remoting {
101 class ClipboardWin : public Clipboard {
102 public:
103 ClipboardWin();
105 virtual void Start(
106 scoped_ptr<protocol::ClipboardStub> client_clipboard) OVERRIDE;
107 virtual void InjectClipboardEvent(
108 const protocol::ClipboardEvent& event) OVERRIDE;
109 virtual void Stop() OVERRIDE;
111 private:
112 void OnClipboardUpdate();
114 // Handles messages received by |window_|.
115 bool HandleMessage(UINT message,
116 WPARAM wparam,
117 LPARAM lparam,
118 LRESULT* result);
120 scoped_ptr<protocol::ClipboardStub> client_clipboard_;
121 AddClipboardFormatListenerFn* add_clipboard_format_listener_;
122 RemoveClipboardFormatListenerFn* remove_clipboard_format_listener_;
124 // Used to subscribe to WM_CLIPBOARDUPDATE messages.
125 scoped_ptr<base::win::MessageWindow> window_;
127 DISALLOW_COPY_AND_ASSIGN(ClipboardWin);
130 ClipboardWin::ClipboardWin()
131 : add_clipboard_format_listener_(NULL),
132 remove_clipboard_format_listener_(NULL) {
135 void ClipboardWin::Start(
136 scoped_ptr<protocol::ClipboardStub> client_clipboard) {
137 DCHECK(!add_clipboard_format_listener_);
138 DCHECK(!remove_clipboard_format_listener_);
139 DCHECK(!window_);
141 client_clipboard_.swap(client_clipboard);
143 // user32.dll is statically linked.
144 HMODULE user32 = GetModuleHandle(L"user32.dll");
145 CHECK(user32);
147 add_clipboard_format_listener_ =
148 reinterpret_cast<AddClipboardFormatListenerFn*>(
149 GetProcAddress(user32, "AddClipboardFormatListener"));
150 if (add_clipboard_format_listener_) {
151 remove_clipboard_format_listener_ =
152 reinterpret_cast<RemoveClipboardFormatListenerFn*>(
153 GetProcAddress(user32, "RemoveClipboardFormatListener"));
154 // If AddClipboardFormatListener() present, RemoveClipboardFormatListener()
155 // should be available too.
156 CHECK(remove_clipboard_format_listener_);
157 } else {
158 LOG(WARNING) << "AddClipboardFormatListener() is not available.";
161 window_.reset(new base::win::MessageWindow());
162 if (!window_->Create(base::Bind(&ClipboardWin::HandleMessage,
163 base::Unretained(this)))) {
164 LOG(ERROR) << "Couldn't create clipboard window.";
165 window_.reset();
166 return;
169 if (add_clipboard_format_listener_) {
170 if (!(*add_clipboard_format_listener_)(window_->hwnd())) {
171 LOG(WARNING) << "AddClipboardFormatListener() failed: " << GetLastError();
176 void ClipboardWin::Stop() {
177 client_clipboard_.reset();
179 if (window_ && remove_clipboard_format_listener_)
180 (*remove_clipboard_format_listener_)(window_->hwnd());
182 window_.reset();
185 void ClipboardWin::InjectClipboardEvent(
186 const protocol::ClipboardEvent& event) {
187 if (!window_)
188 return;
190 // Currently we only handle UTF-8 text.
191 if (event.mime_type().compare(kMimeTypeTextUtf8) != 0)
192 return;
193 if (!StringIsUtf8(event.data().c_str(), event.data().length())) {
194 LOG(ERROR) << "ClipboardEvent: data is not UTF-8 encoded.";
195 return;
198 base::string16 text = base::UTF8ToUTF16(ReplaceLfByCrLf(event.data()));
200 ScopedClipboard clipboard;
201 if (!clipboard.Init(window_->hwnd())) {
202 LOG(WARNING) << "Couldn't open the clipboard.";
203 return;
206 clipboard.Empty();
208 HGLOBAL text_global =
209 ::GlobalAlloc(GMEM_MOVEABLE, (text.size() + 1) * sizeof(WCHAR));
210 if (!text_global) {
211 LOG(WARNING) << "Couldn't allocate global memory.";
212 return;
215 LPWSTR text_global_locked =
216 reinterpret_cast<LPWSTR>(::GlobalLock(text_global));
217 memcpy(text_global_locked, text.data(), text.size() * sizeof(WCHAR));
218 text_global_locked[text.size()] = (WCHAR)0;
219 ::GlobalUnlock(text_global);
221 clipboard.SetData(CF_UNICODETEXT, text_global);
224 void ClipboardWin::OnClipboardUpdate() {
225 DCHECK(window_);
227 if (::IsClipboardFormatAvailable(CF_UNICODETEXT)) {
228 base::string16 text;
229 // Add a scope, so that we keep the clipboard open for as short a time as
230 // possible.
232 ScopedClipboard clipboard;
233 if (!clipboard.Init(window_->hwnd())) {
234 LOG(WARNING) << "Couldn't open the clipboard." << GetLastError();
235 return;
238 HGLOBAL text_global = clipboard.GetData(CF_UNICODETEXT);
239 if (!text_global) {
240 LOG(WARNING) << "Couldn't get data from the clipboard: "
241 << GetLastError();
242 return;
245 base::win::ScopedHGlobal<WCHAR> text_lock(text_global);
246 if (!text_lock.get()) {
247 LOG(WARNING) << "Couldn't lock clipboard data: " << GetLastError();
248 return;
250 text.assign(text_lock.get());
253 protocol::ClipboardEvent event;
254 event.set_mime_type(kMimeTypeTextUtf8);
255 event.set_data(ReplaceCrLfByLf(base::UTF16ToUTF8(text)));
257 if (client_clipboard_.get()) {
258 client_clipboard_->InjectClipboardEvent(event);
263 bool ClipboardWin::HandleMessage(
264 UINT message, WPARAM wparam, LPARAM lparam, LRESULT* result) {
265 if (message == WM_CLIPBOARDUPDATE) {
266 OnClipboardUpdate();
267 *result = 0;
268 return true;
271 return false;
274 scoped_ptr<Clipboard> Clipboard::Create() {
275 return scoped_ptr<Clipboard>(new ClipboardWin());
278 } // namespace remoting