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/run_loop.h"
8 #include "ui/base/x/selection_utils.h"
9 #include "ui/base/x/x11_util.h"
10 #include "ui/events/platform/platform_event_dispatcher.h"
11 #include "ui/events/platform/platform_event_source.h"
12 #include "ui/gfx/x/x11_types.h"
18 const char kChromeSelection
[] = "CHROME_SELECTION";
20 const char* kAtomsToCache
[] = {
27 SelectionRequestor::SelectionRequestor(Display
* x_display
,
30 PlatformEventDispatcher
* dispatcher
)
31 : x_display_(x_display
),
33 selection_name_(selection_name
),
34 dispatcher_(dispatcher
),
35 atom_cache_(x_display_
, kAtomsToCache
) {
38 SelectionRequestor::~SelectionRequestor() {}
40 bool SelectionRequestor::PerformBlockingConvertSelection(
42 scoped_refptr
<base::RefCountedMemory
>* out_data
,
43 size_t* out_data_bytes
,
44 size_t* out_data_items
,
46 // The name of the property that we are either:
47 // - Passing as a parameter with the XConvertSelection() request.
49 // - Asking the selection owner to set on |x_window_|.
50 Atom property
= atom_cache_
.GetAtom(kChromeSelection
);
52 XConvertSelection(x_display_
,
59 // Now that we've thrown our message off to the X11 server, we block waiting
61 PendingRequest
pending_request(target
);
62 BlockTillSelectionNotifyForRequest(&pending_request
);
65 if (pending_request
.returned_property
== property
) {
66 success
= ui::GetRawBytesOfProperty(x_window_
,
67 pending_request
.returned_property
,
68 out_data
, out_data_bytes
,
69 out_data_items
, out_type
);
71 if (pending_request
.returned_property
!= None
)
72 XDeleteProperty(x_display_
, x_window_
, pending_request
.returned_property
);
76 void SelectionRequestor::PerformBlockingConvertSelectionWithParameter(
78 const std::vector
< ::Atom
>& parameter
) {
79 SetAtomArrayProperty(x_window_
, kChromeSelection
, "ATOM", parameter
);
80 PerformBlockingConvertSelection(target
, NULL
, NULL
, NULL
, NULL
);
83 SelectionData
SelectionRequestor::RequestAndWaitForTypes(
84 const std::vector
< ::Atom
>& types
) {
85 for (std::vector
< ::Atom
>::const_iterator it
= types
.begin();
86 it
!= types
.end(); ++it
) {
87 scoped_refptr
<base::RefCountedMemory
> data
;
88 size_t data_bytes
= 0;
90 if (PerformBlockingConvertSelection(*it
,
96 return SelectionData(type
, data
);
100 return SelectionData();
103 void SelectionRequestor::OnSelectionNotify(const XSelectionEvent
& event
) {
104 // Find the PendingRequest for the corresponding XConvertSelection call. If
105 // there are multiple pending requests on the same target, satisfy them in
107 PendingRequest
* request_notified
= NULL
;
108 if (selection_name_
== event
.selection
) {
109 for (std::list
<PendingRequest
*>::iterator iter
= pending_requests_
.begin();
110 iter
!= pending_requests_
.end(); ++iter
) {
111 PendingRequest
* request
= *iter
;
112 if (request
->returned
)
114 if (request
->target
!= event
.target
)
116 request_notified
= request
;
121 // This event doesn't correspond to any XConvertSelection calls that we
122 // issued in PerformBlockingConvertSelection. This shouldn't happen, but any
123 // client can send any message, so it can happen.
124 if (!request_notified
) {
125 // ICCCM requires us to delete the property passed into SelectionNotify. If
126 // |request_notified| is true, the property will be deleted when the run
128 if (event
.property
!= None
)
129 XDeleteProperty(x_display_
, x_window_
, event
.property
);
133 request_notified
->returned_property
= event
.property
;
134 request_notified
->returned
= true;
136 if (!request_notified
->quit_closure
.is_null())
137 request_notified
->quit_closure
.Run();
140 void SelectionRequestor::BlockTillSelectionNotifyForRequest(
141 PendingRequest
* request
) {
142 pending_requests_
.push_back(request
);
144 const int kMaxWaitTimeForClipboardResponse
= 300;
145 if (PlatformEventSource::GetInstance()) {
146 base::MessageLoopForUI
* loop
= base::MessageLoopForUI::current();
147 base::MessageLoop::ScopedNestableTaskAllower
allow_nested(loop
);
148 base::RunLoop run_loop
;
150 request
->quit_closure
= run_loop
.QuitClosure();
151 loop
->PostDelayedTask(
153 request
->quit_closure
,
154 base::TimeDelta::FromMilliseconds(kMaxWaitTimeForClipboardResponse
));
158 // This occurs if PerformBlockingConvertSelection() is called during
159 // shutdown and the PlatformEventSource has already been destroyed.
160 base::TimeTicks start
= base::TimeTicks::Now();
161 while (!request
->returned
) {
162 if (XPending(x_display_
)) {
164 XNextEvent(x_display_
, &event
);
165 dispatcher_
->DispatchEvent(&event
);
167 base::TimeDelta wait_time
= base::TimeTicks::Now() - start
;
168 if (wait_time
.InMilliseconds() > kMaxWaitTimeForClipboardResponse
)
173 DCHECK(!pending_requests_
.empty());
174 DCHECK_EQ(request
, pending_requests_
.back());
175 pending_requests_
.pop_back();
178 SelectionRequestor::PendingRequest::PendingRequest(Atom target
)
180 returned_property(None
),
184 SelectionRequestor::PendingRequest::~PendingRequest() {