OLD | NEW |
---|---|
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 <algorithm> |
8 #include <X11/Xlib.h> | 8 #include <X11/Xlib.h> |
9 | 9 |
10 #include "base/run_loop.h" | 10 #include "base/run_loop.h" |
11 #include "ui/base/x/selection_utils.h" | 11 #include "ui/base/x/selection_utils.h" |
12 #include "ui/base/x/x11_util.h" | 12 #include "ui/base/x/x11_util.h" |
13 #include "ui/events/platform/platform_event_dispatcher.h" | 13 #include "ui/events/platform/platform_event_dispatcher.h" |
14 #include "ui/events/platform/platform_event_source.h" | 14 #include "ui/events/platform/platform_event_source.h" |
15 #include "ui/gfx/x/x11_types.h" | 15 #include "ui/gfx/x/x11_types.h" |
16 | 16 |
17 namespace ui { | 17 namespace ui { |
18 | 18 |
19 namespace { | 19 namespace { |
20 | 20 |
21 const char kChromeSelection[] = "CHROME_SELECTION"; | 21 const char kChromeSelection[] = "CHROME_SELECTION"; |
22 const char kIncr[] = "INCR"; | |
22 | 23 |
23 const char* kAtomsToCache[] = { | 24 const char* kAtomsToCache[] = { |
24 kChromeSelection, | 25 kChromeSelection, |
26 kIncr, | |
25 NULL | 27 NULL |
26 }; | 28 }; |
27 | 29 |
28 // The amount of time to wait for a request to complete. | 30 // The period of |abort_timer_|. Arbitrary but must be <= than |
29 const int kRequestTimeoutMs = 300; | 31 // kRequestTimeoutMs. |
32 const int kTimerPeriodMs = 100; | |
33 | |
34 // The amount of time to wait for a request to complete before aborting it. | |
35 const int kRequestTimeoutMs = 10000; | |
36 | |
37 COMPILE_ASSERT(kTimerPeriodMs <= kRequestTimeoutMs, | |
38 timer_period_must_be_less_or_equal_to_request_timeout); | |
39 | |
40 // Combines |data| into a single RefCountedMemory object. | |
41 scoped_refptr<base::RefCountedMemory> CombineRefCountedMemory( | |
42 const std::vector<scoped_refptr<base::RefCountedMemory> >& data) { | |
43 if (data.size() == 1u) | |
44 return data[0]; | |
45 | |
46 size_t length = 0; | |
47 for (size_t i = 0; i < data.size(); ++i) | |
48 length += data[i]->size(); | |
49 std::vector<unsigned char> combined_data; | |
50 combined_data.reserve(length); | |
51 | |
52 for (size_t i = 0; i < data.size(); ++i) { | |
53 combined_data.insert(combined_data.end(), | |
54 data[i]->front(), | |
55 data[i]->front() + data[i]->size()); | |
56 } | |
57 return scoped_refptr<base::RefCountedMemory>( | |
58 base::RefCountedBytes::TakeVector(&combined_data)); | |
59 } | |
30 | 60 |
31 } // namespace | 61 } // namespace |
32 | 62 |
33 SelectionRequestor::SelectionRequestor(XDisplay* x_display, | 63 SelectionRequestor::SelectionRequestor(XDisplay* x_display, |
34 XID x_window, | 64 XID x_window, |
35 PlatformEventDispatcher* dispatcher) | 65 PlatformEventDispatcher* dispatcher) |
36 : x_display_(x_display), | 66 : x_display_(x_display), |
37 x_window_(x_window), | 67 x_window_(x_window), |
38 x_property_(None), | 68 x_property_(None), |
39 dispatcher_(dispatcher), | 69 dispatcher_(dispatcher), |
(...skipping 26 matching lines...) Expand all Loading... | |
66 request_it - requests_.begin()) { | 96 request_it - requests_.begin()) { |
67 --current_request_index_; | 97 --current_request_index_; |
68 } | 98 } |
69 requests_.erase(request_it); | 99 requests_.erase(request_it); |
70 | 100 |
71 if (requests_.empty()) | 101 if (requests_.empty()) |
72 abort_timer_.Stop(); | 102 abort_timer_.Stop(); |
73 | 103 |
74 if (request.success) { | 104 if (request.success) { |
75 if (out_data) | 105 if (out_data) |
76 *out_data = request.out_data; | 106 *out_data = CombineRefCountedMemory(request.out_data); |
77 if (out_data_items) | 107 if (out_data_items) |
78 *out_data_items = request.out_data_items; | 108 *out_data_items = request.out_data_items; |
79 if (out_type) | 109 if (out_type) |
80 *out_type = request.out_type; | 110 *out_type = request.out_type; |
81 } | 111 } |
82 return request.success; | 112 return request.success; |
83 } | 113 } |
84 | 114 |
85 void SelectionRequestor::PerformBlockingConvertSelectionWithParameter( | 115 void SelectionRequestor::PerformBlockingConvertSelectionWithParameter( |
86 XAtom selection, | 116 XAtom selection, |
(...skipping 29 matching lines...) Expand all Loading... | |
116 if (!request || | 146 if (!request || |
117 request->completed || | 147 request->completed || |
118 request->selection != event.xselection.selection || | 148 request->selection != event.xselection.selection || |
119 request->target != event.xselection.target) { | 149 request->target != event.xselection.target) { |
120 // ICCCM requires us to delete the property passed into SelectionNotify. | 150 // ICCCM requires us to delete the property passed into SelectionNotify. |
121 if (event_property != None) | 151 if (event_property != None) |
122 XDeleteProperty(x_display_, x_window_, event_property); | 152 XDeleteProperty(x_display_, x_window_, event_property); |
123 return; | 153 return; |
124 } | 154 } |
125 | 155 |
126 request->success = false; | 156 bool success = false; |
127 if (event_property == x_property_) { | 157 if (event_property == x_property_) { |
128 request->success = ui::GetRawBytesOfProperty(x_window_, | 158 scoped_refptr<base::RefCountedMemory> out_data; |
129 x_property_, | 159 success = ui::GetRawBytesOfProperty(x_window_, |
130 &request->out_data, | 160 x_property_, |
131 &request->out_data_items, | 161 &out_data, |
132 &request->out_type); | 162 &request->out_data_items, |
163 &request->out_type); | |
164 if (success) | |
165 request->out_data.push_back(out_data); | |
133 } | 166 } |
134 if (event_property != None) | 167 if (event_property != None) |
135 XDeleteProperty(x_display_, x_window_, event_property); | 168 XDeleteProperty(x_display_, x_window_, event_property); |
136 | 169 |
137 CompleteRequest(current_request_index_); | 170 if (request->out_type == atom_cache_.GetAtom(kIncr)) { |
pkotwicz
2014/07/26 20:09:53
I now clear request->out_data and request->out_dat
| |
171 request->data_sent_incrementally = true; | |
172 request->out_data.clear(); | |
173 request->out_data_items = 0u; | |
174 request->out_type = None; | |
175 request->timeout = base::TimeTicks::Now() + | |
176 base::TimeDelta::FromMilliseconds(kRequestTimeoutMs); | |
177 } else { | |
178 CompleteRequest(current_request_index_, success); | |
179 } | |
180 } | |
181 | |
182 bool SelectionRequestor::CanDispatchPropertyEvent(const XEvent& event) { | |
183 return event.xproperty.window == x_window_ && | |
184 event.xproperty.atom == x_property_ && | |
185 event.xproperty.state == PropertyNewValue; | |
186 } | |
187 | |
188 void SelectionRequestor::OnPropertyEvent(const XEvent& event) { | |
189 Request* request = GetCurrentRequest(); | |
190 if (!request || !request->data_sent_incrementally) | |
pkotwicz
2014/07/26 20:09:53
I now check request->data_sent_incrementally
This
| |
191 return; | |
192 | |
193 scoped_refptr<base::RefCountedMemory> out_data; | |
194 size_t out_data_items = 0u; | |
195 Atom out_type = None; | |
196 bool success = ui::GetRawBytesOfProperty(x_window_, | |
197 x_property_, | |
198 &out_data, | |
199 &out_data_items, | |
200 &out_type); | |
201 if (!success) { | |
202 CompleteRequest(current_request_index_, false); | |
203 return; | |
204 } | |
205 | |
206 if (request->out_type != None && request->out_type != out_type) { | |
207 CompleteRequest(current_request_index_, false); | |
208 return; | |
209 } | |
210 | |
211 request->out_data.push_back(out_data); | |
212 request->out_data_items += out_data_items; | |
213 request->out_type = out_type; | |
214 | |
215 // Delete the property to tell the selection owner to send the next chunk. | |
216 XDeleteProperty(x_display_, x_window_, x_property_); | |
217 | |
218 request->timeout = base::TimeTicks::Now() + | |
219 base::TimeDelta::FromMilliseconds(kRequestTimeoutMs); | |
220 | |
221 if (out_data->size() == 0u) | |
222 CompleteRequest(current_request_index_, true); | |
138 } | 223 } |
139 | 224 |
140 void SelectionRequestor::AbortStaleRequests() { | 225 void SelectionRequestor::AbortStaleRequests() { |
141 base::TimeTicks now = base::TimeTicks::Now(); | 226 base::TimeTicks now = base::TimeTicks::Now(); |
142 for (size_t i = current_request_index_; | 227 for (size_t i = current_request_index_; i < requests_.size(); ++i) { |
143 i < requests_.size() && requests_[i]->timeout <= now; | 228 if (requests_[i]->timeout <= now) |
144 ++i) { | 229 CompleteRequest(i, false); |
145 CompleteRequest(i); | |
146 } | 230 } |
147 } | 231 } |
148 | 232 |
149 void SelectionRequestor::CompleteRequest(size_t index) { | 233 void SelectionRequestor::CompleteRequest(size_t index, bool success) { |
150 if (index >= requests_.size()) | 234 if (index >= requests_.size()) |
151 return; | 235 return; |
152 | 236 |
153 Request* request = requests_[index]; | 237 Request* request = requests_[index]; |
154 if (request->completed) | 238 if (request->completed) |
155 return; | 239 return; |
240 request->success = success; | |
156 request->completed = true; | 241 request->completed = true; |
157 | 242 |
158 if (index == current_request_index_) { | 243 if (index == current_request_index_) { |
159 ++current_request_index_; | 244 while (GetCurrentRequest() && GetCurrentRequest()->completed) |
pkotwicz
2014/07/26 20:09:53
It is now possible for the timeouts not to be in i
| |
245 ++current_request_index_; | |
160 ConvertSelectionForCurrentRequest(); | 246 ConvertSelectionForCurrentRequest(); |
161 } | 247 } |
162 | 248 |
163 if (!request->quit_closure.is_null()) | 249 if (!request->quit_closure.is_null()) |
164 request->quit_closure.Run(); | 250 request->quit_closure.Run(); |
165 } | 251 } |
166 | 252 |
167 void SelectionRequestor::ConvertSelectionForCurrentRequest() { | 253 void SelectionRequestor::ConvertSelectionForCurrentRequest() { |
168 Request* request = GetCurrentRequest(); | 254 Request* request = GetCurrentRequest(); |
169 if (request) { | 255 if (request) { |
170 XConvertSelection(x_display_, | 256 XConvertSelection(x_display_, |
171 request->selection, | 257 request->selection, |
172 request->target, | 258 request->target, |
173 x_property_, | 259 x_property_, |
174 x_window_, | 260 x_window_, |
175 CurrentTime); | 261 CurrentTime); |
176 } | 262 } |
177 } | 263 } |
178 | 264 |
179 void SelectionRequestor::BlockTillSelectionNotifyForRequest(Request* request) { | 265 void SelectionRequestor::BlockTillSelectionNotifyForRequest(Request* request) { |
180 if (PlatformEventSource::GetInstance()) { | 266 if (PlatformEventSource::GetInstance()) { |
181 if (!abort_timer_.IsRunning()) { | 267 if (!abort_timer_.IsRunning()) { |
182 abort_timer_.Start(FROM_HERE, | 268 abort_timer_.Start(FROM_HERE, |
183 base::TimeDelta::FromMilliseconds(kRequestTimeoutMs), | 269 base::TimeDelta::FromMilliseconds(kTimerPeriodMs), |
184 this, | 270 this, |
185 &SelectionRequestor::AbortStaleRequests); | 271 &SelectionRequestor::AbortStaleRequests); |
186 } | 272 } |
187 | 273 |
188 base::MessageLoop::ScopedNestableTaskAllower allow_nested( | 274 base::MessageLoop::ScopedNestableTaskAllower allow_nested( |
189 base::MessageLoopForUI::current()); | 275 base::MessageLoopForUI::current()); |
190 base::RunLoop run_loop; | 276 base::RunLoop run_loop; |
191 request->quit_closure = run_loop.QuitClosure(); | 277 request->quit_closure = run_loop.QuitClosure(); |
192 run_loop.Run(); | 278 run_loop.Run(); |
193 | 279 |
(...skipping 18 matching lines...) Expand all Loading... | |
212 SelectionRequestor::Request* SelectionRequestor::GetCurrentRequest() { | 298 SelectionRequestor::Request* SelectionRequestor::GetCurrentRequest() { |
213 return current_request_index_ == requests_.size() ? | 299 return current_request_index_ == requests_.size() ? |
214 NULL : requests_[current_request_index_]; | 300 NULL : requests_[current_request_index_]; |
215 } | 301 } |
216 | 302 |
217 SelectionRequestor::Request::Request(XAtom selection, | 303 SelectionRequestor::Request::Request(XAtom selection, |
218 XAtom target, | 304 XAtom target, |
219 base::TimeTicks timeout) | 305 base::TimeTicks timeout) |
220 : selection(selection), | 306 : selection(selection), |
221 target(target), | 307 target(target), |
308 data_sent_incrementally(false), | |
222 out_data_items(0u), | 309 out_data_items(0u), |
223 out_type(None), | 310 out_type(None), |
224 success(false), | 311 success(false), |
225 timeout(timeout), | 312 timeout(timeout), |
226 completed(false) { | 313 completed(false) { |
227 } | 314 } |
228 | 315 |
229 SelectionRequestor::Request::~Request() { | 316 SelectionRequestor::Request::~Request() { |
230 } | 317 } |
231 | 318 |
232 } // namespace ui | 319 } // namespace ui |
OLD | NEW |