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