Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / remoting / host / clipboard_win.cc
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.
4
5 #include "remoting/host/clipboard.h"
6
7 #include <windows.h>
8
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"
22
23 namespace {
24
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) {
31   }
32
33   ~ScopedClipboard() {
34     if (opened_) {
35       ::CloseClipboard();
36     }
37   }
38
39   bool Init(HWND owner) {
40     const int kMaxAttemptsToOpenClipboard = 5;
41     const base::TimeDelta kSleepTimeBetweenAttempts =
42         base::TimeDelta::FromMilliseconds(5);
43
44     if (opened_) {
45       NOTREACHED();
46       return true;
47     }
48
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);
53       }
54       if (::OpenClipboard(owner)) {
55         opened_ = true;
56         return true;
57       }
58     }
59     return false;
60   }
61
62   BOOL Empty() {
63     if (!opened_) {
64       NOTREACHED();
65       return false;
66     }
67     return ::EmptyClipboard();
68   }
69
70   void SetData(UINT uFormat, HANDLE hMem) {
71     if (!opened_) {
72       NOTREACHED();
73       return;
74     }
75     // The caller must not close the handle that ::SetClipboardData returns.
76     ::SetClipboardData(uFormat, hMem);
77   }
78
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;
86     }
87     return ::GetClipboardData(format);
88   }
89
90  private:
91   bool opened_;
92 };
93
94 typedef BOOL (WINAPI AddClipboardFormatListenerFn)(HWND);
95 typedef BOOL (WINAPI RemoveClipboardFormatListenerFn)(HWND);
96
97 }  // namespace
98
99 namespace remoting {
100
101 class ClipboardWin : public Clipboard {
102  public:
103   ClipboardWin();
104
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;
110
111  private:
112   void OnClipboardUpdate();
113
114   // Handles messages received by |window_|.
115   bool HandleMessage(UINT message,
116                      WPARAM wparam,
117                      LPARAM lparam,
118                      LRESULT* result);
119
120   scoped_ptr<protocol::ClipboardStub> client_clipboard_;
121   AddClipboardFormatListenerFn* add_clipboard_format_listener_;
122   RemoveClipboardFormatListenerFn* remove_clipboard_format_listener_;
123
124   // Used to subscribe to WM_CLIPBOARDUPDATE messages.
125   scoped_ptr<base::win::MessageWindow> window_;
126
127   DISALLOW_COPY_AND_ASSIGN(ClipboardWin);
128 };
129
130 ClipboardWin::ClipboardWin()
131     : add_clipboard_format_listener_(NULL),
132       remove_clipboard_format_listener_(NULL) {
133 }
134
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_);
140
141   client_clipboard_.swap(client_clipboard);
142
143   // user32.dll is statically linked.
144   HMODULE user32 = GetModuleHandle(L"user32.dll");
145   CHECK(user32);
146
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.";
159   }
160
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;
167   }
168
169   if (add_clipboard_format_listener_) {
170     if (!(*add_clipboard_format_listener_)(window_->hwnd())) {
171       LOG(WARNING) << "AddClipboardFormatListener() failed: " << GetLastError();
172     }
173   }
174 }
175
176 void ClipboardWin::Stop() {
177   client_clipboard_.reset();
178
179   if (window_ && remove_clipboard_format_listener_)
180     (*remove_clipboard_format_listener_)(window_->hwnd());
181
182   window_.reset();
183 }
184
185 void ClipboardWin::InjectClipboardEvent(
186     const protocol::ClipboardEvent& event) {
187   if (!window_)
188     return;
189
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;
196   }
197
198   base::string16 text = base::UTF8ToUTF16(ReplaceLfByCrLf(event.data()));
199
200   ScopedClipboard clipboard;
201   if (!clipboard.Init(window_->hwnd())) {
202     LOG(WARNING) << "Couldn't open the clipboard.";
203     return;
204   }
205
206   clipboard.Empty();
207
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;
213   }
214
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);
220
221   clipboard.SetData(CF_UNICODETEXT, text_global);
222 }
223
224 void ClipboardWin::OnClipboardUpdate() {
225   DCHECK(window_);
226
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.
231     {
232       ScopedClipboard clipboard;
233       if (!clipboard.Init(window_->hwnd())) {
234         LOG(WARNING) << "Couldn't open the clipboard." << GetLastError();
235         return;
236       }
237
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;
243       }
244
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;
249       }
250       text.assign(text_lock.get());
251     }
252
253     protocol::ClipboardEvent event;
254     event.set_mime_type(kMimeTypeTextUtf8);
255     event.set_data(ReplaceCrLfByLf(base::UTF16ToUTF8(text)));
256
257     if (client_clipboard_.get()) {
258       client_clipboard_->InjectClipboardEvent(event);
259     }
260   }
261 }
262
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;
269   }
270
271   return false;
272 }
273
274 scoped_ptr<Clipboard> Clipboard::Create() {
275   return make_scoped_ptr(new ClipboardWin());
276 }
277
278 }  // namespace remoting