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