selection_requestor.cc 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. // Copyright (c) 2013 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. #include "ui/base/x/selection_requestor.h"
  5. #include <algorithm>
  6. #include "base/memory/ref_counted_memory.h"
  7. #include "ui/base/x/selection_owner.h"
  8. #include "ui/base/x/selection_utils.h"
  9. #include "ui/base/x/x11_clipboard_helper.h"
  10. #include "ui/base/x/x11_util.h"
  11. #include "ui/gfx/x/x11_atom_cache.h"
  12. #include "ui/gfx/x/xproto.h"
  13. #include "ui/gfx/x/xproto_util.h"
  14. namespace ui {
  15. namespace {
  16. const char kChromeSelection[] = "CHROME_SELECTION";
  17. // The amount of time to wait for a request to complete before aborting it.
  18. const int kRequestTimeoutMs = 1000;
  19. // Combines |data| into a single std::vector<uint8_t>.
  20. std::vector<uint8_t> CombineData(
  21. const std::vector<scoped_refptr<base::RefCountedMemory>>& data) {
  22. size_t bytes = 0;
  23. for (const auto& datum : data)
  24. bytes += datum->size();
  25. std::vector<uint8_t> combined;
  26. combined.reserve(bytes);
  27. for (const auto& datum : data) {
  28. std::copy(datum->data(), datum->data() + datum->size(),
  29. std::back_inserter(combined));
  30. }
  31. return combined;
  32. }
  33. } // namespace
  34. SelectionRequestor::SelectionRequestor(x11::Window x_window,
  35. XClipboardHelper* helper)
  36. : x_window_(x_window),
  37. helper_(helper),
  38. x_property_(x11::GetAtom(kChromeSelection)) {}
  39. SelectionRequestor::~SelectionRequestor() = default;
  40. bool SelectionRequestor::PerformBlockingConvertSelection(
  41. x11::Atom selection,
  42. x11::Atom target,
  43. std::vector<uint8_t>* out_data,
  44. x11::Atom* out_type) {
  45. base::TimeTicks timeout =
  46. base::TimeTicks::Now() + base::Milliseconds(kRequestTimeoutMs);
  47. Request request(selection, target, timeout);
  48. requests_.push_back(&request);
  49. if (current_request_index_ == (requests_.size() - 1))
  50. ConvertSelectionForCurrentRequest();
  51. BlockTillSelectionNotifyForRequest(&request);
  52. auto request_it = std::find(requests_.begin(), requests_.end(), &request);
  53. CHECK(request_it != requests_.end());
  54. if (static_cast<int>(current_request_index_) >
  55. request_it - requests_.begin()) {
  56. --current_request_index_;
  57. }
  58. requests_.erase(request_it);
  59. if (request.success) {
  60. if (out_data)
  61. *out_data = CombineData(request.out_data);
  62. if (out_type)
  63. *out_type = request.out_type;
  64. }
  65. return request.success;
  66. }
  67. void SelectionRequestor::PerformBlockingConvertSelectionWithParameter(
  68. x11::Atom selection,
  69. x11::Atom target,
  70. const std::vector<x11::Atom>& parameter) {
  71. SetArrayProperty(x_window_, x11::GetAtom(kChromeSelection), x11::Atom::ATOM,
  72. parameter);
  73. PerformBlockingConvertSelection(selection, target, nullptr, nullptr);
  74. }
  75. SelectionData SelectionRequestor::RequestAndWaitForTypes(
  76. x11::Atom selection,
  77. const std::vector<x11::Atom>& types) {
  78. for (const x11::Atom& item : types) {
  79. std::vector<uint8_t> data;
  80. x11::Atom type = x11::Atom::None;
  81. if (PerformBlockingConvertSelection(selection, item, &data, &type) &&
  82. type == item) {
  83. return SelectionData(type, base::RefCountedBytes::TakeVector(&data));
  84. }
  85. }
  86. return SelectionData();
  87. }
  88. void SelectionRequestor::OnSelectionNotify(
  89. const x11::SelectionNotifyEvent& selection) {
  90. Request* request = GetCurrentRequest();
  91. x11::Atom event_property = selection.property;
  92. if (!request || request->completed ||
  93. request->selection != selection.selection ||
  94. request->target != selection.target) {
  95. // ICCCM requires us to delete the property passed into SelectionNotify.
  96. if (event_property != x11::Atom::None)
  97. x11::DeleteProperty(x_window_, event_property);
  98. return;
  99. }
  100. bool success = false;
  101. if (event_property == x_property_) {
  102. scoped_refptr<base::RefCountedMemory> out_data;
  103. success = ui::GetRawBytesOfProperty(x_window_, x_property_, &out_data,
  104. &request->out_type);
  105. if (success) {
  106. request->out_data.clear();
  107. request->out_data.push_back(out_data);
  108. }
  109. }
  110. if (event_property != x11::Atom::None)
  111. x11::DeleteProperty(x_window_, event_property);
  112. if (request->out_type == x11::GetAtom(kIncr)) {
  113. request->data_sent_incrementally = true;
  114. request->out_data.clear();
  115. request->out_type = x11::Atom::None;
  116. request->timeout =
  117. base::TimeTicks::Now() + base::Milliseconds(kRequestTimeoutMs);
  118. } else {
  119. CompleteRequest(current_request_index_, success);
  120. }
  121. }
  122. bool SelectionRequestor::CanDispatchPropertyEvent(
  123. const x11::PropertyNotifyEvent& prop) {
  124. return prop.window == x_window_ && prop.atom == x_property_ &&
  125. prop.state == x11::Property::NewValue;
  126. }
  127. void SelectionRequestor::OnPropertyEvent(
  128. const x11::PropertyNotifyEvent& event) {
  129. Request* request = GetCurrentRequest();
  130. if (!request || !request->data_sent_incrementally)
  131. return;
  132. scoped_refptr<base::RefCountedMemory> out_data;
  133. x11::Atom out_type = x11::Atom::None;
  134. bool success =
  135. ui::GetRawBytesOfProperty(x_window_, x_property_, &out_data, &out_type);
  136. if (!success) {
  137. CompleteRequest(current_request_index_, false);
  138. return;
  139. }
  140. if (request->out_type != x11::Atom::None && request->out_type != out_type) {
  141. CompleteRequest(current_request_index_, false);
  142. return;
  143. }
  144. request->out_data.push_back(out_data);
  145. request->out_type = out_type;
  146. // Delete the property to tell the selection owner to send the next chunk.
  147. x11::DeleteProperty(x_window_, x_property_);
  148. request->timeout =
  149. base::TimeTicks::Now() + base::Milliseconds(kRequestTimeoutMs);
  150. if (!out_data->size())
  151. CompleteRequest(current_request_index_, true);
  152. }
  153. void SelectionRequestor::AbortStaleRequests() {
  154. base::TimeTicks now = base::TimeTicks::Now();
  155. for (size_t i = current_request_index_; i < requests_.size(); ++i) {
  156. if (requests_[i]->timeout <= now)
  157. CompleteRequest(i, false);
  158. }
  159. }
  160. void SelectionRequestor::CompleteRequest(size_t index, bool success) {
  161. if (index >= requests_.size())
  162. return;
  163. Request* request = requests_[index];
  164. if (request->completed)
  165. return;
  166. request->success = success;
  167. request->completed = true;
  168. if (index == current_request_index_) {
  169. while (GetCurrentRequest() && GetCurrentRequest()->completed)
  170. ++current_request_index_;
  171. ConvertSelectionForCurrentRequest();
  172. }
  173. }
  174. void SelectionRequestor::ConvertSelectionForCurrentRequest() {
  175. Request* request = GetCurrentRequest();
  176. if (request) {
  177. x11::Connection::Get()->ConvertSelection({
  178. .requestor = x_window_,
  179. .selection = request->selection,
  180. .target = request->target,
  181. .property = x_property_,
  182. .time = x11::Time::CurrentTime,
  183. });
  184. }
  185. }
  186. void SelectionRequestor::BlockTillSelectionNotifyForRequest(Request* request) {
  187. auto* connection = x11::Connection::Get();
  188. auto& events = connection->events();
  189. size_t i = 0;
  190. while (!request->completed && request->timeout > base::TimeTicks::Now()) {
  191. connection->Flush();
  192. connection->ReadResponses();
  193. size_t events_size = events.size();
  194. for (; i < events_size; ++i) {
  195. auto& event = events[i];
  196. if (helper_->DispatchEvent(event))
  197. event = x11::Event();
  198. }
  199. DCHECK_EQ(events_size, events.size());
  200. }
  201. AbortStaleRequests();
  202. }
  203. SelectionRequestor::Request* SelectionRequestor::GetCurrentRequest() {
  204. return current_request_index_ == requests_.size()
  205. ? nullptr
  206. : requests_[current_request_index_];
  207. }
  208. SelectionRequestor::Request::Request(x11::Atom selection,
  209. x11::Atom target,
  210. base::TimeTicks timeout)
  211. : selection(selection),
  212. target(target),
  213. data_sent_incrementally(false),
  214. out_type(x11::Atom::None),
  215. success(false),
  216. timeout(timeout),
  217. completed(false) {}
  218. SelectionRequestor::Request::~Request() = default;
  219. } // namespace ui