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

Side by Side Diff: ui/base/x/selection_owner.cc

Issue 397223002: Enable copying long text to Chrome and pasting long text from Chrome (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_owner.h" 5 #include "ui/base/x/selection_owner.h"
6 6
7 #include <algorithm>
7 #include <X11/Xlib.h> 8 #include <X11/Xlib.h>
8 #include <X11/Xatom.h> 9 #include <X11/Xatom.h>
9 10
10 #include "base/logging.h" 11 #include "base/logging.h"
11 #include "ui/base/x/selection_utils.h" 12 #include "ui/base/x/selection_utils.h"
13 #include "ui/base/x/x11_foreign_window_manager.h"
12 #include "ui/base/x/x11_util.h" 14 #include "ui/base/x/x11_util.h"
13 15
14 namespace ui { 16 namespace ui {
15 17
16 namespace { 18 namespace {
17 19
18 const char kAtomPair[] = "ATOM_PAIR"; 20 const char kAtomPair[] = "ATOM_PAIR";
21 const char kIncr[] = "INCR";
19 const char kMultiple[] = "MULTIPLE"; 22 const char kMultiple[] = "MULTIPLE";
20 const char kSaveTargets[] = "SAVE_TARGETS"; 23 const char kSaveTargets[] = "SAVE_TARGETS";
21 const char kTargets[] = "TARGETS"; 24 const char kTargets[] = "TARGETS";
22 25
23 const char* kAtomsToCache[] = { 26 const char* kAtomsToCache[] = {
24 kAtomPair, 27 kAtomPair,
28 kIncr,
25 kMultiple, 29 kMultiple,
26 kSaveTargets, 30 kSaveTargets,
27 kTargets, 31 kTargets,
28 NULL 32 NULL
29 }; 33 };
30 34
35 // The period of |incr_timer_|. Arbitrary but must be <= than
36 // kIncrTransferTimeoutMs.
37 const int kIncrTimerPeriodMs = 1000;
Daniel Erat 2014/07/25 19:38:44 s/Incr/Incremental/ here and in other constants
38
39 // The amount of time to wait for the selection requestor to process the data
40 // sent by the selection owner before aborting an incremental data transfer.
41 const int kIncrTransferTimeoutMs = 10000;
42
43 // Returns a conservative max size of the data we can pass into
44 // XChangeProperty(). Copied from GTK.
45 size_t GetMaxRequestSize(XDisplay* display) {
46 long extended_max_size = XExtendedMaxRequestSize(display);
47 return std::min(
48 static_cast<long>(0x40000),
49 (extended_max_size ? extended_max_size : XMaxRequestSize(display)) - 100);
Daniel Erat 2014/07/25 19:38:43 nit: check for negative wraparound
pkotwicz 2014/07/26 20:09:53 Done.
50 }
51
31 // Gets the value of an atom pair array property. On success, true is returned 52 // Gets the value of an atom pair array property. On success, true is returned
32 // and the value is stored in |value|. 53 // and the value is stored in |value|.
33 bool GetAtomPairArrayProperty(XID window, 54 bool GetAtomPairArrayProperty(XID window,
34 XAtom property, 55 XAtom property,
35 std::vector<std::pair<XAtom,XAtom> >* value) { 56 std::vector<std::pair<XAtom,XAtom> >* value) {
36 XAtom type = None; 57 XAtom type = None;
37 int format = 0; // size in bits of each item in 'property' 58 int format = 0; // size in bits of each item in 'property'
38 unsigned long num_items = 0; 59 unsigned long num_items = 0;
39 unsigned char* properties = NULL; 60 unsigned char* properties = NULL;
40 unsigned long remaining_bytes = 0; 61 unsigned long remaining_bytes = 0;
(...skipping 30 matching lines...) Expand all
71 } 92 }
72 93
73 } // namespace 94 } // namespace
74 95
75 SelectionOwner::SelectionOwner(XDisplay* x_display, 96 SelectionOwner::SelectionOwner(XDisplay* x_display,
76 XID x_window, 97 XID x_window,
77 XAtom selection_name) 98 XAtom selection_name)
78 : x_display_(x_display), 99 : x_display_(x_display),
79 x_window_(x_window), 100 x_window_(x_window),
80 selection_name_(selection_name), 101 selection_name_(selection_name),
102 max_request_size_(GetMaxRequestSize(x_display)),
81 atom_cache_(x_display_, kAtomsToCache) { 103 atom_cache_(x_display_, kAtomsToCache) {
82 } 104 }
83 105
84 SelectionOwner::~SelectionOwner() { 106 SelectionOwner::~SelectionOwner() {
85 // If we are the selection owner, we need to release the selection so we 107 // If we are the selection owner, we need to release the selection so we
86 // don't receive further events. However, we don't call ClearSelectionOwner() 108 // don't receive further events. However, we don't call ClearSelectionOwner()
87 // because we don't want to do this indiscriminately. 109 // because we don't want to do this indiscriminately.
88 if (XGetSelectionOwner(x_display_, selection_name_) == x_window_) 110 if (XGetSelectionOwner(x_display_, selection_name_) == x_window_)
89 XSetSelectionOwner(x_display_, selection_name_, None, CurrentTime); 111 XSetSelectionOwner(x_display_, selection_name_, None, CurrentTime);
90 } 112 }
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
166 XSendEvent(x_display_, requestor, False, 0, &reply); 188 XSendEvent(x_display_, requestor, False, 0, &reply);
167 } 189 }
168 190
169 void SelectionOwner::OnSelectionClear(const XEvent& event) { 191 void SelectionOwner::OnSelectionClear(const XEvent& event) {
170 DLOG(ERROR) << "SelectionClear"; 192 DLOG(ERROR) << "SelectionClear";
171 193
172 // TODO(erg): If we receive a SelectionClear event while we're handling data, 194 // TODO(erg): If we receive a SelectionClear event while we're handling data,
173 // we need to delay clearing. 195 // we need to delay clearing.
174 } 196 }
175 197
198 bool SelectionOwner::CanDispatchPropertyEvent(const XEvent& event) {
199 return event.xproperty.state == PropertyDelete &&
200 FindIncrTransferForEvent(event) != incrs_.end();
201 }
202
203 void SelectionOwner::OnPropertyEvent(const XEvent& event) {
204 std::vector<IncrTransfer>::iterator it = FindIncrTransferForEvent(event);
205 if (it == incrs_.end())
206 return;
207
208 ProcessIncr(&(*it));
209 if (!it->data.get())
210 CompleteIncrTransfer(it);
211 }
212
176 bool SelectionOwner::ProcessTarget(XAtom target, 213 bool SelectionOwner::ProcessTarget(XAtom target,
177 XID requestor, 214 XID requestor,
178 XAtom property) { 215 XAtom property) {
179 XAtom multiple_atom = atom_cache_.GetAtom(kMultiple); 216 XAtom multiple_atom = atom_cache_.GetAtom(kMultiple);
180 XAtom save_targets_atom = atom_cache_.GetAtom(kSaveTargets); 217 XAtom save_targets_atom = atom_cache_.GetAtom(kSaveTargets);
181 XAtom targets_atom = atom_cache_.GetAtom(kTargets); 218 XAtom targets_atom = atom_cache_.GetAtom(kTargets);
182 219
183 if (target == multiple_atom || target == save_targets_atom) 220 if (target == multiple_atom || target == save_targets_atom)
184 return false; 221 return false;
185 222
186 if (target == targets_atom) { 223 if (target == targets_atom) {
187 // We have been asked for TARGETS. Send an atom array back with the data 224 // We have been asked for TARGETS. Send an atom array back with the data
188 // types we support. 225 // types we support.
189 std::vector<XAtom> targets; 226 std::vector<XAtom> targets;
190 targets.push_back(targets_atom); 227 targets.push_back(targets_atom);
191 targets.push_back(save_targets_atom); 228 targets.push_back(save_targets_atom);
192 targets.push_back(multiple_atom); 229 targets.push_back(multiple_atom);
193 RetrieveTargets(&targets); 230 RetrieveTargets(&targets);
194 231
195 XChangeProperty(x_display_, requestor, property, XA_ATOM, 32, 232 XChangeProperty(x_display_, requestor, property, XA_ATOM, 32,
196 PropModeReplace, 233 PropModeReplace,
197 reinterpret_cast<unsigned char*>(&targets.front()), 234 reinterpret_cast<unsigned char*>(&targets.front()),
198 targets.size()); 235 targets.size());
199 return true; 236 return true;
200 } else { 237 } else {
201 // Try to find the data type in map. 238 // Try to find the data type in map.
202 SelectionFormatMap::const_iterator it = format_map_.find(target); 239 SelectionFormatMap::const_iterator it = format_map_.find(target);
203 if (it != format_map_.end()) { 240 if (it != format_map_.end()) {
204 XChangeProperty(x_display_, requestor, property, target, 8, 241 if (it->second->size() > max_request_size_) {
205 PropModeReplace, 242 // We must send the data back in several chunks due to a limitation in
206 const_cast<unsigned char*>( 243 // the size of X requests. Notify the selection requestor that the data
207 reinterpret_cast<const unsigned char*>( 244 // will be sent incrementally by returning data of type "INCR".
208 it->second->front())), 245 int length = it->second->size();
209 it->second->size()); 246 XChangeProperty(x_display_,
247 requestor,
248 property,
249 atom_cache_.GetAtom(kIncr),
250 32,
251 PropModeReplace,
252 reinterpret_cast<unsigned char*>(&length),
253 1);
254
255 // Wait for the selection requestor to indicate that it has processed
256 // the selection result before sending the first chunk of data. The
257 // selection requestor indicates this by deleting |property|.
258 base::TimeTicks timeout =
259 base::TimeTicks::Now() +
260 base::TimeDelta::FromMilliseconds(kIncrTransferTimeoutMs);
261 int foreign_window_manager_id =
262 ui::XForeignWindowManager::GetInstance()->RequestEvents(
263 requestor, PropertyChangeMask);
264 incrs_.push_back(IncrTransfer(requestor,
265 target,
266 property,
267 it->second,
268 0,
269 timeout,
270 foreign_window_manager_id));
271
272 // Start a timer to abort the data transfer in case that the selection
273 // requestor does not support the INCR property or gets destroyed during
274 // the data transfer.
275 if (!incr_timer_.IsRunning()) {
276 incr_timer_.Start(
277 FROM_HERE,
278 base::TimeDelta::FromMilliseconds(kIncrTimerPeriodMs),
279 this,
280 &SelectionOwner::AbortStaleIncrTransfers);
281 }
282 } else {
283 XChangeProperty(
284 x_display_,
285 requestor,
286 property,
287 target,
288 8,
289 PropModeReplace,
290 const_cast<unsigned char*>(it->second->front()),
291 it->second->size());
292 }
210 return true; 293 return true;
211 } 294 }
212 // I would put error logging here, but GTK ignores TARGETS and spams us 295 // I would put error logging here, but GTK ignores TARGETS and spams us
213 // looking for its own internal types. 296 // looking for its own internal types.
214 } 297 }
215 return false; 298 return false;
216 } 299 }
217 300
301 void SelectionOwner::ProcessIncr(IncrTransfer* transfer) {
302 size_t remaining = transfer->data->size() - transfer->offset;
303 size_t chunk_length = std::min(remaining, max_request_size_);
304 XChangeProperty(
305 x_display_,
306 transfer->window,
307 transfer->property,
308 transfer->target,
309 8,
310 PropModeReplace,
311 const_cast<unsigned char*>(transfer->data->front() + transfer->offset),
312 chunk_length);
313 transfer->offset += chunk_length;
314 transfer->timeout = base::TimeTicks::Now() +
315 base::TimeDelta::FromMilliseconds(kIncrTransferTimeoutMs);
316
317 // When offset == data->size(), we still need to transfer a zero-sized chunk
318 // to notify the selection requestor that the transfer is complete. Clear
319 // transfer->data once the zero-sized chunk is sent to indicate that state
320 // related to this data transfer can be cleared.
321 if (chunk_length == 0)
322 transfer->data = NULL;
323 }
324
325 void SelectionOwner::AbortStaleIncrTransfers() {
326 base::TimeTicks now = base::TimeTicks::Now();
327 for (int i = static_cast<int>(incrs_.size()) - 1; i >= 0; --i) {
328 if (incrs_[i].timeout < now)
329 CompleteIncrTransfer(incrs_.begin() + i);
330 }
331 }
332
333 void SelectionOwner::CompleteIncrTransfer(
334 std::vector<IncrTransfer>::iterator it) {
335 ui::XForeignWindowManager::GetInstance()->CancelRequest(
336 it->foreign_window_manager_id);
337 incrs_.erase(it);
338
339 if (incrs_.empty())
340 incr_timer_.Stop();
341 }
342
343 std::vector<SelectionOwner::IncrTransfer>::iterator
344 SelectionOwner::FindIncrTransferForEvent(const XEvent& event) {
345 for (std::vector<IncrTransfer>::iterator it = incrs_.begin();
346 it != incrs_.end(); ++it) {
347 if (it->window == event.xproperty.window &&
348 it->property == event.xproperty.atom) {
349 return it;
350 }
351 }
352 return incrs_.end();
353 }
354
355 SelectionOwner::IncrTransfer::IncrTransfer(
356 XID window,
357 XAtom target,
358 XAtom property,
359 const scoped_refptr<base::RefCountedMemory>& data,
360 int offset,
361 base::TimeTicks timeout,
362 int foreign_window_manager_id)
363 : window(window),
364 target(target),
365 property(property),
366 data(data),
367 offset(offset),
368 timeout(timeout),
369 foreign_window_manager_id(foreign_window_manager_id) {
370 }
371
372 SelectionOwner::IncrTransfer::~IncrTransfer() {
373 }
374
218 } // namespace ui 375 } // namespace ui
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698