Index: ui/base/x/selection_requestor.cc |
diff --git a/ui/base/x/selection_requestor.cc b/ui/base/x/selection_requestor.cc |
index 444401ae42fa3f84bed4157defef92b13e4f9a45..5cab04fd91e46f5e0e2cf5478b182e07f09dade8 100644 |
--- a/ui/base/x/selection_requestor.cc |
+++ b/ui/base/x/selection_requestor.cc |
@@ -19,14 +19,44 @@ namespace ui { |
namespace { |
const char kChromeSelection[] = "CHROME_SELECTION"; |
+const char kIncr[] = "INCR"; |
const char* kAtomsToCache[] = { |
kChromeSelection, |
+ kIncr, |
NULL |
}; |
-// The amount of time to wait for a request to complete. |
-const int kRequestTimeoutMs = 300; |
+// The period of |abort_timer_|. Arbitrary but must be <= than |
+// kRequestTimeoutMs. |
+const int kTimerPeriodMs = 100; |
+ |
+// The amount of time to wait for a request to complete before aborting it. |
+const int kRequestTimeoutMs = 10000; |
+ |
+COMPILE_ASSERT(kTimerPeriodMs <= kRequestTimeoutMs, |
+ timer_period_must_be_less_or_equal_to_request_timeout); |
+ |
+// Combines |data| into a single RefCountedMemory object. |
+scoped_refptr<base::RefCountedMemory> CombineRefCountedMemory( |
+ const std::vector<scoped_refptr<base::RefCountedMemory> >& data) { |
+ if (data.size() == 1u) |
+ return data[0]; |
+ |
+ size_t length = 0; |
+ for (size_t i = 0; i < data.size(); ++i) |
+ length += data[i]->size(); |
+ std::vector<unsigned char> combined_data; |
+ combined_data.reserve(length); |
+ |
+ for (size_t i = 0; i < data.size(); ++i) { |
+ combined_data.insert(combined_data.end(), |
+ data[i]->front(), |
+ data[i]->front() + data[i]->size()); |
+ } |
+ return scoped_refptr<base::RefCountedMemory>( |
+ base::RefCountedBytes::TakeVector(&combined_data)); |
+} |
} // namespace |
@@ -55,7 +85,7 @@ bool SelectionRequestor::PerformBlockingConvertSelection( |
base::TimeDelta::FromMilliseconds(kRequestTimeoutMs); |
Request request(selection, target, timeout); |
requests_.push_back(&request); |
- if (requests_.size() == 1u) |
+ if (current_request_index_ == (requests_.size() - 1)) |
ConvertSelectionForCurrentRequest(); |
BlockTillSelectionNotifyForRequest(&request); |
@@ -73,7 +103,7 @@ bool SelectionRequestor::PerformBlockingConvertSelection( |
if (request.success) { |
if (out_data) |
- *out_data = request.out_data; |
+ *out_data = CombineRefCountedMemory(request.out_data); |
if (out_data_items) |
*out_data_items = request.out_data_items; |
if (out_type) |
@@ -123,40 +153,98 @@ void SelectionRequestor::OnSelectionNotify(const XEvent& event) { |
return; |
} |
- request->success = false; |
+ bool success = false; |
if (event_property == x_property_) { |
- request->success = ui::GetRawBytesOfProperty(x_window_, |
- x_property_, |
- &request->out_data, |
- &request->out_data_items, |
- &request->out_type); |
+ scoped_refptr<base::RefCountedMemory> out_data; |
+ success = ui::GetRawBytesOfProperty(x_window_, |
+ x_property_, |
+ &out_data, |
+ &request->out_data_items, |
+ &request->out_type); |
+ if (success) { |
+ request->out_data.clear(); |
+ request->out_data.push_back(out_data); |
+ } |
} |
if (event_property != None) |
XDeleteProperty(x_display_, x_window_, event_property); |
- CompleteRequest(current_request_index_); |
+ if (request->out_type == atom_cache_.GetAtom(kIncr)) { |
+ request->data_sent_incrementally = true; |
+ request->out_data.clear(); |
+ request->out_data_items = 0u; |
+ request->out_type = None; |
+ request->timeout = base::TimeTicks::Now() + |
+ base::TimeDelta::FromMilliseconds(kRequestTimeoutMs); |
+ } else { |
+ CompleteRequest(current_request_index_, success); |
+ } |
+} |
+ |
+bool SelectionRequestor::CanDispatchPropertyEvent(const XEvent& event) { |
+ return event.xproperty.window == x_window_ && |
+ event.xproperty.atom == x_property_ && |
+ event.xproperty.state == PropertyNewValue; |
+} |
+ |
+void SelectionRequestor::OnPropertyEvent(const XEvent& event) { |
+ Request* request = GetCurrentRequest(); |
+ if (!request || !request->data_sent_incrementally) |
+ return; |
+ |
+ scoped_refptr<base::RefCountedMemory> out_data; |
+ size_t out_data_items = 0u; |
+ Atom out_type = None; |
+ bool success = ui::GetRawBytesOfProperty(x_window_, |
+ x_property_, |
+ &out_data, |
+ &out_data_items, |
+ &out_type); |
+ if (!success) { |
+ CompleteRequest(current_request_index_, false); |
+ return; |
+ } |
+ |
+ if (request->out_type != None && request->out_type != out_type) { |
+ CompleteRequest(current_request_index_, false); |
+ return; |
+ } |
+ |
+ request->out_data.push_back(out_data); |
+ request->out_data_items += out_data_items; |
+ request->out_type = out_type; |
+ |
+ // Delete the property to tell the selection owner to send the next chunk. |
+ XDeleteProperty(x_display_, x_window_, x_property_); |
+ |
+ request->timeout = base::TimeTicks::Now() + |
+ base::TimeDelta::FromMilliseconds(kRequestTimeoutMs); |
+ |
+ if (out_data->size() == 0u) |
+ CompleteRequest(current_request_index_, true); |
} |
void SelectionRequestor::AbortStaleRequests() { |
base::TimeTicks now = base::TimeTicks::Now(); |
- for (size_t i = current_request_index_; |
- i < requests_.size() && requests_[i]->timeout <= now; |
- ++i) { |
- CompleteRequest(i); |
+ for (size_t i = current_request_index_; i < requests_.size(); ++i) { |
+ if (requests_[i]->timeout <= now) |
+ CompleteRequest(i, false); |
} |
} |
-void SelectionRequestor::CompleteRequest(size_t index) { |
+void SelectionRequestor::CompleteRequest(size_t index, bool success) { |
if (index >= requests_.size()) |
return; |
Request* request = requests_[index]; |
if (request->completed) |
return; |
+ request->success = success; |
request->completed = true; |
if (index == current_request_index_) { |
- ++current_request_index_; |
+ while (GetCurrentRequest() && GetCurrentRequest()->completed) |
+ ++current_request_index_; |
ConvertSelectionForCurrentRequest(); |
} |
@@ -180,7 +268,7 @@ void SelectionRequestor::BlockTillSelectionNotifyForRequest(Request* request) { |
if (PlatformEventSource::GetInstance()) { |
if (!abort_timer_.IsRunning()) { |
abort_timer_.Start(FROM_HERE, |
- base::TimeDelta::FromMilliseconds(kRequestTimeoutMs), |
+ base::TimeDelta::FromMilliseconds(kTimerPeriodMs), |
this, |
&SelectionRequestor::AbortStaleRequests); |
} |
@@ -219,6 +307,7 @@ SelectionRequestor::Request::Request(XAtom selection, |
base::TimeTicks timeout) |
: selection(selection), |
target(target), |
+ data_sent_incrementally(false), |
out_data_items(0u), |
out_type(None), |
success(false), |