Chromium Code Reviews| 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(UINT message, WPARAM wparam, LPARAM lparam, |
| 121 LPARAM lParam); | 119 LRESULT* result) OVERRIDE; |
| 122 | 120 |
| 123 scoped_ptr<protocol::ClipboardStub> client_clipboard_; | 121 scoped_ptr<protocol::ClipboardStub> client_clipboard_; |
| 124 HWND hwnd_; | |
| 125 AddClipboardFormatListenerFn* add_clipboard_format_listener_; | 122 AddClipboardFormatListenerFn* add_clipboard_format_listener_; |
| 126 RemoveClipboardFormatListenerFn* remove_clipboard_format_listener_; | 123 RemoveClipboardFormatListenerFn* remove_clipboard_format_listener_; |
| 127 bool load_functions_tried_; | 124 |
| 125 // Used to subsribe to WM_CLIPBOARDUPDATE messages. | |
|
dcaiafa
2013/03/28 23:25:35
nit: subscribe
alexeypa (please no reviews)
2013/03/29 16:57:01
Done.
| |
| 126 scoped_ptr<win::MessageWindow> window_; | |
| 128 | 127 |
| 129 DISALLOW_COPY_AND_ASSIGN(ClipboardWin); | 128 DISALLOW_COPY_AND_ASSIGN(ClipboardWin); |
| 130 }; | 129 }; |
| 131 | 130 |
| 132 ClipboardWin::ClipboardWin() | 131 ClipboardWin::ClipboardWin() |
| 133 : hwnd_(NULL), | 132 : add_clipboard_format_listener_(NULL), |
| 134 add_clipboard_format_listener_(NULL), | 133 remove_clipboard_format_listener_(NULL) { |
| 135 remove_clipboard_format_listener_(NULL), | |
| 136 load_functions_tried_(false) { | |
| 137 } | 134 } |
| 138 | 135 |
| 139 void ClipboardWin::Start( | 136 void ClipboardWin::Start( |
| 140 scoped_ptr<protocol::ClipboardStub> client_clipboard) { | 137 scoped_ptr<protocol::ClipboardStub> client_clipboard) { |
| 138 DCHECK(!add_clipboard_format_listener_); | |
| 139 DCHECK(!remove_clipboard_format_listener_); | |
| 140 DCHECK(!window_); | |
| 141 | |
| 141 client_clipboard_.swap(client_clipboard); | 142 client_clipboard_.swap(client_clipboard); |
| 142 | 143 |
| 143 if (!load_functions_tried_) { | 144 // user32.dll is statically linked. |
| 144 load_functions_tried_ = true; | 145 HMODULE user32 = GetModuleHandle(L"user32.dll"); |
| 145 HMODULE user32_module = ::GetModuleHandle(L"user32.dll"); | 146 CHECK(user32); |
| 146 if (!user32_module) { | 147 |
| 147 LOG(WARNING) << "Couldn't find user32.dll."; | 148 add_clipboard_format_listener_ = |
| 148 } else { | 149 reinterpret_cast<AddClipboardFormatListenerFn*>( |
| 149 add_clipboard_format_listener_ = | 150 GetProcAddress(user32, "AddClipboardFormatListener")); |
| 150 reinterpret_cast<AddClipboardFormatListenerFn*>( | 151 if (add_clipboard_format_listener_) { |
| 151 ::GetProcAddress(user32_module, "AddClipboardFormatListener")); | 152 remove_clipboard_format_listener_ = |
| 152 remove_clipboard_format_listener_ = | 153 reinterpret_cast<RemoveClipboardFormatListenerFn*>( |
| 153 reinterpret_cast<RemoveClipboardFormatListenerFn*>( | 154 GetProcAddress(user32, "RemoveClipboardFormatListener")); |
| 154 ::GetProcAddress(user32_module, "RemoveClipboardFormatListener")); | 155 // If AddClipboardFormatListener() present, RemoveClipboardFormatListener() |
| 155 if (!HaveClipboardListenerApi()) { | 156 // should be available too. |
| 156 LOG(WARNING) << "Couldn't load AddClipboardFormatListener or " | 157 CHECK(remove_clipboard_format_listener_); |
| 157 << "RemoveClipboardFormatListener."; | 158 } else { |
| 158 } | 159 LOG(WARNING) << "AddClipboardFormatListener() is not available."; |
| 159 } | |
| 160 } | 160 } |
| 161 | 161 |
| 162 if (!RegisterWindowClass()) { | 162 window_.reset(new win::MessageWindow()); |
| 163 LOG(ERROR) << "Couldn't register clipboard window class."; | 163 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."; | 164 LOG(ERROR) << "Couldn't create clipboard window."; |
| 165 window_.reset(); | |
| 175 return; | 166 return; |
| 176 } | 167 } |
| 177 | 168 |
| 178 if (HaveClipboardListenerApi()) { | 169 if (add_clipboard_format_listener_) { |
| 179 if (!(*add_clipboard_format_listener_)(hwnd_)) { | 170 if (!(*add_clipboard_format_listener_)(window_->hwnd())) { |
| 180 LOG(WARNING) << "AddClipboardFormatListener() failed: " << GetLastError(); | 171 LOG(WARNING) << "AddClipboardFormatListener() failed: " << GetLastError(); |
| 181 } | 172 } |
| 182 } | 173 } |
| 183 } | 174 } |
| 184 | 175 |
| 185 void ClipboardWin::Stop() { | 176 void ClipboardWin::Stop() { |
| 186 client_clipboard_.reset(); | 177 client_clipboard_.reset(); |
| 187 | 178 |
| 188 if (hwnd_) { | 179 if (window_ && remove_clipboard_format_listener_) |
| 189 if (HaveClipboardListenerApi()) { | 180 (*remove_clipboard_format_listener_)(window_->hwnd()); |
| 190 (*remove_clipboard_format_listener_)(hwnd_); | 181 |
| 191 } | 182 window_.reset(); |
| 192 ::DestroyWindow(hwnd_); | |
| 193 hwnd_ = NULL; | |
| 194 } | |
| 195 } | 183 } |
| 196 | 184 |
| 197 void ClipboardWin::InjectClipboardEvent( | 185 void ClipboardWin::InjectClipboardEvent( |
| 198 const protocol::ClipboardEvent& event) { | 186 const protocol::ClipboardEvent& event) { |
| 199 if (!hwnd_) | 187 if (!window_) |
| 200 return; | 188 return; |
| 201 | 189 |
| 202 // Currently we only handle UTF-8 text. | 190 // Currently we only handle UTF-8 text. |
| 203 if (event.mime_type().compare(kMimeTypeTextUtf8) != 0) | 191 if (event.mime_type().compare(kMimeTypeTextUtf8) != 0) |
| 204 return; | 192 return; |
| 205 if (!StringIsUtf8(event.data().c_str(), event.data().length())) { | 193 if (!StringIsUtf8(event.data().c_str(), event.data().length())) { |
| 206 LOG(ERROR) << "ClipboardEvent: data is not UTF-8 encoded."; | 194 LOG(ERROR) << "ClipboardEvent: data is not UTF-8 encoded."; |
| 207 return; | 195 return; |
| 208 } | 196 } |
| 209 | 197 |
| 210 string16 text = UTF8ToUTF16(ReplaceLfByCrLf(event.data())); | 198 string16 text = UTF8ToUTF16(ReplaceLfByCrLf(event.data())); |
| 211 | 199 |
| 212 ScopedClipboard clipboard; | 200 ScopedClipboard clipboard; |
| 213 if (!clipboard.Init(hwnd_)) { | 201 if (!clipboard.Init(window_->hwnd())) { |
| 214 LOG(WARNING) << "Couldn't open the clipboard."; | 202 LOG(WARNING) << "Couldn't open the clipboard."; |
| 215 return; | 203 return; |
| 216 } | 204 } |
| 217 | 205 |
| 218 clipboard.Empty(); | 206 clipboard.Empty(); |
| 219 | 207 |
| 220 HGLOBAL text_global = | 208 HGLOBAL text_global = |
| 221 ::GlobalAlloc(GMEM_MOVEABLE, (text.size() + 1) * sizeof(WCHAR)); | 209 ::GlobalAlloc(GMEM_MOVEABLE, (text.size() + 1) * sizeof(WCHAR)); |
| 222 if (!text_global) { | 210 if (!text_global) { |
| 223 LOG(WARNING) << "Couldn't allocate global memory."; | 211 LOG(WARNING) << "Couldn't allocate global memory."; |
| 224 return; | 212 return; |
| 225 } | 213 } |
| 226 | 214 |
| 227 LPWSTR text_global_locked = | 215 LPWSTR text_global_locked = |
| 228 reinterpret_cast<LPWSTR>(::GlobalLock(text_global)); | 216 reinterpret_cast<LPWSTR>(::GlobalLock(text_global)); |
| 229 memcpy(text_global_locked, text.data(), text.size() * sizeof(WCHAR)); | 217 memcpy(text_global_locked, text.data(), text.size() * sizeof(WCHAR)); |
| 230 text_global_locked[text.size()] = (WCHAR)0; | 218 text_global_locked[text.size()] = (WCHAR)0; |
| 231 ::GlobalUnlock(text_global); | 219 ::GlobalUnlock(text_global); |
| 232 | 220 |
| 233 clipboard.SetData(CF_UNICODETEXT, text_global); | 221 clipboard.SetData(CF_UNICODETEXT, text_global); |
| 234 } | 222 } |
| 235 | 223 |
| 236 void ClipboardWin::OnClipboardUpdate() { | 224 void ClipboardWin::OnClipboardUpdate() { |
| 237 DCHECK(hwnd_); | 225 DCHECK(window_); |
| 238 | 226 |
| 239 if (::IsClipboardFormatAvailable(CF_UNICODETEXT)) { | 227 if (::IsClipboardFormatAvailable(CF_UNICODETEXT)) { |
| 240 string16 text; | 228 string16 text; |
| 241 // Add a scope, so that we keep the clipboard open for as short a time as | 229 // Add a scope, so that we keep the clipboard open for as short a time as |
| 242 // possible. | 230 // possible. |
| 243 { | 231 { |
| 244 ScopedClipboard clipboard; | 232 ScopedClipboard clipboard; |
| 245 if (!clipboard.Init(hwnd_)) { | 233 if (!clipboard.Init(window_->hwnd())) { |
| 246 LOG(WARNING) << "Couldn't open the clipboard." << GetLastError(); | 234 LOG(WARNING) << "Couldn't open the clipboard." << GetLastError(); |
| 247 return; | 235 return; |
| 248 } | 236 } |
| 249 | 237 |
| 250 HGLOBAL text_global = clipboard.GetData(CF_UNICODETEXT); | 238 HGLOBAL text_global = clipboard.GetData(CF_UNICODETEXT); |
| 251 if (!text_global) { | 239 if (!text_global) { |
| 252 LOG(WARNING) << "Couldn't get data from the clipboard: " | 240 LOG(WARNING) << "Couldn't get data from the clipboard: " |
| 253 << GetLastError(); | 241 << GetLastError(); |
| 254 return; | 242 return; |
| 255 } | 243 } |
| 256 | 244 |
| 257 base::win::ScopedHGlobal<WCHAR> text_lock(text_global); | 245 base::win::ScopedHGlobal<WCHAR> text_lock(text_global); |
| 258 if (!text_lock.get()) { | 246 if (!text_lock.get()) { |
| 259 LOG(WARNING) << "Couldn't lock clipboard data: " << GetLastError(); | 247 LOG(WARNING) << "Couldn't lock clipboard data: " << GetLastError(); |
| 260 return; | 248 return; |
| 261 } | 249 } |
| 262 text.assign(text_lock.get()); | 250 text.assign(text_lock.get()); |
| 263 } | 251 } |
| 264 | 252 |
| 265 protocol::ClipboardEvent event; | 253 protocol::ClipboardEvent event; |
| 266 event.set_mime_type(kMimeTypeTextUtf8); | 254 event.set_mime_type(kMimeTypeTextUtf8); |
| 267 event.set_data(ReplaceCrLfByLf(UTF16ToUTF8(text))); | 255 event.set_data(ReplaceCrLfByLf(UTF16ToUTF8(text))); |
| 268 | 256 |
| 269 if (client_clipboard_.get()) { | 257 if (client_clipboard_.get()) { |
| 270 client_clipboard_->InjectClipboardEvent(event); | 258 client_clipboard_->InjectClipboardEvent(event); |
| 271 } | 259 } |
| 272 } | 260 } |
| 273 } | 261 } |
| 274 | 262 |
| 275 bool ClipboardWin::HaveClipboardListenerApi() { | 263 bool ClipboardWin::HandleMessage( |
| 276 return add_clipboard_format_listener_ && remove_clipboard_format_listener_; | 264 UINT message, WPARAM wparam, LPARAM lparam, LRESULT* result) { |
| 277 } | 265 if (message == WM_CLIPBOARDUPDATE) { |
| 278 | 266 OnClipboardUpdate(); |
| 279 bool ClipboardWin::RegisterWindowClass() { | 267 *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; | 268 return true; |
| 285 } | 269 } |
| 286 | 270 |
| 287 WNDCLASSEX window_class; | 271 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 } | 272 } |
| 319 | 273 |
| 320 scoped_ptr<Clipboard> Clipboard::Create() { | 274 scoped_ptr<Clipboard> Clipboard::Create() { |
| 321 return scoped_ptr<Clipboard>(new ClipboardWin()); | 275 return scoped_ptr<Clipboard>(new ClipboardWin()); |
| 322 } | 276 } |
| 323 | 277 |
| 324 } // namespace remoting | 278 } // namespace remoting |
| OLD | NEW |