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.
4
5 #include "ui/base/x/selection_requestor.h"
6
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"
11
12 namespace ui {
13
14 namespace {
15
16 const char kChromeSelection[] = "CHROME_SELECTION";
17
18 const char* kAtomsToCache[] = {
19 kChromeSelection,
20 NULL
21 };
22
23 } // namespace
24
SelectionRequestor(Display * x_display,Window x_window,Atom selection_name)25 SelectionRequestor::SelectionRequestor(Display* x_display,
26 Window x_window,
27 Atom selection_name)
28 : x_display_(x_display),
29 x_window_(x_window),
30 selection_name_(selection_name),
31 atom_cache_(x_display_, kAtomsToCache) {
32 }
33
~SelectionRequestor()34 SelectionRequestor::~SelectionRequestor() {}
35
PerformBlockingConvertSelection(Atom target,scoped_refptr<base::RefCountedMemory> * out_data,size_t * out_data_bytes,size_t * out_data_items,Atom * out_type)36 bool SelectionRequestor::PerformBlockingConvertSelection(
37 Atom target,
38 scoped_refptr<base::RefCountedMemory>* out_data,
39 size_t* out_data_bytes,
40 size_t* out_data_items,
41 Atom* out_type) {
42 // The name of the property we're asking to be set on |x_window_|.
43 Atom property_to_set = atom_cache_.GetAtom(kChromeSelection);
44
45 XConvertSelection(x_display_,
46 selection_name_,
47 target,
48 property_to_set,
49 x_window_,
50 CurrentTime);
51
52 // Now that we've thrown our message off to the X11 server, we block waiting
53 // for a response.
54 base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
55 base::MessageLoop::ScopedNestableTaskAllower allow_nested(loop);
56 base::RunLoop run_loop(base::MessagePumpX11::Current());
57
58 PendingRequest pending_request(target, run_loop.QuitClosure());
59 pending_requests_.push_back(&pending_request);
60 run_loop.Run();
61 DCHECK(!pending_requests_.empty());
62 DCHECK_EQ(&pending_request, pending_requests_.back());
63 pending_requests_.pop_back();
64
65 if (pending_request.returned_property != property_to_set)
66 return false;
67
68 return ui::GetRawBytesOfProperty(x_window_, pending_request.returned_property,
69 out_data, out_data_bytes, out_data_items,
70 out_type);
71 }
72
RequestAndWaitForTypes(const std::vector<::Atom> & types)73 SelectionData SelectionRequestor::RequestAndWaitForTypes(
74 const std::vector< ::Atom>& types) {
75 for (std::vector< ::Atom>::const_iterator it = types.begin();
76 it != types.end(); ++it) {
77 scoped_refptr<base::RefCountedMemory> data;
78 size_t data_bytes = 0;
79 ::Atom type = None;
80 if (PerformBlockingConvertSelection(*it,
81 &data,
82 &data_bytes,
83 NULL,
84 &type) &&
85 type == *it) {
86 return SelectionData(type, data);
87 }
88 }
89
90 return SelectionData();
91 }
92
OnSelectionNotify(const XSelectionEvent & event)93 void SelectionRequestor::OnSelectionNotify(const XSelectionEvent& event) {
94 // Find the PendingRequest for the corresponding XConvertSelection call. If
95 // there are multiple pending requests on the same target, satisfy them in
96 // FIFO order.
97 PendingRequest* request_notified = NULL;
98 if (selection_name_ == event.selection) {
99 for (std::list<PendingRequest*>::iterator iter = pending_requests_.begin();
100 iter != pending_requests_.end(); ++iter) {
101 PendingRequest* request = *iter;
102 if (request->returned)
103 continue;
104 if (request->target != event.target)
105 continue;
106 request_notified = request;
107 break;
108 }
109 }
110
111 // This event doesn't correspond to any XConvertSelection calls that we
112 // issued in PerformBlockingConvertSelection. This shouldn't happen, but any
113 // client can send any message, so it can happen.
114 if (!request_notified)
115 return;
116
117 request_notified->returned_property = event.property;
118 request_notified->returned = true;
119 request_notified->quit_closure.Run();
120 }
121
PendingRequest(Atom target,base::Closure quit_closure)122 SelectionRequestor::PendingRequest::PendingRequest(Atom target,
123 base::Closure quit_closure)
124 : target(target),
125 quit_closure(quit_closure),
126 returned_property(None),
127 returned(false) {
128 }
129
~PendingRequest()130 SelectionRequestor::PendingRequest::~PendingRequest() {
131 }
132
133 } // namespace ui
134