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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ui/base/x/selection_requestor.h" 5 #include "ui/base/x/selection_requestor.h"
6 6
7 #include <algorithm>
7 #include <X11/Xlib.h> 8 #include <X11/Xlib.h>
8 9
9 #include "base/run_loop.h" 10 #include "base/run_loop.h"
10 #include "ui/base/x/selection_utils.h" 11 #include "ui/base/x/selection_utils.h"
11 #include "ui/base/x/x11_util.h" 12 #include "ui/base/x/x11_util.h"
12 #include "ui/events/platform/platform_event_dispatcher.h" 13 #include "ui/events/platform/platform_event_dispatcher.h"
13 #include "ui/events/platform/platform_event_source.h" 14 #include "ui/events/platform/platform_event_source.h"
14 #include "ui/gfx/x/x11_types.h" 15 #include "ui/gfx/x/x11_types.h"
15 16
16 namespace ui { 17 namespace ui {
17 18
18 namespace { 19 namespace {
19 20
20 const char kChromeSelection[] = "CHROME_SELECTION"; 21 const char kChromeSelection[] = "CHROME_SELECTION";
21 22
22 const char* kAtomsToCache[] = { 23 const char* kAtomsToCache[] = {
23 kChromeSelection, 24 kChromeSelection,
24 NULL 25 NULL
25 }; 26 };
26 27
28 // The amount of time to wait for a request to complete.
29 const int kRequestTimeoutMs = 300;
30
27 } // namespace 31 } // namespace
28 32
29 SelectionRequestor::SelectionRequestor(XDisplay* x_display, 33 SelectionRequestor::SelectionRequestor(XDisplay* x_display,
30 XID x_window, 34 XID x_window,
31 XAtom selection_name,
32 PlatformEventDispatcher* dispatcher) 35 PlatformEventDispatcher* dispatcher)
33 : x_display_(x_display), 36 : x_display_(x_display),
34 x_window_(x_window), 37 x_window_(x_window),
35 selection_name_(selection_name), 38 x_property_(None),
36 dispatcher_(dispatcher), 39 dispatcher_(dispatcher),
40 current_request_index_(0u),
37 atom_cache_(x_display_, kAtomsToCache) { 41 atom_cache_(x_display_, kAtomsToCache) {
42 x_property_ = atom_cache_.GetAtom(kChromeSelection);
38 } 43 }
39 44
40 SelectionRequestor::~SelectionRequestor() {} 45 SelectionRequestor::~SelectionRequestor() {}
41 46
42 bool SelectionRequestor::PerformBlockingConvertSelection( 47 bool SelectionRequestor::PerformBlockingConvertSelection(
48 XAtom selection,
43 XAtom target, 49 XAtom target,
44 scoped_refptr<base::RefCountedMemory>* out_data, 50 scoped_refptr<base::RefCountedMemory>* out_data,
45 size_t* out_data_items, 51 size_t* out_data_items,
46 XAtom* out_type) { 52 XAtom* out_type) {
47 // The name of the property that we are either: 53 base::TimeTicks timeout =
48 // - Passing as a parameter with the XConvertSelection() request. 54 base::TimeTicks::Now() +
49 // OR 55 base::TimeDelta::FromMilliseconds(kRequestTimeoutMs);
50 // - Asking the selection owner to set on |x_window_|. 56 Request request(selection, target, timeout);
51 XAtom property = atom_cache_.GetAtom(kChromeSelection); 57 requests_.push_back(&request);
58 if (requests_.size() == 1u)
59 ConvertSelectionForCurrentRequest();
60 BlockTillSelectionNotifyForRequest(&request);
52 61
53 XConvertSelection(x_display_, 62 std::vector<Request*>::iterator request_it = std::find(
54 selection_name_, 63 requests_.begin(), requests_.end(), &request);
55 target, 64 CHECK(request_it != requests_.end());
56 property, 65 if (static_cast<int>(current_request_index_) >
57 x_window_, 66 request_it - requests_.begin()) {
58 CurrentTime); 67 --current_request_index_;
68 }
69 requests_.erase(request_it);
59 70
60 // Now that we've thrown our message off to the X11 server, we block waiting 71 if (requests_.empty())
61 // for a response. 72 abort_timer_.Stop();
62 PendingRequest pending_request(target);
63 BlockTillSelectionNotifyForRequest(&pending_request);
64 73
65 bool success = false; 74 if (out_data)
66 if (pending_request.returned_property == property) { 75 *out_data = request.out_data;
67 success = ui::GetRawBytesOfProperty(x_window_, 76 if (out_data_items)
68 pending_request.returned_property, 77 *out_data_items = request.out_data_items;
69 out_data, out_data_items, out_type); 78 if (out_type)
70 } 79 *out_type = request.out_type;
71 if (pending_request.returned_property != None) 80 return request.success;
72 XDeleteProperty(x_display_, x_window_, pending_request.returned_property);
73 return success;
74 } 81 }
75 82
76 void SelectionRequestor::PerformBlockingConvertSelectionWithParameter( 83 void SelectionRequestor::PerformBlockingConvertSelectionWithParameter(
84 XAtom selection,
77 XAtom target, 85 XAtom target,
78 const std::vector<XAtom>& parameter) { 86 const std::vector<XAtom>& parameter) {
79 SetAtomArrayProperty(x_window_, kChromeSelection, "ATOM", parameter); 87 SetAtomArrayProperty(x_window_, kChromeSelection, "ATOM", parameter);
80 PerformBlockingConvertSelection(target, NULL, NULL, NULL); 88 PerformBlockingConvertSelection(selection, target, NULL, NULL, NULL);
81 } 89 }
82 90
83 SelectionData SelectionRequestor::RequestAndWaitForTypes( 91 SelectionData SelectionRequestor::RequestAndWaitForTypes(
92 XAtom selection,
84 const std::vector<XAtom>& types) { 93 const std::vector<XAtom>& types) {
85 for (std::vector<XAtom>::const_iterator it = types.begin(); 94 for (std::vector<XAtom>::const_iterator it = types.begin();
86 it != types.end(); ++it) { 95 it != types.end(); ++it) {
87 scoped_refptr<base::RefCountedMemory> data; 96 scoped_refptr<base::RefCountedMemory> data;
88 XAtom type = None; 97 XAtom type = None;
89 if (PerformBlockingConvertSelection(*it, 98 if (PerformBlockingConvertSelection(selection,
99 *it,
90 &data, 100 &data,
91 NULL, 101 NULL,
92 &type) && 102 &type) &&
93 type == *it) { 103 type == *it) {
94 return SelectionData(type, data); 104 return SelectionData(type, data);
95 } 105 }
96 } 106 }
97 107
98 return SelectionData(); 108 return SelectionData();
99 } 109 }
100 110
101 void SelectionRequestor::OnSelectionNotify(const XEvent& event) { 111 void SelectionRequestor::OnSelectionNotify(const XEvent& event) {
102 // Find the PendingRequest for the corresponding XConvertSelection call. If 112 Request* request = GetCurrentRequest();
103 // there are multiple pending requests on the same target, satisfy them in 113 XAtom event_property = event.xselection.property;
104 // FIFO order. 114 if (!request ||
105 PendingRequest* request_notified = NULL; 115 request->completed ||
106 if (selection_name_ == event.xselection.selection) { 116 request->selection != event.xselection.selection ||
107 for (std::list<PendingRequest*>::iterator iter = pending_requests_.begin(); 117 request->target != event.xselection.target) {
108 iter != pending_requests_.end(); ++iter) { 118 // ICCCM requires us to delete the property passed into SelectionNotify.
109 PendingRequest* request = *iter; 119 if (event_property != None)
110 if (request->returned) 120 XDeleteProperty(x_display_, x_window_, event_property);
111 continue;
112 if (request->target != event.xselection.target)
113 continue;
114 request_notified = request;
115 break;
116 }
117 }
118
119 // This event doesn't correspond to any XConvertSelection calls that we
120 // issued in PerformBlockingConvertSelection. This shouldn't happen, but any
121 // client can send any message, so it can happen.
122 XAtom returned_property = event.xselection.property;
123 if (!request_notified) {
124 // ICCCM requires us to delete the property passed into SelectionNotify. If
125 // |request_notified| is true, the property will be deleted when the run
126 // loop has quit.
127 if (returned_property != None)
128 XDeleteProperty(x_display_, x_window_, returned_property);
129 return; 121 return;
130 } 122 }
131 123
132 request_notified->returned_property = returned_property; 124 request->success = false;
133 request_notified->returned = true; 125 if (event_property == x_property_) {
126 request->success = ui::GetRawBytesOfProperty(x_window_,
127 x_property_,
128 &request->out_data,
129 &request->out_data_items,
130 &request->out_type);
131 }
132 if (event_property != None)
133 XDeleteProperty(x_display_, x_window_, event_property);
134 134
135 if (!request_notified->quit_closure.is_null()) 135 CompleteRequest(current_request_index_);
136 request_notified->quit_closure.Run();
137 } 136 }
138 137
139 void SelectionRequestor::BlockTillSelectionNotifyForRequest( 138 void SelectionRequestor::AbortStaleRequests() {
140 PendingRequest* request) { 139 base::TimeTicks now = base::TimeTicks::Now();
141 pending_requests_.push_back(request); 140 for (size_t i = 0; i < requests_.size(); ++i) {
141 if (requests_[i]->timeout <= now)
142 CompleteRequest(i);
143 }
144 }
142 145
143 const int kMaxWaitTimeForClipboardResponse = 300; 146 void SelectionRequestor::CompleteRequest(size_t index) {
147 if (index >= requests_.size())
148 return;
149
150 Request* request = requests_[index];
151 if (request->completed)
152 return;
153 request->completed = true;
154
155 if (index == current_request_index_) {
156 ++current_request_index_;
157 ConvertSelectionForCurrentRequest();
158 }
159
160 if (!request->quit_closure.is_null())
161 request->quit_closure.Run();
162 }
163
164 void SelectionRequestor::ConvertSelectionForCurrentRequest() {
165 Request* request = GetCurrentRequest();
166 if (request) {
167 XConvertSelection(x_display_,
168 request->selection,
169 request->target,
170 x_property_,
171 x_window_,
172 CurrentTime);
173 }
174 }
175
176 void SelectionRequestor::BlockTillSelectionNotifyForRequest(Request* request) {
144 if (PlatformEventSource::GetInstance()) { 177 if (PlatformEventSource::GetInstance()) {
145 base::MessageLoopForUI* loop = base::MessageLoopForUI::current(); 178 if (!abort_timer_.IsRunning()) {
146 base::MessageLoop::ScopedNestableTaskAllower allow_nested(loop); 179 abort_timer_.Start(FROM_HERE,
180 base::TimeDelta::FromMilliseconds(kRequestTimeoutMs),
181 this,
182 &SelectionRequestor::AbortStaleRequests);
183 }
184
185 base::MessageLoop::ScopedNestableTaskAllower allow_nested(
186 base::MessageLoopForUI::current());
147 base::RunLoop run_loop; 187 base::RunLoop run_loop;
188 request->quit_closure = run_loop.QuitClosure();
189 run_loop.Run();
148 190
149 request->quit_closure = run_loop.QuitClosure(); 191 // We cannot put logic to process the next request here because the RunLoop
150 loop->PostDelayedTask( 192 // might be nested. For instance, request 'B' may start a RunLoop while the
151 FROM_HERE, 193 // RunLoop for request 'A' is running. It is not possible to end the RunLoop
152 request->quit_closure, 194 // for request 'A' without first ending the RunLoop for request 'B'.
153 base::TimeDelta::FromMilliseconds(kMaxWaitTimeForClipboardResponse));
154
155 run_loop.Run();
156 } else { 195 } else {
157 // This occurs if PerformBlockingConvertSelection() is called during 196 // This occurs if PerformBlockingConvertSelection() is called during
158 // shutdown and the PlatformEventSource has already been destroyed. 197 // shutdown and the PlatformEventSource has already been destroyed.
159 base::TimeTicks start = base::TimeTicks::Now(); 198 while (!request->completed &&
160 while (!request->returned) { 199 request->timeout > base::TimeTicks::Now()) {
161 if (XPending(x_display_)) { 200 if (XPending(x_display_)) {
162 XEvent event; 201 XEvent event;
163 XNextEvent(x_display_, &event); 202 XNextEvent(x_display_, &event);
164 dispatcher_->DispatchEvent(&event); 203 dispatcher_->DispatchEvent(&event);
165 } 204 }
166 base::TimeDelta wait_time = base::TimeTicks::Now() - start;
167 if (wait_time.InMilliseconds() > kMaxWaitTimeForClipboardResponse)
168 break;
169 } 205 }
170 } 206 }
171
172 DCHECK(!pending_requests_.empty());
173 DCHECK_EQ(request, pending_requests_.back());
174 pending_requests_.pop_back();
175 } 207 }
176 208
177 SelectionRequestor::PendingRequest::PendingRequest(XAtom target) 209 SelectionRequestor::Request* SelectionRequestor::GetCurrentRequest() {
178 : target(target), 210 return current_request_index_ == requests_.size() ?
179 returned_property(None), 211 NULL : requests_[current_request_index_];
180 returned(false) {
181 } 212 }
182 213
183 SelectionRequestor::PendingRequest::~PendingRequest() { 214 SelectionRequestor::Request::Request(XAtom selection,
215 XAtom target,
216 base::TimeTicks timeout)
217 : selection(selection),
218 target(target),
219 out_data_items(0u),
220 out_type(None),
221 success(false),
222 timeout(timeout),
223 completed(false) {
224 }
225
226 SelectionRequestor::Request::~Request() {
184 } 227 }
185 228
186 } // namespace ui 229 } // namespace ui
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698