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
3 // found in the LICENSE file.
5 #include "ui/base/x/selection_requestor.h"
7 #include "base/message_loop/message_pump_x11.h"
8 #include "base/run_loop.h"
9 #include "ui/base/x/selection_utils.h"
10 #include "ui/base/x/x11_util.h"
16 const char kChromeSelection[] = "CHROME_SELECTION";
18 const char* kAtomsToCache[] = {
25 SelectionRequestor::SelectionRequestor(Display* x_display,
28 : x_display_(x_display),
30 selection_name_(selection_name),
31 atom_cache_(x_display_, kAtomsToCache) {
34 SelectionRequestor::~SelectionRequestor() {}
36 bool SelectionRequestor::PerformBlockingConvertSelection(
38 scoped_refptr<base::RefCountedMemory>* out_data,
39 size_t* out_data_bytes,
40 size_t* out_data_items,
42 // The name of the property we're asking to be set on |x_window_|.
43 Atom property_to_set = atom_cache_.GetAtom(kChromeSelection);
45 XConvertSelection(x_display_,
52 // Now that we've thrown our message off to the X11 server, we block waiting
54 base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
55 base::MessageLoop::ScopedNestableTaskAllower allow_nested(loop);
56 base::RunLoop run_loop;
58 // Stop waiting for a response after a certain amount of time.
59 const int kMaxWaitTimeForClipboardResponse = 300;
60 loop->PostDelayedTask(
62 run_loop.QuitClosure(),
63 base::TimeDelta::FromMilliseconds(kMaxWaitTimeForClipboardResponse));
65 PendingRequest pending_request(target, run_loop.QuitClosure());
66 pending_requests_.push_back(&pending_request);
68 DCHECK(!pending_requests_.empty());
69 DCHECK_EQ(&pending_request, pending_requests_.back());
70 pending_requests_.pop_back();
73 if (pending_request.returned_property == property_to_set) {
74 success = ui::GetRawBytesOfProperty(x_window_,
75 pending_request.returned_property,
76 out_data, out_data_bytes,
77 out_data_items, out_type);
79 if (pending_request.returned_property != None)
80 XDeleteProperty(x_display_, x_window_, pending_request.returned_property);
84 SelectionData SelectionRequestor::RequestAndWaitForTypes(
85 const std::vector< ::Atom>& types) {
86 for (std::vector< ::Atom>::const_iterator it = types.begin();
87 it != types.end(); ++it) {
88 scoped_refptr<base::RefCountedMemory> data;
89 size_t data_bytes = 0;
91 if (PerformBlockingConvertSelection(*it,
97 return SelectionData(type, data);
101 return SelectionData();
104 void SelectionRequestor::OnSelectionNotify(const XSelectionEvent& event) {
105 // Find the PendingRequest for the corresponding XConvertSelection call. If
106 // there are multiple pending requests on the same target, satisfy them in
108 PendingRequest* request_notified = NULL;
109 if (selection_name_ == event.selection) {
110 for (std::list<PendingRequest*>::iterator iter = pending_requests_.begin();
111 iter != pending_requests_.end(); ++iter) {
112 PendingRequest* request = *iter;
113 if (request->returned)
115 if (request->target != event.target)
117 request_notified = request;
122 // This event doesn't correspond to any XConvertSelection calls that we
123 // issued in PerformBlockingConvertSelection. This shouldn't happen, but any
124 // client can send any message, so it can happen.
125 if (!request_notified) {
126 // ICCCM requires us to delete the property passed into SelectionNotify. If
127 // |request_notified| is true, the property will be deleted when the run
129 if (event.property != None)
130 XDeleteProperty(x_display_, x_window_, event.property);
134 request_notified->returned_property = event.property;
135 request_notified->returned = true;
136 request_notified->quit_closure.Run();
139 SelectionRequestor::PendingRequest::PendingRequest(Atom target,
140 base::Closure quit_closure)
142 quit_closure(quit_closure),
143 returned_property(None),
147 SelectionRequestor::PendingRequest::~PendingRequest() {