Flutter Engine
win32_platform_handler.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter 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 "flutter/shell/platform/windows/win32_platform_handler.h"
6 
7 #include <windows.h>
8 
9 #include <cstring>
10 #include <iostream>
11 #include <optional>
12 
13 #include "flutter/shell/platform/common/cpp/json_method_codec.h"
14 #include "flutter/shell/platform/windows/flutter_windows_view.h"
15 #include "flutter/shell/platform/windows/string_conversion.h"
16 
17 static constexpr char kChannelName[] = "flutter/platform";
18 
19 static constexpr char kGetClipboardDataMethod[] = "Clipboard.getData";
20 static constexpr char kSetClipboardDataMethod[] = "Clipboard.setData";
21 
22 static constexpr char kTextPlainFormat[] = "text/plain";
23 static constexpr char kTextKey[] = "text";
24 
25 static constexpr char kClipboardError[] = "Clipboard error";
26 static constexpr char kUnknownClipboardFormatMessage[] =
27  "Unknown clipboard format";
28 
29 namespace flutter {
30 
31 namespace {
32 
33 // A scoped wrapper for GlobalAlloc/GlobalFree.
34 class ScopedGlobalMemory {
35  public:
36  // Allocates |bytes| bytes of global memory with the given flags.
37  ScopedGlobalMemory(unsigned int flags, size_t bytes) {
38  memory_ = ::GlobalAlloc(flags, bytes);
39  if (!memory_) {
40  std::cerr << "Unable to allocate global memory: " << ::GetLastError();
41  }
42  }
43 
44  ~ScopedGlobalMemory() {
45  if (memory_) {
46  if (::GlobalFree(memory_) != nullptr) {
47  std::cerr << "Failed to free global allocation: " << ::GetLastError();
48  }
49  }
50  }
51 
52  // Prevent copying.
53  ScopedGlobalMemory(ScopedGlobalMemory const&) = delete;
54  ScopedGlobalMemory& operator=(ScopedGlobalMemory const&) = delete;
55 
56  // Returns the memory pointer, which will be nullptr if allocation failed.
57  void* get() { return memory_; }
58 
59  void* release() {
60  void* memory = memory_;
61  memory_ = nullptr;
62  return memory;
63  }
64 
65  private:
66  HGLOBAL memory_;
67 };
68 
69 // A scoped wrapper for GlobalLock/GlobalUnlock.
70 class ScopedGlobalLock {
71  public:
72  // Attempts to acquire a global lock on |memory| for the life of this object.
73  ScopedGlobalLock(HGLOBAL memory) {
74  source_ = memory;
75  if (memory) {
76  locked_memory_ = ::GlobalLock(memory);
77  if (!locked_memory_) {
78  std::cerr << "Unable to acquire global lock: " << ::GetLastError();
79  }
80  }
81  }
82 
83  ~ScopedGlobalLock() {
84  if (locked_memory_) {
85  if (!::GlobalUnlock(source_)) {
86  DWORD error = ::GetLastError();
87  if (error != NO_ERROR) {
88  std::cerr << "Unable to release global lock: " << ::GetLastError();
89  }
90  }
91  }
92  }
93 
94  // Prevent copying.
95  ScopedGlobalLock(ScopedGlobalLock const&) = delete;
96  ScopedGlobalLock& operator=(ScopedGlobalLock const&) = delete;
97 
98  // Returns the locked memory pointer, which will be nullptr if acquiring the
99  // lock failed.
100  void* get() { return locked_memory_; }
101 
102  private:
103  HGLOBAL source_;
104  void* locked_memory_;
105 };
106 
107 // A Clipboard wrapper that automatically closes the clipboard when it goes out
108 // of scope.
109 class ScopedClipboard {
110  public:
111  ScopedClipboard();
112  ~ScopedClipboard();
113 
114  // Prevent copying.
115  ScopedClipboard(ScopedClipboard const&) = delete;
116  ScopedClipboard& operator=(ScopedClipboard const&) = delete;
117 
118  // Attempts to open the clipboard for the given window, returning true if
119  // successful.
120  bool Open(HWND window);
121 
122  // Returns true if there is string data available to get.
123  bool HasString();
124 
125  // Returns string data from the clipboard.
126  //
127  // If getting a string fails, returns no value. Get error information with
128  // ::GetLastError().
129  //
130  // Open(...) must have succeeded to call this method.
131  std::optional<std::wstring> GetString();
132 
133  // Sets the string content of the clipboard, returning true on success.
134  //
135  // On failure, get error information with ::GetLastError().
136  //
137  // Open(...) must have succeeded to call this method.
138  bool SetString(const std::wstring string);
139 
140  private:
141  bool opened_ = false;
142 };
143 
144 ScopedClipboard::ScopedClipboard() {}
145 
146 ScopedClipboard::~ScopedClipboard() {
147  if (opened_) {
148  ::CloseClipboard();
149  }
150 }
151 
152 bool ScopedClipboard::Open(HWND window) {
153  opened_ = ::OpenClipboard(window);
154  return opened_;
155 }
156 
157 bool ScopedClipboard::HasString() {
158  // Allow either plain text format, since getting data will auto-interpolate.
159  return ::IsClipboardFormatAvailable(CF_UNICODETEXT) ||
160  ::IsClipboardFormatAvailable(CF_TEXT);
161 }
162 
163 std::optional<std::wstring> ScopedClipboard::GetString() {
164  assert(opened_);
165 
166  HANDLE data = ::GetClipboardData(CF_UNICODETEXT);
167  if (data == nullptr) {
168  return std::nullopt;
169  }
170  ScopedGlobalLock locked_data(data);
171  if (!locked_data.get()) {
172  return std::nullopt;
173  }
174  return std::optional<std::wstring>(static_cast<wchar_t*>(locked_data.get()));
175 }
176 
177 bool ScopedClipboard::SetString(const std::wstring string) {
178  assert(opened_);
179  if (!::EmptyClipboard()) {
180  return false;
181  }
182  size_t null_terminated_byte_count =
183  sizeof(decltype(string)::traits_type::char_type) * (string.size() + 1);
184  ScopedGlobalMemory destination_memory(GMEM_MOVEABLE,
185  null_terminated_byte_count);
186  ScopedGlobalLock locked_memory(destination_memory.get());
187  if (!locked_memory.get()) {
188  return false;
189  }
190  memcpy(locked_memory.get(), string.c_str(), null_terminated_byte_count);
191  if (!::SetClipboardData(CF_UNICODETEXT, locked_memory.get())) {
192  return false;
193  }
194  // The clipboard now owns the global memory.
195  destination_memory.release();
196  return true;
197 }
198 
199 } // namespace
200 
202  FlutterWindowsView* view)
203  : channel_(std::make_unique<flutter::MethodChannel<rapidjson::Document>>(
204  messenger,
205  kChannelName,
206  &flutter::JsonMethodCodec::GetInstance())),
207  view_(view) {
208  channel_->SetMethodCallHandler(
209  [this](
211  std::unique_ptr<flutter::MethodResult<rapidjson::Document>> result) {
212  HandleMethodCall(call, std::move(result));
213  });
214 }
215 
216 void PlatformHandler::HandleMethodCall(
218  std::unique_ptr<flutter::MethodResult<rapidjson::Document>> result) {
219  const std::string& method = method_call.method_name();
220  if (method.compare(kGetClipboardDataMethod) == 0) {
221  // Only one string argument is expected.
222  const rapidjson::Value& format = method_call.arguments()[0];
223 
224  if (strcmp(format.GetString(), kTextPlainFormat) != 0) {
226  return;
227  }
228  ScopedClipboard clipboard;
229  if (!clipboard.Open(std::get<HWND>(*view_->GetRenderTarget()))) {
230  rapidjson::Document error_code;
231  error_code.SetInt(::GetLastError());
232  result->Error(kClipboardError, "Unable to open clipboard", error_code);
233  return;
234  }
235  if (!clipboard.HasString()) {
236  result->Success(rapidjson::Document());
237  return;
238  }
239  std::optional<std::wstring> clipboard_string = clipboard.GetString();
240  if (!clipboard_string) {
241  rapidjson::Document error_code;
242  error_code.SetInt(::GetLastError());
243  result->Error(kClipboardError, "Unable to get clipboard data",
244  error_code);
245  return;
246  }
247 
248  rapidjson::Document document;
249  document.SetObject();
250  rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
251  document.AddMember(
252  rapidjson::Value(kTextKey, allocator),
253  rapidjson::Value(Utf8FromUtf16(*clipboard_string), allocator),
254  allocator);
255  result->Success(document);
256  } else if (method.compare(kSetClipboardDataMethod) == 0) {
257  const rapidjson::Value& document = *method_call.arguments();
258  rapidjson::Value::ConstMemberIterator itr = document.FindMember(kTextKey);
259  if (itr == document.MemberEnd()) {
261  return;
262  }
263 
264  ScopedClipboard clipboard;
265 
266  if (!clipboard.Open(std::get<HWND>(*view_->GetRenderTarget()))) {
267  rapidjson::Document error_code;
268  error_code.SetInt(::GetLastError());
269  result->Error(kClipboardError, "Unable to open clipboard", error_code);
270  return;
271  }
272  if (!clipboard.SetString(Utf16FromUtf8(itr->value.GetString()))) {
273  rapidjson::Document error_code;
274  error_code.SetInt(::GetLastError());
275  result->Error(kClipboardError, "Unable to set clipboard data",
276  error_code);
277  return;
278  }
279  result->Success();
280  } else {
281  result->NotImplemented();
282  }
283 }
284 
285 } // namespace flutter
G_BEGIN_DECLS FlMethodCall * method_call
const T * arguments() const
Definition: method_call.h:34
static constexpr char kGetClipboardDataMethod[]
FlMethodResponse GError ** error
PlatformHandler(flutter::BinaryMessenger *messenger, GLFWwindow *window)
Definition: ref_ptr.h:252
static constexpr char kClipboardError[]
constexpr std::size_t size(T(&array)[N])
Definition: size.h:13
static constexpr char kChannelName[]
std::string Utf8FromUtf16(const std::wstring &utf16_string)
static constexpr char kTextPlainFormat[]
static constexpr char kSetClipboardDataMethod[]
static constexpr char kUnknownClipboardFormatMessage[]
std::wstring Utf16FromUtf8(const std::string &utf8_string)
static constexpr char kTextKey[]
WindowsRenderTarget * GetRenderTarget() const
const std::string & method_name() const
Definition: method_call.h:31
DEF_SWITCHES_START snapshot asset Path to the directory containing the four files specified by VmSnapshotInstructions and IsolateSnapshotInstructions vm snapshot The VM instructions snapshot that will be memory mapped as read and executable SnapshotAssetPath must be present isolate snapshot The isolate instructions snapshot that will be memory mapped as read and executable SnapshotAssetPath must be present icu symbol Prefix for the symbols representing ICU data linked into the Flutter library dart flags
Definition: switches.h:66