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

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

Powered by Google App Engine
This is Rietveld 408576698