Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(423)

Unified Diff: ui/base/x/selection_owner.cc

Issue 397223002: Enable copying long text to Chrome and pasting long text from Chrome (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ui/base/x/selection_owner.cc
diff --git a/ui/base/x/selection_owner.cc b/ui/base/x/selection_owner.cc
index 1664e5912e334d90304b56499f305c3ff97aa592..ac0b594a0f8abff1d5da03f45369bd43e94cb95d 100644
--- a/ui/base/x/selection_owner.cc
+++ b/ui/base/x/selection_owner.cc
@@ -4,11 +4,13 @@
#include "ui/base/x/selection_owner.h"
+#include <algorithm>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include "base/logging.h"
#include "ui/base/x/selection_utils.h"
+#include "ui/base/x/x11_foreign_window_manager.h"
#include "ui/base/x/x11_util.h"
namespace ui {
@@ -16,18 +18,37 @@ namespace ui {
namespace {
const char kAtomPair[] = "ATOM_PAIR";
+const char kIncr[] = "INCR";
const char kMultiple[] = "MULTIPLE";
const char kSaveTargets[] = "SAVE_TARGETS";
const char kTargets[] = "TARGETS";
const char* kAtomsToCache[] = {
kAtomPair,
+ kIncr,
kMultiple,
kSaveTargets,
kTargets,
NULL
};
+// The period of |incr_timer_|. Arbitrary but must be <= than
+// kIncrTransferTimeoutMs.
+const int kIncrTimerPeriodMs = 1000;
Daniel Erat 2014/07/25 19:38:44 s/Incr/Incremental/ here and in other constants
+
+// The amount of time to wait for the selection requestor to process the data
+// sent by the selection owner before aborting an incremental data transfer.
+const int kIncrTransferTimeoutMs = 10000;
+
+// Returns a conservative max size of the data we can pass into
+// XChangeProperty(). Copied from GTK.
+size_t GetMaxRequestSize(XDisplay* display) {
+ long extended_max_size = XExtendedMaxRequestSize(display);
+ return std::min(
+ static_cast<long>(0x40000),
+ (extended_max_size ? extended_max_size : XMaxRequestSize(display)) - 100);
Daniel Erat 2014/07/25 19:38:43 nit: check for negative wraparound
pkotwicz 2014/07/26 20:09:53 Done.
+}
+
// Gets the value of an atom pair array property. On success, true is returned
// and the value is stored in |value|.
bool GetAtomPairArrayProperty(XID window,
@@ -78,6 +99,7 @@ SelectionOwner::SelectionOwner(XDisplay* x_display,
: x_display_(x_display),
x_window_(x_window),
selection_name_(selection_name),
+ max_request_size_(GetMaxRequestSize(x_display)),
atom_cache_(x_display_, kAtomsToCache) {
}
@@ -173,6 +195,21 @@ void SelectionOwner::OnSelectionClear(const XEvent& event) {
// we need to delay clearing.
}
+bool SelectionOwner::CanDispatchPropertyEvent(const XEvent& event) {
+ return event.xproperty.state == PropertyDelete &&
+ FindIncrTransferForEvent(event) != incrs_.end();
+}
+
+void SelectionOwner::OnPropertyEvent(const XEvent& event) {
+ std::vector<IncrTransfer>::iterator it = FindIncrTransferForEvent(event);
+ if (it == incrs_.end())
+ return;
+
+ ProcessIncr(&(*it));
+ if (!it->data.get())
+ CompleteIncrTransfer(it);
+}
+
bool SelectionOwner::ProcessTarget(XAtom target,
XID requestor,
XAtom property) {
@@ -201,12 +238,58 @@ bool SelectionOwner::ProcessTarget(XAtom target,
// Try to find the data type in map.
SelectionFormatMap::const_iterator it = format_map_.find(target);
if (it != format_map_.end()) {
- XChangeProperty(x_display_, requestor, property, target, 8,
- PropModeReplace,
- const_cast<unsigned char*>(
- reinterpret_cast<const unsigned char*>(
- it->second->front())),
- it->second->size());
+ 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".
+ int 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(kIncrTransferTimeoutMs);
+ int foreign_window_manager_id =
+ ui::XForeignWindowManager::GetInstance()->RequestEvents(
+ requestor, PropertyChangeMask);
+ incrs_.push_back(IncrTransfer(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 (!incr_timer_.IsRunning()) {
+ incr_timer_.Start(
+ FROM_HERE,
+ base::TimeDelta::FromMilliseconds(kIncrTimerPeriodMs),
+ this,
+ &SelectionOwner::AbortStaleIncrTransfers);
+ }
+ } else {
+ XChangeProperty(
+ x_display_,
+ requestor,
+ property,
+ target,
+ 8,
+ PropModeReplace,
+ const_cast<unsigned char*>(it->second->front()),
+ it->second->size());
+ }
return true;
}
// I would put error logging here, but GTK ignores TARGETS and spams us
@@ -215,4 +298,78 @@ bool SelectionOwner::ProcessTarget(XAtom target,
return false;
}
+void SelectionOwner::ProcessIncr(IncrTransfer* transfer) {
+ size_t remaining = transfer->data->size() - transfer->offset;
+ size_t chunk_length = std::min(remaining, max_request_size_);
+ XChangeProperty(
+ x_display_,
+ transfer->window,
+ transfer->property,
+ transfer->target,
+ 8,
+ PropModeReplace,
+ const_cast<unsigned char*>(transfer->data->front() + transfer->offset),
+ chunk_length);
+ transfer->offset += chunk_length;
+ transfer->timeout = base::TimeTicks::Now() +
+ base::TimeDelta::FromMilliseconds(kIncrTransferTimeoutMs);
+
+ // When offset == data->size(), we still need to transfer a zero-sized chunk
+ // to notify the selection requestor that the transfer is complete. Clear
+ // transfer->data once the zero-sized chunk is sent to indicate that state
+ // related to this data transfer can be cleared.
+ if (chunk_length == 0)
+ transfer->data = NULL;
+}
+
+void SelectionOwner::AbortStaleIncrTransfers() {
+ base::TimeTicks now = base::TimeTicks::Now();
+ for (int i = static_cast<int>(incrs_.size()) - 1; i >= 0; --i) {
+ if (incrs_[i].timeout < now)
+ CompleteIncrTransfer(incrs_.begin() + i);
+ }
+}
+
+void SelectionOwner::CompleteIncrTransfer(
+ std::vector<IncrTransfer>::iterator it) {
+ ui::XForeignWindowManager::GetInstance()->CancelRequest(
+ it->foreign_window_manager_id);
+ incrs_.erase(it);
+
+ if (incrs_.empty())
+ incr_timer_.Stop();
+}
+
+std::vector<SelectionOwner::IncrTransfer>::iterator
+ SelectionOwner::FindIncrTransferForEvent(const XEvent& event) {
+ for (std::vector<IncrTransfer>::iterator it = incrs_.begin();
+ it != incrs_.end(); ++it) {
+ if (it->window == event.xproperty.window &&
+ it->property == event.xproperty.atom) {
+ return it;
+ }
+ }
+ return incrs_.end();
+}
+
+SelectionOwner::IncrTransfer::IncrTransfer(
+ XID window,
+ XAtom target,
+ XAtom property,
+ const scoped_refptr<base::RefCountedMemory>& data,
+ int offset,
+ base::TimeTicks timeout,
+ int foreign_window_manager_id)
+ : window(window),
+ target(target),
+ property(property),
+ data(data),
+ offset(offset),
+ timeout(timeout),
+ foreign_window_manager_id(foreign_window_manager_id) {
+}
+
+SelectionOwner::IncrTransfer::~IncrTransfer() {
+}
+
} // namespace ui

Powered by Google App Engine
This is Rietveld 408576698