OLD | NEW |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 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 "ui/base/x/selection_owner.h" | 5 #include "ui/base/x/selection_owner.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <X11/Xlib.h> | 8 #include <X11/Xlib.h> |
9 #include <X11/Xatom.h> | 9 #include <X11/Xatom.h> |
10 | 10 |
11 #include "base/logging.h" | 11 #include "base/logging.h" |
12 #include "ui/base/x/selection_utils.h" | 12 #include "ui/base/x/selection_utils.h" |
13 #include "ui/base/x/x11_foreign_window_manager.h" | 13 #include "ui/base/x/x11_foreign_window_manager.h" |
14 #include "ui/base/x/x11_util.h" | 14 #include "ui/base/x/x11_util.h" |
| 15 #include "ui/events/platform/x11/x11_event_source.h" |
15 | 16 |
16 namespace ui { | 17 namespace ui { |
17 | 18 |
18 namespace { | 19 namespace { |
19 | 20 |
20 const char kAtomPair[] = "ATOM_PAIR"; | 21 const char kAtomPair[] = "ATOM_PAIR"; |
21 const char kIncr[] = "INCR"; | 22 const char kIncr[] = "INCR"; |
22 const char kMultiple[] = "MULTIPLE"; | 23 const char kMultiple[] = "MULTIPLE"; |
23 const char kSaveTargets[] = "SAVE_TARGETS"; | 24 const char kSaveTargets[] = "SAVE_TARGETS"; |
24 const char kTargets[] = "TARGETS"; | 25 const char kTargets[] = "TARGETS"; |
| 26 const char kTimestamp[] = "TIMESTAMP"; |
25 | 27 |
26 const char* kAtomsToCache[] = { | 28 const char* kAtomsToCache[] = {kAtomPair, kIncr, kMultiple, kSaveTargets, |
27 kAtomPair, | 29 kTargets, kTimestamp, NULL}; |
28 kIncr, | |
29 kMultiple, | |
30 kSaveTargets, | |
31 kTargets, | |
32 NULL | |
33 }; | |
34 | 30 |
35 // The period of |incremental_transfer_abort_timer_|. Arbitrary but must be <= | 31 // The period of |incremental_transfer_abort_timer_|. Arbitrary but must be <= |
36 // than kIncrementalTransferTimeoutMs. | 32 // than kIncrementalTransferTimeoutMs. |
37 const int kTimerPeriodMs = 1000; | 33 const int kTimerPeriodMs = 1000; |
38 | 34 |
39 // The amount of time to wait for the selection requestor to process the data | 35 // The amount of time to wait for the selection requestor to process the data |
40 // sent by the selection owner before aborting an incremental data transfer. | 36 // sent by the selection owner before aborting an incremental data transfer. |
41 const int kIncrementalTransferTimeoutMs = 10000; | 37 const int kIncrementalTransferTimeoutMs = 10000; |
42 | 38 |
43 static_assert(kTimerPeriodMs <= kIncrementalTransferTimeoutMs, | 39 static_assert(kTimerPeriodMs <= kIncrementalTransferTimeoutMs, |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
115 | 111 |
116 void SelectionOwner::RetrieveTargets(std::vector<XAtom>* targets) { | 112 void SelectionOwner::RetrieveTargets(std::vector<XAtom>* targets) { |
117 for (SelectionFormatMap::const_iterator it = format_map_.begin(); | 113 for (SelectionFormatMap::const_iterator it = format_map_.begin(); |
118 it != format_map_.end(); ++it) { | 114 it != format_map_.end(); ++it) { |
119 targets->push_back(it->first); | 115 targets->push_back(it->first); |
120 } | 116 } |
121 } | 117 } |
122 | 118 |
123 void SelectionOwner::TakeOwnershipOfSelection( | 119 void SelectionOwner::TakeOwnershipOfSelection( |
124 const SelectionFormatMap& data) { | 120 const SelectionFormatMap& data) { |
| 121 // Save the last server timestamp seen from X, to satisfy requests for the |
| 122 // TIMESTAMP target later… |
| 123 acquired_selection_timestamp_ = |
| 124 X11EventSource::GetInstance()->last_seen_server_time(); |
| 125 // …but always pass CurrentTime to XSetSelectionOwner to increase the chances |
| 126 // of this succeeding. |
125 XSetSelectionOwner(x_display_, selection_name_, x_window_, CurrentTime); | 127 XSetSelectionOwner(x_display_, selection_name_, x_window_, CurrentTime); |
126 | 128 |
127 if (XGetSelectionOwner(x_display_, selection_name_) == x_window_) { | 129 if (XGetSelectionOwner(x_display_, selection_name_) == x_window_) { |
128 // The X server agrees that we are the selection owner. Commit our data. | 130 // The X server agrees that we are the selection owner. Commit our data. |
129 format_map_ = data; | 131 format_map_ = data; |
130 } | 132 } |
131 } | 133 } |
132 | 134 |
133 void SelectionOwner::ClearSelectionOwner() { | 135 void SelectionOwner::ClearSelectionOwner() { |
134 XSetSelectionOwner(x_display_, selection_name_, None, CurrentTime); | 136 XSetSelectionOwner(x_display_, selection_name_, None, CurrentTime); |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
212 if (!it->data.get()) | 214 if (!it->data.get()) |
213 CompleteIncrementalTransfer(it); | 215 CompleteIncrementalTransfer(it); |
214 } | 216 } |
215 | 217 |
216 bool SelectionOwner::ProcessTarget(XAtom target, | 218 bool SelectionOwner::ProcessTarget(XAtom target, |
217 XID requestor, | 219 XID requestor, |
218 XAtom property) { | 220 XAtom property) { |
219 XAtom multiple_atom = atom_cache_.GetAtom(kMultiple); | 221 XAtom multiple_atom = atom_cache_.GetAtom(kMultiple); |
220 XAtom save_targets_atom = atom_cache_.GetAtom(kSaveTargets); | 222 XAtom save_targets_atom = atom_cache_.GetAtom(kSaveTargets); |
221 XAtom targets_atom = atom_cache_.GetAtom(kTargets); | 223 XAtom targets_atom = atom_cache_.GetAtom(kTargets); |
| 224 XAtom timestamp_atom = atom_cache_.GetAtom(kTimestamp); |
222 | 225 |
223 if (target == multiple_atom || target == save_targets_atom) | 226 if (target == multiple_atom || target == save_targets_atom) |
224 return false; | 227 return false; |
225 | 228 |
| 229 if (target == timestamp_atom) { |
| 230 XChangeProperty( |
| 231 x_display_, requestor, property, XA_INTEGER, 32, PropModeReplace, |
| 232 reinterpret_cast<unsigned char*>(&acquired_selection_timestamp_), 1); |
| 233 return true; |
| 234 } |
| 235 |
226 if (target == targets_atom) { | 236 if (target == targets_atom) { |
227 // We have been asked for TARGETS. Send an atom array back with the data | 237 // We have been asked for TARGETS. Send an atom array back with the data |
228 // types we support. | 238 // types we support. |
229 std::vector<XAtom> targets; | 239 std::vector<XAtom> targets; |
| 240 targets.push_back(timestamp_atom); |
230 targets.push_back(targets_atom); | 241 targets.push_back(targets_atom); |
231 targets.push_back(save_targets_atom); | 242 targets.push_back(save_targets_atom); |
232 targets.push_back(multiple_atom); | 243 targets.push_back(multiple_atom); |
233 RetrieveTargets(&targets); | 244 RetrieveTargets(&targets); |
234 | 245 |
235 XChangeProperty(x_display_, requestor, property, XA_ATOM, 32, | 246 XChangeProperty(x_display_, requestor, property, XA_ATOM, 32, |
236 PropModeReplace, | 247 PropModeReplace, |
237 reinterpret_cast<unsigned char*>(&targets.front()), | 248 reinterpret_cast<unsigned char*>(&targets.front()), |
238 targets.size()); | 249 targets.size()); |
239 return true; | 250 return true; |
240 } else { | 251 } |
241 // Try to find the data type in map. | |
242 SelectionFormatMap::const_iterator it = format_map_.find(target); | |
243 if (it != format_map_.end()) { | |
244 if (it->second->size() > max_request_size_) { | |
245 // We must send the data back in several chunks due to a limitation in | |
246 // the size of X requests. Notify the selection requestor that the data | |
247 // will be sent incrementally by returning data of type "INCR". | |
248 long length = it->second->size(); | |
249 XChangeProperty(x_display_, | |
250 requestor, | |
251 property, | |
252 atom_cache_.GetAtom(kIncr), | |
253 32, | |
254 PropModeReplace, | |
255 reinterpret_cast<unsigned char*>(&length), | |
256 1); | |
257 | 252 |
258 // Wait for the selection requestor to indicate that it has processed | 253 // Try to find the data type in map. |
259 // the selection result before sending the first chunk of data. The | 254 SelectionFormatMap::const_iterator it = format_map_.find(target); |
260 // selection requestor indicates this by deleting |property|. | 255 if (it != format_map_.end()) { |
261 base::TimeTicks timeout = | 256 if (it->second->size() > max_request_size_) { |
262 base::TimeTicks::Now() + | 257 // We must send the data back in several chunks due to a limitation in |
263 base::TimeDelta::FromMilliseconds(kIncrementalTransferTimeoutMs); | 258 // the size of X requests. Notify the selection requestor that the data |
264 int foreign_window_manager_id = | 259 // will be sent incrementally by returning data of type "INCR". |
265 ui::XForeignWindowManager::GetInstance()->RequestEvents( | 260 long length = it->second->size(); |
266 requestor, PropertyChangeMask); | 261 XChangeProperty(x_display_, |
267 incremental_transfers_.push_back( | 262 requestor, |
268 IncrementalTransfer(requestor, | 263 property, |
269 target, | 264 atom_cache_.GetAtom(kIncr), |
270 property, | 265 32, |
271 it->second, | 266 PropModeReplace, |
272 0, | 267 reinterpret_cast<unsigned char*>(&length), |
273 timeout, | 268 1); |
274 foreign_window_manager_id)); | |
275 | 269 |
276 // Start a timer to abort the data transfer in case that the selection | 270 // Wait for the selection requestor to indicate that it has processed |
277 // requestor does not support the INCR property or gets destroyed during | 271 // the selection result before sending the first chunk of data. The |
278 // the data transfer. | 272 // selection requestor indicates this by deleting |property|. |
279 if (!incremental_transfer_abort_timer_.IsRunning()) { | 273 base::TimeTicks timeout = |
280 incremental_transfer_abort_timer_.Start( | 274 base::TimeTicks::Now() + |
281 FROM_HERE, | 275 base::TimeDelta::FromMilliseconds(kIncrementalTransferTimeoutMs); |
282 base::TimeDelta::FromMilliseconds(kTimerPeriodMs), | 276 int foreign_window_manager_id = |
283 this, | 277 ui::XForeignWindowManager::GetInstance()->RequestEvents( |
284 &SelectionOwner::AbortStaleIncrementalTransfers); | 278 requestor, PropertyChangeMask); |
285 } | 279 incremental_transfers_.push_back( |
286 } else { | 280 IncrementalTransfer(requestor, |
287 XChangeProperty( | 281 target, |
288 x_display_, | 282 property, |
289 requestor, | 283 it->second, |
290 property, | 284 0, |
291 target, | 285 timeout, |
292 8, | 286 foreign_window_manager_id)); |
293 PropModeReplace, | 287 |
294 const_cast<unsigned char*>(it->second->front()), | 288 // Start a timer to abort the data transfer in case that the selection |
295 it->second->size()); | 289 // requestor does not support the INCR property or gets destroyed during |
| 290 // the data transfer. |
| 291 if (!incremental_transfer_abort_timer_.IsRunning()) { |
| 292 incremental_transfer_abort_timer_.Start( |
| 293 FROM_HERE, |
| 294 base::TimeDelta::FromMilliseconds(kTimerPeriodMs), |
| 295 this, |
| 296 &SelectionOwner::AbortStaleIncrementalTransfers); |
296 } | 297 } |
297 return true; | 298 } else { |
| 299 XChangeProperty( |
| 300 x_display_, |
| 301 requestor, |
| 302 property, |
| 303 target, |
| 304 8, |
| 305 PropModeReplace, |
| 306 const_cast<unsigned char*>(it->second->front()), |
| 307 it->second->size()); |
298 } | 308 } |
299 // I would put error logging here, but GTK ignores TARGETS and spams us | 309 return true; |
300 // looking for its own internal types. | |
301 } | 310 } |
| 311 |
| 312 // I would put error logging here, but GTK ignores TARGETS and spams us |
| 313 // looking for its own internal types. |
302 return false; | 314 return false; |
303 } | 315 } |
304 | 316 |
305 void SelectionOwner::ProcessIncrementalTransfer(IncrementalTransfer* transfer) { | 317 void SelectionOwner::ProcessIncrementalTransfer(IncrementalTransfer* transfer) { |
306 size_t remaining = transfer->data->size() - transfer->offset; | 318 size_t remaining = transfer->data->size() - transfer->offset; |
307 size_t chunk_length = std::min(remaining, max_request_size_); | 319 size_t chunk_length = std::min(remaining, max_request_size_); |
308 XChangeProperty( | 320 XChangeProperty( |
309 x_display_, | 321 x_display_, |
310 transfer->window, | 322 transfer->window, |
311 transfer->property, | 323 transfer->property, |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
376 foreign_window_manager_id(foreign_window_manager_id) { | 388 foreign_window_manager_id(foreign_window_manager_id) { |
377 } | 389 } |
378 | 390 |
379 SelectionOwner::IncrementalTransfer::IncrementalTransfer( | 391 SelectionOwner::IncrementalTransfer::IncrementalTransfer( |
380 const IncrementalTransfer& other) = default; | 392 const IncrementalTransfer& other) = default; |
381 | 393 |
382 SelectionOwner::IncrementalTransfer::~IncrementalTransfer() { | 394 SelectionOwner::IncrementalTransfer::~IncrementalTransfer() { |
383 } | 395 } |
384 | 396 |
385 } // namespace ui | 397 } // namespace ui |
OLD | NEW |