Index: ui/base/x/selection_owner.cc |
diff --git a/ui/base/x/selection_owner.cc b/ui/base/x/selection_owner.cc |
index 170f32e19b2c0ea71e90bdb53e9386c5aea26fb0..e6651f0b814dec70abddccff23f808c41171a0c0 100644 |
--- a/ui/base/x/selection_owner.cc |
+++ b/ui/base/x/selection_owner.cc |
@@ -12,6 +12,7 @@ |
#include "ui/base/x/selection_utils.h" |
#include "ui/base/x/x11_foreign_window_manager.h" |
#include "ui/base/x/x11_util.h" |
+#include "ui/events/platform/x11/x11_event_source.h" |
namespace ui { |
@@ -22,15 +23,10 @@ const char kIncr[] = "INCR"; |
const char kMultiple[] = "MULTIPLE"; |
const char kSaveTargets[] = "SAVE_TARGETS"; |
const char kTargets[] = "TARGETS"; |
+const char kTimestamp[] = "TIMESTAMP"; |
-const char* kAtomsToCache[] = { |
- kAtomPair, |
- kIncr, |
- kMultiple, |
- kSaveTargets, |
- kTargets, |
- NULL |
-}; |
+const char* kAtomsToCache[] = {kAtomPair, kIncr, kMultiple, kSaveTargets, |
+ kTargets, kTimestamp, NULL}; |
// The period of |incremental_transfer_abort_timer_|. Arbitrary but must be <= |
// than kIncrementalTransferTimeoutMs. |
@@ -122,6 +118,12 @@ void SelectionOwner::RetrieveTargets(std::vector<XAtom>* targets) { |
void SelectionOwner::TakeOwnershipOfSelection( |
const SelectionFormatMap& data) { |
+ // Save the last server timestamp seen from X, to satisfy requests for the |
+ // TIMESTAMP target later… |
+ acquired_selection_timestamp_ = |
+ X11EventSource::GetInstance()->last_seen_server_time(); |
+ // …but always pass CurrentTime to XSetSelectionOwner to increase the chances |
+ // of this succeeding. |
XSetSelectionOwner(x_display_, selection_name_, x_window_, CurrentTime); |
if (XGetSelectionOwner(x_display_, selection_name_) == x_window_) { |
@@ -219,14 +221,23 @@ bool SelectionOwner::ProcessTarget(XAtom target, |
XAtom multiple_atom = atom_cache_.GetAtom(kMultiple); |
XAtom save_targets_atom = atom_cache_.GetAtom(kSaveTargets); |
XAtom targets_atom = atom_cache_.GetAtom(kTargets); |
+ XAtom timestamp_atom = atom_cache_.GetAtom(kTimestamp); |
if (target == multiple_atom || target == save_targets_atom) |
return false; |
+ if (target == timestamp_atom) { |
+ XChangeProperty( |
+ x_display_, requestor, property, XA_INTEGER, 32, PropModeReplace, |
+ reinterpret_cast<unsigned char*>(&acquired_selection_timestamp_), 1); |
+ return true; |
+ } |
+ |
if (target == targets_atom) { |
// We have been asked for TARGETS. Send an atom array back with the data |
// types we support. |
std::vector<XAtom> targets; |
+ targets.push_back(timestamp_atom); |
targets.push_back(targets_atom); |
targets.push_back(save_targets_atom); |
targets.push_back(multiple_atom); |
@@ -237,68 +248,69 @@ bool SelectionOwner::ProcessTarget(XAtom target, |
reinterpret_cast<unsigned char*>(&targets.front()), |
targets.size()); |
return true; |
- } else { |
- // Try to find the data type in map. |
- SelectionFormatMap::const_iterator it = format_map_.find(target); |
- if (it != format_map_.end()) { |
- if (it->second->size() > max_request_size_) { |
- // We must send the data back in several chunks due to a limitation in |
- // the size of X requests. Notify the selection requestor that the data |
- // will be sent incrementally by returning data of type "INCR". |
- long length = it->second->size(); |
- XChangeProperty(x_display_, |
- requestor, |
- property, |
- atom_cache_.GetAtom(kIncr), |
- 32, |
- PropModeReplace, |
- reinterpret_cast<unsigned char*>(&length), |
- 1); |
- |
- // Wait for the selection requestor to indicate that it has processed |
- // the selection result before sending the first chunk of data. The |
- // selection requestor indicates this by deleting |property|. |
- base::TimeTicks timeout = |
- base::TimeTicks::Now() + |
- base::TimeDelta::FromMilliseconds(kIncrementalTransferTimeoutMs); |
- int foreign_window_manager_id = |
- ui::XForeignWindowManager::GetInstance()->RequestEvents( |
- requestor, PropertyChangeMask); |
- incremental_transfers_.push_back( |
- IncrementalTransfer(requestor, |
- target, |
- property, |
- it->second, |
- 0, |
- timeout, |
- foreign_window_manager_id)); |
- |
- // Start a timer to abort the data transfer in case that the selection |
- // requestor does not support the INCR property or gets destroyed during |
- // the data transfer. |
- if (!incremental_transfer_abort_timer_.IsRunning()) { |
- incremental_transfer_abort_timer_.Start( |
- FROM_HERE, |
- base::TimeDelta::FromMilliseconds(kTimerPeriodMs), |
- this, |
- &SelectionOwner::AbortStaleIncrementalTransfers); |
- } |
- } else { |
- XChangeProperty( |
- x_display_, |
- requestor, |
- property, |
- target, |
- 8, |
- PropModeReplace, |
- const_cast<unsigned char*>(it->second->front()), |
- it->second->size()); |
+ } |
+ |
+ // Try to find the data type in map. |
+ SelectionFormatMap::const_iterator it = format_map_.find(target); |
+ if (it != format_map_.end()) { |
+ if (it->second->size() > max_request_size_) { |
+ // We must send the data back in several chunks due to a limitation in |
+ // the size of X requests. Notify the selection requestor that the data |
+ // will be sent incrementally by returning data of type "INCR". |
+ long length = it->second->size(); |
+ XChangeProperty(x_display_, |
+ requestor, |
+ property, |
+ atom_cache_.GetAtom(kIncr), |
+ 32, |
+ PropModeReplace, |
+ reinterpret_cast<unsigned char*>(&length), |
+ 1); |
+ |
+ // Wait for the selection requestor to indicate that it has processed |
+ // the selection result before sending the first chunk of data. The |
+ // selection requestor indicates this by deleting |property|. |
+ base::TimeTicks timeout = |
+ base::TimeTicks::Now() + |
+ base::TimeDelta::FromMilliseconds(kIncrementalTransferTimeoutMs); |
+ int foreign_window_manager_id = |
+ ui::XForeignWindowManager::GetInstance()->RequestEvents( |
+ requestor, PropertyChangeMask); |
+ incremental_transfers_.push_back( |
+ IncrementalTransfer(requestor, |
+ target, |
+ property, |
+ it->second, |
+ 0, |
+ timeout, |
+ foreign_window_manager_id)); |
+ |
+ // Start a timer to abort the data transfer in case that the selection |
+ // requestor does not support the INCR property or gets destroyed during |
+ // the data transfer. |
+ if (!incremental_transfer_abort_timer_.IsRunning()) { |
+ incremental_transfer_abort_timer_.Start( |
+ FROM_HERE, |
+ base::TimeDelta::FromMilliseconds(kTimerPeriodMs), |
+ this, |
+ &SelectionOwner::AbortStaleIncrementalTransfers); |
} |
- return true; |
+ } else { |
+ XChangeProperty( |
+ x_display_, |
+ requestor, |
+ property, |
+ target, |
+ 8, |
+ PropModeReplace, |
+ const_cast<unsigned char*>(it->second->front()), |
+ it->second->size()); |
} |
- // I would put error logging here, but GTK ignores TARGETS and spams us |
- // looking for its own internal types. |
+ return true; |
} |
+ |
+ // I would put error logging here, but GTK ignores TARGETS and spams us |
+ // looking for its own internal types. |
return false; |
} |