| 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 |