OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "remoting/host/clipboard.h" | 5 #include "remoting/host/clipboard.h" |
6 | 6 |
7 #include <windows.h> | 7 #include <windows.h> |
8 | 8 |
9 #include "base/basictypes.h" | 9 #include "base/basictypes.h" |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
11 #include "base/logging.h" | 11 #include "base/logging.h" |
12 #include "base/process_util.h" | 12 #include "base/process_util.h" |
13 #include "base/string16.h" | 13 #include "base/string16.h" |
14 #include "base/threading/platform_thread.h" | 14 #include "base/threading/platform_thread.h" |
15 #include "base/utf_string_conversions.h" | 15 #include "base/utf_string_conversions.h" |
16 #include "base/win/scoped_hglobal.h" | 16 #include "base/win/scoped_hglobal.h" |
17 #include "base/win/windows_version.h" | 17 #include "base/win/windows_version.h" |
18 #include "base/win/wrapped_window_proc.h" | 18 #include "base/win/wrapped_window_proc.h" |
19 #include "remoting/base/constants.h" | 19 #include "remoting/base/constants.h" |
20 #include "remoting/base/util.h" | 20 #include "remoting/base/util.h" |
| 21 #include "remoting/host/win/message_window.h" |
21 #include "remoting/proto/event.pb.h" | 22 #include "remoting/proto/event.pb.h" |
22 #include "remoting/protocol/clipboard_stub.h" | 23 #include "remoting/protocol/clipboard_stub.h" |
23 | 24 |
24 namespace { | 25 namespace { |
25 | 26 |
26 const WCHAR kWindowClassName[] = L"clipboardWindowClass"; | |
27 const WCHAR kWindowName[] = L"clipboardWindow"; | |
28 | |
29 // A scoper class that opens and closes the clipboard. | 27 // A scoper class that opens and closes the clipboard. |
30 // This class was adapted from the ScopedClipboard class in | 28 // This class was adapted from the ScopedClipboard class in |
31 // ui/base/clipboard/clipboard_win.cc. | 29 // ui/base/clipboard/clipboard_win.cc. |
32 class ScopedClipboard { | 30 class ScopedClipboard { |
33 public: | 31 public: |
34 ScopedClipboard() : opened_(false) { | 32 ScopedClipboard() : opened_(false) { |
35 } | 33 } |
36 | 34 |
37 ~ScopedClipboard() { | 35 ~ScopedClipboard() { |
38 if (opened_) { | 36 if (opened_) { |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
95 bool opened_; | 93 bool opened_; |
96 }; | 94 }; |
97 | 95 |
98 typedef BOOL (WINAPI AddClipboardFormatListenerFn)(HWND); | 96 typedef BOOL (WINAPI AddClipboardFormatListenerFn)(HWND); |
99 typedef BOOL (WINAPI RemoveClipboardFormatListenerFn)(HWND); | 97 typedef BOOL (WINAPI RemoveClipboardFormatListenerFn)(HWND); |
100 | 98 |
101 } // namespace | 99 } // namespace |
102 | 100 |
103 namespace remoting { | 101 namespace remoting { |
104 | 102 |
105 class ClipboardWin : public Clipboard { | 103 class ClipboardWin : public Clipboard, |
| 104 public win::MessageWindow::Delegate { |
106 public: | 105 public: |
107 ClipboardWin(); | 106 ClipboardWin(); |
108 | 107 |
109 virtual void Start( | 108 virtual void Start( |
110 scoped_ptr<protocol::ClipboardStub> client_clipboard) OVERRIDE; | 109 scoped_ptr<protocol::ClipboardStub> client_clipboard) OVERRIDE; |
111 virtual void InjectClipboardEvent( | 110 virtual void InjectClipboardEvent( |
112 const protocol::ClipboardEvent& event) OVERRIDE; | 111 const protocol::ClipboardEvent& event) OVERRIDE; |
113 virtual void Stop() OVERRIDE; | 112 virtual void Stop() OVERRIDE; |
114 | 113 |
115 private: | 114 private: |
116 void OnClipboardUpdate(); | 115 void OnClipboardUpdate(); |
117 bool HaveClipboardListenerApi(); | |
118 | 116 |
119 static bool RegisterWindowClass(); | 117 // win::MessageWindow::Delegate interface. |
120 static LRESULT CALLBACK WndProc(HWND hwmd, UINT msg, WPARAM wParam, | 118 virtual bool HandleMessage(HWND hwnd, |
121 LPARAM lParam); | 119 UINT message, |
| 120 WPARAM wparam, |
| 121 LPARAM lparam, |
| 122 LRESULT* result) OVERRIDE; |
122 | 123 |
123 scoped_ptr<protocol::ClipboardStub> client_clipboard_; | 124 scoped_ptr<protocol::ClipboardStub> client_clipboard_; |
124 HWND hwnd_; | |
125 AddClipboardFormatListenerFn* add_clipboard_format_listener_; | 125 AddClipboardFormatListenerFn* add_clipboard_format_listener_; |
126 RemoveClipboardFormatListenerFn* remove_clipboard_format_listener_; | 126 RemoveClipboardFormatListenerFn* remove_clipboard_format_listener_; |
127 bool load_functions_tried_; | 127 |
| 128 // Used to subscribe to WM_CLIPBOARDUPDATE messages. |
| 129 scoped_ptr<win::MessageWindow> window_; |
128 | 130 |
129 DISALLOW_COPY_AND_ASSIGN(ClipboardWin); | 131 DISALLOW_COPY_AND_ASSIGN(ClipboardWin); |
130 }; | 132 }; |
131 | 133 |
132 ClipboardWin::ClipboardWin() | 134 ClipboardWin::ClipboardWin() |
133 : hwnd_(NULL), | 135 : add_clipboard_format_listener_(NULL), |
134 add_clipboard_format_listener_(NULL), | 136 remove_clipboard_format_listener_(NULL) { |
135 remove_clipboard_format_listener_(NULL), | |
136 load_functions_tried_(false) { | |
137 } | 137 } |
138 | 138 |
139 void ClipboardWin::Start( | 139 void ClipboardWin::Start( |
140 scoped_ptr<protocol::ClipboardStub> client_clipboard) { | 140 scoped_ptr<protocol::ClipboardStub> client_clipboard) { |
| 141 DCHECK(!add_clipboard_format_listener_); |
| 142 DCHECK(!remove_clipboard_format_listener_); |
| 143 DCHECK(!window_); |
| 144 |
141 client_clipboard_.swap(client_clipboard); | 145 client_clipboard_.swap(client_clipboard); |
142 | 146 |
143 if (!load_functions_tried_) { | 147 // user32.dll is statically linked. |
144 load_functions_tried_ = true; | 148 HMODULE user32 = GetModuleHandle(L"user32.dll"); |
145 HMODULE user32_module = ::GetModuleHandle(L"user32.dll"); | 149 CHECK(user32); |
146 if (!user32_module) { | 150 |
147 LOG(WARNING) << "Couldn't find user32.dll."; | 151 add_clipboard_format_listener_ = |
148 } else { | 152 reinterpret_cast<AddClipboardFormatListenerFn*>( |
149 add_clipboard_format_listener_ = | 153 GetProcAddress(user32, "AddClipboardFormatListener")); |
150 reinterpret_cast<AddClipboardFormatListenerFn*>( | 154 if (add_clipboard_format_listener_) { |
151 ::GetProcAddress(user32_module, "AddClipboardFormatListener")); | 155 remove_clipboard_format_listener_ = |
152 remove_clipboard_format_listener_ = | 156 reinterpret_cast<RemoveClipboardFormatListenerFn*>( |
153 reinterpret_cast<RemoveClipboardFormatListenerFn*>( | 157 GetProcAddress(user32, "RemoveClipboardFormatListener")); |
154 ::GetProcAddress(user32_module, "RemoveClipboardFormatListener")); | 158 // If AddClipboardFormatListener() present, RemoveClipboardFormatListener() |
155 if (!HaveClipboardListenerApi()) { | 159 // should be available too. |
156 LOG(WARNING) << "Couldn't load AddClipboardFormatListener or " | 160 CHECK(remove_clipboard_format_listener_); |
157 << "RemoveClipboardFormatListener."; | 161 } else { |
158 } | 162 LOG(WARNING) << "AddClipboardFormatListener() is not available."; |
159 } | |
160 } | 163 } |
161 | 164 |
162 if (!RegisterWindowClass()) { | 165 window_.reset(new win::MessageWindow()); |
163 LOG(ERROR) << "Couldn't register clipboard window class."; | 166 if (!window_->Create(this)) { |
164 return; | |
165 } | |
166 hwnd_ = ::CreateWindow(kWindowClassName, | |
167 kWindowName, | |
168 0, 0, 0, 0, 0, | |
169 HWND_MESSAGE, | |
170 NULL, | |
171 base::GetModuleFromAddress(&WndProc), | |
172 this); | |
173 if (!hwnd_) { | |
174 LOG(ERROR) << "Couldn't create clipboard window."; | 167 LOG(ERROR) << "Couldn't create clipboard window."; |
| 168 window_.reset(); |
175 return; | 169 return; |
176 } | 170 } |
177 | 171 |
178 if (HaveClipboardListenerApi()) { | 172 if (add_clipboard_format_listener_) { |
179 if (!(*add_clipboard_format_listener_)(hwnd_)) { | 173 if (!(*add_clipboard_format_listener_)(window_->hwnd())) { |
180 LOG(WARNING) << "AddClipboardFormatListener() failed: " << GetLastError(); | 174 LOG(WARNING) << "AddClipboardFormatListener() failed: " << GetLastError(); |
181 } | 175 } |
182 } | 176 } |
183 } | 177 } |
184 | 178 |
185 void ClipboardWin::Stop() { | 179 void ClipboardWin::Stop() { |
186 client_clipboard_.reset(); | 180 client_clipboard_.reset(); |
187 | 181 |
188 if (hwnd_) { | 182 if (window_ && remove_clipboard_format_listener_) |
189 if (HaveClipboardListenerApi()) { | 183 (*remove_clipboard_format_listener_)(window_->hwnd()); |
190 (*remove_clipboard_format_listener_)(hwnd_); | 184 |
191 } | 185 window_.reset(); |
192 ::DestroyWindow(hwnd_); | |
193 hwnd_ = NULL; | |
194 } | |
195 } | 186 } |
196 | 187 |
197 void ClipboardWin::InjectClipboardEvent( | 188 void ClipboardWin::InjectClipboardEvent( |
198 const protocol::ClipboardEvent& event) { | 189 const protocol::ClipboardEvent& event) { |
199 if (!hwnd_) | 190 if (!window_) |
200 return; | 191 return; |
201 | 192 |
202 // Currently we only handle UTF-8 text. | 193 // Currently we only handle UTF-8 text. |
203 if (event.mime_type().compare(kMimeTypeTextUtf8) != 0) | 194 if (event.mime_type().compare(kMimeTypeTextUtf8) != 0) |
204 return; | 195 return; |
205 if (!StringIsUtf8(event.data().c_str(), event.data().length())) { | 196 if (!StringIsUtf8(event.data().c_str(), event.data().length())) { |
206 LOG(ERROR) << "ClipboardEvent: data is not UTF-8 encoded."; | 197 LOG(ERROR) << "ClipboardEvent: data is not UTF-8 encoded."; |
207 return; | 198 return; |
208 } | 199 } |
209 | 200 |
210 string16 text = UTF8ToUTF16(ReplaceLfByCrLf(event.data())); | 201 string16 text = UTF8ToUTF16(ReplaceLfByCrLf(event.data())); |
211 | 202 |
212 ScopedClipboard clipboard; | 203 ScopedClipboard clipboard; |
213 if (!clipboard.Init(hwnd_)) { | 204 if (!clipboard.Init(window_->hwnd())) { |
214 LOG(WARNING) << "Couldn't open the clipboard."; | 205 LOG(WARNING) << "Couldn't open the clipboard."; |
215 return; | 206 return; |
216 } | 207 } |
217 | 208 |
218 clipboard.Empty(); | 209 clipboard.Empty(); |
219 | 210 |
220 HGLOBAL text_global = | 211 HGLOBAL text_global = |
221 ::GlobalAlloc(GMEM_MOVEABLE, (text.size() + 1) * sizeof(WCHAR)); | 212 ::GlobalAlloc(GMEM_MOVEABLE, (text.size() + 1) * sizeof(WCHAR)); |
222 if (!text_global) { | 213 if (!text_global) { |
223 LOG(WARNING) << "Couldn't allocate global memory."; | 214 LOG(WARNING) << "Couldn't allocate global memory."; |
224 return; | 215 return; |
225 } | 216 } |
226 | 217 |
227 LPWSTR text_global_locked = | 218 LPWSTR text_global_locked = |
228 reinterpret_cast<LPWSTR>(::GlobalLock(text_global)); | 219 reinterpret_cast<LPWSTR>(::GlobalLock(text_global)); |
229 memcpy(text_global_locked, text.data(), text.size() * sizeof(WCHAR)); | 220 memcpy(text_global_locked, text.data(), text.size() * sizeof(WCHAR)); |
230 text_global_locked[text.size()] = (WCHAR)0; | 221 text_global_locked[text.size()] = (WCHAR)0; |
231 ::GlobalUnlock(text_global); | 222 ::GlobalUnlock(text_global); |
232 | 223 |
233 clipboard.SetData(CF_UNICODETEXT, text_global); | 224 clipboard.SetData(CF_UNICODETEXT, text_global); |
234 } | 225 } |
235 | 226 |
236 void ClipboardWin::OnClipboardUpdate() { | 227 void ClipboardWin::OnClipboardUpdate() { |
237 DCHECK(hwnd_); | 228 DCHECK(window_); |
238 | 229 |
239 if (::IsClipboardFormatAvailable(CF_UNICODETEXT)) { | 230 if (::IsClipboardFormatAvailable(CF_UNICODETEXT)) { |
240 string16 text; | 231 string16 text; |
241 // Add a scope, so that we keep the clipboard open for as short a time as | 232 // Add a scope, so that we keep the clipboard open for as short a time as |
242 // possible. | 233 // possible. |
243 { | 234 { |
244 ScopedClipboard clipboard; | 235 ScopedClipboard clipboard; |
245 if (!clipboard.Init(hwnd_)) { | 236 if (!clipboard.Init(window_->hwnd())) { |
246 LOG(WARNING) << "Couldn't open the clipboard." << GetLastError(); | 237 LOG(WARNING) << "Couldn't open the clipboard." << GetLastError(); |
247 return; | 238 return; |
248 } | 239 } |
249 | 240 |
250 HGLOBAL text_global = clipboard.GetData(CF_UNICODETEXT); | 241 HGLOBAL text_global = clipboard.GetData(CF_UNICODETEXT); |
251 if (!text_global) { | 242 if (!text_global) { |
252 LOG(WARNING) << "Couldn't get data from the clipboard: " | 243 LOG(WARNING) << "Couldn't get data from the clipboard: " |
253 << GetLastError(); | 244 << GetLastError(); |
254 return; | 245 return; |
255 } | 246 } |
256 | 247 |
257 base::win::ScopedHGlobal<WCHAR> text_lock(text_global); | 248 base::win::ScopedHGlobal<WCHAR> text_lock(text_global); |
258 if (!text_lock.get()) { | 249 if (!text_lock.get()) { |
259 LOG(WARNING) << "Couldn't lock clipboard data: " << GetLastError(); | 250 LOG(WARNING) << "Couldn't lock clipboard data: " << GetLastError(); |
260 return; | 251 return; |
261 } | 252 } |
262 text.assign(text_lock.get()); | 253 text.assign(text_lock.get()); |
263 } | 254 } |
264 | 255 |
265 protocol::ClipboardEvent event; | 256 protocol::ClipboardEvent event; |
266 event.set_mime_type(kMimeTypeTextUtf8); | 257 event.set_mime_type(kMimeTypeTextUtf8); |
267 event.set_data(ReplaceCrLfByLf(UTF16ToUTF8(text))); | 258 event.set_data(ReplaceCrLfByLf(UTF16ToUTF8(text))); |
268 | 259 |
269 if (client_clipboard_.get()) { | 260 if (client_clipboard_.get()) { |
270 client_clipboard_->InjectClipboardEvent(event); | 261 client_clipboard_->InjectClipboardEvent(event); |
271 } | 262 } |
272 } | 263 } |
273 } | 264 } |
274 | 265 |
275 bool ClipboardWin::HaveClipboardListenerApi() { | 266 bool ClipboardWin::HandleMessage( |
276 return add_clipboard_format_listener_ && remove_clipboard_format_listener_; | 267 HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam, LRESULT* result) { |
277 } | 268 if (message == WM_CLIPBOARDUPDATE) { |
278 | 269 OnClipboardUpdate(); |
279 bool ClipboardWin::RegisterWindowClass() { | 270 *result = 0; |
280 // This method is only called on the UI thread, so it doesn't matter | |
281 // that the following test is not thread-safe. | |
282 static bool registered = false; | |
283 if (registered) { | |
284 return true; | 271 return true; |
285 } | 272 } |
286 | 273 |
287 WNDCLASSEX window_class; | 274 return false; |
288 base::win::InitializeWindowClass( | |
289 kWindowClassName, | |
290 base::win::WrappedWindowProc<WndProc>, | |
291 0, 0, 0, NULL, NULL, NULL, NULL, NULL, | |
292 &window_class); | |
293 if (!::RegisterClassEx(&window_class)) { | |
294 return false; | |
295 } | |
296 | |
297 registered = true; | |
298 return true; | |
299 } | |
300 | |
301 LRESULT CALLBACK ClipboardWin::WndProc(HWND hwnd, UINT msg, WPARAM wparam, | |
302 LPARAM lparam) { | |
303 if (msg == WM_CREATE) { | |
304 CREATESTRUCT* cs = reinterpret_cast<CREATESTRUCT*>(lparam); | |
305 ::SetWindowLongPtr(hwnd, | |
306 GWLP_USERDATA, | |
307 reinterpret_cast<LONG_PTR>(cs->lpCreateParams)); | |
308 return 0; | |
309 } | |
310 ClipboardWin* clipboard = | |
311 reinterpret_cast<ClipboardWin*>(::GetWindowLongPtr(hwnd, GWLP_USERDATA)); | |
312 switch (msg) { | |
313 case WM_CLIPBOARDUPDATE: | |
314 clipboard->OnClipboardUpdate(); | |
315 return 0; | |
316 } | |
317 return ::DefWindowProc(hwnd, msg, wparam, lparam); | |
318 } | 275 } |
319 | 276 |
320 scoped_ptr<Clipboard> Clipboard::Create() { | 277 scoped_ptr<Clipboard> Clipboard::Create() { |
321 return scoped_ptr<Clipboard>(new ClipboardWin()); | 278 return scoped_ptr<Clipboard>(new ClipboardWin()); |
322 } | 279 } |
323 | 280 |
324 } // namespace remoting | 281 } // namespace remoting |
OLD | NEW |