| 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), |
| 40 current_request_index_(0u), | 70 current_request_index_(0u), |
| 41 atom_cache_(x_display_, kAtomsToCache) { | 71 atom_cache_(x_display_, kAtomsToCache) { |
| 42 x_property_ = atom_cache_.GetAtom(kChromeSelection); | 72 x_property_ = atom_cache_.GetAtom(kChromeSelection); |
| 43 } | 73 } |
| 44 | 74 |
| 45 SelectionRequestor::~SelectionRequestor() {} | 75 SelectionRequestor::~SelectionRequestor() {} |
| 46 | 76 |
| 47 bool SelectionRequestor::PerformBlockingConvertSelection( | 77 bool SelectionRequestor::PerformBlockingConvertSelection( |
| 48 XAtom selection, | 78 XAtom selection, |
| 49 XAtom target, | 79 XAtom target, |
| 50 scoped_refptr<base::RefCountedMemory>* out_data, | 80 scoped_refptr<base::RefCountedMemory>* out_data, |
| 51 size_t* out_data_items, | 81 size_t* out_data_items, |
| 52 XAtom* out_type) { | 82 XAtom* out_type) { |
| 53 base::TimeTicks timeout = | 83 base::TimeTicks timeout = |
| 54 base::TimeTicks::Now() + | 84 base::TimeTicks::Now() + |
| 55 base::TimeDelta::FromMilliseconds(kRequestTimeoutMs); | 85 base::TimeDelta::FromMilliseconds(kRequestTimeoutMs); |
| 56 Request request(selection, target, timeout); | 86 Request request(selection, target, timeout); |
| 57 requests_.push_back(&request); | 87 requests_.push_back(&request); |
| 58 if (requests_.size() == 1u) | 88 if (current_request_index_ == (requests_.size() - 1)) |
| 59 ConvertSelectionForCurrentRequest(); | 89 ConvertSelectionForCurrentRequest(); |
| 60 BlockTillSelectionNotifyForRequest(&request); | 90 BlockTillSelectionNotifyForRequest(&request); |
| 61 | 91 |
| 62 std::vector<Request*>::iterator request_it = std::find( | 92 std::vector<Request*>::iterator request_it = std::find( |
| 63 requests_.begin(), requests_.end(), &request); | 93 requests_.begin(), requests_.end(), &request); |
| 64 CHECK(request_it != requests_.end()); | 94 CHECK(request_it != requests_.end()); |
| 65 if (static_cast<int>(current_request_index_) > | 95 if (static_cast<int>(current_request_index_) > |
| 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.clear(); |
| 166 request->out_data.push_back(out_data); |
| 167 } |
| 133 } | 168 } |
| 134 if (event_property != None) | 169 if (event_property != None) |
| 135 XDeleteProperty(x_display_, x_window_, event_property); | 170 XDeleteProperty(x_display_, x_window_, event_property); |
| 136 | 171 |
| 137 CompleteRequest(current_request_index_); | 172 if (request->out_type == atom_cache_.GetAtom(kIncr)) { |
| 173 request->data_sent_incrementally = true; |
| 174 request->out_data.clear(); |
| 175 request->out_data_items = 0u; |
| 176 request->out_type = None; |
| 177 request->timeout = base::TimeTicks::Now() + |
| 178 base::TimeDelta::FromMilliseconds(kRequestTimeoutMs); |
| 179 } else { |
| 180 CompleteRequest(current_request_index_, success); |
| 181 } |
| 182 } |
| 183 |
| 184 bool SelectionRequestor::CanDispatchPropertyEvent(const XEvent& event) { |
| 185 return event.xproperty.window == x_window_ && |
| 186 event.xproperty.atom == x_property_ && |
| 187 event.xproperty.state == PropertyNewValue; |
| 188 } |
| 189 |
| 190 void SelectionRequestor::OnPropertyEvent(const XEvent& event) { |
| 191 Request* request = GetCurrentRequest(); |
| 192 if (!request || !request->data_sent_incrementally) |
| 193 return; |
| 194 |
| 195 scoped_refptr<base::RefCountedMemory> out_data; |
| 196 size_t out_data_items = 0u; |
| 197 Atom out_type = None; |
| 198 bool success = ui::GetRawBytesOfProperty(x_window_, |
| 199 x_property_, |
| 200 &out_data, |
| 201 &out_data_items, |
| 202 &out_type); |
| 203 if (!success) { |
| 204 CompleteRequest(current_request_index_, false); |
| 205 return; |
| 206 } |
| 207 |
| 208 if (request->out_type != None && request->out_type != out_type) { |
| 209 CompleteRequest(current_request_index_, false); |
| 210 return; |
| 211 } |
| 212 |
| 213 request->out_data.push_back(out_data); |
| 214 request->out_data_items += out_data_items; |
| 215 request->out_type = out_type; |
| 216 |
| 217 // Delete the property to tell the selection owner to send the next chunk. |
| 218 XDeleteProperty(x_display_, x_window_, x_property_); |
| 219 |
| 220 request->timeout = base::TimeTicks::Now() + |
| 221 base::TimeDelta::FromMilliseconds(kRequestTimeoutMs); |
| 222 |
| 223 if (out_data->size() == 0u) |
| 224 CompleteRequest(current_request_index_, true); |
| 138 } | 225 } |
| 139 | 226 |
| 140 void SelectionRequestor::AbortStaleRequests() { | 227 void SelectionRequestor::AbortStaleRequests() { |
| 141 base::TimeTicks now = base::TimeTicks::Now(); | 228 base::TimeTicks now = base::TimeTicks::Now(); |
| 142 for (size_t i = current_request_index_; | 229 for (size_t i = current_request_index_; i < requests_.size(); ++i) { |
| 143 i < requests_.size() && requests_[i]->timeout <= now; | 230 if (requests_[i]->timeout <= now) |
| 144 ++i) { | 231 CompleteRequest(i, false); |
| 145 CompleteRequest(i); | |
| 146 } | 232 } |
| 147 } | 233 } |
| 148 | 234 |
| 149 void SelectionRequestor::CompleteRequest(size_t index) { | 235 void SelectionRequestor::CompleteRequest(size_t index, bool success) { |
| 150 if (index >= requests_.size()) | 236 if (index >= requests_.size()) |
| 151 return; | 237 return; |
| 152 | 238 |
| 153 Request* request = requests_[index]; | 239 Request* request = requests_[index]; |
| 154 if (request->completed) | 240 if (request->completed) |
| 155 return; | 241 return; |
| 242 request->success = success; |
| 156 request->completed = true; | 243 request->completed = true; |
| 157 | 244 |
| 158 if (index == current_request_index_) { | 245 if (index == current_request_index_) { |
| 159 ++current_request_index_; | 246 while (GetCurrentRequest() && GetCurrentRequest()->completed) |
| 247 ++current_request_index_; |
| 160 ConvertSelectionForCurrentRequest(); | 248 ConvertSelectionForCurrentRequest(); |
| 161 } | 249 } |
| 162 | 250 |
| 163 if (!request->quit_closure.is_null()) | 251 if (!request->quit_closure.is_null()) |
| 164 request->quit_closure.Run(); | 252 request->quit_closure.Run(); |
| 165 } | 253 } |
| 166 | 254 |
| 167 void SelectionRequestor::ConvertSelectionForCurrentRequest() { | 255 void SelectionRequestor::ConvertSelectionForCurrentRequest() { |
| 168 Request* request = GetCurrentRequest(); | 256 Request* request = GetCurrentRequest(); |
| 169 if (request) { | 257 if (request) { |
| 170 XConvertSelection(x_display_, | 258 XConvertSelection(x_display_, |
| 171 request->selection, | 259 request->selection, |
| 172 request->target, | 260 request->target, |
| 173 x_property_, | 261 x_property_, |
| 174 x_window_, | 262 x_window_, |
| 175 CurrentTime); | 263 CurrentTime); |
| 176 } | 264 } |
| 177 } | 265 } |
| 178 | 266 |
| 179 void SelectionRequestor::BlockTillSelectionNotifyForRequest(Request* request) { | 267 void SelectionRequestor::BlockTillSelectionNotifyForRequest(Request* request) { |
| 180 if (PlatformEventSource::GetInstance()) { | 268 if (PlatformEventSource::GetInstance()) { |
| 181 if (!abort_timer_.IsRunning()) { | 269 if (!abort_timer_.IsRunning()) { |
| 182 abort_timer_.Start(FROM_HERE, | 270 abort_timer_.Start(FROM_HERE, |
| 183 base::TimeDelta::FromMilliseconds(kRequestTimeoutMs), | 271 base::TimeDelta::FromMilliseconds(kTimerPeriodMs), |
| 184 this, | 272 this, |
| 185 &SelectionRequestor::AbortStaleRequests); | 273 &SelectionRequestor::AbortStaleRequests); |
| 186 } | 274 } |
| 187 | 275 |
| 188 base::MessageLoop::ScopedNestableTaskAllower allow_nested( | 276 base::MessageLoop::ScopedNestableTaskAllower allow_nested( |
| 189 base::MessageLoopForUI::current()); | 277 base::MessageLoopForUI::current()); |
| 190 base::RunLoop run_loop; | 278 base::RunLoop run_loop; |
| 191 request->quit_closure = run_loop.QuitClosure(); | 279 request->quit_closure = run_loop.QuitClosure(); |
| 192 run_loop.Run(); | 280 run_loop.Run(); |
| 193 | 281 |
| (...skipping 18 matching lines...) Expand all Loading... |
| 212 SelectionRequestor::Request* SelectionRequestor::GetCurrentRequest() { | 300 SelectionRequestor::Request* SelectionRequestor::GetCurrentRequest() { |
| 213 return current_request_index_ == requests_.size() ? | 301 return current_request_index_ == requests_.size() ? |
| 214 NULL : requests_[current_request_index_]; | 302 NULL : requests_[current_request_index_]; |
| 215 } | 303 } |
| 216 | 304 |
| 217 SelectionRequestor::Request::Request(XAtom selection, | 305 SelectionRequestor::Request::Request(XAtom selection, |
| 218 XAtom target, | 306 XAtom target, |
| 219 base::TimeTicks timeout) | 307 base::TimeTicks timeout) |
| 220 : selection(selection), | 308 : selection(selection), |
| 221 target(target), | 309 target(target), |
| 310 data_sent_incrementally(false), |
| 222 out_data_items(0u), | 311 out_data_items(0u), |
| 223 out_type(None), | 312 out_type(None), |
| 224 success(false), | 313 success(false), |
| 225 timeout(timeout), | 314 timeout(timeout), |
| 226 completed(false) { | 315 completed(false) { |
| 227 } | 316 } |
| 228 | 317 |
| 229 SelectionRequestor::Request::~Request() { | 318 SelectionRequestor::Request::~Request() { |
| 230 } | 319 } |
| 231 | 320 |
| 232 } // namespace ui | 321 } // namespace ui |
| OLD | NEW |