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

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

Issue 391593002: Process SelectionRequestor::PerformBlockingConvertSelection() requests one at a time (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_requestor.cc
diff --git a/ui/base/x/selection_requestor.cc b/ui/base/x/selection_requestor.cc
index 9268a7f3538bdb42d1a4b53d1dcc26272077ec25..83dc1f0c666f121b746d5b03ff2baad1b850600d 100644
--- a/ui/base/x/selection_requestor.cc
+++ b/ui/base/x/selection_requestor.cc
@@ -4,6 +4,7 @@
#include "ui/base/x/selection_requestor.h"
+#include <algorithm>
#include <X11/Xlib.h>
#include "base/run_loop.h"
@@ -24,69 +25,78 @@ const char* kAtomsToCache[] = {
NULL
};
+// The amount of time to wait for a request to complete.
+const int kRequestTimeoutMs = 300;
+
} // namespace
SelectionRequestor::SelectionRequestor(XDisplay* x_display,
XID x_window,
- XAtom selection_name,
PlatformEventDispatcher* dispatcher)
: x_display_(x_display),
x_window_(x_window),
- selection_name_(selection_name),
+ x_property_(None),
dispatcher_(dispatcher),
+ current_request_index_(0u),
atom_cache_(x_display_, kAtomsToCache) {
+ x_property_ = atom_cache_.GetAtom(kChromeSelection);
}
SelectionRequestor::~SelectionRequestor() {}
bool SelectionRequestor::PerformBlockingConvertSelection(
+ XAtom selection,
XAtom target,
scoped_refptr<base::RefCountedMemory>* out_data,
size_t* out_data_items,
XAtom* out_type) {
- // The name of the property that we are either:
- // - Passing as a parameter with the XConvertSelection() request.
- // OR
- // - Asking the selection owner to set on |x_window_|.
- XAtom property = atom_cache_.GetAtom(kChromeSelection);
-
- XConvertSelection(x_display_,
- selection_name_,
- target,
- property,
- x_window_,
- CurrentTime);
-
- // Now that we've thrown our message off to the X11 server, we block waiting
- // for a response.
- PendingRequest pending_request(target);
- BlockTillSelectionNotifyForRequest(&pending_request);
-
- bool success = false;
- if (pending_request.returned_property == property) {
- success = ui::GetRawBytesOfProperty(x_window_,
- pending_request.returned_property,
- out_data, out_data_items, out_type);
+ base::TimeTicks timeout =
+ base::TimeTicks::Now() +
+ base::TimeDelta::FromMilliseconds(kRequestTimeoutMs);
+ Request request(selection, target, timeout);
+ requests_.push_back(&request);
+ if (requests_.size() == 1u)
+ ConvertSelectionForCurrentRequest();
+ BlockTillSelectionNotifyForRequest(&request);
+
+ std::vector<Request*>::iterator request_it = std::find(
+ requests_.begin(), requests_.end(), &request);
+ CHECK(request_it != requests_.end());
+ if (static_cast<int>(current_request_index_) >
+ request_it - requests_.begin()) {
+ --current_request_index_;
}
- if (pending_request.returned_property != None)
- XDeleteProperty(x_display_, x_window_, pending_request.returned_property);
- return success;
+ requests_.erase(request_it);
+
+ if (requests_.empty())
+ abort_timer_.Stop();
+
+ if (out_data)
+ *out_data = request.out_data;
+ if (out_data_items)
+ *out_data_items = request.out_data_items;
+ if (out_type)
+ *out_type = request.out_type;
+ return request.success;
}
void SelectionRequestor::PerformBlockingConvertSelectionWithParameter(
+ XAtom selection,
XAtom target,
const std::vector<XAtom>& parameter) {
SetAtomArrayProperty(x_window_, kChromeSelection, "ATOM", parameter);
- PerformBlockingConvertSelection(target, NULL, NULL, NULL);
+ PerformBlockingConvertSelection(selection, target, NULL, NULL, NULL);
}
SelectionData SelectionRequestor::RequestAndWaitForTypes(
+ XAtom selection,
const std::vector<XAtom>& types) {
for (std::vector<XAtom>::const_iterator it = types.begin();
it != types.end(); ++it) {
scoped_refptr<base::RefCountedMemory> data;
XAtom type = None;
- if (PerformBlockingConvertSelection(*it,
+ if (PerformBlockingConvertSelection(selection,
+ *it,
&data,
NULL,
&type) &&
@@ -99,88 +109,121 @@ SelectionData SelectionRequestor::RequestAndWaitForTypes(
}
void SelectionRequestor::OnSelectionNotify(const XEvent& event) {
- // Find the PendingRequest for the corresponding XConvertSelection call. If
- // there are multiple pending requests on the same target, satisfy them in
- // FIFO order.
- PendingRequest* request_notified = NULL;
- if (selection_name_ == event.xselection.selection) {
- for (std::list<PendingRequest*>::iterator iter = pending_requests_.begin();
- iter != pending_requests_.end(); ++iter) {
- PendingRequest* request = *iter;
- if (request->returned)
- continue;
- if (request->target != event.xselection.target)
- continue;
- request_notified = request;
- break;
- }
+ Request* request = GetCurrentRequest();
+ XAtom event_property = event.xselection.property;
+ if (!request ||
+ request->completed ||
+ request->selection != event.xselection.selection ||
+ request->target != event.xselection.target) {
+ // ICCCM requires us to delete the property passed into SelectionNotify.
+ if (event_property != None)
+ XDeleteProperty(x_display_, x_window_, event_property);
+ return;
}
- // This event doesn't correspond to any XConvertSelection calls that we
- // issued in PerformBlockingConvertSelection. This shouldn't happen, but any
- // client can send any message, so it can happen.
- XAtom returned_property = event.xselection.property;
- if (!request_notified) {
- // ICCCM requires us to delete the property passed into SelectionNotify. If
- // |request_notified| is true, the property will be deleted when the run
- // loop has quit.
- if (returned_property != None)
- XDeleteProperty(x_display_, x_window_, returned_property);
- return;
+ request->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);
}
+ if (event_property != None)
+ XDeleteProperty(x_display_, x_window_, event_property);
- request_notified->returned_property = returned_property;
- request_notified->returned = true;
+ CompleteRequest(current_request_index_);
+}
- if (!request_notified->quit_closure.is_null())
- request_notified->quit_closure.Run();
+void SelectionRequestor::AbortStaleRequests() {
+ base::TimeTicks now = base::TimeTicks::Now();
+ for (size_t i = 0; i < requests_.size(); ++i) {
+ if (requests_[i]->timeout <= now)
+ CompleteRequest(i);
+ }
}
-void SelectionRequestor::BlockTillSelectionNotifyForRequest(
- PendingRequest* request) {
- pending_requests_.push_back(request);
+void SelectionRequestor::CompleteRequest(size_t index) {
+ if (index >= requests_.size())
+ return;
- const int kMaxWaitTimeForClipboardResponse = 300;
+ Request* request = requests_[index];
+ if (request->completed)
+ return;
+ request->completed = true;
+
+ if (index == current_request_index_) {
+ ++current_request_index_;
+ ConvertSelectionForCurrentRequest();
+ }
+
+ if (!request->quit_closure.is_null())
+ request->quit_closure.Run();
+}
+
+void SelectionRequestor::ConvertSelectionForCurrentRequest() {
+ Request* request = GetCurrentRequest();
+ if (request) {
+ XConvertSelection(x_display_,
+ request->selection,
+ request->target,
+ x_property_,
+ x_window_,
+ CurrentTime);
+ }
+}
+
+void SelectionRequestor::BlockTillSelectionNotifyForRequest(Request* request) {
if (PlatformEventSource::GetInstance()) {
- base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
- base::MessageLoop::ScopedNestableTaskAllower allow_nested(loop);
- base::RunLoop run_loop;
+ if (!abort_timer_.IsRunning()) {
+ abort_timer_.Start(FROM_HERE,
+ base::TimeDelta::FromMilliseconds(kRequestTimeoutMs),
+ this,
+ &SelectionRequestor::AbortStaleRequests);
+ }
+ base::MessageLoop::ScopedNestableTaskAllower allow_nested(
+ base::MessageLoopForUI::current());
+ base::RunLoop run_loop;
request->quit_closure = run_loop.QuitClosure();
- loop->PostDelayedTask(
- FROM_HERE,
- request->quit_closure,
- base::TimeDelta::FromMilliseconds(kMaxWaitTimeForClipboardResponse));
-
run_loop.Run();
+
+ // We cannot put logic to process the next request here because the RunLoop
+ // might be nested. For instance, request 'B' may start a RunLoop while the
+ // RunLoop for request 'A' is running. It is not possible to end the RunLoop
+ // for request 'A' without first ending the RunLoop for request 'B'.
} else {
// This occurs if PerformBlockingConvertSelection() is called during
// shutdown and the PlatformEventSource has already been destroyed.
- base::TimeTicks start = base::TimeTicks::Now();
- while (!request->returned) {
+ while (!request->completed &&
+ request->timeout > base::TimeTicks::Now()) {
if (XPending(x_display_)) {
XEvent event;
XNextEvent(x_display_, &event);
dispatcher_->DispatchEvent(&event);
}
- base::TimeDelta wait_time = base::TimeTicks::Now() - start;
- if (wait_time.InMilliseconds() > kMaxWaitTimeForClipboardResponse)
- break;
}
}
+}
- DCHECK(!pending_requests_.empty());
- DCHECK_EQ(request, pending_requests_.back());
- pending_requests_.pop_back();
+SelectionRequestor::Request* SelectionRequestor::GetCurrentRequest() {
+ return current_request_index_ == requests_.size() ?
+ NULL : requests_[current_request_index_];
}
-SelectionRequestor::PendingRequest::PendingRequest(XAtom target)
- : target(target),
- returned_property(None),
- returned(false) {
+SelectionRequestor::Request::Request(XAtom selection,
+ XAtom target,
+ base::TimeTicks timeout)
+ : selection(selection),
+ target(target),
+ out_data_items(0u),
+ out_type(None),
+ success(false),
+ timeout(timeout),
+ completed(false) {
}
-SelectionRequestor::PendingRequest::~PendingRequest() {
+SelectionRequestor::Request::~Request() {
}
} // namespace ui

Powered by Google App Engine
This is Rietveld 408576698